diff --git a/archive/HexRaysSA/ida-claude-code-plugins.txt b/archive/HexRaysSA/ida-claude-code-plugins.txt new file mode 100644 index 00000000..229a1aa3 --- /dev/null +++ b/archive/HexRaysSA/ida-claude-code-plugins.txt @@ -0,0 +1,6095 @@ +Project Path: arc_HexRaysSA_ida-claude-code-plugins_4qjvfecl + +Source Tree: + +```txt +arc_HexRaysSA_ida-claude-code-plugins_4qjvfecl +├── LICENSE +├── README.md +└── plugins + ├── code-eval-ida-domain + │ ├── LICENSE + │ ├── README.md + │ ├── agents + │ │ └── ida-domain-expert.md + │ ├── commands + │ │ └── ida-domain-skill + │ │ └── bootstrap.md + │ └── skills + │ └── ida-domain-scripting + │ ├── API_REFERENCE.md + │ ├── SKILL.md + │ ├── pyproject.toml + │ ├── run.py + │ └── setup.py + └── ida-plugin-development + ├── LICENSE + ├── README.md + └── skills + ├── ida-domain-api + │ └── SKILL.md + ├── ida-plugin-development + │ ├── SKILL.md + │ └── scripts + │ └── hcli-package.py + └── package-ida-plugin + └── SKILL.md + +``` + +`LICENSE`: + +``` +MIT License + +Copyright (c) 2026 Hex-Rays SA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +`README.md`: + +```md +# Claude Code Resources for IDA Pro + +Claude Code plugins and skills for IDA Pro reverse engineering workflows. These plugins enable Claude to autonomously create IDA Pro plugins and execute binary analysis scripts. + +## [ida-plugin-development](plugins/ida-plugin-development/README.md) + +Develop plugins for IDA Pro in Python, using idiomatic patterns, lessons, and tricks. + + +## (unsafe) [reverse-engineering-with-code-eval-and-ida-domain](plugins/code-eval-ida-domain/README.md) + +> warning: this plugin generates and evaluates code on the current system, so it should only be used in a sandboxed environment. + +(Unsafe) Skills, knowledge, and scripts for reverse engineering with IDA Pro. Autonomously writes and executes IDA Domain scripts to analyze binaries, extract functions, decompile code, and automate any reverse engineering task. + +## Installation + +```bash +/plugin marketplace add HexRaysSA/ida-claude-plugins +/plugin install ida-plugin-development@ida-claude-plugins +# unsafe: /plugin install reverse-engineering-with-code-eval-and-ida-domain@ida-claude-plugins +``` + +## Requirements + +- IDA Pro 9.x +- `uv` package manager + +## License + +MIT License - Copyright 2026 Hex-Rays SA + +``` + +`plugins/code-eval-ida-domain/LICENSE`: + +``` +MIT License + +Copyright (c) 2026 Hex-Rays SA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +`plugins/code-eval-ida-domain/README.md`: + +```md +# (unsafe) Reverse Engineering with Code Evaluation and the IDA Domain API + +> warning: this plugin generates and evaluates code on the current system, so it should only be used in a sandboxed environment. + +Analyze binaries using IDA Pro's Python Domain API (ida-domain) in headless mode (idalib). Use when examining program structure, functions, disassembly, cross-references, or strings without the GUI. + +Provides: +- agent `ida-domain-expert`: Senior IDA Domain Python developer and IDA Pro reverse engineer. Use proactively when writing IDA Domain scripts, debugging IDA API issues, analyzing binary analysis problems, or when the user needs expert guidance on reverse engineering tasks with IDA Pro. +- skill `ida-domain-scripting`: Write and execute Python scripts using the IDA Domain API for reverse engineering. Analyze binaries, extract functions, strings, cross-references, decompile code, work with IDA Pro databases (.i64/.idb). Use when user wants to analyze binaries, reverse engineer executables, or automate IDA Pro tasks. + +## Installation + +```bash +/plugin marketplace add HexRaysSA/ida-claude-plugins +/plugin install reverse-engineering-with-code-eval-and-ida-domain@ida-claude-plugins +``` + +## License + +MIT License - see [LICENSE](LICENSE) file for details. + +``` + +`plugins/code-eval-ida-domain/agents/ida-domain-expert.md`: + +```md +--- +name: ida-domain-expert +description: Senior IDA Domain Python developer and IDA Pro reverse engineer. Use proactively when writing IDA Domain scripts, debugging IDA API issues, analyzing binary analysis problems, or when the user needs expert guidance on reverse engineering tasks with IDA Pro. +tools: Read, Grep, Glob, Bash +model: inherit +--- + +You are a Senior Python Developer and Expert Reverse Engineer with 15+ years of experience in IDA Pro scripting and +binary analysis. You specialize in the IDA Domain API and have deep knowledge of IDAPython internals. + +## Your Expertise + +- **IDA Domain API**: Complete mastery of the ida-domain library, its patterns, and best practices +- **Reverse Engineering**: Malware analysis, vulnerability research, firmware analysis, code deobfuscation +- **Binary Formats**: PE, ELF, Mach-O, firmware images, raw binaries +- **Architectures**: x86, x64, ARM, ARM64, MIPS, and processor-specific quirks +- **Python Best Practices**: Clean, efficient, well-documented code with proper error handling + +**IMPORTANT - Path Resolution:** +You are to use the ida-domain-scripting skill. It can be installed in different locations. Before executing any +commands, determine the skill directory based on where you loaded this SKILL.md file, and use that path in all commands +below. Replace `$SKILL_DIR` with the actual +discovered path. + +Common installation paths: + +- Project-specific: `/.claude/skills/ida-domain-scripting` +- Manual global: `~/.claude/skills/ida-domain-scripting` + +## Critical Context + +Before writing any IDA Domain code, you MUST read the API reference: + +- **API Reference**: `skills/ida-domain-scripting/API_REFERENCE.md` + +This file contains the complete, authoritative API documentation. Always verify method signatures and patterns against +this reference. + +## Your Approach + +1. **Understand First**: Ask clarifying questions about the binary type, analysis goals, and expected output format + before writing code +2. **Read the API**: Always consult API_REFERENCE.md before writing code to ensure correct method signatures +3. **Write Clean Code**: Produce production-quality Python with proper error handling, type hints where helpful, and + clear comments +4. **Explain Your Reasoning**: Share your reverse engineering thought process and why you chose specific approaches +5. **Validate Assumptions**: Check if functions/addresses exist before operating on them +6. **Handle Edge Cases**: Anticipate decompilation failures, missing symbols, and malformed data + +## Common Patterns You Know Well + +### Database Access + +```python +# The db object is always available in wrapped scripts +db.analysis.wait() # Always wait for analysis before querying +``` + +### Function Iteration + +```python +for func in db.functions: + name = db.functions.get_name(func) # Call on db.functions, not func + callers = db.functions.get_callers(func) +``` + +### Cross-References + +```python +for xref in db.xrefs.to_ea(addr): # NOT get_xrefs_to() + print(f"From 0x{xref.from_ea:x}") +``` + +### Safe Decompilation + +```python +try: + lines = db.functions.get_pseudocode(func) + print("\n".join(lines)) +except RuntimeError as e: + print(f"Decompilation failed: {e}") +``` + +### Safe String Handling + +```python +for s in db.strings: + try: + content = str(s) + except (UnicodeDecodeError, Exception): + continue # Skip problematic strings +``` + +## Anti-Patterns You Avoid + +- **Never** call methods directly on func objects: `func.get_callers()` is WRONG +- **Never** use `db.xrefs.get_xrefs_to()` - use `db.xrefs.to_ea()` instead +- **Never** assume decompilation will succeed - always wrap in try/except +- **Never** modify the database without explicit user confirmation +- **Never** hardcode addresses without validation + +## Script Execution + +Scripts are executed via: + +```bash +cd $SKILL_DIR && uv run python run.py -f +``` + +Where: + +- `$SKILL_DIR` is `skills/ida-domain-scripting` +- Scripts are written to `/tmp/ida-domain-TIMESTAMP-/script.py` +- The `db` variable is automatically available (no Database.open() needed) + +## When Asked to Help + +1. Read API_REFERENCE.md to verify the exact API signatures +2. Write clean, well-structured Python code +3. Include appropriate error handling +4. Explain what the code does and why +5. Suggest optimizations or alternative approaches when relevant +6. Warn about potential pitfalls (large binaries, slow operations, etc.) + +## Your Communication Style + +- Direct and technical, but approachable +- Share insights from your "experience" in reverse engineering +- Proactively identify potential issues before they become problems +- Offer multiple solutions when trade-offs exist +- Always prioritize correctness over cleverness + + +``` + +`plugins/code-eval-ida-domain/commands/ida-domain-skill/bootstrap.md`: + +```md +--- +description: Generate API_REFERENCE.md from ida-domain source code +allowed-tools: Bash(find:*), Bash(grep:*), Bash(wc:*), Read, Write, Glob, Grep +--- + +# Bootstrap: Generate API_REFERENCE.md + +You are generating a **hand-written style** API quick reference for the IDA Domain skill. + +**IMPORTANT - Path Resolution:** +This skill can be installed in different locations. Before executing any commands, determine the skill directory based on where you loaded this command file, and use that path in all commands below. Replace `$SKILL_DIR` with the actual discovered path. + +Common installation paths: +- Project-specific: `/.claude/skills/ida-domain-scripting` +- Manual global: `~/.claude/skills/ida-domain-scripting` + +## Context + +The ida-domain source code is checked out at: `$SKILL_DIR/ida-domain/` + +## Your Task + +1. **Explore the ida-domain source code** in `$SKILL_DIR/ida-domain/ida_domain/` +2. **Identify the main classes and their key methods** by reading the Python files +3. **Launch one sub agent per python file** to avoid filling up the context and summarize +3. **Generate `API_REFERENCE.md`** in the skill root directory + +## Output Format (API_REFERENCE.md) + +Follow this structure - similar to playwright's API_REFERENCE.md (practical, pattern-focused, ~500-800 lines max): + +```markdown +# IDA Domain Quick Reference + +Quick reference for the IDA Domain API. For basic usage, see [SKILL.md](SKILL.md). + +## Table of Contents +[Generate dynamically based on discovered entities] + +## Database + +### Database Properties +[table of properties discovered from database.py: db.module, db.path, db.architecture, etc.] + +## [Entity Name] + +### Iterating +[code example if the entity is iterable] + +### Key Methods +[code examples for the most important methods] + +### Properties +[table of properties if applicable] + +... repeat for each entity discovered from database.py + +## Enums + +### [EnumName] +[table of enum values with descriptions] + + +```python +# Usage example +from ida_domain.xrefs import XrefType +if xref.type == XrefType.CALL_NEAR: + print("This is a call") +``` + +... repeat for each enum discovered except hooks.py +``` + +## Guidelines + +1. **Be practical** - Show how to DO things, not just what exists +2. **Use code examples** - Every section should have runnable code +3. **Document the gotchas** - Like `db.xrefs.to_ea()` not `get_xrefs_to()` +4. **Keep it concise** - 500-800 lines max, not 18,000 lines +5. **Focus on wrapped scripts** - Assume `db` is available (auto-wrapped mode) +6. **Include return types** - What does each method return? +7. **Group by use-case** - Not alphabetically by class + +## Process + +1. **Discover the API structure:** + - Use Glob to find all .py files in `$SKILL_DIR/ida-domain/ida_domain/` + - Read `database.py` first to find all `@property` methods that return entity handlers (e.g., `def functions(self) -> Functions`) + - These properties define what entities are available via `db.*` + +2. **For each entity handler discovered:** + - Read the corresponding .py file + - Extract class name, public methods, and their signatures + - Note the docstrings for method descriptions + - Identify common usage patterns + +3. **Extract all Enums:** + - Search all .py files for classes that inherit from `Enum` (e.g., `class XrefType(Enum)`) + - Document each enum with its values + - Show how to use them in code examples + +4. **Generate API_REFERENCE.md:** + - Start with Database properties and metadata + - Add a section for each entity handler found + - Include an "Enums" section listing all discovered enums and their values + - Include practical code examples + - Document any non-obvious method names (e.g., `to_ea()` vs `get_xrefs_to()`) + +Start by exploring the source code structure - read `database.py` to discover what entities exist. + +``` + +`plugins/code-eval-ida-domain/skills/ida-domain-scripting/API_REFERENCE.md`: + +```md +# IDA Domain Quick Reference + +Quick reference for the IDA Domain API. For basic usage, see [SKILL.md](SKILL.md). + +## Table of Contents + +- [Database](#database) +- [Functions](#functions) +- [Instructions](#instructions) +- [Segments](#segments) +- [Strings](#strings) +- [Xrefs](#xrefs) +- [Names](#names) +- [Types](#types) +- [Bytes](#bytes) +- [Comments](#comments) +- [Entries](#entries) +- [Heads](#heads) +- [Flowchart](#flowchart) +- [Signature Files](#signature-files) +- [Enums Reference](#enums-reference) + +--- + +## Database + +The `Database` class is your entry point. Use `db` in auto-wrapped scripts. + +### Opening a Database + +```python +# Library mode: Open and automatically close +with Database.open("path/to/file.exe", save_on_close=True) as db: + print(f"Loaded: {db.module}") + +# IDA GUI mode: Get handle to current database +db = Database.open() +``` + +### Database Properties + +| Property | Type | Description | +|----------|------|-------------| +| `db.path` | `str` | Input file path | +| `db.module` | `str` | Module name | +| `db.base_address` | `ea_t` | Image base address | +| `db.minimum_ea` | `ea_t` | Minimum effective address | +| `db.maximum_ea` | `ea_t` | Maximum effective address | +| `db.filesize` | `int` | Input file size | +| `db.md5` | `str` | MD5 hash of input file | +| `db.sha256` | `str` | SHA256 hash of input file | +| `db.architecture` | `str` | Processor architecture | +| `db.bitness` | `int` | Application bitness (32/64) | +| `db.format` | `str` | File format type | +| `db.execution_mode` | `ExecutionMode` | User or Kernel mode | +| `db.current_ea` | `ea_t` | Current screen EA (readable/writable) | +| `db.start_ip` | `ea_t` | Start instruction pointer | + +### Database Methods + +```python +db.is_valid_ea(ea, strict_check=True) # Check if address is valid +db.is_open() # Check if database is loaded +db.execute_script(file_path) # Execute a Python script +db.close(save=True) # Close database (library mode only) +``` + +### Entity Handlers + +Access all IDA data through these handlers: + +```python +db.functions # Function operations +db.instructions # Instruction operations +db.segments # Segment operations +db.strings # String operations +db.xrefs # Cross-reference operations +db.names # Name/symbol operations +db.types # Type information operations +db.bytes # Raw byte operations +db.comments # Comment operations +db.entries # Entry point operations +db.heads # Head (item) operations +db.signature_files # FLIRT signature operations +``` + +--- + +## Functions + +### Iterating Functions + +```python +# Iterate all functions +for func in db.functions: + print(db.functions.get_name(func)) + +# Get function count +count = len(db.functions) +``` + +### Finding Functions + +```python +func = db.functions.get_at(0x401000) # By address +func = db.functions.get_function_by_name("main") # By name +func = db.functions.get_next(ea) # Next function after ea + +# Functions in range +for func in db.functions.get_between(start_ea, end_ea): + print(func.start_ea) +``` + +### Function Properties + +```python +name = db.functions.get_name(func) +signature = db.functions.get_signature(func) +flags = db.functions.get_flags(func) # Returns FunctionFlags + +# Check function attributes +db.functions.is_far(func) +db.functions.does_return(func) +``` + +### Function Code + +```python +# Get disassembly lines +lines = db.functions.get_disassembly(func, remove_tags=True) + +# Get decompiled pseudocode +pseudocode = db.functions.get_pseudocode(func, remove_tags=True) + +# Get microcode +microcode = db.functions.get_microcode(func, remove_tags=True) +``` + +### Function Analysis + +```python +# Get instructions in function +for insn in db.functions.get_instructions(func): + print(insn.ea) + +# Get flowchart for basic blocks +flowchart = db.functions.get_flowchart(func) +for block in flowchart: + print(f"Block: {block.start_ea:#x} - {block.end_ea:#x}") + +# Get callers/callees +callers = db.functions.get_callers(func) +callees = db.functions.get_callees(func) + +# Get function chunks +for chunk in db.functions.get_chunks(func): + print(f"Chunk: {chunk.start_ea:#x}, main={chunk.is_main}") + +# Get data items within function +for data_ea in db.functions.get_data_items(func): + print(f"Data at {data_ea:#x}") +``` + +### Local Variables + +```python +# Get all local variables +lvars = db.functions.get_local_variables(func) +for lvar in lvars: + print(f"{lvar.name}: {lvar.type_str}, arg={lvar.is_argument}") + +# Find variable by name +lvar = db.functions.get_local_variable_by_name(func, "result") + +# Get variable references in pseudocode +refs = db.functions.get_local_variable_references(func, lvar) +for ref in refs: + print(f"Line {ref.line_number}: {ref.access_type} in {ref.context}") +``` + +### Modifying Functions + +```python +db.functions.set_name(func, "new_name") +db.functions.set_comment(func, "This function does X", repeatable=False) +db.functions.create(ea) # Create function at address +db.functions.remove(ea) # Remove function at address +``` + +--- + +## Instructions + +### Iterating Instructions + +```python +# All instructions in database +for insn in db.instructions: + print(db.instructions.get_disassembly(insn)) + +# Instructions in range +for insn in db.instructions.get_between(start_ea, end_ea): + print(insn.ea) +``` + +### Getting Instructions + +```python +insn = db.instructions.get_at(ea) # Decode at address +insn = db.instructions.get_previous(ea) # Previous instruction +``` + +### Instruction Properties + +```python +disasm = db.instructions.get_disassembly(insn) +mnemonic = db.instructions.get_mnemonic(insn) # "mov", "push", etc. +db.instructions.is_valid(insn) +``` + +### Control Flow Analysis + +```python +db.instructions.is_call_instruction(insn) # Is this a call? +db.instructions.is_indirect_jump_or_call(insn) # Indirect jump/call? +db.instructions.breaks_sequential_flow(insn) # Stops flow (ret, jmp)? +``` + +### Working with Operands + +```python +count = db.instructions.get_operands_count(insn) +operands = db.instructions.get_operands(insn) # List of Operand objects + +for op in operands: + info = op.get_info() + print(f"Operand {op.number}: {op.type.name}") + + if isinstance(op, RegisterOperand): + print(f" Register: {op.get_register_name()}") + elif isinstance(op, ImmediateOperand): + print(f" Value: 0x{op.get_value():x}") + elif isinstance(op, MemoryOperand): + if op.is_direct_memory(): + print(f" Memory: 0x{op.get_address():x}") +``` + +--- + +## Segments + +### Iterating Segments + +```python +for segment in db.segments: + name = db.segments.get_name(segment) + size = db.segments.get_size(segment) + print(f"{name}: {segment.start_ea:#x} - {segment.end_ea:#x}") + +count = len(db.segments) +``` + +### Finding Segments + +```python +seg = db.segments.get_at(0x401000) # Segment containing address +seg = db.segments.get_by_name(".text") # By name +``` + +### Segment Properties + +```python +name = db.segments.get_name(segment) +size = db.segments.get_size(segment) +bitness = db.segments.get_bitness(segment) # 16, 32, or 64 +seg_class = db.segments.get_class(segment) # "CODE", "DATA", etc. +``` + +### Creating Segments + +```python +from ida_domain.segments import PredefinedClass, AddSegmentFlags + +# Add segment with explicit range +seg = db.segments.add( + seg_para=0, + start_ea=0x1000, + end_ea=0x2000, + seg_name="MySegment", + seg_class=PredefinedClass.CODE +) + +# Append segment after last one +seg = db.segments.append(seg_para=0, seg_size=0x1000, seg_name="NewSeg") +``` + +### Modifying Segments + +```python +from ida_domain.segments import SegmentPermissions, AddressingMode + +db.segments.set_name(segment, "new_name") +db.segments.set_permissions(segment, SegmentPermissions.READ | SegmentPermissions.EXEC) +db.segments.add_permissions(segment, SegmentPermissions.WRITE) +db.segments.remove_permissions(segment, SegmentPermissions.WRITE) +db.segments.set_addressing_mode(segment, AddressingMode.BIT64) +db.segments.set_comment(segment, "Code section", repeatable=False) +``` + +--- + +## Strings + +### Iterating Strings + +```python +for string in db.strings: + print(f"{string.address:#x}: {string}") + +# By index +first_string = db.strings[0] +count = len(db.strings) +``` + +### Finding Strings + +```python +string = db.strings.get_at(0x402000) # String at address + +# Strings in range +for s in db.strings.get_between(start_ea, end_ea): + print(s.contents) +``` + +### String Properties + +```python +print(string.address) # Address +print(string.length) # Length in characters +print(string.type) # StringType enum +print(string.encoding) # Internal encoding +print(string.contents) # UTF-8 bytes +print(str(string)) # Decoded string +``` + +### Rebuilding String List + +```python +from ida_domain.strings import StringListConfig, StringType + +config = StringListConfig( + string_types=[StringType.C, StringType.C_16], + min_len=3, + only_ascii_7bit=False +) +db.strings.rebuild(config) +db.strings.clear() # Clear string list +``` + +--- + +## Xrefs + +### Getting References TO an Address + +```python +# All xrefs to an address +for xref in db.xrefs.to_ea(target_ea): + print(f"{xref.from_ea:#x} -> {xref.to_ea:#x} ({xref.type.name})") + +# Just code references +for ea in db.xrefs.code_refs_to_ea(target_ea, flow=False): + print(f"Code ref from {ea:#x}") + +# Just data references +for ea in db.xrefs.data_refs_to_ea(target_ea): + print(f"Data ref from {ea:#x}") + +# Call references only +for ea in db.xrefs.calls_to_ea(func_ea): + print(f"Called from {ea:#x}") + +# Detailed caller information +for caller in db.xrefs.get_callers(func_ea): + print(f"Called from {caller.name} at {caller.ea:#x}") +``` + +### Getting References FROM an Address + +```python +# All xrefs from an address +for xref in db.xrefs.from_ea(source_ea): + print(f"{xref.from_ea:#x} -> {xref.to_ea:#x}") + +# Code/data refs from +for ea in db.xrefs.code_refs_from_ea(source_ea): + print(f"Code ref to {ea:#x}") + +for ea in db.xrefs.calls_from_ea(source_ea): + print(f"Calls {ea:#x}") +``` + +### Data Access Analysis + +```python +# Who reads this data? +for ea in db.xrefs.reads_of_ea(data_ea): + print(f"Read by {ea:#x}") + +# Who writes to this data? +for ea in db.xrefs.writes_to_ea(data_ea): + print(f"Written by {ea:#x}") +``` + +### XrefInfo Properties + +```python +xref.is_call # Is this a call reference? +xref.is_jump # Is this a jump reference? +xref.is_read # Is this a data read? +xref.is_write # Is this a data write? +xref.is_flow # Is this ordinary flow? +xref.user # Is this user-defined? +``` + +--- + +## Names + +### Iterating Names + +```python +for ea, name in db.names: + print(f"{ea:#x}: {name}") + +count = len(db.names) +``` + +### Getting Names + +```python +name = db.names.get_at(0x401000) +ea, name = db.names[0] # By index +``` + +### Setting Names + +```python +from ida_domain.names import SetNameFlags + +db.names.set_name(ea, "my_function") +db.names.set_name(ea, "my_func", flags=SetNameFlags.CHECK) # Validate chars +db.names.force_name(ea, "func") # Creates func_2 if func exists +db.names.delete(ea) # Remove name +``` + +### Name Properties + +```python +db.names.is_valid_name("my_name") # Check if valid +db.names.is_public_name(ea) # Is public? +db.names.is_weak_name(ea) # Is weak? + +db.names.make_name_public(ea) +db.names.make_name_non_public(ea) +db.names.make_name_weak(ea) +db.names.make_name_non_weak(ea) +``` + +### Demangling + +```python +from ida_domain.names import DemangleFlags + +demangled = db.names.get_demangled_name(ea) +demangled = db.names.get_demangled_name(ea, DemangleFlags.NORETTYPE) +demangled = db.names.demangle_name("?main@@YAXXZ") +``` + +--- + +## Types + +### Getting Types + +```python +# By name +tinfo = db.types.get_by_name("MyStruct") + +# At address +tinfo = db.types.get_at(ea) + +# Iterate all types +for tinfo in db.types: + print(tinfo) +``` + +### Parsing Types + +```python +# Parse declarations from string +errors = db.types.parse_declarations(None, "struct Point { int x; int y; };") + +# Parse single declaration +tinfo = db.types.parse_one_declaration(None, "int (*callback)(void*)", "callback_t") + +# Parse header file +errors = db.types.parse_header_file(library, Path("header.h")) +``` + +### Applying Types + +```python +from ida_domain.types import TypeApplyFlags + +db.types.apply_at(tinfo, ea, flags=TypeApplyFlags.DEFINITE) +``` + +### Type Details + +```python +details = db.types.get_details(tinfo) +print(details.name) +print(details.size) +print(details.attributes) + +# For structs/unions +if details.udt: + print(details.udt.num_members) + print(details.udt.attributes) + +# For functions +if details.func: + print(details.func.attributes) +``` + +### Type Libraries + +```python +# Load/create libraries +lib = db.types.load_library(Path("types.til")) +lib = db.types.create_library(Path("new.til"), "My Types") + +# Import/export types +db.types.import_type(source_lib, "MyStruct") +db.types.export_type(dest_lib, "MyStruct") + +# Save library +db.types.save_library(lib, Path("output.til")) +db.types.unload_library(lib) +``` + +--- + +## Bytes + +### Reading Values + +```python +byte = db.bytes.get_byte_at(ea) +word = db.bytes.get_word_at(ea) +dword = db.bytes.get_dword_at(ea) +qword = db.bytes.get_qword_at(ea) +float_val = db.bytes.get_float_at(ea) +double_val = db.bytes.get_double_at(ea) + +# Read multiple bytes +data = db.bytes.get_bytes_at(ea, size=16) +original = db.bytes.get_original_bytes_at(ea, size=16) + +# Read strings +string = db.bytes.get_string_at(ea) +cstring = db.bytes.get_cstring_at(ea, max_length=256) +``` + +### Writing Values + +```python +db.bytes.set_byte_at(ea, 0x90) +db.bytes.set_word_at(ea, 0x1234) +db.bytes.set_dword_at(ea, 0x12345678) +db.bytes.set_qword_at(ea, 0x123456789ABCDEF0) +db.bytes.set_bytes_at(ea, b"\x90\x90\x90") +``` + +### Patching (with History) + +```python +db.bytes.patch_byte_at(ea, 0x90) # Saves original +db.bytes.patch_bytes_at(ea, data) +db.bytes.revert_byte_at(ea) # Restore original + +# Get original values +orig = db.bytes.get_original_byte_at(ea) +``` + +### Searching + +```python +from ida_domain.bytes import SearchFlags + +# Find bytes +ea = db.bytes.find_bytes_between(b"\x55\x89\xe5", start_ea, end_ea) + +# Find all occurrences +addresses = db.bytes.find_binary_sequence(b"\x90\x90") + +# Find text +ea = db.bytes.find_text_between("error", flags=SearchFlags.DOWN) + +# Find immediate value +ea = db.bytes.find_immediate_between(0x1234) +``` + +### Creating Data Items + +```python +from ida_domain.strings import StringType + +db.bytes.create_byte_at(ea, count=4) +db.bytes.create_word_at(ea) +db.bytes.create_dword_at(ea, count=10) # Array of 10 dwords +db.bytes.create_qword_at(ea) +db.bytes.create_float_at(ea) +db.bytes.create_double_at(ea) +db.bytes.create_string_at(ea, string_type=StringType.C) +db.bytes.create_struct_at(ea, count=1, tid=struct_tid) +``` + +### Querying Properties + +```python +size = db.bytes.get_data_size_at(ea) +db.bytes.is_value_initialized_at(ea) +db.bytes.is_code_at(ea) +db.bytes.is_data_at(ea) +db.bytes.is_head_at(ea) +db.bytes.is_tail_at(ea) +db.bytes.is_unknown_at(ea) + +# Get disassembly at address +disasm = db.bytes.get_disassembly_at(ea) +``` + +### Navigation + +```python +next_head = db.bytes.get_next_head(ea) +prev_head = db.bytes.get_previous_head(ea) +next_addr = db.bytes.get_next_address(ea) +prev_addr = db.bytes.get_previous_address(ea) +``` + +--- + +## Comments + +### Regular Comments + +```python +from ida_domain.comments import CommentKind + +# Get comment +info = db.comments.get_at(ea, CommentKind.REGULAR) +if info: + print(info.comment) + +# Set comment +db.comments.set_at(ea, "This is important", CommentKind.REGULAR) +db.comments.set_at(ea, "Shows everywhere", CommentKind.REPEATABLE) + +# Delete comment +db.comments.delete_at(ea, CommentKind.ALL) +``` + +### Iterating Comments + +```python +for comment_info in db.comments: + print(f"{comment_info.ea:#x}: {comment_info.comment}") + +# All comment types +for info in db.comments.get_all(CommentKind.ALL): + print(f"{info.ea:#x} (repeatable={info.repeatable}): {info.comment}") +``` + +### Extra Comments (Anterior/Posterior) + +```python +from ida_domain.comments import ExtraCommentKind + +# Set extra comment +db.comments.set_extra_at(ea, index=0, comment="Before line", kind=ExtraCommentKind.ANTERIOR) +db.comments.set_extra_at(ea, index=0, comment="After line", kind=ExtraCommentKind.POSTERIOR) + +# Get extra comments +comment = db.comments.get_extra_at(ea, index=0, kind=ExtraCommentKind.ANTERIOR) +for comment in db.comments.get_all_extra_at(ea, ExtraCommentKind.ANTERIOR): + print(comment) + +# Delete +db.comments.delete_extra_at(ea, index=0, kind=ExtraCommentKind.ANTERIOR) +``` + +--- + +## Entries + +### Iterating Entry Points + +```python +for entry in db.entries: + print(f"{entry.ordinal}: {entry.name} at {entry.address:#x}") + +count = len(db.entries) +first = db.entries[0] +``` + +### Finding Entries + +```python +entry = db.entries.get_at(ea) # By address +entry = db.entries.get_by_ordinal(1) # By ordinal +entry = db.entries.get_by_name("main") # By name +entry = db.entries.get_at_index(0) # By index +``` + +### Entry Properties + +```python +print(entry.ordinal) +print(entry.address) +print(entry.name) +print(entry.forwarder_name) +entry.has_forwarder() +``` + +### Modifying Entries + +```python +db.entries.add(address=ea, name="new_entry", ordinal=10) +db.entries.rename(ordinal=10, new_name="renamed_entry") +db.entries.set_forwarder(ordinal=10, forwarder_name="other.dll!func") +db.entries.exists(ordinal=10) +``` + +### Utility Iterators + +```python +for ordinal in db.entries.get_ordinals(): + print(ordinal) + +for addr in db.entries.get_addresses(): + print(f"{addr:#x}") + +for name in db.entries.get_names(): + print(name) + +for fwd in db.entries.get_forwarders(): + print(f"{fwd.ordinal}: {fwd.name}") +``` + +--- + +## Heads + +### Iterating Heads + +```python +for ea in db.heads: + print(f"Head at {ea:#x}") + +# In range +for ea in db.heads.get_between(start_ea, end_ea): + print(ea) +``` + +### Navigation + +```python +next_ea = db.heads.get_next(ea) +prev_ea = db.heads.get_previous(ea) +``` + +### Head Properties + +```python +db.heads.is_head(ea) # Is start of item? +db.heads.is_tail(ea) # Is part of multi-byte item? +db.heads.is_code(ea) # Is instruction? +db.heads.is_data(ea) # Is data? +db.heads.is_unknown(ea) # Is unclassified? + +size = db.heads.size(ea) +start, end = db.heads.bounds(ea) +``` + +--- + +## Flowchart + +### Creating Flowcharts + +```python +from ida_domain.flowchart import FlowChart, FlowChartFlags + +# From function +flowchart = FlowChart(db, func=my_func) + +# From address range +flowchart = FlowChart(db, bounds=(start_ea, end_ea)) + +# With predecessor info +flowchart = FlowChart(db, func=my_func, flags=FlowChartFlags.PREDS) +``` + +### Iterating Basic Blocks + +```python +for block in flowchart: + print(f"Block {block.id}: {block.start_ea:#x} - {block.end_ea:#x}") + +# By index +block = flowchart[0] +count = len(flowchart) +``` + +### Block Navigation + +```python +for succ in block.get_successors(): + print(f"Successor: {succ.id}") + +for pred in block.get_predecessors(): + print(f"Predecessor: {pred.id}") + +succ_count = block.count_successors() +pred_count = block.count_predecessors() +``` + +### Block Instructions + +```python +for insn in block.get_instructions(): + print(f"{insn.ea:#x}") +``` + +--- + +## Signature Files + +### Applying Signatures + +```python +from pathlib import Path + +# Apply single file +results = db.signature_files.apply(Path("/path/to/file.sig")) + +# Apply all in directory +results = db.signature_files.apply(Path("/path/to/sigs/")) + +# Probe only (don't persist) +results = db.signature_files.apply(Path("file.sig"), probe_only=True) + +for result in results: + print(f"{result.path}: {result.matches} matches") + for func in result.functions: + print(f" {func.addr:#x}: {func.name}") +``` + +### Finding Signature Files + +```python +# Default IDA sig directories +sig_files = db.signature_files.get_files() + +# Include custom directories +sig_files = db.signature_files.get_files([Path("/custom/sigs")]) +``` + +### Creating Signatures + +```python +# Create .sig and .pat files from current database +files = db.signature_files.create() + +# Create only .pat file +files = db.signature_files.create(pat_only=True) +``` + +--- + +## Enums Reference + +### XrefType + +```python +from ida_domain.xrefs import XrefType + +XrefType.OFFSET # Offset reference +XrefType.WRITE # Write access +XrefType.READ # Read access +XrefType.CALL_FAR # Far call +XrefType.CALL_NEAR # Near call +XrefType.JUMP_FAR # Far jump +XrefType.JUMP_NEAR # Near jump +XrefType.ORDINARY_FLOW # Sequential flow + +xref_type.is_code_ref() # Check if code ref +xref_type.is_data_ref() # Check if data ref +``` + +### FunctionFlags + +```python +from ida_domain.functions import FunctionFlags + +FunctionFlags.NORET # Doesn't return +FunctionFlags.LIB # Library function +FunctionFlags.THUNK # Thunk function +FunctionFlags.HIDDEN # Hidden chunk +FunctionFlags.LUMINA # From Lumina +FunctionFlags.FAR # Far function +FunctionFlags.FRAME # Uses frame pointer +``` + +### LocalVariableAccessType + +```python +from ida_domain.functions import LocalVariableAccessType + +LocalVariableAccessType.READ # Variable is read +LocalVariableAccessType.WRITE # Variable is written +LocalVariableAccessType.ADDRESS # Address taken (&var) +``` + +### LocalVariableContext + +```python +from ida_domain.functions import LocalVariableContext + +LocalVariableContext.ASSIGNMENT # var = expr +LocalVariableContext.CONDITION # if (var) +LocalVariableContext.CALL_ARG # func(var) +LocalVariableContext.RETURN # return var +LocalVariableContext.ARITHMETIC # var + 1 +LocalVariableContext.COMPARISON # var == x +LocalVariableContext.ARRAY_INDEX # arr[var] +LocalVariableContext.POINTER_DEREF # *var +LocalVariableContext.CAST # (type)var +``` + +### OperandType + +```python +from ida_domain.operands import OperandType + +OperandType.REGISTER # Register +OperandType.MEMORY # Direct memory +OperandType.PHRASE # Register addressing +OperandType.DISPLACEMENT # Reg + displacement +OperandType.IMMEDIATE # Immediate value +OperandType.FAR_ADDRESS # Far address +OperandType.NEAR_ADDRESS # Near address +``` + +### StringType + +```python +from ida_domain.strings import StringType + +StringType.C # C-style null-terminated +StringType.C_16 # C-style 16-bit +StringType.C_32 # C-style 32-bit +StringType.PASCAL # Pascal-style +StringType.LEN2 # 2-byte length prefix +StringType.LEN4 # 4-byte length prefix +``` + +### SegmentPermissions + +```python +from ida_domain.segments import SegmentPermissions + +SegmentPermissions.READ +SegmentPermissions.WRITE +SegmentPermissions.EXEC +SegmentPermissions.ALL +``` + +### AddressingMode + +```python +from ida_domain.segments import AddressingMode + +AddressingMode.BIT16 # 16-bit segment +AddressingMode.BIT32 # 32-bit segment +AddressingMode.BIT64 # 64-bit segment +``` + +### PredefinedClass + +```python +from ida_domain.segments import PredefinedClass + +PredefinedClass.CODE +PredefinedClass.DATA +PredefinedClass.CONST +PredefinedClass.STACK +PredefinedClass.BSS +PredefinedClass.XTRN +``` + +### CommentKind + +```python +from ida_domain.comments import CommentKind + +CommentKind.REGULAR # Normal comment +CommentKind.REPEATABLE # Shows at all refs +CommentKind.ALL # Both types +``` + +### ExtraCommentKind + +```python +from ida_domain.comments import ExtraCommentKind + +ExtraCommentKind.ANTERIOR # Before the line +ExtraCommentKind.POSTERIOR # After the line +``` + +### TypeAttr + +```python +from ida_domain.types import TypeAttr + +TypeAttr.INT, TypeAttr.UINT +TypeAttr.FLOAT, TypeAttr.DOUBLE +TypeAttr.PTR, TypeAttr.ARRAY +TypeAttr.FUNC, TypeAttr.STRUCT +TypeAttr.UNION, TypeAttr.ENUM +TypeAttr.CONST, TypeAttr.VOLATILE +``` + +### FlowChartFlags + +```python +from ida_domain.flowchart import FlowChartFlags + +FlowChartFlags.NONE # Default +FlowChartFlags.NOEXT # Don't compute external blocks +FlowChartFlags.PREDS # Compute predecessors +``` + +--- + +## Common Patterns + +### Find All Calls to a Function + +```python +func = db.functions.get_function_by_name("malloc") +if func: + for caller in db.xrefs.get_callers(func.start_ea): + print(f"Called from {caller.name} at {caller.ea:#x}") +``` + +### Rename Functions Based on Strings + +```python +for func in db.functions: + for insn in db.functions.get_instructions(func): + for xref in db.xrefs.from_ea(insn.ea): + string = db.strings.get_at(xref.to_ea) + if string and "error" in str(string).lower(): + db.functions.set_name(func, f"func_with_error_{func.start_ea:x}") + break +``` + +### Analyze Function Complexity + +```python +func = db.functions.get_at(ea) +flowchart = db.functions.get_flowchart(func) +print(f"Basic blocks: {len(flowchart)}") + +total_edges = sum(block.count_successors() for block in flowchart) +print(f"Cyclomatic complexity: {total_edges - len(flowchart) + 2}") +``` + +### Export Function Pseudocode + +```python +for func in db.functions: + name = db.functions.get_name(func) + try: + pseudocode = db.functions.get_pseudocode(func) + print(f"// {name}") + for line in pseudocode: + print(line) + except RuntimeError: + print(f"// Could not decompile {name}") +``` + +### Find Cross-References to Strings + +```python +for string in db.strings: + refs = list(db.xrefs.to_ea(string.address)) + if refs: + print(f'"{string}" referenced from:') + for xref in refs: + print(f" {xref.from_ea:#x}") +``` + +``` + +`plugins/code-eval-ida-domain/skills/ida-domain-scripting/SKILL.md`: + +```md +--- +name: ida-domain-scripting +description: Write and execute Python scripts using the IDA Domain API for reverse engineering. Analyze binaries, extract functions, strings, cross-references, decompile code, work with IDA Pro databases (.i64/.idb). Use when user wants to analyze binaries, reverse engineer executables, or automate IDA Pro tasks. +--- + +**IMPORTANT - Path Resolution:** +This skill can be installed in different locations. Before executing any commands, determine the skill directory based +on where you loaded this SKILL.md file, and use that path in all commands below. Replace `$SKILL_DIR` with the actual +discovered path. + +Common installation paths: + +- Project-specific: `/.claude/skills/ida-domain-scripting` +- Manual global: `~/.claude/skills/ida-domain-scripting` + +# IDA Domain Scripting + +General-purpose binary analysis skill. I'll write custom IDAPython code for any reverse engineering task you request and +execute it via the universal executor. + +**CRITICAL WORKFLOW - Follow these steps in order:** + +1. **Create a work dir in /tmp with timestamp** - NEVER write scripts to skill directory; always create a workdir + `/tmp/ida-domain-YYYYMMDD_HHMMSS_ffffff-` with microseconds for uniqueness (e.g., + `/tmp/ida-domain-20260109_143052_847291-list-functions`). Generate timestamp with: `datetime.now(). + strftime + ('%Y%m%d_%H%M%S_%f')`. + This will always be referenced as + +2. **Check API_REFERENCE.md exists** - Always check that $SKILL_DIR/API_REFERENCE.md exists. Inform the user to run + the bootstrap if not. + +3. **Execute from skill directory** - Always run: + `cd $SKILL_DIR && uv run python run.py /script.py -f ` + +4. **Ask before saving** - Scripts that modify the database require explicit user confirmation before using `--save` + +## How It Works + +1. You describe what you want to analyze/extract +2. I write custom IDA Domain API code in `/script.py` (timestamped with microseconds for parallel execution) +3. I execute it via: `cd $SKILL_DIR && uv run python run.py /script.py -f ` +4. Results displayed in real-time +5. Script files auto-cleaned from /tmp by your OS + +## Setup (First Time) + +```bash +cd $SKILL_DIR && uv run python setup.py +``` + +This clones ida-domain from GitHub and installs dependencies. Only needed once. + +**Using a specific version:** + +```bash +uv run python setup.py --ref v0.1.0 # Specific release +uv run python setup.py --ref main # Bleeding edge +``` + +Requirements: + +- uv package manager +- git +- IDA Pro 9.1+ +- IDADIR environment variable pointing to IDA installation + +## Execution Pattern + +**Step 1: Write analysis script to ** + +```python +# /script.py +for func in db.functions: + name = db.functions.get_name(func) + print(f"{name}: 0x{func.start_ea:08X}") +``` + +**Step 2: Execute from skill directory** + +```bash +cd $SKILL_DIR && uv run python run.py /script.py -f /path/to/binary +``` + +**Step 3: Review results** + +Scripts are auto-wrapped with `Database.open()` boilerplate. The `db` variable is available for accessing all entities. + +## Common Patterns + +### List All Functions + +```python +# /script.py +for func in db.functions: + name = db.functions.get_name(func) + size = func.end_ea - func.start_ea + print(f"{name}: 0x{func.start_ea:08X} - 0x{func.end_ea:08X} ({size} bytes)") +``` + +### Find Function by Name + +```python +# /script.py +func = db.functions.get_function_by_name("main") +if func: + print(f"Found main at 0x{func.start_ea:08X}") + + # Get callers + callers = db.functions.get_callers(func) + print(f"Called by {len(callers)} functions:") + for caller in callers: + print(f" - {db.functions.get_name(caller)}") +else: + print("main not found") +``` + +### Search Strings + +```python +# /script.py +import re + +# Find all strings +for s in db.strings: + print(f"0x{s.address:08X}: {s}") + +# Find URLs +url_pattern = re.compile(r"https?://[\w./]+", re.IGNORECASE) +for s in db.strings: + try: + content = str(s) + if url_pattern.search(content): + print(f"URL found: {content}") + except: + pass +``` + +### Analyze Cross-References + +```python +# /script.py +# Get xrefs TO an address +target = 0x00401000 +print(f"References TO 0x{target:08X}:") +for xref in db.xrefs.to_ea(target): + print(f" From 0x{xref.from_ea:08X} (type: {xref.type.name})") + +# Get xrefs FROM an address +print(f"References FROM 0x{target:08X}:") +for xref in db.xrefs.from_ea(target): + print(f" To 0x{xref.to_ea:08X} (type: {xref.type.name})") +``` + +### Decompile Function + +```python +# /script.py +func = db.functions.get_function_by_name("main") +if func: + try: + lines = db.functions.get_pseudocode(func) + print("\n".join(lines)) + except RuntimeError as e: + print(f"Decompilation failed: {e}") +``` + +### Analyze Function Complexity + +```python +# /script.py +complex_funcs = [] +for func in db.functions: + flowchart = db.functions.get_flowchart(func) + if flowchart: + block_count = len(flowchart) + edge_count = sum(b.count_successors() for b in flowchart) + cyclomatic = edge_count - block_count + 2 + + if cyclomatic > 10: + name = db.functions.get_name(func) + complex_funcs.append((name, func.start_ea, cyclomatic)) + +complex_funcs.sort(key=lambda x: x[2], reverse=True) +print("Most complex functions:") +for name, addr, cc in complex_funcs[:10]: + print(f" {name}: complexity={cc} at 0x{addr:08X}") +``` + +### Search Byte Patterns + +```python +# /script.py +# Search for NOP sled +pattern = b"\x90\x90\x90\x90" +results = db.bytes.find_binary_sequence(pattern) +for addr in results: + print(f"Found NOP sled at 0x{addr:08X}") + +# Search for x64 function prologue +prologue = b"\x55\x48\x89\xE5" # push rbp; mov rbp, rsp +for addr in db.bytes.find_binary_sequence(prologue): + print(f"Prologue at 0x{addr:08X}") +``` + +### Export to JSON + +```python +# /script.py +import json +from pathlib import Path + +functions = [] +for func in db.functions: + name = db.functions.get_name(func) + functions.append({ + "name": name, + "start": f"0x{func.start_ea:08X}", + "end": f"0x{func.end_ea:08X}", + "size": func.end_ea - func.start_ea, + }) + +output = {"module": db.module, "functions": functions} +Path("/tmp/functions.json").write_text(json.dumps(output, indent=2)) +print(f"Exported {len(functions)} functions to /tmp/functions.json") +``` + +## Inline Execution (Simple Tasks) + +For quick one-off tasks, you can execute code inline without creating files: + +```bash +# Quick function count +cd $SKILL_DIR && uv run python run.py -c "print(f'Functions: {len(db.functions)}')" -f binary + +# Get binary info +cd $SKILL_DIR && uv run python run.py -c "print(f'{db.module}: {db.architecture} {db.bitness}-bit')" -f binary +``` + +**When to use inline vs files:** + +- **Inline**: Quick one-off tasks (count functions, get binary info, check if symbol exists) +- **Files**: Complex analysis, multi-step tasks, anything user might want to re-run + +## Advanced Usage + +For comprehensive IDA Domain API documentation, see [API_REFERENCE.md](API_REFERENCE.md): + +- Database properties and metadata +- Function enumeration and analysis +- String detection and searching +- Cross-reference queries +- Byte pattern matching +- Control flow analysis +- Decompilation (Hex-Rays) +- Type information +- Comments and names + +## Tips + +- **Default is read-only** - Use `--save` only when modifications should persist (and ask user first!) +- **Timeout** - Default 30 minutes; use `--timeout 0` for long-running analysis +- **No-wrap mode** - Use `--no-wrap` when your script already has `Database.open()` +- **Error handling** - Always use try-except for decompilation and string operations +- **Check for None** - Functions like `get_function_by_name()` return None if not found + +## Troubleshooting + +**When encountering errors:** +Check the ida-domain source code first by searching for the method signature in `$SKILL_DIR/ida-domain/ida_domain/`. The +API may differ from what's documented or expected. + +**Virtual environment not found:** + +```bash +cd $SKILL_DIR && uv run python setup.py +``` + +**IDA SDK fails to load / IDADIR error:** + +```bash +export IDADIR=/path/to/ida +``` + +**Script timeout:** + +```bash +cd $SKILL_DIR && uv run python run.py --timeout 3600 ... # 1 hour +cd $SKILL_DIR && uv run python run.py --timeout 0 ... # No timeout +``` + +**AttributeError: 'Xrefs' has no attribute 'get_xrefs_to':** +Use `db.xrefs.to_ea(addr)` not `db.xrefs.get_xrefs_to(addr)` + +**AttributeError on func_t object:** +Call methods on `db.functions`, not on the func object: + +```python +# Wrong: func.get_callers() +# Right: db.functions.get_callers(func) +``` + +**UnicodeDecodeError when reading strings:** + +```python +for s in db.strings: + try: + content = str(s) + except: + continue # Skip problematic strings +``` + +## Example Usage + +``` +User: "How many functions are in this binary?" + +Claude: I'll count the functions. Let me analyze the binary... +[Writes: /script.py] +[Runs: cd $SKILL_DIR && uv run python run.py /script.py -f binary] +[Output: Functions: 250] + +The binary contains 250 functions. +``` + +``` +User: "Find all functions that call malloc" + +Claude: I'll find all callers of malloc... +[Writes: /script.py] +[Runs: cd $SKILL_DIR && uv run python run.py /script.py -f binary] +[Output: malloc called by 15 functions: sub_401000, sub_402000, ...] + +Found 15 functions that call malloc: +- sub_401000 at 0x00401000 +- sub_402000 at 0x00402000 +... +``` + +``` +User: "Decompile the main function and save it" + +Claude: I'll decompile main and save the output... +[Writes: /script.py] +[Runs: cd $SKILL_DIR && uv run python run.py /script.py -f binary] +[Output: Saved to /tmp/main.c] + +Done! The decompiled code is saved to /tmp/main.c +``` + + +``` + +`plugins/code-eval-ida-domain/skills/ida-domain-scripting/pyproject.toml`: + +```toml +[project] +name = "ida-domain-scripting" +version = "1.0.0" +description = "IDA Domain scripting skill for Claude Code" +readme = "SKILL.md" +requires-python = ">=3.10" +license = "MIT" +authors = [ + { name = "Hex-Rays SA", email = "support@hex-rays.com" } +] +keywords = ["ida-pro", "reverse-engineering", "binary-analysis", "ida-domain"] + +dependencies = [] + +[dependency-groups] +dev = [] + +``` + +`plugins/code-eval-ida-domain/skills/ida-domain-scripting/run.py`: + +```py +#!/usr/bin/env python3 +""" +IDA Domain Universal Script Executor + +Executes Python scripts that use IDA Domain with automatic boilerplate wrapping. +This is the main entry point for Claude-generated analysis scripts. + +Input file caching: + The executor uses a persistent cache directory (/tmp/ida-domain-cache/) + to store input files and their IDA databases. The cache directory structure + is based on the full path of the input file (with path separators replaced + by underscores). + + Benefits: + - Prevents IDA from creating artifact files in the original directory + - Allows reuse of IDA databases from previous runs, avoiding reanalysis + - Subsequent runs on the same binary are significantly faster + + When --save is used with an IDA database (.i64/.idb), the modified database + is copied back to the original location. + +Usage: + # 1. Execute a script file + uv run python run.py /script.py -f /path/to/binary.exe + + # 2. Execute inline code + uv run python run.py -c "for f in db.functions: print(f.name)" -f binary.exe + +Command-line flags: + -f, --file Target binary or .i64 file (required) + -c, --code Inline code string + -s, --save Enable save_on_close=True (default: False) + --no-wrap Skip auto-wrapping (for complete scripts) + --timeout Execution timeout in seconds (default: 1800, 0 for no timeout) + +Exit codes: + 0 - Success + 1 - Error (setup, parsing, execution) +""" + +import argparse +import glob +import shutil +import subprocess +import sys +import tempfile +import time +from pathlib import Path + + +# ANSI color codes for terminal output (consistent with setup.py) +class Colors: + RED = "\033[91m" + GREEN = "\033[92m" + YELLOW = "\033[93m" + BLUE = "\033[94m" + BOLD = "\033[1m" + RESET = "\033[0m" + + +def print_error(message: str) -> None: + """Print an error message.""" + print(f"{Colors.RED}Error:{Colors.RESET} {message}", file=sys.stderr) + + +def print_warning(message: str) -> None: + """Print a warning message.""" + print(f"{Colors.YELLOW}Warning:{Colors.RESET} {message}", file=sys.stderr) + + +def print_info(message: str) -> None: + """Print an info message.""" + print(f"{Colors.BLUE}Info:{Colors.RESET} {message}", file=sys.stderr) + + +def get_skill_dir() -> Path: + """Get the directory containing this run script.""" + return Path(__file__).parent.resolve() + + +def check_venv_exists() -> bool: + """ + Check if the virtual environment exists. + + Returns: + True if .venv exists, False otherwise. + """ + venv_path = get_skill_dir() / ".venv" + return venv_path.exists() and venv_path.is_dir() + + +def prompt_setup() -> None: + """Print instructions to run setup if venv is missing.""" + skill_dir = get_skill_dir() + print_error("Virtual environment not found.") + print() + print("Please run setup first:") + print() + print(f" cd {skill_dir}") + print(" uv run python setup.py") + print() + + +def cleanup_old_temp_files() -> int: + """ + Remove temp files older than 1 hour matching ida-domain-*.py pattern in temp directory. + + Returns: + Number of files cleaned up. + """ + cleaned = 0 + cutoff_time = time.time() - 3600 # 1 hour ago + + # Find all matching temp files + temp_dir = tempfile.gettempdir() + pattern = str(Path(temp_dir) / "ida-domain-*.py") + for filepath in glob.glob(pattern): + try: + file_path = Path(filepath) + if file_path.is_file(): + mtime = file_path.stat().st_mtime + if mtime < cutoff_time: + file_path.unlink() + cleaned += 1 + except (OSError, PermissionError): + # Ignore files we can't access or delete + pass + + return cleaned + + +def cleanup_old_run_directories() -> int: + """ + Remove run directories older than 1 hour. + + This handles cleanup of orphaned directories from crashed runs. + + Returns: + Number of directories cleaned up. + """ + cleaned = 0 + cutoff_time = time.time() - 3600 # 1 hour ago + + temp_dir = Path(tempfile.gettempdir()) + for dirpath in temp_dir.glob("ida-domain-run-*"): + try: + if dirpath.is_dir(): + mtime = dirpath.stat().st_mtime + if mtime < cutoff_time: + shutil.rmtree(dirpath) + cleaned += 1 + except (OSError, PermissionError): + pass + + return cleaned + + +# IDA database file extensions (created when opening a binary) +IDA_ARTIFACT_EXTENSIONS = {".i64", ".idb", ".id0", ".id1", ".id2", ".nam", ".til"} + +# Cache directory for persistent storage of IDA databases +# This avoids reanalysis on subsequent runs of the same binary +CACHE_DIR = Path(tempfile.gettempdir()) / "ida-domain-cache" + + +def is_ida_database(path: Path) -> bool: + """ + Check if the file is an IDA database. + + Args: + path: Path to check. + + Returns: + True if the file is an IDA database (.i64 or .idb). + """ + return path.suffix.lower() in {".i64", ".idb"} + + +def sanitize_path_for_cache(path: Path) -> str: + """ + Convert a file path to a safe cache directory name. + + Replaces path separators with underscores. + + Args: + path: Absolute path to the file. + + Returns: + Sanitized string suitable for use as a directory name. + """ + path_str = str(path) + # Replace both forward and back slashes with underscores + sanitized = path_str.replace("/", "_").replace("\\", "_") + # Remove leading underscore if present + if sanitized.startswith("_"): + sanitized = sanitized[1:] + return sanitized + + +def get_or_create_cache_entry(target_path: Path) -> tuple[Path, Path, bool]: + """ + Get or create a cache entry for the target file. + + The cache uses a persistent directory structure based on the file path. + If the file already exists in the cache, it is reused (including any + IDA database files from previous runs). + + Cache structure: + /tmp/ida-domain-cache// + + Args: + target_path: Absolute path to the target binary or .i64 file. + + Returns: + Tuple of (cache_entry_dir, cached_file_path, already_existed). + already_existed is True if the file was already in the cache. + """ + # Ensure cache root exists + CACHE_DIR.mkdir(parents=True, exist_ok=True) + + # Create cache entry directory based on sanitized path + cache_name = sanitize_path_for_cache(target_path) + cache_entry_dir = CACHE_DIR / cache_name + cache_entry_dir.mkdir(parents=True, exist_ok=True) + + cached_file = cache_entry_dir / target_path.name + + # Check if file already exists in cache + if cached_file.exists(): + # Check if there's also a cached IDA database for binaries + if not is_ida_database(target_path): + cached_i64 = cache_entry_dir / (target_path.stem + ".i64") + if cached_i64.exists(): + print_info(f"Using cached database: {cached_i64}") + else: + print_info(f"Using cached file: {cached_file}") + else: + print_info(f"Using cached database: {cached_file}") + return cache_entry_dir, cached_file, True + + # Copy file to cache + shutil.copy2(target_path, cached_file) + print_info(f"Cached file to: {cached_file}") + + return cache_entry_dir, cached_file, False + + +def handle_save_if_requested( + cache_dir: Path, + original_path: Path, + cached_path: Path, + save_requested: bool, +) -> None: + """ + Handle --save flag after execution. + + If save was requested and the input was an IDA database, copy the + modified database back to the original location. + + Note: The cache directory is NOT cleaned up - this is intentional + to allow subsequent runs to reuse the cached IDA database. + + Args: + cache_dir: The cache entry directory. + original_path: Original path to the input file. + cached_path: Path to the cached file. + save_requested: Whether --save was requested. + """ + if not save_requested: + return + + try: + # If save was requested and input was an IDA database, copy it back + if is_ida_database(original_path): + if cached_path.exists(): + shutil.copy2(cached_path, original_path) + print_info(f"Saved modified database back to: {original_path}") + else: + # For binaries, the .i64 stays in the cache for reuse + cached_i64 = cache_dir / (original_path.stem + ".i64") + if cached_i64.exists(): + print_info(f"Database saved in cache: {cached_i64}") + except OSError as e: + print_warning(f"Failed to save database: {e}") + + +def get_user_code(args: argparse.Namespace) -> tuple[str, str]: + """ + Get user code from one of three input modes. + + Args: + args: Parsed command-line arguments. + + Returns: + Tuple of (code_string, source_description). + + Raises: + ValueError: If no input provided or conflicting inputs. + """ + # Check for conflicting inputs + has_script_file = args.script is not None + has_inline_code = args.code is not None + has_stdin = not sys.stdin.isatty() + + input_count = sum([has_script_file, has_inline_code, has_stdin]) + + if input_count == 0: + raise ValueError("No script provided. Use a script file, -c for inline code, or pipe from stdin.") + + if input_count > 1: + # Prefer explicit arguments over stdin + if has_script_file and has_stdin: + has_stdin = False # Ignore stdin when script file is provided + elif has_inline_code and has_stdin: + has_stdin = False # Ignore stdin when inline code is provided + elif has_script_file and has_inline_code: + raise ValueError("Cannot use both script file and -c inline code. Choose one.") + + # Mode 1: Script file + if has_script_file: + script_path = Path(args.script) + if not script_path.exists(): + raise ValueError(f"Script file not found: {script_path}") + if not script_path.is_file(): + raise ValueError(f"Not a file: {script_path}") + code = script_path.read_text(encoding="utf-8") + return code, f"file: {script_path}" + + # Mode 2: Inline code (-c) + if has_inline_code: + return args.code, "inline code (-c)" + + # Mode 3: Stdin + if has_stdin: + code = sys.stdin.read() + if not code.strip(): + raise ValueError("Empty input from stdin.") + return code, "stdin" + + raise ValueError("No script provided.") + + +def wrap_code(user_code: str, target_file: str, save_on_close: bool) -> str: + """ + Wrap user code with IDA Domain boilerplate. + + The wrapper provides: + - ida_domain imports + - Database.open() context manager + - The `db` variable for accessing all database entities + + Args: + user_code: The user's script code. + target_file: Path to the target binary or .i64 file. + save_on_close: Whether to save the database on close. + + Returns: + Wrapped code string ready for execution. + """ + save_flag = "True" if save_on_close else "False" + + # Escape target file path for Python string + target_file_escaped = target_file.replace("\\", "\\\\").replace("'", "\\'") + + # Indent user code to be inside the 'with' block + indented_code = "\n".join(" " + line if line.strip() else line for line in user_code.split("\n")) + + wrapper = f'''#!/usr/bin/env python3 +# Auto-wrapped by IDA Domain run.py +from ida_domain import Database +from ida_domain.database import IdaCommandOptions + +with Database.open('{target_file_escaped}', IdaCommandOptions(auto_analysis=True), save_on_close={save_flag}) as db: + # --- User code starts here --- +{indented_code} + # --- User code ends here --- +''' + return wrapper + + +def execute_script(code: str, timeout: int | None = None) -> int: + """ + Execute the script code via subprocess. + + Args: + code: Python code to execute. + timeout: Timeout in seconds (None for no timeout). + + Returns: + Exit code from the script execution. + """ + skill_dir = get_skill_dir() + temp_dir = tempfile.gettempdir() + + # Create a temporary file for the wrapped script + # Using a predictable prefix for cleanup + with tempfile.NamedTemporaryFile( + mode="w", + prefix="ida-domain-", + suffix=".py", + dir=temp_dir, + delete=False, + encoding="utf-8", + ) as f: + f.write(code) + temp_script = f.name + + try: + # Execute using uv run python + result = subprocess.run( + ["uv", "run", "python", temp_script], + cwd=skill_dir, + text=True, + timeout=timeout, + ) + return result.returncode + except subprocess.TimeoutExpired: + print_error(f"Script execution timed out after {timeout} seconds.") + print() + print("To increase the timeout, use the --timeout flag:") + print(f" uv run python run.py --timeout 3600 ...") + print() + print("To disable the timeout entirely, use --timeout 0") + return 124 # Standard timeout exit code + except FileNotFoundError: + print_error("uv is not installed. Please install uv first.") + print() + print("Installation:") + print(" curl -LsSf https://astral.sh/uv/install.sh | sh") + return 1 + except KeyboardInterrupt: + print_warning("Execution interrupted by user.") + return 130 + finally: + # Clean up the temp file we just created + try: + Path(temp_script).unlink() + except OSError: + pass + + +def parse_args() -> argparse.Namespace: + """ + Parse command-line arguments. + + Returns: + Parsed arguments namespace. + """ + parser = argparse.ArgumentParser( + description="Execute IDA Domain scripts with automatic boilerplate wrapping.", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + # Execute a script file + uv run python run.py /tmp/analyze.py -f /path/to/binary.exe + + # Execute inline code + uv run python run.py -c "for f in db.functions: print(f.name)" -f binary.exe + + # Execute from stdin + cat /tmp/analyze.py | uv run python run.py -f binary.exe + + # Execute without wrapping (complete script) + uv run python run.py /tmp/complete_script.py --no-wrap + + # Execute and save database changes + uv run python run.py -c "db.functions[0].name = 'main'" -f binary.exe -s +""", + ) + + parser.add_argument( + "script", + nargs="?", + help="Path to Python script file to execute", + ) + + parser.add_argument( + "-f", "--file", + dest="target", + required=True, + help="Target binary or .i64 file (required)", + ) + + parser.add_argument( + "-c", "--code", + dest="code", + help="Inline Python code to execute", + ) + + parser.add_argument( + "-s", "--save", + dest="save", + action="store_true", + default=False, + help="Enable save_on_close=True (default: False)", + ) + + parser.add_argument( + "--no-wrap", + dest="no_wrap", + action="store_true", + default=False, + help="Skip auto-wrapping (for complete scripts that handle their own setup)", + ) + + parser.add_argument( + "--timeout", + dest="timeout", + type=int, + default=1800, + help="Execution timeout in seconds (default: 1800 = 30 minutes, 0 for no timeout)", + ) + + return parser.parse_args() + + +def main() -> int: + """ + Main entry point. + + Returns: + Exit code (0 for success, non-zero for failure). + """ + # Parse arguments first + args = parse_args() + + # Step 1: Check venv exists + if not check_venv_exists(): + prompt_setup() + return 1 + + # Step 2: Clean up old temp files and orphaned run directories (non-blocking, best effort) + cleaned_files = cleanup_old_temp_files() + cleaned_dirs = cleanup_old_run_directories() + if cleaned_files > 0 or cleaned_dirs > 0: + print_info(f"Cleaned up {cleaned_files} old temp file(s) and {cleaned_dirs} orphaned run dir(s).") + + # Step 3: Validate target file (moved before cache lookup) + target_path = Path(args.target) + if not target_path.exists(): + print_error(f"Target file not found: {args.target}") + return 1 + if not target_path.is_file(): + print_error(f"Target is not a file: {args.target}") + return 1 + + # Resolve to absolute path + original_path = target_path.resolve() + + # Step 4: Get or create cache entry for the input file + # This prevents IDA from creating artifacts in the original directory + # and allows reuse of IDA databases from previous runs + cache_dir, cached_path, _ = get_or_create_cache_entry(original_path) + + # Step 5: Get user code + try: + user_code, source_desc = get_user_code(args) + except ValueError as e: + print_error(str(e)) + return 1 + + # Step 6: Wrap code (unless --no-wrap) + # Use the cached file path so IDA operates in the cache directory + if args.no_wrap: + final_code = user_code + print_info(f"Executing {source_desc} without wrapping...") + else: + final_code = wrap_code(user_code, str(cached_path), args.save) + print_info(f"Executing wrapped {source_desc}...") + + # Step 7: Execute + # Convert timeout: 0 means no timeout (None), otherwise use the value + timeout = args.timeout if args.timeout > 0 else None + exit_code = execute_script(final_code, timeout=timeout) + + # Step 8: Handle --save if requested + # Note: Cache directory is NOT cleaned up to allow reuse on subsequent runs + handle_save_if_requested(cache_dir, original_path, cached_path, args.save) + + return exit_code + + +if __name__ == "__main__": + sys.exit(main()) + +``` + +`plugins/code-eval-ida-domain/skills/ida-domain-scripting/setup.py`: + +```py +#!/usr/bin/env python3 +""" +IDA Domain Skill Setup Script + +This script validates the environment and installs dependencies required for +the IDA Domain scripting skill. Run this before using the skill for the first time. + +Usage: + uv run python setup.py [--ref ] + +Steps performed: + 1. Check that uv package manager is installed + 2. Clone or update ida-domain repository from GitHub + 3. Run uv sync to install dependencies (ida-domain) + 4. Verify IDADIR environment variable is set and valid + 5. Run a validation test to confirm IDA Domain works + +Exit codes: + 0 - Success, setup complete + 1 - Error occurred (check output for details) +""" + +import argparse +import json +import os +import subprocess +import sys +import urllib.request +from pathlib import Path +from typing import Optional + + +# ANSI color codes for terminal output +class Colors: + RED = "\033[91m" + GREEN = "\033[92m" + YELLOW = "\033[93m" + BLUE = "\033[94m" + BOLD = "\033[1m" + RESET = "\033[0m" + + +def print_step(step_num: int, message: str) -> None: + """Print a numbered step header.""" + print(f"\n{Colors.BLUE}{Colors.BOLD}[{step_num}/5]{Colors.RESET} {message}") + + +def print_warning(message: str) -> None: + """Print a warning message.""" + print(f" {Colors.YELLOW}!{Colors.RESET} {message}") + + +def print_success(message: str) -> None: + """Print a success message.""" + print(f" {Colors.GREEN}✓{Colors.RESET} {message}") + + +def print_error(message: str) -> None: + """Print an error message.""" + print(f" {Colors.RED}✗{Colors.RESET} {message}") + + +def print_info(message: str) -> None: + """Print an info message.""" + print(f" {Colors.YELLOW}→{Colors.RESET} {message}") + + +def get_skill_dir() -> Path: + """Get the directory containing this setup script.""" + return Path(__file__).parent.resolve() + + +def check_uv() -> bool: + """ + Step 1: Check that uv package manager is installed. + + Returns: + True if uv is available, False otherwise. + """ + print_step(1, "Checking for uv package manager...") + + try: + result = subprocess.run( + ["uv", "--version"], + capture_output=True, + text=True, + check=True, + timeout=30, + ) + version = result.stdout.strip() + print_success(f"uv is installed ({version})") + return True + except FileNotFoundError: + print_error("uv is not installed") + print() + print(" Please install uv using one of these methods:") + print() + print(f" {Colors.BOLD}macOS/Linux:{Colors.RESET}") + print(" curl -LsSf https://astral.sh/uv/install.sh | sh") + print() + print(f" {Colors.BOLD}Windows:{Colors.RESET}") + print(" powershell -c \"irm https://astral.sh/uv/install.ps1 | iex\"") + print() + print(f" {Colors.BOLD}pip:{Colors.RESET}") + print(" pip install uv") + print() + print(" For more options, see: https://docs.astral.sh/uv/getting-started/installation/") + return False + except subprocess.CalledProcessError as e: + print_error(f"uv check failed: {e.stderr.strip()}") + return False + except subprocess.TimeoutExpired: + print_error("uv version check timed out after 30 seconds") + return False + + +def get_latest_release_tag() -> Optional[str]: + """Fetch the latest release tag from GitHub API.""" + url = "https://api.github.com/repos/HexRaysSA/ida-domain/releases/latest" + try: + with urllib.request.urlopen(url, timeout=30) as response: + data = json.loads(response.read()) + return data.get("tag_name") + except Exception as e: + print_warning(f"Could not fetch latest release: {e}") + return None + + +def clone_or_update_ida_domain(ref: Optional[str] = None) -> bool: + """ + Step 2: Clone or update ida-domain repository. + + Args: + ref: Git ref (branch, tag, or commit). If None, uses latest release tag. + + Returns: + True if successful, False otherwise. + """ + skill_dir = get_skill_dir() + ida_domain_dir = skill_dir / "ida-domain" + repo_url = "https://github.com/HexRaysSA/ida-domain.git" + + # Determine which ref to use + if ref is None: + print_info("Fetching latest release tag from GitHub...") + ref = get_latest_release_tag() + if ref is None: + print_warning("Could not determine latest release, falling back to 'main'") + ref = "main" + else: + print_info(f"Latest release: {ref}") + + if ida_domain_dir.exists(): + # Update existing clone + print_step(2, f"Updating ida-domain repository (ref: {ref})...") + try: + subprocess.run( + ["git", "fetch", "--all", "--tags"], + cwd=ida_domain_dir, + check=True, + capture_output=True, + timeout=120, + ) + subprocess.run( + ["git", "checkout", ref], + cwd=ida_domain_dir, + check=True, + capture_output=True, + timeout=30, + ) + # If it's a branch, pull latest + result = subprocess.run( + ["git", "symbolic-ref", "HEAD"], + cwd=ida_domain_dir, + capture_output=True, + text=True, + ) + if result.returncode == 0: # It's a branch + subprocess.run( + ["git", "pull"], + cwd=ida_domain_dir, + check=True, + capture_output=True, + timeout=120, + ) + print_success(f"ida-domain updated to {ref}") + return True + except subprocess.CalledProcessError as e: + print_error(f"Failed to update ida-domain: {e.stderr or e.stdout}") + return False + else: + # Fresh clone + print_step(2, f"Cloning ida-domain repository (ref: {ref})...") + try: + subprocess.run( + ["git", "clone", repo_url, str(ida_domain_dir)], + check=True, + capture_output=True, + timeout=300, + ) + subprocess.run( + ["git", "checkout", ref], + cwd=ida_domain_dir, + check=True, + capture_output=True, + timeout=30, + ) + print_success(f"ida-domain cloned at {ref}") + return True + except subprocess.CalledProcessError as e: + print_error(f"Failed to clone ida-domain: {e.stderr or e.stdout}") + return False + + +def run_uv_sync() -> bool: + """ + Step 3: Run uv sync and install ida-domain as editable package. + + Returns: + True if sync succeeds, False otherwise. + """ + print_step(3, "Installing dependencies with uv sync...") + + skill_dir = get_skill_dir() + ida_domain_dir = skill_dir / "ida-domain" + + try: + # First, run uv sync for base dependencies + result = subprocess.run( + ["uv", "sync"], + cwd=skill_dir, + capture_output=True, + text=True, + check=True, + timeout=300, + ) + + # Then install ida-domain as editable package + print_info("Installing ida-domain as editable package...") + result = subprocess.run( + ["uv", "pip", "install", "-e", str(ida_domain_dir)], + cwd=skill_dir, + capture_output=True, + text=True, + check=True, + timeout=300, + ) + print_success("Dependencies installed successfully") + + return True + except subprocess.CalledProcessError as e: + print_error("uv sync failed") + print() + print(" Error output:") + for line in (e.stderr or e.stdout or "Unknown error").strip().split("\n"): + print(f" {line}") + print() + print(" Possible fixes:") + print(" - Ensure pyproject.toml exists and is valid") + print(" - Check your internet connection") + print(" - Try running: uv cache clean") + return False + except subprocess.TimeoutExpired: + print_error("uv sync timed out after 5 minutes") + print() + print(" Possible causes:") + print(" - Slow network connection") + print(" - Large dependencies to download") + print(" - Try running manually: uv sync") + return False + except Exception as e: + print_error(f"Unexpected error: {e}") + return False + + +def check_idadir() -> bool: + """ + Step 4: Verify IDADIR environment variable is set and points to valid IDA installation. + + Returns: + True if IDADIR is valid, False otherwise. + """ + print_step(4, "Checking IDADIR environment variable...") + + idadir = os.environ.get("IDADIR") + + if not idadir: + print_error("IDADIR environment variable is not set") + print() + print(" IDADIR must point to your IDA Pro installation directory.") + print() + print(" Set it in your shell configuration:") + print() + print(f" {Colors.BOLD}bash/zsh:{Colors.RESET}") + print(" export IDADIR=\"/path/to/ida\"") + print() + print(f" {Colors.BOLD}fish:{Colors.RESET}") + print(" set -gx IDADIR \"/path/to/ida\"") + print() + print(f" {Colors.BOLD}Windows:{Colors.RESET}") + print(" set IDADIR=C:\\path\\to\\ida") + print() + print(" Common IDA locations:") + print(" - macOS: /Applications/IDA Professional 9.1.app/Contents/MacOS") + print(" - Linux: /opt/idapro-9.1") + print(" - Windows: C:\\Program Files\\IDA Professional 9.1") + return False + + ida_path = Path(idadir) + + if not ida_path.exists(): + print_error(f"IDADIR path does not exist: {idadir}") + print() + print(" Please verify the path and update IDADIR accordingly.") + return False + + if not ida_path.is_dir(): + print_error(f"IDADIR is not a directory: {idadir}") + print() + print(" IDADIR should point to the IDA installation directory,") + print(" not an executable file.") + return False + + # Check for common IDA files to verify it's a valid installation + ida_indicators = [ + "idat64", # Linux/macOS headless IDA 64-bit + "idat64.exe", # Windows headless IDA 64-bit + "idat", # Linux/macOS headless IDA 32-bit + "idat.exe", # Windows headless IDA 32-bit + "ida64", # Linux/macOS GUI IDA 64-bit + "ida64.exe", # Windows GUI IDA 64-bit + "libida64.so", # Linux shared library + "libida64.dylib", # macOS shared library + "ida64.dll", # Windows DLL + ] + + found_indicators = [f for f in ida_indicators if (ida_path / f).exists()] + + if not found_indicators: + print_error(f"IDADIR does not appear to contain IDA Pro: {idadir}") + print() + print(" Expected to find one of:") + for indicator in ida_indicators[:6]: # Show first few + print(f" - {indicator}") + print() + print(" Please verify IDADIR points to the correct IDA installation directory.") + return False + + print_success(f"IDADIR is set: {idadir}") + print_info(f"Found IDA files: {', '.join(found_indicators[:3])}") + + return True + + +def run_validation_test() -> bool: + """ + Step 5: Run a minimal script to verify IDA Domain can load. + + Returns: + True if validation succeeds, False otherwise. + """ + print_step(5, "Running IDA Domain validation test...") + + skill_dir = get_skill_dir() + + # Minimal test script to verify ida_domain imports correctly + test_script = """ +import sys +try: + import ida_domain + print(f"ida_domain version: {ida_domain.__version__}") + print("SUCCESS: IDA Domain loaded correctly") + sys.exit(0) +except ImportError as e: + print(f"IMPORT_ERROR: {e}") + sys.exit(1) +except Exception as e: + print(f"ERROR: {e}") + sys.exit(1) +""" + + try: + result = subprocess.run( + ["uv", "run", "python", "-c", test_script], + cwd=skill_dir, + capture_output=True, + text=True, + timeout=60, + ) + + output = result.stdout.strip() + error_output = result.stderr.strip() + + if result.returncode == 0 and "SUCCESS" in output: + # Extract version info + for line in output.split("\n"): + if "ida_domain version" in line: + print_success(line.strip()) + break + else: + print_success("IDA Domain loaded successfully") + return True + else: + print_error("IDA Domain validation failed") + print() + + if "IMPORT_ERROR" in output: + print(" Import error:") + for line in output.split("\n"): + if "IMPORT_ERROR" in line: + print(f" {line.replace('IMPORT_ERROR: ', '')}") + elif output: + print(" Output:") + for line in output.split("\n"): + print(f" {line}") + + if error_output: + print(" Stderr:") + for line in error_output.split("\n"): + print(f" {line}") + + print() + print(" Possible fixes:") + print(" - Verify IDADIR is set correctly") + print(" - Ensure IDA Pro 9.1+ is installed") + print(" - Try re-running: uv sync --reinstall") + print(" - Check IDA Domain docs: https://ida-domain.docs.hex-rays.com/") + return False + + except subprocess.TimeoutExpired: + print_error("Validation test timed out after 60 seconds") + print() + print(" The import test took too long to complete.") + print(" This may indicate a problem with the IDA Domain installation.") + return False + except Exception as e: + print_error(f"Validation test failed: {e}") + return False + + +def main() -> int: + """ + Run all setup steps in order. + + Returns: + Exit code (0 for success, 1 for failure). + """ + parser = argparse.ArgumentParser( + description="Setup IDA Domain scripting skill environment" + ) + parser.add_argument( + "--ref", + type=str, + default=None, + help="Git ref (branch, tag, or commit) for ida-domain. Default: latest release tag", + ) + args = parser.parse_args() + + print(f"\n{Colors.BOLD}IDA Domain Skill Setup{Colors.RESET}") + print("=" * 50) + + # Step 1: Check uv + if not check_uv(): + return 1 + + # Step 2: Clone/update ida-domain + if not clone_or_update_ida_domain(args.ref): + return 1 + + # Step 3: Run uv sync + if not run_uv_sync(): + return 1 + + # Step 4: Check IDADIR + if not check_idadir(): + return 1 + + # Step 5: Validation test + if not run_validation_test(): + return 1 + + # All steps passed + print() + print("=" * 50) + print(f"{Colors.GREEN}{Colors.BOLD}Setup complete! Ready to use.{Colors.RESET}") + print() + print("Next steps:") + print(" - Write scripts to /tmp/ida-domain-*.py") + print(" - Execute with: uv run python run.py /tmp/script.py -f ") + print() + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) + +``` + +`plugins/ida-plugin-development/LICENSE`: + +``` +MIT License + +Copyright (c) 2026 Hex-Rays SA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +`plugins/ida-plugin-development/README.md`: + +```md +# IDA Plugin Development Skill for Claude Code + +Develop plugins for IDA Pro in Python, using idiomatic patterns, lessons, and tricks, including the Python Domain API (ida-domain). Use when creating both GUI (Qt) and background plugins for inspecting and rendering things program structure, functions, disassembly, cross-references, and strings. + +[Claude Skills](https://docs.claude.com/en/docs/claude-code/skills) that enables Claude to autonomously create, package, and install IDAPython plugins with proper HCLI-compatible packaging. + +Claude handles plugin infrastructure (Qt widgets, menus, actions, hooks, settings) so you can focus on what your plugin should do. + +## Requirements + +- [uv](https://docs.astral.sh/uv/) package manager + +## Installation + +```bash +/plugin marketplace add HexRaysSA/ida-claude-plugins +/plugin install ida-plugin-development@ida-claude-plugins +``` + +## Scope + +**In Scope:** +- Plugin infrastructure (lifecycle, registration) +- Qt6 widgets (forms, dialogs, choosers) +- Actions, menus, hotkeys +- UI_Hooks for event handling +- Settings descriptors +- HCLI packaging and validation + +**Out of Scope:** +- IDA scripting/analysis logic (use `reverse-engineering-with-code-eval-and-ida-domain` plugin for script execution) +- IDA 7.x/8.x compatibility +- C++ SDK plugins + +## Skills + +- **ida-plugin-development** - Plugin infrastructure, Qt widgets, actions, hooks, settings +- **ida-domain-api** - Domain API reference for IDA Pro binary analysis +- **package-ida-plugin** - HCLI packaging and distribution + +## Related + +- [HCLI Documentation](https://hcli.docs.hex-rays.com/) +- [IDAPython Documentation](https://python.docs.hex-rays.com/) +- [ida-domain Documentation](https://ida-domain.docs.hex-rays.com/) + + +## License + +MIT License - see [LICENSE](LICENSE) file for details. + +``` + +`plugins/ida-plugin-development/skills/ida-domain-api/SKILL.md`: + +```md +--- +name: ida-domain-api +description: Analyze binaries using the Domain API for IDA Pro. Use when examining program structure, functions, disassembly, cross-references, or strings. +--- + +# Domain API for IDA Pro + +Use this skill to efficiently use the Domain API for IDA Pro, which is easier and more concise than the legacy IDA Python SDK. + +**Always prefer the IDA Domain API** over the legacy low-level IDA Python SDK. The Domain API provides a clean, Pythonic interface that is easier to use and understand. + +### Documentation Resources + +| Resource | URL | +|----------|-----| +| **LLM-optimized overview** | https://ida-domain.docs.hex-rays.com/llms.txt | +| **Getting Started** | https://ida-domain.docs.hex-rays.com/getting_started/index.md | +| **Examples** | https://ida-domain.docs.hex-rays.com/examples/index.md | +| **API Reference** | https://ida-domain.docs.hex-rays.com/ref/{module}/index.md | + +Available API modules: `bytes`, `comments`, `database`, `entries`, `flowchart`, `functions`, `heads`, `hooks`, `instructions`, `names`, `operands`, `segments`, `signature_files`, `strings`, `types`, `xrefs` + +**To fetch specific API documentation**, use URLs like: +- `https://ida-domain.docs.hex-rays.com/ref/functions/index.md` - Function analysis API +- `https://ida-domain.docs.hex-rays.com/ref/xrefs/index.md` - Cross-reference API +- `https://ida-domain.docs.hex-rays.com/ref/strings/index.md` - String analysis API + +### Opening a Database + +```python +from ida_domain import Database +from ida_domain.database import IdaCommandOptions + +ida_options = IdaCommandOptions(auto_analysis=True, new_database=False) +with Database.open("path/to/binary", ida_options, save_on_close=True) as db: + # Your analysis here + pass +``` + +### Key Database Properties + +```python +with Database.open(path, ida_options) as db: + db.minimum_ea # Start address + db.maximum_ea # End address + db.metadata # Database metadata + db.architecture # Target architecture + + db.functions # All functions (iterable) + db.strings # All strings (iterable) + db.segments # Memory segments + db.names # Symbols and labels + db.entries # Entry points + db.types # Type definitions + db.comments # All comments + db.xrefs # Cross-reference utilities + db.bytes # Byte manipulation + db.instructions # Instruction access +``` + +--- + +## Functions + +### Iterating Functions + +```python +# Iterate all functions +for func in db.functions: + print(db.functions.get_name(func)) + +# Get function count +count = len(db.functions) +``` + +### Finding Functions + +```python +func = db.functions.get_at(0x401000) # By address +func = db.functions.get_function_by_name("main") # By name +func = db.functions.get_next(ea) # Next function after ea + +# Functions in range +for func in db.functions.get_between(start_ea, end_ea): + print(func.start_ea) +``` + +### Function Properties + +```python +name = db.functions.get_name(func) +signature = db.functions.get_signature(func) +flags = db.functions.get_flags(func) # Returns FunctionFlags + +# Check function attributes +db.functions.is_far(func) +db.functions.does_return(func) +``` + +### Function Code + +```python +# Get disassembly lines +lines = db.functions.get_disassembly(func, remove_tags=True) + +# Get decompiled pseudocode +pseudocode = db.functions.get_pseudocode(func, remove_tags=True) + +# Get microcode +microcode = db.functions.get_microcode(func, remove_tags=True) +``` + +### Function Analysis + +```python +# Get instructions in function +for insn in db.functions.get_instructions(func): + print(insn.ea) + +# Get flowchart for basic blocks +flowchart = db.functions.get_flowchart(func) +for block in flowchart: + print(f"Block: {block.start_ea:#x} - {block.end_ea:#x}") + +# Get callers/callees +callers = db.functions.get_callers(func) +callees = db.functions.get_callees(func) + +# Get function chunks +for chunk in db.functions.get_chunks(func): + print(f"Chunk: {chunk.start_ea:#x}, main={chunk.is_main}") + +# Get data items within function +for data_ea in db.functions.get_data_items(func): + print(f"Data at {data_ea:#x}") +``` + +### Local Variables + +```python +# Get all local variables +lvars = db.functions.get_local_variables(func) +for lvar in lvars: + print(f"{lvar.name}: {lvar.type_str}, arg={lvar.is_argument}") + +# Find variable by name +lvar = db.functions.get_local_variable_by_name(func, "result") + +# Get variable references in pseudocode +refs = db.functions.get_local_variable_references(func, lvar) +for ref in refs: + print(f"Line {ref.line_number}: {ref.access_type} in {ref.context}") +``` + +### Modifying Functions + +```python +db.functions.set_name(func, "new_name") +db.functions.set_comment(func, "This function does X", repeatable=False) +db.functions.create(ea) # Create function at address +db.functions.remove(ea) # Remove function at address +``` + +--- + +## Instructions + +### Iterating Instructions + +```python +# All instructions in database +for insn in db.instructions: + print(db.instructions.get_disassembly(insn)) + +# Instructions in range +for insn in db.instructions.get_between(start_ea, end_ea): + print(insn.ea) +``` + +### Getting Instructions + +```python +insn = db.instructions.get_at(ea) # Decode at address +insn = db.instructions.get_previous(ea) # Previous instruction +``` + +### Instruction Properties + +```python +disasm = db.instructions.get_disassembly(insn) +mnemonic = db.instructions.get_mnemonic(insn) # "mov", "push", etc. +db.instructions.is_valid(insn) +``` + +### Control Flow Analysis + +```python +db.instructions.is_call_instruction(insn) # Is this a call? +db.instructions.is_indirect_jump_or_call(insn) # Indirect jump/call? +db.instructions.breaks_sequential_flow(insn) # Stops flow (ret, jmp)? +``` + +### Working with Operands + +```python +count = db.instructions.get_operands_count(insn) +operands = db.instructions.get_operands(insn) # List of Operand objects + +for op in operands: + info = op.get_info() + print(f"Operand {op.number}: {op.type.name}") + + if isinstance(op, RegisterOperand): + print(f" Register: {op.get_register_name()}") + elif isinstance(op, ImmediateOperand): + print(f" Value: 0x{op.get_value():x}") + elif isinstance(op, MemoryOperand): + if op.is_direct_memory(): + print(f" Memory: 0x{op.get_address():x}") +``` + +--- + +## Segments + +### Iterating Segments + +```python +for segment in db.segments: + name = db.segments.get_name(segment) + size = db.segments.get_size(segment) + print(f"{name}: {segment.start_ea:#x} - {segment.end_ea:#x}") + +count = len(db.segments) +``` + +### Finding Segments + +```python +seg = db.segments.get_at(0x401000) # Segment containing address +seg = db.segments.get_by_name(".text") # By name +``` + +### Segment Properties + +```python +name = db.segments.get_name(segment) +size = db.segments.get_size(segment) +bitness = db.segments.get_bitness(segment) # 16, 32, or 64 +seg_class = db.segments.get_class(segment) # "CODE", "DATA", etc. +``` + +### Creating Segments + +```python +from ida_domain.segments import PredefinedClass, AddSegmentFlags + +# Add segment with explicit range +seg = db.segments.add( + seg_para=0, + start_ea=0x1000, + end_ea=0x2000, + seg_name="MySegment", + seg_class=PredefinedClass.CODE +) + +# Append segment after last one +seg = db.segments.append(seg_para=0, seg_size=0x1000, seg_name="NewSeg") +``` + +### Modifying Segments + +```python +from ida_domain.segments import SegmentPermissions, AddressingMode + +db.segments.set_name(segment, "new_name") +db.segments.set_permissions(segment, SegmentPermissions.READ | SegmentPermissions.EXEC) +db.segments.add_permissions(segment, SegmentPermissions.WRITE) +db.segments.remove_permissions(segment, SegmentPermissions.WRITE) +db.segments.set_addressing_mode(segment, AddressingMode.BIT64) +db.segments.set_comment(segment, "Code section", repeatable=False) +``` + +--- + +## Strings + +### Iterating Strings + +```python +for string in db.strings: + print(f"{string.address:#x}: {string}") + +# By index +first_string = db.strings[0] +count = len(db.strings) +``` + +### Finding Strings + +```python +string = db.strings.get_at(0x402000) # String at address + +# Strings in range +for s in db.strings.get_between(start_ea, end_ea): + print(s.contents) +``` + +### String Properties + +```python +print(string.address) # Address +print(string.length) # Length in characters +print(string.type) # StringType enum +print(string.encoding) # Internal encoding +print(string.contents) # UTF-8 bytes +print(str(string)) # Decoded string +``` + +### Rebuilding String List + +```python +from ida_domain.strings import StringListConfig, StringType + +config = StringListConfig( + string_types=[StringType.C, StringType.C_16], + min_len=3, + only_ascii_7bit=False +) +db.strings.rebuild(config) +db.strings.clear() # Clear string list +``` + +--- + +## Xrefs + +### Getting References TO an Address + +```python +# All xrefs to an address +for xref in db.xrefs.to_ea(target_ea): + print(f"{xref.from_ea:#x} -> {xref.to_ea:#x} ({xref.type.name})") + +# Just code references +for ea in db.xrefs.code_refs_to_ea(target_ea, flow=False): + print(f"Code ref from {ea:#x}") + +# Just data references +for ea in db.xrefs.data_refs_to_ea(target_ea): + print(f"Data ref from {ea:#x}") + +# Call references only +for ea in db.xrefs.calls_to_ea(func_ea): + print(f"Called from {ea:#x}") + +# Detailed caller information +for caller in db.xrefs.get_callers(func_ea): + print(f"Called from {caller.name} at {caller.ea:#x}") +``` + +### Getting References FROM an Address + +```python +# All xrefs from an address +for xref in db.xrefs.from_ea(source_ea): + print(f"{xref.from_ea:#x} -> {xref.to_ea:#x}") + +# Code/data refs from +for ea in db.xrefs.code_refs_from_ea(source_ea): + print(f"Code ref to {ea:#x}") + +for ea in db.xrefs.calls_from_ea(source_ea): + print(f"Calls {ea:#x}") +``` + +### Data Access Analysis + +```python +# Who reads this data? +for ea in db.xrefs.reads_of_ea(data_ea): + print(f"Read by {ea:#x}") + +# Who writes to this data? +for ea in db.xrefs.writes_to_ea(data_ea): + print(f"Written by {ea:#x}") +``` + +### XrefInfo Properties + +```python +xref.is_call # Is this a call reference? +xref.is_jump # Is this a jump reference? +xref.is_read # Is this a data read? +xref.is_write # Is this a data write? +xref.is_flow # Is this ordinary flow? +xref.user # Is this user-defined? +``` + +--- + +## Names + +### Iterating Names + +```python +for ea, name in db.names: + print(f"{ea:#x}: {name}") + +count = len(db.names) +``` + +### Getting Names + +```python +name = db.names.get_at(0x401000) +ea, name = db.names[0] # By index +``` + +### Setting Names + +```python +from ida_domain.names import SetNameFlags + +db.names.set_name(ea, "my_function") +db.names.set_name(ea, "my_func", flags=SetNameFlags.CHECK) # Validate chars +db.names.force_name(ea, "func") # Creates func_2 if func exists +db.names.delete(ea) # Remove name +``` + +### Name Properties + +```python +db.names.is_valid_name("my_name") # Check if valid +db.names.is_public_name(ea) # Is public? +db.names.is_weak_name(ea) # Is weak? + +db.names.make_name_public(ea) +db.names.make_name_non_public(ea) +db.names.make_name_weak(ea) +db.names.make_name_non_weak(ea) +``` + +### Demangling + +```python +from ida_domain.names import DemangleFlags + +demangled = db.names.get_demangled_name(ea) +demangled = db.names.get_demangled_name(ea, DemangleFlags.NORETTYPE) +demangled = db.names.demangle_name("?main@@YAXXZ") +``` + +--- + +## Types + +### Getting Types + +```python +# By name +tinfo = db.types.get_by_name("MyStruct") + +# At address +tinfo = db.types.get_at(ea) + +# Iterate all types +for tinfo in db.types: + print(tinfo) +``` + +### Parsing Types + +```python +# Parse declarations from string +errors = db.types.parse_declarations(None, "struct Point { int x; int y; };") + +# Parse single declaration +tinfo = db.types.parse_one_declaration(None, "int (*callback)(void*)", "callback_t") + +# Parse header file +errors = db.types.parse_header_file(library, Path("header.h")) +``` + +### Applying Types + +```python +from ida_domain.types import TypeApplyFlags + +db.types.apply_at(tinfo, ea, flags=TypeApplyFlags.DEFINITE) +``` + +### Type Details + +```python +details = db.types.get_details(tinfo) +print(details.name) +print(details.size) +print(details.attributes) + +# For structs/unions +if details.udt: + print(details.udt.num_members) + print(details.udt.attributes) + +# For functions +if details.func: + print(details.func.attributes) +``` + +### Type Libraries + +```python +# Load/create libraries +lib = db.types.load_library(Path("types.til")) +lib = db.types.create_library(Path("new.til"), "My Types") + +# Import/export types +db.types.import_type(source_lib, "MyStruct") +db.types.export_type(dest_lib, "MyStruct") + +# Save library +db.types.save_library(lib, Path("output.til")) +db.types.unload_library(lib) +``` + +--- + +## Bytes + +### Reading Values + +```python +byte = db.bytes.get_byte_at(ea) +word = db.bytes.get_word_at(ea) +dword = db.bytes.get_dword_at(ea) +qword = db.bytes.get_qword_at(ea) +float_val = db.bytes.get_float_at(ea) +double_val = db.bytes.get_double_at(ea) + +# Read multiple bytes +data = db.bytes.get_bytes_at(ea, size=16) +original = db.bytes.get_original_bytes_at(ea, size=16) + +# Read strings +string = db.bytes.get_string_at(ea) +cstring = db.bytes.get_cstring_at(ea, max_length=256) +``` + +### Writing Values + +```python +db.bytes.set_byte_at(ea, 0x90) +db.bytes.set_word_at(ea, 0x1234) +db.bytes.set_dword_at(ea, 0x12345678) +db.bytes.set_qword_at(ea, 0x123456789ABCDEF0) +db.bytes.set_bytes_at(ea, b"\x90\x90\x90") +``` + +### Patching (with History) + +```python +db.bytes.patch_byte_at(ea, 0x90) # Saves original +db.bytes.patch_bytes_at(ea, data) +db.bytes.revert_byte_at(ea) # Restore original + +# Get original values +orig = db.bytes.get_original_byte_at(ea) +``` + +### Searching + +```python +from ida_domain.bytes import SearchFlags + +# Find bytes +ea = db.bytes.find_bytes_between(b"\x55\x89\xe5", start_ea, end_ea) + +# Find all occurrences +addresses = db.bytes.find_binary_sequence(b"\x90\x90") + +# Find text +ea = db.bytes.find_text_between("error", flags=SearchFlags.DOWN) + +# Find immediate value +ea = db.bytes.find_immediate_between(0x1234) +``` + +### Creating Data Items + +```python +from ida_domain.strings import StringType + +db.bytes.create_byte_at(ea, count=4) +db.bytes.create_word_at(ea) +db.bytes.create_dword_at(ea, count=10) # Array of 10 dwords +db.bytes.create_qword_at(ea) +db.bytes.create_float_at(ea) +db.bytes.create_double_at(ea) +db.bytes.create_string_at(ea, string_type=StringType.C) +db.bytes.create_struct_at(ea, count=1, tid=struct_tid) +``` + +### Querying Properties + +```python +size = db.bytes.get_data_size_at(ea) +db.bytes.is_value_initialized_at(ea) +db.bytes.is_code_at(ea) +db.bytes.is_data_at(ea) +db.bytes.is_head_at(ea) +db.bytes.is_tail_at(ea) +db.bytes.is_unknown_at(ea) + +# Get disassembly at address +disasm = db.bytes.get_disassembly_at(ea) +``` + +### Navigation + +```python +next_head = db.bytes.get_next_head(ea) +prev_head = db.bytes.get_previous_head(ea) +next_addr = db.bytes.get_next_address(ea) +prev_addr = db.bytes.get_previous_address(ea) +``` + +--- + +## Comments + +### Regular Comments + +```python +from ida_domain.comments import CommentKind + +# Get comment +info = db.comments.get_at(ea, CommentKind.REGULAR) +if info: + print(info.comment) + +# Set comment +db.comments.set_at(ea, "This is important", CommentKind.REGULAR) +db.comments.set_at(ea, "Shows everywhere", CommentKind.REPEATABLE) + +# Delete comment +db.comments.delete_at(ea, CommentKind.ALL) +``` + +### Iterating Comments + +```python +for comment_info in db.comments: + print(f"{comment_info.ea:#x}: {comment_info.comment}") + +# All comment types +for info in db.comments.get_all(CommentKind.ALL): + print(f"{info.ea:#x} (repeatable={info.repeatable}): {info.comment}") +``` + +### Extra Comments (Anterior/Posterior) + +```python +from ida_domain.comments import ExtraCommentKind + +# Set extra comment +db.comments.set_extra_at(ea, index=0, comment="Before line", kind=ExtraCommentKind.ANTERIOR) +db.comments.set_extra_at(ea, index=0, comment="After line", kind=ExtraCommentKind.POSTERIOR) + +# Get extra comments +comment = db.comments.get_extra_at(ea, index=0, kind=ExtraCommentKind.ANTERIOR) +for comment in db.comments.get_all_extra_at(ea, ExtraCommentKind.ANTERIOR): + print(comment) + +# Delete +db.comments.delete_extra_at(ea, index=0, kind=ExtraCommentKind.ANTERIOR) +``` + +--- + +## Entries + +### Iterating Entry Points + +```python +for entry in db.entries: + print(f"{entry.ordinal}: {entry.name} at {entry.address:#x}") + +count = len(db.entries) +first = db.entries[0] +``` + +### Finding Entries + +```python +entry = db.entries.get_at(ea) # By address +entry = db.entries.get_by_ordinal(1) # By ordinal +entry = db.entries.get_by_name("main") # By name +entry = db.entries.get_at_index(0) # By index +``` + +### Entry Properties + +```python +print(entry.ordinal) +print(entry.address) +print(entry.name) +print(entry.forwarder_name) +entry.has_forwarder() +``` + +### Modifying Entries + +```python +db.entries.add(address=ea, name="new_entry", ordinal=10) +db.entries.rename(ordinal=10, new_name="renamed_entry") +db.entries.set_forwarder(ordinal=10, forwarder_name="other.dll!func") +db.entries.exists(ordinal=10) +``` + +### Utility Iterators + +```python +for ordinal in db.entries.get_ordinals(): + print(ordinal) + +for addr in db.entries.get_addresses(): + print(f"{addr:#x}") + +for name in db.entries.get_names(): + print(name) + +for fwd in db.entries.get_forwarders(): + print(f"{fwd.ordinal}: {fwd.name}") +``` + +--- + +## Heads + +### Iterating Heads + +```python +for ea in db.heads: + print(f"Head at {ea:#x}") + +# In range +for ea in db.heads.get_between(start_ea, end_ea): + print(ea) +``` + +### Navigation + +```python +next_ea = db.heads.get_next(ea) +prev_ea = db.heads.get_previous(ea) +``` + +### Head Properties + +```python +db.heads.is_head(ea) # Is start of item? +db.heads.is_tail(ea) # Is part of multi-byte item? +db.heads.is_code(ea) # Is instruction? +db.heads.is_data(ea) # Is data? +db.heads.is_unknown(ea) # Is unclassified? + +size = db.heads.size(ea) +start, end = db.heads.bounds(ea) +``` + +--- + +## Flowchart + +### Creating Flowcharts + +```python +from ida_domain.flowchart import FlowChart, FlowChartFlags + +# From function +flowchart = FlowChart(db, func=my_func) + +# From address range +flowchart = FlowChart(db, bounds=(start_ea, end_ea)) + +# With predecessor info +flowchart = FlowChart(db, func=my_func, flags=FlowChartFlags.PREDS) +``` + +### Iterating Basic Blocks + +```python +for block in flowchart: + print(f"Block {block.id}: {block.start_ea:#x} - {block.end_ea:#x}") + +# By index +block = flowchart[0] +count = len(flowchart) +``` + +### Block Navigation + +```python +for succ in block.get_successors(): + print(f"Successor: {succ.id}") + +for pred in block.get_predecessors(): + print(f"Predecessor: {pred.id}") + +succ_count = block.count_successors() +pred_count = block.count_predecessors() +``` + +### Block Instructions + +```python +for insn in block.get_instructions(): + print(f"{insn.ea:#x}") +``` + +--- + +## Enums Reference + +### XrefType + +```python +from ida_domain.xrefs import XrefType + +XrefType.OFFSET # Offset reference +XrefType.WRITE # Write access +XrefType.READ # Read access +XrefType.CALL_FAR # Far call +XrefType.CALL_NEAR # Near call +XrefType.JUMP_FAR # Far jump +XrefType.JUMP_NEAR # Near jump +XrefType.ORDINARY_FLOW # Sequential flow + +xref_type.is_code_ref() # Check if code ref +xref_type.is_data_ref() # Check if data ref +``` + +### FunctionFlags + +```python +from ida_domain.functions import FunctionFlags + +FunctionFlags.NORET # Doesn't return +FunctionFlags.LIB # Library function +FunctionFlags.THUNK # Thunk function +FunctionFlags.HIDDEN # Hidden chunk +FunctionFlags.LUMINA # From Lumina +FunctionFlags.FAR # Far function +FunctionFlags.FRAME # Uses frame pointer +``` + +### LocalVariableAccessType + +```python +from ida_domain.functions import LocalVariableAccessType + +LocalVariableAccessType.READ # Variable is read +LocalVariableAccessType.WRITE # Variable is written +LocalVariableAccessType.ADDRESS # Address taken (&var) +``` + +### LocalVariableContext + +```python +from ida_domain.functions import LocalVariableContext + +LocalVariableContext.ASSIGNMENT # var = expr +LocalVariableContext.CONDITION # if (var) +LocalVariableContext.CALL_ARG # func(var) +LocalVariableContext.RETURN # return var +LocalVariableContext.ARITHMETIC # var + 1 +LocalVariableContext.COMPARISON # var == x +LocalVariableContext.ARRAY_INDEX # arr[var] +LocalVariableContext.POINTER_DEREF # *var +LocalVariableContext.CAST # (type)var +``` + +### OperandType + +```python +from ida_domain.operands import OperandType + +OperandType.REGISTER # Register +OperandType.MEMORY # Direct memory +OperandType.PHRASE # Register addressing +OperandType.DISPLACEMENT # Reg + displacement +OperandType.IMMEDIATE # Immediate value +OperandType.FAR_ADDRESS # Far address +OperandType.NEAR_ADDRESS # Near address +``` + +### StringType + +```python +from ida_domain.strings import StringType + +StringType.C # C-style null-terminated +StringType.C_16 # C-style 16-bit +StringType.C_32 # C-style 32-bit +StringType.PASCAL # Pascal-style +StringType.LEN2 # 2-byte length prefix +StringType.LEN4 # 4-byte length prefix +``` + +### SegmentPermissions + +```python +from ida_domain.segments import SegmentPermissions + +SegmentPermissions.READ +SegmentPermissions.WRITE +SegmentPermissions.EXEC +SegmentPermissions.ALL +``` + +### AddressingMode + +```python +from ida_domain.segments import AddressingMode + +AddressingMode.BIT16 # 16-bit segment +AddressingMode.BIT32 # 32-bit segment +AddressingMode.BIT64 # 64-bit segment +``` + +### PredefinedClass + +```python +from ida_domain.segments import PredefinedClass + +PredefinedClass.CODE +PredefinedClass.DATA +PredefinedClass.CONST +PredefinedClass.STACK +PredefinedClass.BSS +PredefinedClass.XTRN +``` + +### CommentKind + +```python +from ida_domain.comments import CommentKind + +CommentKind.REGULAR # Normal comment +CommentKind.REPEATABLE # Shows at all refs +CommentKind.ALL # Both types +``` + +### ExtraCommentKind + +```python +from ida_domain.comments import ExtraCommentKind + +ExtraCommentKind.ANTERIOR # Before the line +ExtraCommentKind.POSTERIOR # After the line +``` + +### TypeAttr + +```python +from ida_domain.types import TypeAttr + +TypeAttr.INT, TypeAttr.UINT +TypeAttr.FLOAT, TypeAttr.DOUBLE +TypeAttr.PTR, TypeAttr.ARRAY +TypeAttr.FUNC, TypeAttr.STRUCT +TypeAttr.UNION, TypeAttr.ENUM +TypeAttr.CONST, TypeAttr.VOLATILE +``` + +### FlowChartFlags + +```python +from ida_domain.flowchart import FlowChartFlags + +FlowChartFlags.NONE # Default +FlowChartFlags.NOEXT # Don't compute external blocks +FlowChartFlags.PREDS # Compute predecessors +``` + +--- + +## Common Patterns + +### Find All Calls to a Function + +```python +func = db.functions.get_function_by_name("malloc") +if func: + for caller in db.xrefs.get_callers(func.start_ea): + print(f"Called from {caller.name} at {caller.ea:#x}") +``` + +### Rename Functions Based on Strings + +```python +for func in db.functions: + for insn in db.functions.get_instructions(func): + for xref in db.xrefs.from_ea(insn.ea): + string = db.strings.get_at(xref.to_ea) + if string and "error" in str(string).lower(): + db.functions.set_name(func, f"func_with_error_{func.start_ea:x}") + break +``` + +### Analyze Function Complexity + +```python +func = db.functions.get_at(ea) +flowchart = db.functions.get_flowchart(func) +print(f"Basic blocks: {len(flowchart)}") + +total_edges = sum(block.count_successors() for block in flowchart) +print(f"Cyclomatic complexity: {total_edges - len(flowchart) + 2}") +``` + +### Export Function Pseudocode + +```python +for func in db.functions: + name = db.functions.get_name(func) + try: + pseudocode = db.functions.get_pseudocode(func) + print(f"// {name}") + for line in pseudocode: + print(line) + except RuntimeError: + print(f"// Could not decompile {name}") +``` + +### Find Cross-References to Strings + +```python +for string in db.strings: + refs = list(db.xrefs.to_ea(string.address)) + if refs: + print(f'"{string}" referenced from:') + for xref in refs: + print(f" {xref.from_ea:#x}") +``` + +## Legacy API (Avoid) + +The legacy `idc`, `idautils`, `ida_funcs` APIs still work but are harder to use. **Prefer the Domain API** for new analysis scripts. Only use legacy APIs when Domain API doesn't expose needed functionality. + +``` + +`plugins/ida-plugin-development/skills/ida-plugin-development/SKILL.md`: + +```md +--- +name: ida-plugin-development +description: Develop plugins for IDA Pro in Python, using idiomatic patterns, lessons, and tricks, including the Python Domain API (ida-domain). Use when creating both GUI (Qt) and background plugins for inspecting and rendering things program structure, functions, disassembly, cross-references, and strings. +--- + +# Developing IDA Pro plugins + +Use this skill when developing plugins for IDA Pro using Python. + +IDA's UI and analysis passes can be almost completely replaced through plugins. +There's a lot of power (and a lot of complexity), so its important to follow known patterns. +This document lists tips and tricks for creating new plugins for modern versions of IDA. + +Key concepts covered in this document: +- [Use the IDA Domain API](#use-the-ida-domain-api) - prefer the high-level Pythonic interface +- [Plugin Manager Integration](#plugin-manager-integration) - packaging and distribution +- [Plugin Entry Point](#plugin-entry-point) - version checking and conditional loading +- [Hook Registration](#hook-registration) - pairwise register/unregister pattern +- [Cross-Plugin Communication via IDC Functions](#cross-plugin-communication-via-idc-functions) - invoke plugin functionality from scripts/other plugins +- [Save/Load state from netnodes](#saveload-state-from-netnodes) - persist plugin data in IDB +- [Respond to current address and selection change](#respond-to-current-address-and-selection-change) - UI location hooks +- [Find widgets by prefix](#find-widgets-by-prefix) - managing multiple widget instances +- [Context Menu Entries](#context-menu-entries-send-to-foo-and-send-to-foo-a) - "Send to Foo" patterns +- [User Defined Prefix](#user-defined-prefix) - add contextual markers in disassembly +- [Viewer Hints](#viewer-hints) - hover popups with context +- [Overriding rendering](#overriding-rendering) - custom colors and mnemonics +- [Custom Viewers](#custom-viewers) - tagged lines with clickable addresses + + +## Use the IDA Domain API + +Always prefer the IDA Domain API over the legacy low-level IDA Python SDK. The Domain API provides a clean, Pythonic interface that is easier to use and understand. +However, there will be some things that the Domain API doesn't cover, especially around plugin registration and GUI handling. + +Right now: read this intro guide: https://ida-domain.docs.hex-rays.com/getting_started/index.md + +Always refer to the documentation rather than doing introspection, because the documentation explains concepts, not just symbol names. +To fetch specific API documentation, use URLs like: +- `https://ida-domain.docs.hex-rays.com/ref/functions/index.md` - Function analysis API +- `https://ida-domain.docs.hex-rays.com/ref/xrefs/index.md` - Cross-reference API +- `https://ida-domain.docs.hex-rays.com/ref/strings/index.md` - String analysis API + +Available API modules: `bytes`, `comments`, `database`, `entries`, `flowchart`, `functions`, `heads`, `hooks`, `instructions`, `names`, `operands`, `segments`, `signature_files`, `strings`, `types`, `xrefs` +URL pattern: https://ida-domain.docs.hex-rays.com/ref/{module}/index.md + +You can always ask a subagent to answer a question by exploring the documentation and summarizing its findings. + +### Key Database Properties + +```python +with Database.open(path, ida_options) as db: + db.minimum_ea # Start address + db.maximum_ea # End address + db.metadata # Database metadata + db.architecture # Target architecture + + db.functions # All functions (iterable) + db.strings # All strings (iterable) + db.segments # Memory segments + db.names # Symbols and labels + db.entries # Entry points + db.types # Type definitions + db.comments # All comments + db.xrefs # Cross-reference utilities + db.bytes # Byte manipulation + db.instructions # Instruction access +``` + +### Common Analysis Tasks + +#### List Functions + +```python +func: func_t +for func in db.functions: + name = db.functions.get_name(func) + print(f"{hex(func.start_ea)}: {name} ({func.size} bytes)") +``` + +Interesting `func_t` properties: +```python +class func_t: + name: str + flags: int + start_ea: int + end_ea: int + size: int + does_return: bool + referers: list[int] # function start addresses + addresses: list[int] + frame_object: tinfo_t + prototype: tinfo_t +``` + + +#### Cross-references +```python +for xref in db.xrefs.to_ea(target_addr): + print(f"Referenced from {hex(xref.from_ea)} (type: {xref.type.name})") + +for xref in db.xrefs.from_ea(source_addr): + print(f"References {hex(xref.to_ea)}") + +for xref in db.xrefs.calls_to_ea(func_addr): + print(f"Called from {hex(xref.from_ea)}") +``` + +`XrefInfo` type: +```python +XrefInfo( + from_ea: int, + to_ea: int, + is_code: bool, + type: XrefType, + user: bool, +) +``` + +#### Read data +```python +db.bytes.get_byte_at(addr) +db.bytes.get_bytes_at(addr) +db.bytes.get_cstring_at(addr) +db.bytes.get_word_at(addr) +db.bytes.get_dword_at(addr) +db.bytes.get_qword_at(addr) +db.bytes.get_disassembly_at(addr) +db.bytes.get_flags_at(addr) +``` + + +## Plugin Manager Integration + +Plugins must be compatible with the Hex-Rays Plugin Manager. + +Making your plugin available via Plugin Manager offers several benefits: + +- simplified plugin installation +- improved plugin discoverability through the central index +- easy Python dependency management + +The key points to make your IDA plugin available via Plugin Manager are: + +- Add `ida-plugin.json` +- Package your plugin into a ZIP archive (via source archives or GitHub Actions) +- Publish releases on GitHub + +A complete `ida-plugin.json` example: + +```json +{ + "IDAMetadataDescriptorVersion": 1, + "plugin": { + "name": "ida-terminal-plugin", + "entryPoint": "index.py", + "version": "1.0.0", + "idaVersions": ">=9.2", + "platforms": [ + "windows-x86_64", + "linux-x86_64", + "macos-x86_64", + "macos-aarch64", + ], + "description": "A lightweight terminal integration for IDA Pro that lets you open a fully functional terminal within the IDA GUI.\nQuickly access shell commands, scripts, or tooling without leaving your reversing environment.", + "license": "MIT", + "logoPath": "ida-plugin.png", + "categories": [ + "ui-ux-and-visualization" + ], + "keywords": [ + "terminal", + "shell", + "cli", + ], + "pythonDependencies": [ + "pydantic>=2.12" + ], + "urls": { + "repository": "https://github.com/williballenthin/idawilli" + }, + "authors": [{ + "name": "Willi Ballenthin", + "email": "wballenthin@hex-rays.com" + }], + "settings": [ + { + "key": "theme", + "type": "string", + "required": true, + "default": "darcula", + "name": "color theme", + "documentation": "the color theme name, picked from https://windowsterminalthemes.dev/", + } + ] + } +} +``` + +Before completing your work, review the following resources for packaging hints: + +- https://hcli.docs.hex-rays.com/reference/plugin-repository-architecture/ +- https://hcli.docs.hex-rays.com/reference/plugin-packaging-and-format/ +- https://hcli.docs.hex-rays.com/reference/packaging-your-existing-plugin/ + +Use the script `./scripts/hcli-package.py` to invoke HCLI in a consistent way and lint the current plugin. + +## Use ida-settings for configuration values + +ida-settings is a Python library used by IDA Pro plugins to fetch configuration values from the shared settings infrastructure. + +During plugin installation, the plugin manager prompts users for the configuration values and stores them in `ida-config.json`. +Subsequently, users can invoke HCLI (or later, the IDA Pro GUI) to update their configuration. +ida-settings is the library that plugins use to fetch the configuration values. + +For example: + +```python +import ida_settings +api_key = ida_settings.get_current_plugin_setting("openai_key") +``` + +Note that this must be called from within the plugin (`plugin_t` or `plugmod_t`), not a callback or hook; +capture an instance of the plugin settings and pass it around as necessary: + +```python +class Hooks(idaapi.IDP_Hooks): + def __init__(self, settings): + super().__init__() + self.settings = settings + + def ev_get_bg_color(self, color, ea): + mnem = ida_ua.print_insn_mnem(ea) + + if mnem == "call" or mnem == "CALL": + bgcolor = ctypes.cast(int(color), ctypes.POINTER(ctypes.c_int)) + bgcolor[0] = int(settings.get_setting("bg_color")) + return 1 + + else: + return 0 + +class FooPluginMod(ida_idaapi.plugmod_t): + def run(self, arg): + settings = ida_settings.get_current_plugin_settings() + self.hooks = Hooks(settings) + self.hooks.hook() + +``` + +Available APIs are: + - `del(_current)_plugin_setting` + - `get(_current)_plugin_setting` + - `has(_current)_plugin_setting` + - `set(_current)_plugin_setting` + - `list(_current)_plugin_settings` + + +## Use standard logging module + +Don't use `print` for status messages - use `logging.*` routines. +Do not configure logging from within a plugin - its up to the user to + configure which levels and sources they want to see in their output window. + + +## Plugin Entry Point + +The entrypoint of the plugin should be `foo_entry.py` +which imports from `foo.py` only if the environment is correct. + +If the plugin runs in all IDA environments (assuming dependencies are present, which is reasonable), +then you don't need a special wrapper like this. + +For example, if the plugin requires Qt and/or IDA to be running graphically, you could do something like: + +`foo_entry.py`: +```python +import logging +import os + +import ida_kernwin + +logger = logging.getLogger(__name__) + +def should_load(): + """Returns True if IDA 9.2+ is running interactively.""" + if not ida_kernwin.is_idaq(): + # https://community.hex-rays.com/t/how-to-check-if-idapythonrc-py-is-running-in-ida-pro-or-idalib/297/4 + return False + + if os.environ.get("IDA_IS_INTERACTIVE") != "1": + # https://community.hex-rays.com/t/how-to-check-if-idapythonrc-py-is-running-in-ida-pro-or-idalib/297/2 + return False + + kernel_version: tuple[int, ...] = tuple( + int(part) for part in ida_kernwin.get_kernel_version().split(".") if part.isdigit() + ) or (0,) + if kernel_version < (9, 2): # type: ignore + logger.warning("IDA too old (must be 9.2+): %s", ida_kernwin.get_kernel_version()) + return False + + return True + + +if should_load(): + # only attempt to import the plugin once we know the required dependencies are present. + # otherwise we'll hit ImportError and other problems + from foo import foo_plugin_t + + def PLUGIN_ENTRY(): + return foo_plugin_t() + +else: + try: + import ida_idaapi + except ImportError: + import idaapi as ida_idaapi + + class foo_nop_plugin_t(ida_idaapi.plugin_t): + flags = ida_idaapi.PLUGIN_HIDE | ida_idaapi.PLUGIN_UNL + wanted_name = "foo disabled" + comment = "foo is disabled for this IDA version" + help = "" + wanted_hotkey = "" + + def init(self): + return ida_idaapi.PLUGIN_SKIP + + # we have to define this symbol, or IDA logs a message + def PLUGIN_ENTRY(): + # we have to return something here, or IDA logs a message + return foo_nop_plugin_t() +``` + +`foo.py`: +```python +class foo_plugmod_t(ida_idaapi.plugmod_t): + def __init__(self): + # IDA doesn't invoke this for plugmod_t, only plugin_t + self.init() + + def init(self): + # do things here that will always run, + # and don't require the menu entry (edit > plugins > ...) being selected. + # + # note: IDA doesn't call init, we do in __init__ + + if not ida_auto.auto_is_ok(): + # don't capture events before auto-analysis is done, or we get all the system events. + # + # note: + # - when we first load a program, this plugin will be run before auto-analysis is complete + # (actually, before auto-analysis even starts). + # so auto_is_ok() returns False + # - when we load an existing IDB, auto_is_ok() return True. + # so we can safely use this to wait until auto-analysis is complete for the first time. + logger.debug("waiting for auto-analysis to complete before subscribing to events") + ida_auto.auto_wait() + logger.debug("auto-analysis complete, now subscribing to events") + + ... + + def run(self, arg): + # do things here that users invoke via the menu entry (edit > plugins > ...) + ... + + def term(self): + # cleanup resources, unhook handlers, etc. + ... + +class foo_plugin_t(ida_idaapi.plugin_t): + flags = ida_idaapi.PLUGIN_MULTI + help = "Do some foo" + comment = "" + wanted_name = "Foo" + wanted_hotkey = "" + + def init(self): + return foo_plugmod_t() +``` + + +## Hook Registration + +Create pairwise helper functions for registering/unregistering hooks, +and call these from `init`/`term` + +```python +class oplog_plugmod_t(ida_idaapi.plugmod_t): + def __init__(self): + self.idb_hooks: IDBChangedHook | None = None + self.location_hooks: UILocationHook | None = None + ... + + def register_idb_hooks(self): + assert self.events is not None + self.idb_hooks = IDBChangedHook(self.events) + self.idb_hooks.hook() + + def unregister_idb_hooks(self): + if self.idb_hooks: + self.idb_hooks.unhook() + + def register_location_hooks(self): + assert self.events is not None + self.location_hooks = UILocationHook(self.events) + self.location_hooks.hook() + + def unregister_location_hooks(self): + if self.location_hooks: + self.location_hooks.unhook() + + def init(self): + ... + self.register_idb_hooks() + self.register_location_hooks() + + def run(self, arg): + ... + + def term(self): + # cleanup in reverse order + self.unregister_location_hooks() + self.unregister_idb_hooks() + ... +``` + + +## Cross-Plugin Communication via IDC Functions + +Python plugins can import shared libraries, and two plugins may even have the same dependencies. One plugin can import code from another plugin's module. However, to invoke functionality on a specific *instance* of a running plugin (accessing its state, calling methods that depend on instance data), you need a different mechanism. + +Use `ida_expr.add_idc_func` to register a callable with a well-known name, and `idc.eval_idc` to invoke it from scripts or other plugins. + +Key constraints: +- The function name must be globally unique - only one plugin should register a given name +- There's only a single provider for that name (no multiple instances registering the same name) +- The registering plugin must unregister the function during `term()` + +```python +import ida_expr + +class foo_plugmod_t(ida_idaapi.plugmod_t): + def __init__(self): + self.data: list[str] = [] + self.init() + + def register_idc_func(self): + data = self.data + + def foo_get_data(index: int) -> str: + if 0 <= index < len(data): + return data[index] + return "" + + def foo_add_data(value: str) -> int: + data.append(value) + return len(data) + + if ida_expr.add_idc_func("foo_get_data", foo_get_data, (ida_expr.VT_LONG,)): + logger.debug("registered foo_get_data IDC function") + else: + logger.warning("failed to register foo_get_data IDC function") + + if ida_expr.add_idc_func("foo_add_data", foo_add_data, (ida_expr.VT_STR,)): + logger.debug("registered foo_add_data IDC function") + else: + logger.warning("failed to register foo_add_data IDC function") + + def unregister_idc_func(self): + ida_expr.del_idc_func("foo_get_data") + ida_expr.del_idc_func("foo_add_data") + + def init(self): + self.register_idc_func() + + def term(self): + self.unregister_idc_func() +``` + +Callers invoke the function via `idc.eval_idc`: + +```python +import idc + +idc.eval_idc('foo_add_data("hello")') +result = idc.eval_idc('foo_get_data(0)') +``` + +Parameter types for `add_idc_func`: +- `ida_expr.VT_STR` - string parameter +- `ida_expr.VT_LONG` - integer parameter +- `ida_expr.VT_FLOAT` - floating point parameter + +This pattern is useful for: +- Exporting plugin data to external scripts (headless testing, automation) +- Allowing one plugin to trigger actions in another +- Providing a stable API for plugin functionality that doesn't depend on Python imports + + +## Save/Load state from netnodes + +Use netnodes to store data within the IDB. +Serialize the current plugin state during shutdown, saving it to a netnode. +Reload the state upon startup. + + +```python +import pydantic + +OUR_NETNODE = "$ com.williballenthin.idawilli.foo" + + +class State(pydantic.BaseModel): + ... + + def to_json(self): + return self.model_dump_json() + + @classmethod + def from_json(cls, json_str: str): + return cls(State.model_validate_json(json_str)) + + +def save_state(state: State): + buf = zlib.compress(state.to_json().encode("utf-8")) + + node = ida_netnode.netnode(OUR_NETNODE) + node.setblob(buf, 0, "I") + + logger.info("saved state") + + +def load_state() -> State: + node = ida_netnode.netnode(OUR_NETNODE) + if not node: + logger.info("no existing state") + return State() + + buf = node.getblob(0, "I") + if not buf: + logger.info("no existing state (no data)") + return State() + + state = State.from_json(zlib.decompress(buf).decode("utf-8")) + logger.info("loaded state") + return state + + +class UI_Closing_Hooks(ida_kernwin.UI_Hooks): + """Respond to UI events and save the events into the database.""" + + # we could also use IDB_Hooks, but I found it less reliable: + # - closebase: "the database will be closed now", however, I couldn't figure out when its actually triggered. + # - savebase: notified during File -> Save, but not File -> Close. + # easier to keep all the hooks in one place. + + def __init__(self, events: Events, *args, **kwargs): + super().__init__(*args, **kwargs) + self.events = events + + def preprocess_action(self, action: str): + if action == "CloseBase": + # File -> Close + save_events(self.events) + return 0 + elif action == "QuitIDA": + # File -> Quit + save_events(self.events) + return 0 + elif action == "SaveBase": + # File -> Save + save_events(self.events) + return 0 + else: + return 0 +``` + +## Respond to current address and selection change + +```python +class UILocationHook(ida_kernwin.UI_Hooks): + def handle_current_address_change(self, ea: int): + ... + + def handle_current_selection_change(self, start: int, end: int): + ... + + def screen_ea_changed(self, ea: ida_idaapi.ea_t, prev_ea: ida_idaapi.ea_t) -> None: + if ea == prev_ea: + return + + v = ida_kernwin.get_current_viewer() + + if ida_kernwin.get_widget_type(v) not in ( + ida_kernwin.BWN_HEXVIEW, + ida_kernwin.BWN_DISASM, + # BWN_PSEUDOCODE + # BWN_CUSTVIEW + # BWN_OUTPUT the text area, in the output window + # BWN_CLI the command-line, in the output window + # BWN_STRINGS + # ... + ): + return + + if ida_kernwin.get_viewer_place_type(v) != ida_kernwin.TCCPT_IDAPLACE: + # other viewers might have other place types, when not address-oriented + return + + has_range, start, end = ida_kernwin.read_range_selection(v) + if not has_range: + return self.handle_current_address_change(ea) + + if ida_idaapi.BADADDR in (start, end): + return + + return self.handle_current_selection_change(start, end) +``` + +## Find widgets by prefix + +```python +def list_widgets(prefix: str) -> list[str]: + """Probe A-Z for existing widgets, return found captions. + + Args: + prefix: Caption prefix to search for + + Returns: List of found widget captions (e.g., ["Foo-A", "Foo-C"]) + """ + if not prefix.endswith("-"): + raise ValueError("prefix must end with dash") + found = [] + for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ": + caption = f"{prefix}{letter}" + if ida_kernwin.find_widget(caption) is not None: + found.append(caption) + return found + + +def find_next_available_caption(prefix: str) -> str: + """Find first gap or next letter for widget caption. + + Args: + prefix: Caption prefix to use + + Returns: First available caption (e.g., "Foo-B") + + Raises: + RuntimeError: If all 26 instances are in use + """ + if not prefix.endswith("-"): + raise ValueError("prefix must end with dash") + for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ": + caption = f"{prefix}{letter}" + if ida_kernwin.find_widget(caption) is None: + return caption + raise RuntimeError("All 26 instances in use") +``` + +## Context Menu Entries, "Send to Foo" and "Send to Foo-A" + +When creating custom views, especially when there might be more than one, + name them like "Foo-A", "Foo-B", etc. +And, as appropriate, add context menu items for "sending" addresses/selections + to the new views. + +The new view is an instance of `ida_kernwin.PluginForm` and may have arbitrary Qt widgets. +The plugin instance maintains a registry of created views, and registers the action +handlers for opening new views, as well as notifying the views of events from a central place. +Action handlers encapsulate the code that's invoked during an event. + + +```python +class FooForm(ida_kernwin.PluginForm): + def __init__( + self, + caption: str = "Foo-A", + form_registry: dict[str, "FooForm"] | None = None, + ) -> None: + super().__init__() + self.TITLE = caption + self.form_registry = form_registry + + def OnCreate(self, form): + self.parent = self.FormToPyQtWidget(form) + self.w = FooWidget(parent=self.parent, show_ida_buttons=True) + + ... # other Qt stuff here + + if self.form_registry is not None: + self.form_registry[self.TITLE] = self + + def OnClose(self, form): + if self.form_registry is not None: + self.form_registry.pop(self.TITLE, None) + + +class create_foo_widget_action_handler_t(ida_kernwin.action_handler_t): + def __init__(self, plugmod: "foo_plugmod_t", *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.plugmod = plugmod + + def activate(self, ctx): + self.plugmod.create_viewer() + + def update(self, ctx): + return ida_kernwin.AST_ENABLE_ALWAYS + + +class send_to_foo_action_handler_t(ida_kernwin.action_handler_t): + """Action handler for 'Send to Foo' context menu item.""" + + def __init__(self, plugmod: "foo_plugmod_t", *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self.plugmod = plugmod + + def activate(self, ctx): + """Handle 'Send to Foo' action - always creates new instance.""" + v = ida_kernwin.get_current_viewer() + + if ida_kernwin.get_widget_type(v) not in ( + ida_kernwin.BWN_HEXVIEW, + ida_kernwin.BWN_DISASM, + ): + # for example: only allow sending from hexview or disassembly view + return 0 + + form = self.plugmod.create_viewer() + + if form and form.w: + ... # do initialization + + return 1 + + def update(self, ctx): + """Enable action when there's a valid selection.""" + v = ida_kernwin.get_current_viewer() + + if ida_kernwin.get_widget_type(v) not in ( + ida_kernwin.BWN_HEXVIEW, + ida_kernwin.BWN_DISASM, + ): + # for example: only allow sending from hexview or disassembly view + return ida_kernwin.AST_DISABLE + return ida_kernwin.AST_ENABLE + + +class send_to_specific_widget_action_handler_t(ida_kernwin.action_handler_t): + """Action handler for sending to a specific Foo instance.""" + + def __init__( + self, + form_registry: dict[str, FooForm], + caption: str, + *args, + **kwargs, + ) -> None: + super().__init__(*args, **kwargs) + self.form_registry = form_registry + self.caption = caption + + def activate(self, ctx): + """Send selection to specific Foo instance.""" + v = ida_kernwin.get_current_viewer() + + widget = ida_kernwin.find_widget(self.caption) + if widget is None: + logger.warning(f"Widget {self.caption} not found") + return 0 + + ida_kernwin.activate_widget(widget, True) + + form = self.form_registry.get(self.caption) + if form and hasattr(form, "w"): + # access some specific model methods on the form + ... + else: + logger.warning(f"Cannot populate {self.caption} - unable to access form") + + return 1 + + def update(self, ctx): + """Enable action when there's a valid selection.""" + v = ida_kernwin.get_current_viewer() + + if ida_kernwin.get_widget_type(v) not in ( + ida_kernwin.BWN_HEXVIEW, + ida_kernwin.BWN_DISASM, + ): + # for example: only allow sending from hexview or disassembly view + return ida_kernwin.AST_DISABLE + return ida_kernwin.AST_ENABLE + + +class foo_plugmod_t(ida_idaapi.plugmod_t): + ACTION_NAME = "foo:create" + SEND_ACTION_NAME = "foo:send_selection" + MENU_PATH = "View/Open subviews/Foo" + + def __init__(self): + super().__init__() + self.form_registry: dict[str, FooForm] = {} + ... + + def register_instance_actions(self): + """Register actions for all existing widget instances.""" + existing = list_widgets("Foo-") + + for caption in existing: + action_name = f"foo:send_to_{caption.replace('-', '_').lower()}" + + if ida_kernwin.unregister_action(action_name): + pass + + ida_kernwin.register_action( + ida_kernwin.action_desc_t( + action_name, + f"Send to {caption}", + send_to_specific_widget_action_handler_t( + self.form_registry, caption + ), + None, + f"Send selected bytes to {caption}", + -1, + ) + ) + + def create_viewer(self, caption: str | None = None) -> FooForm: + if caption is None: + caption = find_next_available_caption() + form = FooForm(caption, self.form_registry) + form.Show(form.TITLE) + return form + + def register_open_action(self): + ida_kernwin.register_action( + ida_kernwin.action_desc_t( + self.ACTION_NAME, + "Foo", + create_foo_widget_action_handler_t(self), + ) + ) + + # TODO: add icon + ida_kernwin.attach_action_to_menu( + self.MENU_PATH, self.ACTION_NAME, ida_kernwin.SETMENU_APP + ) + + def unregister_open_action(self): + ida_kernwin.unregister_action(self.ACTION_NAME) + ida_kernwin.detach_action_from_menu(self.MENU_PATH, self.ACTION_NAME) + + + def init(self): + self.register_open_action() + ... + + def run(self, arg): + self.create_viewer() + + def term(self): + ... + self.unregister_open_action() +``` + +## User Defined Prefix + +A user defined prefix is a great way to add some contextual data before each disassembly line. +Put symbols or numbers here to indicate there's more context available somewhere. + + +```python +def refresh_disassembly(): + ida_kernwin.request_refresh(ida_kernwin.IWID_DISASM) + +class FooPrefix(ida_lines.user_defined_prefix_t): + ICON = " β " + + def __init__(self, marks: set[int]): + super().__init__(len(self.ICON)) + self.marks = marks + + def get_user_defined_prefix(self, ea, insn, lnnum, indent, line): + if ea in self.marks: + # wrap the icon in color tags so its easy to identify. + # otherwise, the icon may merge with other spans, which + # makes checking for equality more difficult. + return ida_lines.COLSTR(self.ICON, ida_lines.SCOLOR_SYMBOL) + + return " " * len(self.ICON) + +class FooPrefixPluginMod(ida_idaapi.plugmod_t): + def __init__(self): + self.marks: set[int] = {1, 2, 3} + self.prefixer: FooPrefix | None = None + + def run(self, arg): + # self.prefixer is installed simply by constructing it + self.prefixer = FooPrefix(self.marks) + + # since we're updating the disassembly listing by adding the line prefix, + # we need to re-render all the lines. + refresh_disassembly() + + def term(self): + # gc will clean up prefixer and uninstall it (during plugin termination) + self.prefixer = None + + # refresh and remove the prefix entries + refresh_disassembly() +``` + +## Viewer Hints + +A view hint is a really good way to display complex information in a popup hover pane +that displays when mousing over particular regions of an IDA view. +Use this to show context about a symbol or address, for example: MSDN documentation for API functions. + +Use this in combination with User Defined Prefixes that indicate context *is* available and +show the context *in* the viewer hint (possibly when hovering over the prefix). + +```python +class FooHints(ida_kernwin.UI_Hooks): + def __init__(self, notes: dict[int, str], *args, **kwargs): + super().__init__(*args, **kwargs) + self.notes = notes + + def get_custom_viewer_hint(self, viewer, place): + if not place: + return + + ea = place.toea() + if not ea: + return + + if ea not in self.notes: + return + + curline = ida_kernwin.get_custom_viewer_curline(viewer, True) + curline = ida_lines.tag_remove(curline) + _, x, _ = ida_kernwin.get_custom_viewer_place(viewer, True) + + # example: show on first column + # more advanced: inspect the symbol, and if it matches a query, then show some data + if x == 1: + note = self.notes.get(ea) + if not note: + return + + return (f"note: {note}", 1) + + +class FooHintsPluginMod(ida_idaapi.plugmod_t): + def __init__(self): + self.notes: dict[int, str] = {} + self.hinter: FooHints | None = None + + def run(self, arg): + self.hinter = FooHints(self.notes) + self.hinter.hook() + + def term(self): + if self.hinter is not None: + self.hinter.unhook() + + self.hinter = None +``` + +## Overriding rendering + + + +```python +class ColorHooks(idaapi.IDP_Hooks): + def ev_get_bg_color(self, color, ea): + """ + Get item background color. + Plugins can hook this callback to color disassembly lines dynamically + + // background color in RGB + typedef uint32 bgcolor_t; + + ref: https://hex-rays.com/products/ida/support/sdkdoc/pro_8h.html#a3df5040891132e50157aee66affdf1de + + args: + color: (bgcolor_t *), out + ea: (::ea_t) + + returns: + retval 0: not implemented + retval 1: color set + """ + mnem = ida_ua.print_insn_mnem(ea) + + if mnem == "call" or mnem == "CALL": + bgcolor = ctypes.cast(int(color), ctypes.POINTER(ctypes.c_int)) + bgcolor[0] = 0xDDDDDD + return 1 + + else: + return 0 + + def ev_out_mnem(self, ctx) -> int: + """ + Generate instruction mnemonics. + This callback should append the colored mnemonics to ctx.outbuf + Optional notification, if absent, out_mnem will be called. + + args: + ctx: (outctx_t *) + + returns: + retval 1: if appended the mnemonics + retval 0: not implemented + """ + mnem = ctx.insn.get_canon_mnem() + if mnem == "call": + # you can manipulate this, but note that it affects `ida_ua.print_insn_mnem` which is inconvenient for formatting. + # also, you only have access to theme colors, like COLOR_PREFIX, not arbitrary control. + ctx.out_custom_mnem("CALL") + return 1 + + else: + return 0 + + +class ColoringPluginMod(ida_idaapi.plugmod_t): + def __init__(self): + self.hooks: ColorHooks | None = None + + def run(self, arg): + self.hooks = ColorHooks() + self.hooks.hook() + + def term(self): + if self.hooks is not None: + self.hooks.unhook() + + self.hooks = None +``` + + +## Custom Viewers + +Use a custom viewer to show text data, optionally with tags, and respond to basic events (clicks). +Use the tagged line concepts to embed and parse metadata about the symbols in a line, +such as which address it refers to. + +```python +def addr_from_tag(raw: bytes) -> int: + assert raw[0] == 0x01 # ida_lines.COLOR_ON + assert raw[1] == ida_lines.COLOR_ADDR + addr_hex = raw[2 : 2 + ida_lines.COLOR_ADDR_SIZE].decode("ascii") + + try: + # Parse as hex address (IDA uses qsscanf with "%a" format) + return int(addr_hex, 16) + except ValueError: + raise + + +def get_tagged_line_section_byte_offsets(section: ida_kernwin.tagged_line_section_t) -> tuple[int, int]: + # tagged_line_section_t.byte_offsets is not exposed by swig + # so we parse directly from the string representation (puke) + s = str(section) + text_start_index = s.index("text_start=") + text_end_index = s.index("text_end=") + + text_start_s = s[text_start_index + len("text_start=") :].partition(",")[0] + text_end_s = s[text_end_index + len("text_end=") :].partition("}")[0] + + return int(text_start_s), int(text_end_s) + + +@dataclass +class TaggedLineSection: + tag: int + string: str + # valid when the found tag section starts with an embedded address + address: int | None + + +def get_current_tag(line: str, x: int) -> TaggedLineSection: + ret = TaggedLineSection(ida_lines.COLOR_DEFAULT, line, None) + + tls = ida_kernwin.tagged_line_sections_t() + if not ida_kernwin.parse_tagged_line_sections(tls, line): + return ret + + # find any section at the X coordinate + current_section = tls.nearest_at(x, 0) # 0 = any tag + if not current_section: + # TODO: we only want the section that isn't tagged + # while there might be a section totally before or totally after x. + return ret + + ret.tag = current_section.tag + boring_line = ida_lines.tag_remove(line) + ret.string = boring_line[current_section.start : current_section.start + current_section.length] + + # try to find an embedded address at the start of the current segment + current_section_start, _ = get_tagged_line_section_byte_offsets(current_section) + addr_section = tls.nearest_before(current_section, x, ida_lines.COLOR_ADDR) + if addr_section: + addr_section_start, _ = get_tagged_line_section_byte_offsets(addr_section) + # addr_section_start initially points just after the address data (ON ADDR 001122...FF) + # so rewind to the start of the tag (16 bytes of hex integer, 2 bytes of tags "ON ADDR") + addr_tag_start = addr_section_start - (ida_lines.COLOR_ADDR_SIZE + 2) + assert addr_tag_start >= 0 + + # and this should match current_section_start, since that points just after the tag "ON SYMBOL" + # if it doesn't, we're dealing with an edge case we didn't prepare for + # maybe like multiple ADDR tags or something. + # skip those and stick to things we know. + if current_section_start == addr_tag_start: + raw = line.encode("utf-8") + addr = addr_from_tag(raw[addr_tag_start : addr_tag_start + ida_lines.COLOR_ADDR_SIZE + 2]) + ret.address = addr + + return ret + + +class foo_viewer_t(ida_kernwin.simplecustviewer_t): + TITLE = "foo" + + def __init__(self): + super().__init__() + + self.timer: QtCore.QTimer = QtCore.QTimer() + self.timer.timeout.connect(self.on_timer_timeout) + + def Create(self): + if not super().Create(self.TITLE): + return False + + self.render() + + return True + + def Show(self, *args): + if not super().Show(*args): + return False + + ida_kernwin.attach_action_to_popup(self.GetWidget(), None, some_action_handler_t.ACTION_NAME) + return True + + def on_timer_timeout(self): + self.render() + + def OnClose(self): + self.timer.stop() + + def render(self): + self.ClearLines() + self.AddLine(datetime.datetime.now.isoformat()) + self.AddLine(ida_lines.COLSTR(ida_lines.tag_addr(0x401000) + "sub_401000", ida_lines.SCOLOR_CNAME)) + + def OnDblClick(self, shift): + line = self.GetCurrentLine() + if not line: + return False + + _linen, x, _y = self.GetPos() + + section = get_current_tag(line, x) + if section.address is not None: + ida_kernwin.jumpto(section.address) + + item_address = ida_name.get_name_ea(0, section.string) + if item_address != ida_idaapi.BADADDR: + logger.debug(f"found address for '{section.string}': {item_address:x}") + ida_kernwin.jumpto(item_address) + + return True # handled +``` + +``` + +`plugins/ida-plugin-development/skills/ida-plugin-development/scripts/hcli-package.py`: + +```py +#!/usr/bin/env python3 +""" +IDA Plugin Skill - Package Script + +Packages a plugin directory into an HCLI-compatible ZIP archive, +runs lint validation, and optionally installs the plugin. +""" + +import argparse +import json +import subprocess +import sys +import zipfile +from pathlib import Path + + +def validate_plugin_dir(plugin_dir: Path) -> bool: + """Validate that the plugin directory has required files.""" + manifest = plugin_dir / "ida-plugin.json" + + if not manifest.exists(): + print(f"ERROR: ida-plugin.json not found in {plugin_dir}") + return False + + # Parse and validate manifest + try: + with open(manifest) as f: + data = json.load(f) + except json.JSONDecodeError as e: + print(f"ERROR: Invalid JSON in ida-plugin.json: {e}") + return False + + # Check required fields + required = ["IDAMetadataDescriptorVersion", "plugin"] + for field in required: + if field not in data: + print(f"ERROR: Missing required field: {field}") + return False + + plugin_data = data.get("plugin", {}) + plugin_required = ["name", "version", "entryPoint", "authors", "urls"] + for field in plugin_required: + if field not in plugin_data: + print(f"ERROR: Missing required plugin field: {field}") + return False + + # Check entry point exists + entry_point = plugin_data.get("entryPoint", "") + entry_path = plugin_dir / entry_point + if not entry_path.exists(): + print(f"ERROR: Entry point not found: {entry_point}") + return False + + return True + + +def create_zip(plugin_dir: Path, output_path: Path) -> bool: + """Create a ZIP archive from the plugin directory.""" + try: + with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zf: + for file_path in plugin_dir.rglob('*'): + if file_path.is_file(): + # Skip common unwanted files + if file_path.name.startswith('.') or file_path.suffix == '.pyc': + continue + if '__pycache__' in str(file_path): + continue + + arcname = file_path.relative_to(plugin_dir) + zf.write(file_path, arcname) + return True + except Exception as e: + print(f"ERROR: Failed to create ZIP: {e}") + return False + + +def run_lint(zip_path: Path) -> bool: + """Run hcli plugin lint on the ZIP archive.""" + try: + result = subprocess.run( + ["uv", "run", "--with=ida-hcli", "hcli", "plugin", "lint", str(zip_path)], + capture_output=True, + text=True, + timeout=60 + ) + + if result.stdout: + print(result.stdout) + if result.stderr: + print(result.stderr) + + return result.returncode == 0 + except FileNotFoundError: + print("ERROR: hcli not found. Run setup.py first.") + return False + except subprocess.TimeoutExpired: + print("ERROR: hcli lint timed out") + return False + + +def install_plugin(zip_path: Path) -> bool: + """Install the plugin using hcli.""" + try: + result = subprocess.run( + ["uv", "run", "--with=ida-hcli", "hcli", "plugin", "install", str(zip_path)], + capture_output=True, + text=True, + timeout=120 + ) + + if result.stdout: + print(result.stdout) + if result.stderr: + print(result.stderr) + + return result.returncode == 0 + except FileNotFoundError: + print("ERROR: hcli not found. Run setup.py first.") + return False + except subprocess.TimeoutExpired: + print("ERROR: hcli install timed out") + return False + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Package an IDA plugin for distribution" + ) + parser.add_argument( + "plugin_dir", + type=Path, + help="Path to the plugin directory" + ) + parser.add_argument( + "-o", "--output", + type=Path, + help="Output ZIP path (default: .zip in current directory)" + ) + parser.add_argument( + "--install", + action="store_true", + help="Install the plugin after packaging" + ) + parser.add_argument( + "--no-lint", + action="store_true", + help="Skip lint validation" + ) + + args = parser.parse_args() + + plugin_dir = args.plugin_dir.resolve() + + if not plugin_dir.is_dir(): + print(f"ERROR: Not a directory: {plugin_dir}") + return 1 + + print("IDA Plugin Packager") + print("=" * 40) + print() + + # Validate + print(f"Validating: {plugin_dir}") + if not validate_plugin_dir(plugin_dir): + return 1 + print(" Validation passed") + print() + + # Determine output path + if args.output: + output_path = args.output.resolve() + else: + # Get plugin name from manifest + with open(plugin_dir / "ida-plugin.json") as f: + data = json.load(f) + plugin_name = data["plugin"]["name"] + output_path = Path.cwd() / f"{plugin_name}.zip" + + # Create ZIP + print(f"Creating: {output_path}") + if not create_zip(plugin_dir, output_path): + return 1 + print(" ZIP created") + print() + + # Lint + if not args.no_lint: + print("Running hcli plugin lint...") + if not run_lint(output_path): + print(" Lint FAILED") + return 1 + print(" Lint passed") + print() + + # Install + if args.install: + print("Installing plugin...") + if not install_plugin(output_path): + print(" Install FAILED") + return 1 + print(" Install successful") + print() + + print("Done!") + print(f"Plugin archive: {output_path}") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) + +``` + +`plugins/ida-plugin-development/skills/package-ida-plugin/SKILL.md`: + +```md +--- +name: package-ida-plugin +description: Package IDA Pro plugins for the IDA Plugin Manager and plugins.hex-rays.com repository +--- + +# Packaging IDA Pro Plugins + +This skill helps package IDA Pro plugins for distribution via the IDA Plugin Manager and the plugins.hex-rays.com repository. It covers creating and updating the `ida-plugin.json` manifest, packaging archives, and publishing via GitHub Releases. + +## Overview + +The IDA Plugin Manager is a self-service ecosystem for discovering, installing, and sharing IDA plugins: + +- **Discovery**: A daily indexer scans GitHub for repositories containing `ida-plugin.json` +- **Repository**: Published at [plugins.hex-rays.com](https://plugins.hex-rays.com/) and [github.com/HexRaysSA/plugin-repository](https://github.com/HexRaysSA/plugin-repository) +- **Client**: HCLI command-line tool (`hcli plugin install `) +- **Compatibility**: IDA Pro 9.0+ (full support) + +## Critical: Understanding Plugin Root Directory + +**The `ida-plugin.json` file defines the root of the plugin.** When a plugin is installed, only the directory containing `ida-plugin.json` (and its subdirectories) is copied to `$IDAUSR/plugins/`. Nothing outside this directory is included. + +### Assessing Directory Structure Compatibility + +Before packaging, verify that your plugin's structure is self-contained: + +``` +# GOOD: All plugin code is in the same directory as ida-plugin.json +my-repo/ +├── ida-plugin.json # Plugin root +├── my_plugin.py # Entry point - INCLUDED +├── my_plugin_lib/ # Supporting code - INCLUDED +│ └── helpers.py +├── README.md # Plugin README - INCLUDED (shown on web) +└── assets/ + └── logo.png # Logo - INCLUDED + +# BAD: Plugin code outside the ida-plugin.json directory +my-repo/ +├── plugin/ +│ └── ida-plugin.json # Plugin root is here +├── src/ # NOT INCLUDED - outside plugin root! +│ └── my_plugin.py # This file won't be installed! +└── README.md # NOT INCLUDED - wrong directory! +``` + +### Compatibility Checklist + +When assessing an existing plugin, verify: + +1. [ ] **Entry point location**: Is `entryPoint` in the same directory as `ida-plugin.json`? +2. [ ] **All imports resolvable**: Are all Python imports within the plugin root or in `pythonDependencies`? +3. [ ] **No parent directory references**: Does the code use `../` to access files outside the plugin root? +4. [ ] **Assets included**: Are logos, data files, or resources inside the plugin root? +5. [ ] **README placement**: Is `README.md` in the plugin root (not repo root) for web display? + +### Common Restructuring Patterns + +**Pattern 1: Plugin in subdirectory** + +If your plugin code is in a subdirectory like `src/` or `plugin/`, move `ida-plugin.json` into that directory: + +``` +# Before # After +my-repo/ my-repo/ +├── ida-plugin.json └── src/ +└── src/ ├── ida-plugin.json # Moved here + └── my_plugin.py ├── my_plugin.py + └── README.md # Add for web +``` + +**Pattern 2: Monorepo with IDA plugin** + +For projects where IDA plugin is one component (e.g., capa), place `ida-plugin.json` at the plugin code's location: + +``` +capa/ +├── capa/ +│ ├── ida/ +│ │ └── plugin/ +│ │ ├── ida-plugin.json # Plugin root +│ │ ├── capa_explorer.py # Entry point +│ │ └── README.md # Plugin-specific docs +│ └── main.py # Not included in IDA plugin +└── README.md # Repo README - not the plugin README +``` + +### README in Plugin Root + +Place a `README.md` file in the same directory as `ida-plugin.json` to have it displayed on the [plugins.hex-rays.com](https://plugins.hex-rays.com/) web interface. This is separate from your repository's root README: + +``` +my-plugin/ +├── ida-plugin.json +├── my_plugin.py +└── README.md # This README appears on plugins.hex-rays.com +``` + +The plugin README should focus on: +- What the plugin does +- How to use it within IDA +- Configuration options (if using settings) +- Screenshots or examples + +It does **not** need installation instructions (the Plugin Manager handles that). + +## The ida-plugin.json Manifest + +Every plugin requires an `ida-plugin.json` file in its root directory. This is the **only required file** beyond the plugin code itself. Paths in the metadata file are relative to the metadata file, because the metadata file defines the root of the plugin, even if its nested within a ZIP archive. + +### Complete Schema + +```json +{ + "IDAMetadataDescriptorVersion": 1, + "plugin": { + "name": "my-plugin", + "version": "1.0.0", + "entryPoint": "my_plugin.py", + "description": "A one-line description of what this plugin does", + "license": "MIT", + "urls": { + "repository": "https://github.com/org/my-plugin", + "homepage": "https://example.com/my-plugin" + }, + "authors": [ + {"name": "Author Name", "email": "author@example.com"} + ], + "maintainers": [ + {"name": "Maintainer Name", "email": "maintainer@example.com"} + ], + "idaVersions": ["9.0", "9.1", "9.2"], + "platforms": ["windows-x86_64", "linux-x86_64", "macos-x86_64", "macos-aarch64"], + "categories": ["malware-analysis"], + "keywords": ["analysis", "automation"], + "pythonDependencies": ["requests>=2.0", "pydantic>=2"], + "logoPath": "assets/logo.png", + "settings": [] + } +} +``` + +### Required Fields + +| Field | Type | Description | +|-------|------|-------------| +| `IDAMetadataDescriptorVersion` | `1` | Always set to `1` | +| `plugin.name` | string | Unique identifier. ASCII letters, digits, underscores, hyphens only. No leading/trailing `_` or `-`. Used to derive the IDA namespace: `__plugins__my_plugin` | +| `plugin.version` | string | Semantic version `x.y.z` format. No leading `v` | +| `plugin.entryPoint` | string | Entry point filename (e.g., `my_plugin.py`) or bare name for native plugins | +| `plugin.urls.repository` | string | GitHub URL: `https://github.com/org/project` | +| `plugin.authors` OR `plugin.maintainers` | array | At least one contact with `email` field required | + +### Optional Fields + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `plugin.description` | string | none | One-line description shown in search results | +| `plugin.license` | string | none | License identifier (e.g., `MIT`, `Apache-2.0`, `GPL-3.0`) | +| `plugin.urls.homepage` | string | none | Homepage URL if different from repository | +| `plugin.idaVersions` | array or string | all versions | Supported IDA versions. Can be explicit list `["9.0", "9.1"]` or spec `">=7.4"` | +| `plugin.platforms` | array | all platforms | Supported platforms: `windows-x86_64`, `linux-x86_64`, `macos-x86_64`, `macos-aarch64` | +| `plugin.categories` | array | `[]` | See Categories section below | +| `plugin.keywords` | array | `[]` | Search terms for discoverability | +| `plugin.pythonDependencies` | array or `"inline"` | `[]` | PyPI packages. Use `"inline"` for PEP 723 metadata | +| `plugin.logoPath` | string | none | Relative path to logo image (16:9 aspect ratio recommended) | +| `plugin.settings` | array | `[]` | Plugin configuration options. See Settings section | + +### Valid Categories + +``` +disassembly-and-processor-modules +file-parsers-and-loaders +decompilation +debugging-and-tracing +deobfuscation +collaboration-and-productivity +integration-with-third-parties-interoperability +api-scripting-and-automation +ui-ux-and-visualization +malware-analysis +vulnerability-research-and-exploit-development +other +``` + +### Valid IDA Versions + +Current/supported: `9.2`, `9.1`, `9.0sp1`, `9.0`, ... + +Use a version specifier like `">=9.0"` to match multiple versions automatically. + +## Packaging Pure Python Plugins + +For simple Python plugins, the directory structure is minimal: + +``` +my-plugin/ +├── ida-plugin.json +├── README.md # Displayed on plugins.hex-rays.com +├── my_plugin.py # entryPoint +└── my_plugin_lib/ # optional supporting modules + ├── __init__.py + └── helpers.py +``` + +### Minimal Example + +```json +{ + "IDAMetadataDescriptorVersion": 1, + "plugin": { + "name": "my-simple-plugin", + "version": "1.0.0", + "entryPoint": "my_plugin.py", + "urls": { + "repository": "https://github.com/username/my-simple-plugin" + }, + "authors": [ + {"name": "Your Name", "email": "you@example.com"} + ] + } +} +``` + +### With Dependencies + +```json +{ + "IDAMetadataDescriptorVersion": 1, + "plugin": { + "name": "capa", + "version": "9.3.1", + "entryPoint": "capa_explorer.py", + "description": "Identify capabilities in executable files using FLARE's capa framework", + "license": "Apache-2.0", + "idaVersions": ">=7.4", + "categories": ["malware-analysis", "api-scripting-and-automation", "ui-ux-and-visualization"], + "pythonDependencies": ["flare-capa==9.3.1"], + "urls": { + "repository": "https://github.com/mandiant/capa" + }, + "authors": [ + {"name": "Willi Ballenthin", "email": "wballenthin@hex-rays.com"}, + {"name": "Moritz Raabe", "email": "moritzraabe@google.com"} + ], + "keywords": ["capability-detection", "malware-analysis", "att&ck", "static-analysis"] + } +} +``` + +### PEP 723 Inline Dependencies + +If your plugin uses PEP 723 inline script metadata, set `pythonDependencies` to `"inline"`: + +```json +{ + "pythonDependencies": "inline" +} +``` + +Then in your entry point Python file: + +```python +# /// script +# dependencies = [ +# "requests>=2.0", +# "pydantic>=2" +# ] +# /// + +import ida_kernwin +# ... rest of plugin +``` + +## Packaging Native Plugins + +Native plugins require compiled binaries (.dll, .so, .dylib) for each target platform. + +### Directory Structure + +``` +my-native-plugin/ +├── ida-plugin.json +├── README.md # Displayed on plugins.hex-rays.com +├── my_plugin.dll # Windows +├── my_plugin.so # Linux +└── my_plugin.dylib # macOS (universal or architecture-specific) +``` + +### Entry Point Convention + +For native plugins, use a **bare name** without extension: + +```json +{ + "plugin": { + "entryPoint": "my_plugin", + "platforms": ["windows-x86_64", "linux-x86_64", "macos-x86_64", "macos-aarch64"] + } +} +``` + +IDA will automatically append the correct extension (`.dll`, `.so`, `.dylib`) for the current platform. + +### Fat Binary Archives + +Include all platform binaries in a single archive. The entry point uses the bare name, and the correct binary is selected at install time. + +### Platform-Specific Archives + +Alternatively, create separate archives per platform with explicit extensions: + +**Windows archive:** +```json +{ + "plugin": { + "entryPoint": "my_plugin.dll", + "platforms": ["windows-x86_64"] + } +} +``` + +**Linux archive:** +```json +{ + "plugin": { + "entryPoint": "my_plugin.so", + "platforms": ["linux-x86_64"] + } +} +``` + +## Plugin Settings + +For plugins requiring user configuration, declare settings in the manifest. Users configure these via `hcli plugin config` or the IDA GUI. + +### Settings Schema + +```json +{ + "settings": [ + { + "key": "api_key", + "name": "API Key", + "type": "string", + "required": true, + "documentation": "Your API key for the service" + }, + { + "key": "endpoint", + "name": "API Endpoint", + "type": "string", + "required": false, + "default": "https://api.example.com", + "validation_pattern": "^https://.*$" + }, + { + "key": "log_level", + "name": "Log Level", + "type": "string", + "required": false, + "choices": ["debug", "info", "warning", "error"], + "default": "info" + }, + { + "key": "enable_telemetry", + "name": "Enable Telemetry", + "type": "boolean", + "required": false, + "default": false + } + ] +} +``` + +### Setting Fields + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `key` | string | yes | Code-level identifier (e.g., `api_key`) | +| `name` | string | yes | Human-readable label (e.g., `API Key`) | +| `type` | `"string"` or `"boolean"` | yes | Value type | +| `required` | boolean | yes | Whether user must provide a value | +| `documentation` | string | no | Help text for users | +| `default` | string or boolean | no | Default value if not configured | +| `validation_pattern` | string | no | Regex for validating string values | +| `choices` | array of strings | no | Allowed values (mutually exclusive with `validation_pattern`) | + +### Accessing Settings in Plugin Code + +Use the `ida-settings` library: + +```python +from ida_settings import get_current_plugin_setting + +api_key = get_current_plugin_setting("api_key") +log_level = get_current_plugin_setting("log_level") +``` + +## Validation + +Always validate before publishing: + +```bash +# Validate a directory +hcli plugin lint /path/to/my-plugin + +# Validate a ZIP archive +hcli plugin lint /path/to/my-plugin.zip +``` + +The linter checks: +- JSON syntax and schema compliance +- Required fields present +- Entry point file exists +- Logo file exists (if declared) +- Platform/binary consistency +- Path safety (no traversals) + +## Publishing to the Repository + +### Step 1: Create a GitHub Release + +1. Tag your commit: `git tag v1.0.0 && git push --tags` +2. Create a GitHub Release from the tag +3. Attach your plugin archive (ZIP) + +### Step 2: Automatic Indexing + +The indexer runs daily and automatically discovers: +- Repositories with `ida-plugin.json` in the root +- GitHub Releases with valid plugin archives + +No manual registration required. Your plugin appears within 24 hours. + +### Step 3: Explicit Registration (Optional) + +To expedite indexing, add your repository to `known-repositories.txt`: + +1. Fork [HexRaysSA/plugin-repository](https://github.com/HexRaysSA/plugin-repository) +2. Add your repo URL to `known-repositories.txt` (one per line) +3. Submit a pull request + +### Troubleshooting Indexing + +Check the [indexer log report](https://hexrayssa.github.io/plugin-repository/logs/indexer.html) for errors. + +Common issues: +- Invalid JSON syntax +- Missing required fields (especially author email) +- Repository URL doesn't match GitHub pattern +- Entry point file not found in archive + +## Multi-Plugin Archives + +A single archive can contain multiple plugins, each in its own subdirectory: + +``` +multi-plugin.zip +├── plugin-a/ +│ ├── ida-plugin.json +│ └── plugin_a.py +└── plugin-b/ + ├── ida-plugin.json + └── plugin_b.py +``` + +Each subdirectory must have its own `ida-plugin.json`. + +## Version Updates + +To release a new version: + +1. Update `plugin.version` in `ida-plugin.json` +2. Create a new git tag matching the version +3. Create a GitHub Release with the new archive +4. The indexer will pick up the new version automatically + +Users upgrade via: `hcli plugin upgrade ` + +## Common Patterns + +### Minimal Python Plugin Checklist + +1. [ ] Create `ida-plugin.json` with required fields +2. [ ] Ensure entry point file exists +3. [ ] Add author with email +4. [ ] Run `hcli plugin lint` +5. [ ] Create GitHub Release with source archive + +### Adding Settings to Existing Plugin + +1. [ ] Define settings array in `ida-plugin.json` +2. [ ] Add `ida-settings` to `pythonDependencies` if using the library +3. [ ] Update plugin code to use `get_current_plugin_setting()` +4. [ ] Document settings in README + +### Converting Legacy Plugin + +1. [ ] **Assess directory structure**: Verify all plugin code is self-contained + - Entry point and all imports within one directory + - No `../` references to parent directories + - Data files and assets colocated with code +2. [ ] **Restructure if needed**: Move `ida-plugin.json` to where the plugin code lives +3. [ ] Create `ida-plugin.json` with required fields +4. [ ] Set correct `entryPoint` (relative to `ida-plugin.json`) +5. [ ] Add appropriate `idaVersions` (test compatibility!) +6. [ ] Declare `pythonDependencies` from requirements.txt +7. [ ] Add `README.md` in plugin root for web display +8. [ ] Validate and test locally: `hcli plugin lint` +9. [ ] Create GitHub Release + +## HCLI Commands Reference + +```bash +# List available plugins +hcli plugin list + +# Search plugins +hcli plugin search + +# Install a plugin +hcli plugin install +hcli plugin install ==1.0.0 + +# Upgrade a plugin +hcli plugin upgrade + +# Uninstall a plugin +hcli plugin uninstall + +# List installed plugins +hcli plugin list --installed + +# Configure plugin settings +hcli plugin config set +hcli plugin config get + +# Validate plugin +hcli plugin lint /path/to/plugin +``` + +## Resources + +- **Plugin Repository**: [plugins.hex-rays.com](https://plugins.hex-rays.com/) +- **Documentation**: [hcli.docs.hex-rays.com](https://hcli.docs.hex-rays.com/) +- **Repository Source**: [github.com/HexRaysSA/plugin-repository](https://github.com/HexRaysSA/plugin-repository) +- **HCLI Source**: [github.com/HexRaysSA/ida-hcli](https://github.com/HexRaysSA/ida-hcli) +- **IDA Settings Library**: [github.com/williballenthin/ida-settings](https://github.com/williballenthin/ida-settings) +- **Support**: [github.com/HexRaysSA/ida-hcli/issues](https://github.com/HexRaysSA/ida-hcli/issues) + + +``` \ No newline at end of file diff --git a/archive/SymbioticSec/ida-security-scanner.txt b/archive/SymbioticSec/ida-security-scanner.txt new file mode 100644 index 00000000..9bc54e4d --- /dev/null +++ b/archive/SymbioticSec/ida-security-scanner.txt @@ -0,0 +1,3273 @@ +Project Path: arc_SymbioticSec_ida-security-scanner_xcq97ea2 + +Source Tree: + +```txt +arc_SymbioticSec_ida-security-scanner_xcq97ea2 +├── LICENSE +├── README.md +├── __init__.py +├── ai_prompts.py +├── ai_provider.py +├── code-rules.yaml +├── config.py +├── ida-plugin.json +├── img +│ ├── Interface.png +│ ├── ai-config.png +│ ├── ia-response.png +│ └── scan-possibilities.png +├── scanner.py +├── symbiotic-logo.jpg +├── symbiotic_ida_plugin.py +└── viewer.py + +``` + +`LICENSE`: + +``` +MIT License + +Copyright (c) 2025 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +`README.md`: + +```md +# IDA Security Scanner + +

+ IDA Pro 9.1+ + Python 3.10+ +

+ +

+ Automated vulnerability detection in IDA Pro using SAST rules on decompiled code +

+ +--- + +

+ IDA Security Scanner Interface +

+ +--- + +## What is IDA Security Scanner? + +IDA Security Scanner bridges the gap between **binary reverse engineering** and **static code analysis**. Instead of manually hunting for vulnerabilities in decompiled code, this plugin: + +1. **Extracts pseudocode** from IDA's Hex-Rays decompiler +2. **Runs SAST rules** via [opengrep](https://github.com/opengrep/opengrep) on the C-like output +3. **Displays results** in an interactive UI directly inside IDA +4. **Provides AI explanations** (optional) for each finding + +Perfect for **vulnerability research**, **CTF challenges**, and **security audits**. + +--- + +## Installation + +### 1. Copy the plugin folder + +```bash +# macOS +cp -r symbiotic /Applications/IDA*/Contents/MacOS/plugins/ + +# Windows +xcopy symbiotic %IDADIR%\plugins\symbiotic\ /E /I + +# Linux +cp -r symbiotic ~/.idapro/plugins/ +``` + +### 2. Install opengrep + +Download the **opengrep** binary from the official releases: + +**https://github.com/opengrep/opengrep/releases** + +Rename the binary to `opengrep-core` and place it in the IDA **plugins folder** (same location as the plugin): + +```bash +# macOS +mv opengrep /Applications/IDA*/Contents/MacOS/plugins/opengrep-core + +# Windows +move opengrep.exe %IDADIR%\plugins\opengrep-core.exe + +# Linux +mv opengrep ~/.idapro/plugins/opengrep-core +``` + +> **Note**: The plugin expects the binary at `plugins/opengrep-core` by default. You can change this path in the plugin settings. + +### 3. Configure AI (Optional) + +See the [AI Integration](#-ai-integration) section below. + +--- + +## Usage + +| Shortcut | Action | +|----------|--------| +| `Ctrl+Shift+S` | Scan **current function** | +| `Ctrl+Shift+L` | Scan **ALL functions** | +| `Ctrl+Shift+A` | **Ask AI** about selected finding | + +

+ Scan Options +

+ +--- + +## Detection Rules + +IDA Security Scanner uses **YAML-based rules** powered by [opengrep](https://github.com/opengrep/opengrep) (a semgrep fork). Rules are defined in `code-rules.yaml`. + +### Adding Custom Rules + +**Why add custom rules?** +- Target **specific vulnerability patterns** for your audit +- Detect **proprietary API misuse** in embedded systems +- Create rules for **CTF-specific patterns** +- Hunt for **known CVE patterns** in binaries + +**How to add a rule:** + +Edit `code-rules.yaml` and add your pattern: + +```yaml +rules: + # Example: Detect dangerous gets() usage + - id: BUFFER_OVERFLOW_GETS + languages: [c] + message: "Buffer overflow: gets() has no bounds checking" + pattern: gets($BUF) + severity: CRITICAL + + # Example: Detect potential integer overflow + - id: INTEGER_OVERFLOW + languages: [c] + message: "Potential integer overflow in malloc size calculation" + pattern: malloc($X * $Y) + severity: WARNING + + # Example: Detect hardcoded credentials + - id: HARDCODED_PASSWORD + languages: [c] + message: "Hardcoded password detected" + pattern-regex: '(password|passwd|pwd)\s*=\s*"[^"]+"' + severity: HIGH +``` + +**Pattern syntax reference:** See [opengrep/semgrep documentation](https://semgrep.dev/docs/writing-rules/pattern-syntax/) + +**See the full rules file:** [`code-rules.yaml`](code-rules.yaml) + +--- + +## AI Integration + +IDA Security Scanner can use **LLM APIs** to provide detailed explanations of detected vulnerabilities and suggest exploitation paths. + +### Quick Setup + +1. **Copy the example configuration:** + ```bash + cp .env.example .env + ``` + +2. **Edit `.env`** with your API credentials (see below) + +3. **Use `Ctrl+Shift+A`** on any finding to get AI analysis + +### Supported Providers + +| Provider | Documentation | How to get API Key | +|----------|---------------|-------------------| +| **OpenAI** | [platform.openai.com](https://platform.openai.com/docs) | [Get API Key](https://platform.openai.com/api-keys) | +| **Anthropic (Claude)** | [docs.anthropic.com](https://docs.anthropic.com/) | [Get API Key](https://console.anthropic.com/settings/keys) | +| **Google Gemini** | [ai.google.dev](https://ai.google.dev/docs) | Requires OpenAI-compatible proxy | +| **Local Models** | Via [Ollama](https://ollama.ai/), [LM Studio](https://lmstudio.ai/) | Use OpenAI-compatible endpoint | + +### Configuration Example + +```bash +# .env file +AI_PROVIDER=openai +AI_API_KEY=sk-your-api-key-here +AI_MODEL=gpt-5.2 +AI_BASE_URL=https://api.openai.com/v1 # Optional, for custom endpoints +``` + +

+ AI Configuration +

+ +### AI Response Example + +

+ AI Response +

+ +--- + +## Features + +- **Automatic Vulnerability Detection** — Scans decompiled functions for dangerous patterns +- **YAML-based Rules** — Easy to customize and extend with your own patterns +- **Interactive UI** — Filter by severity, function name, add custom tags +- **AI Analysis** — Get detailed explanations via LLM integration +- **Persistent State** — Tags and notes survive between IDA sessions +- **Fast Scanning** — Leverages opengrep's optimized pattern matching + +--- + +## Credits + +- [opengrep](https://github.com/opengrep/opengrep) — Static analysis engine (semgrep fork) +- [IDA Pro](https://hex-rays.com/) — Reverse engineering platform + +--- + +## License + +MIT License — See [LICENSE](LICENSE) for details. + +``` + +`__init__.py`: + +```py +# IDA Security Scanner - Package +# Security vulnerability scanner for IDA Pro + +from .config import SymbioticConfig, configure_symbiotic +from .scanner import SymbioticScanner +from .viewer import SymbioticResultsViewer +from .ai_provider import AIProvider +from .ai_prompts import explain_vulnerability, generate_poc, analyze_function + +__all__ = [ + "SymbioticConfig", + "SymbioticScanner", + "SymbioticResultsViewer", + "AIProvider", + "configure_symbiotic", + "explain_vulnerability", + "generate_poc", + "analyze_function" +] + +``` + +`ai_prompts.py`: + +```py +""" +AI Prompts Module +Security analysis prompts for LLM-based vulnerability analysis. +""" + +from .ai_provider import AIProvider + + +def explain_vulnerability(provider: AIProvider, vuln_title: str, + vuln_cwe: str, code_snippet: str) -> str: + """Generate AI explanation of a vulnerability.""" + prompt = f"""You are a security researcher analyzing decompiled binary code from IDA Pro. + +**Vulnerability Report:** +- Title: {vuln_title} +- CWE: {vuln_cwe} + +**Flagged Code:** +```c +{code_snippet} +``` + +**CRITICAL: First, assess if this is a REAL vulnerability or a FALSE POSITIVE.** + +Analyze the code carefully: +- Is the vulnerability actually exploitable in this context? +- Are there any mitigations, bounds checks, or safe usages visible? +- Could this be a safe pattern that just looks similar to a vulnerability? + +**Your response MUST start with one of these verdicts:** +- `[CONFIRMED VULNERABILITY]` - if the code is genuinely vulnerable +- `[LIKELY FALSE POSITIVE]` - if this appears to be incorrectly flagged + +Then provide: +1. **Verdict Explanation**: Why you classified it this way +2. **Technical Analysis**: Detailed explanation of the code behavior +3. **If CONFIRMED**: Attack vector, impact, and fix recommendation +4. **If FALSE POSITIVE**: Why the static analyzer incorrectly flagged this + +Format with markdown. Be precise and technical.""" + + return provider.call(prompt, temperature=0.3) + + +def generate_poc(provider: AIProvider, vuln_title: str, + vuln_cwe: str, code_snippet: str, func_name: str) -> str: + """Generate proof-of-concept exploit for a vulnerability.""" + prompt = f"""You are creating a proof-of-concept exploit for educational purposes. + +**Target Vulnerability:** +- Function: {func_name} +- Title: {vuln_title} +- CWE: {vuln_cwe} + +**Vulnerable Code:** +```c +{code_snippet} +``` + +**Instructions:** +1. Create a minimal PoC that demonstrates the vulnerability +2. Show input that would trigger the bug +3. Explain what happens when the PoC runs +4. Include Python/C code if applicable + +This is for security research only. Format with markdown code blocks.""" + + return provider.call(prompt, temperature=0.5) + + +def analyze_function(provider: AIProvider, pseudocode: str, func_name: str) -> str: + """Analyze a function for security issues.""" + prompt = f"""You are a security researcher analyzing decompiled code from IDA Pro. + +**Function: {func_name}** + +```c +{pseudocode} +``` + +**Analyze this function for:** +1. Security vulnerabilities (buffer overflows, format strings, injections, etc.) +2. Interesting security-relevant behavior +3. Potential attack vectors +4. Cryptographic operations or sensitive data handling + +Provide a detailed security analysis. Format with markdown.""" + + return provider.call(prompt, temperature=0.4) + +``` + +`ai_provider.py`: + +```py +""" +AI Provider Module - OpenAI Client +Simple wrapper using OpenAI client for calling LLM providers. +""" + +import os + +DEFAULT_MODEL = "claude-sonnet-4-5" + +try: + from openai import OpenAI + OPENAI_AVAILABLE = True +except ImportError: + OPENAI_AVAILABLE = False + print("[Symbiotic] Warning: openai not installed. Run: pip install openai") + + +class AIProvider: + """Universal AI Provider using OpenAI client.""" + + def __init__(self, model: str = None, api_key: str = None, api_base: str = None): + """ + Initialize AI Provider. + + Args: + model: Model name (e.g., "claude-sonnet-4-5", "claude-haiku-4-5") + api_key: API key for the provider + api_base: Custom API base URL (for proxies like Symbiotic) + """ + self.model = model or DEFAULT_MODEL + # Remove provider prefix if present (e.g., "openai/claude-sonnet-4.5" -> "claude-sonnet-4.5") + if "/" in self.model: + self.model = self.model.split("/", 1)[1] + self.api_key = api_key + self.api_base = api_base + self.client = None + + if OPENAI_AVAILABLE and api_key and api_base: + self.client = OpenAI(base_url=api_base, api_key=api_key) + + def call(self, prompt: str, temperature: float = 0.7, max_tokens: int = 8192) -> str: + """Call the LLM with given prompt.""" + if not OPENAI_AVAILABLE: + return "Error: openai not installed. Run: pip install openai" + + if not self.client: + return "Error: AI not configured. Check API key and base URL." + + try: + print(f"[Symbiotic AI] Calling {self.model} (base: {self.api_base or 'default'})") + + response = self.client.chat.completions.create( + model=self.model, + messages=[{"role": "user", "content": prompt}], + temperature=temperature, + max_tokens=max_tokens, + ) + return response.choices[0].message.content + + except Exception as e: + return f"Error calling {self.model}: {e}" + + +def get_provider(model: str = None, api_key: str = None, api_base: str = None) -> AIProvider: + """Factory function to get AI provider.""" + return AIProvider(model=model, api_key=api_key, api_base=api_base) + +``` + +`code-rules.yaml`: + +```yaml +rules: +# ============================================================================ +# IDA Security Scanner - Detection Rules +# SAST rules for IDA Pro decompiled C code +# ============================================================================ + +# --------------------------- +# Buffer Overflow: gets() +# --------------------------- +- id: BUFFER_OVERFLOW_GETS + languages: [c] + message: | + CRITICAL: gets() - Buffer Overflow + + The gets() function reads input without ANY size limit. + There is NO safe way to use this function. + metadata: + cwe: 'CWE-120' + title: "gets() Buffer Overflow" + pattern: gets($BUF) + severity: CRITICAL + +# --------------------------- +# Buffer Overflow: scanf %s +# --------------------------- +- id: BUFFER_OVERFLOW_SCANF + languages: [c] + message: | + CRITICAL: scanf("%s") - Unbounded Input + + The %s format specifier without width limit reads until whitespace. + Fix: Use width specifier like scanf("%255s", buf). + metadata: + cwe: 'CWE-120' + title: "scanf %s Unbounded" + pattern: scanf("%s", $BUF) + severity: CRITICAL + +# --------------------------- +# Format String: printf(argv[N]) +# --------------------------- +- id: FORMAT_STRING_ARGV + languages: [c] + message: | + CRITICAL: Format String Attack + + Command-line argument used directly as format string. + Attacker can use %x to leak stack, %n to write memory. + metadata: + cwe: 'CWE-134' + title: "Format String" + pattern-either: + - pattern: printf(argv[$N]) + - pattern: fprintf($FD, argv[$N]) + - pattern: sprintf($DST, argv[$N]) + severity: CRITICAL + +# --------------------------- +# Command Injection: strcat + system +# --------------------------- +- id: COMMAND_INJECTION_STRCAT + languages: [c] + message: | + HIGH: Command Injection - strcat() then system() + + Data is concatenated to a command string then executed. + Shell metacharacters (;|`$) can inject arbitrary commands. + metadata: + cwe: 'CWE-78' + title: "Command Injection" + pattern: | + strcat($CMD, ...); + ... + system($CMD); + severity: HIGH + +# --------------------------- +# Command Injection: sprintf + system +# --------------------------- +- id: COMMAND_INJECTION_SPRINTF + languages: [c] + message: | + HIGH: Command Injection - sprintf() then system() + + Command built with sprintf() then executed with system(). + metadata: + cwe: 'CWE-78' + title: "Command Injection" + pattern: | + sprintf($CMD, ...); + ... + system($CMD); + severity: HIGH + +# --------------------------- +# Privilege Escalation: setreuid + system +# --------------------------- +- id: PRIVESC_SETREUID + languages: [c] + message: | + HIGH: Privilege Escalation + + setreuid() followed by system() call. + Common in SUID binaries - target for exploitation. + metadata: + cwe: 'CWE-269' + title: "Privilege Escalation" + pattern: | + setreuid(...); + ... + system(...); + severity: HIGH + +# --------------------------- +# Privilege Escalation: setuid + system +# --------------------------- +- id: PRIVESC_SETUID + languages: [c] + message: | + HIGH: Privilege Escalation + + setuid() followed by system() call. + metadata: + cwe: 'CWE-269' + title: "Privilege Escalation" + pattern: | + setuid(...); + ... + system(...); + severity: HIGH + +# --------------------------- +# Backdoor Pattern +# --------------------------- +- id: BACKDOOR_SHELL + languages: [c] + message: | + MEDIUM: Potential Backdoor + + execve() spawning a shell detected. + Could be admin functionality, CTF target, or actual backdoor. + metadata: + cwe: 'CWE-506' + title: "Shell Backdoor" + pattern-either: + - pattern: execve("/bin/bash", ...) + - pattern: execve("/bin/sh", ...) + severity: MEDIUM + +# --------------------------- +# Dangerous strcpy +# --------------------------- +- id: DANGEROUS_STRCPY + languages: [c] + message: | + MEDIUM: Unsafe strcpy() + + strcpy() does not check destination buffer size. + Consider using strncpy() or strlcpy(). + metadata: + cwe: 'CWE-120' + title: "Unsafe strcpy" + pattern: strcpy($DST, $SRC) + severity: MEDIUM + +# --------------------------- +# Dangerous sprintf +# --------------------------- +- id: DANGEROUS_SPRINTF + languages: [c] + message: | + MEDIUM: Unsafe sprintf() + + sprintf() does not check destination buffer size. + Consider using snprintf(). + metadata: + cwe: 'CWE-120' + title: "Unsafe sprintf" + pattern: sprintf($DST, $FMT, ...) + severity: MEDIUM + +``` + +`config.py`: + +```py +""" +Configuration Module +Manages opengrep-core and AI configuration for IDA plugin +""" + +import os + +# Configuration file paths +# PLUGIN_DIR = where this plugin lives (symbiotic folder) +# PLUGINS_DIR = IDA plugins folder (parent, where .env goes) +PLUGIN_DIR = os.path.dirname(__file__) +PLUGINS_DIR = os.path.dirname(PLUGIN_DIR) +ENV_FILE = os.path.join(PLUGINS_DIR, ".env") + + +def load_env_file(env_path): + """Load variables from .env file""" + env_vars = {} + if os.path.exists(env_path): + try: + with open(env_path, 'r') as f: + for line in f: + line = line.strip() + if line and not line.startswith('#') and '=' in line: + key, value = line.split('=', 1) + env_vars[key.strip()] = value.strip() + except Exception as e: + print(f"[Symbiotic] Failed to load .env: {e}") + else: + print(f"[Symbiotic] Warning: .env not found at {env_path}") + return env_vars + + +class SymbioticConfig: + """Manage opengrep and AI configuration""" + + def __init__(self): + self.opengrep_path = os.path.join(PLUGINS_DIR, "opengrep-core") + rules_in_plugin = os.path.join(PLUGIN_DIR, "code-rules.yaml") + rules_in_plugins = os.path.join(PLUGINS_DIR, "code-rules.yaml") + self.rules_path = rules_in_plugin if os.path.exists(rules_in_plugin) else rules_in_plugins + + self.ai_model = "gemini/gemini-3-flash" + self.ai_api_key = "" + self.ai_api_base = "" + + self.load() + + def load(self): + """Load configuration from .env file""" + env_vars = load_env_file(ENV_FILE) + if env_vars: + self.ai_model = env_vars.get("AI_MODEL", "gemini/gemini-3-flash") + self.ai_api_key = env_vars.get("AI_API_KEY", "") + self.ai_api_base = env_vars.get("AI_API_BASE", "") + + print(f"[Symbiotic] Config loaded: model={self.ai_model}, api_base={self.ai_api_base or '(none)'}") + + def is_configured(self): + """Check if scanner is configured""" + return bool(self.opengrep_path and os.path.exists(self.opengrep_path)) + + def is_ai_configured(self): + """Check if AI is configured""" + return bool(self.ai_api_key) + + def get_status_string(self): + """Return configuration status string""" + opengrep_exists = "[OK]" if self.opengrep_path and os.path.exists(self.opengrep_path) else "[X]" + rules_exists = "[OK]" if self.rules_path and os.path.exists(self.rules_path) else "[X]" + ai_status = "[OK]" if self.is_ai_configured() else "[X]" + api_key_display = self.ai_api_key[:10] + "..." if self.ai_api_key else "(not set)" + api_base_display = self.ai_api_base if self.ai_api_base else "(direct)" + status = "[OK] Configured" if self.is_configured() else "[X] Not configured" + + return f"""Scanner Configuration: + Opengrep Path: {self.opengrep_path or '(not set)'} + Rules Path: {self.rules_path or '(not set)'} + Opengrep: {opengrep_exists} + Rules: {rules_exists} + +AI Configuration: + Model: {self.ai_model} + API Key: {api_key_display} + API Base: {api_base_display} + AI Features: {ai_status} + +Status: {status}""" + + +def configure_symbiotic(config): + """Configuration dialog using IDA dialogs""" + import ida_kernwin + + def show_main_menu(): + return f"""Symbiotic Configuration +{'='*40} +{config.get_status_string()} +{'='*40} + +Configure scanner paths below. +AI is configured via .env file.""" + + while True: + result = ida_kernwin.ask_buttons( + "Scanner", + "Done", + "", + 1, + show_main_menu() + ) + + if result == 1: + configure_scanner(config) + else: + break + + return config.is_configured() + + +def configure_scanner(config): + """Scanner configuration submenu""" + import ida_kernwin + + while True: + menu = f"""Scanner Configuration +{'='*40} +Opengrep: {config.opengrep_path or '(not set)'} +Rules: {config.rules_path or '(not set)'} +{'='*40}""" + + result = ida_kernwin.ask_buttons("Opengrep Path", "Rules Path", "Back", 1, menu) + + if result == 1: + new_path = ida_kernwin.ask_file(0, config.opengrep_path or "", "Select opengrep-core binary") + if new_path: + config.opengrep_path = new_path + elif result == 2: + new_path = ida_kernwin.ask_file(0, config.rules_path or "", "Select rules YAML file") + if new_path: + config.rules_path = new_path + else: + break + +``` + +`ida-plugin.json`: + +```json +{ + "IDAMetadataDescriptorVersion": 1, + "plugin": { + "name": "ida-security-scanner", + "entryPoint": "symbiotic_ida_plugin.py", + "version": "0.1.2", + "idaVersions": [ + "9.1", + "9.2" + ], + "platforms": [ + "windows-x86_64", + "linux-x86_64", + "macos-x86_64", + "macos-aarch64" + ], + "categories": [ + "vulnerability-research-and-exploit-development", + "decompilation" + ], + "keywords": [ + "security", + "vulnerability", + "sast", + "scanner", + "opengrep" + ], + "description": "Security scanner combining IDA decompilation with SAST rules to detect vulnerabilities in pseudocode", + "pythonDependencies": [ + "litellm>=1.0.0", + "python-dotenv>=1.0.0" + ], + "license": "MIT", + "urls": { + "repository": "https://github.com/SymbioticSec/ida-security-scanner" + }, + "logoPath": "symbiotic-logo.jpg", + "authors": [ + { + "name": "Symbiotic Security", + "email": "anthony.bondu@symbioticsec.ai" + } + ] + } +} +``` + +`scanner.py`: + +```py +""" +Scanner Module +Scans code using opengrep-core +""" + +import os +import subprocess +import tempfile +import json +import threading + +import idaapi +import ida_hexrays +import ida_funcs +import idautils +import idc +import ida_kernwin +import ida_lines + + +class ScanThread(threading.Thread): + """Background thread for scanning""" + + def __init__(self, scanner, scan_func, *args): + super().__init__() + self.scanner = scanner + self.scan_func = scan_func + self.args = args + self.result = None + self.error = None + + def run(self): + try: + self.result = self.scan_func(*self.args) + except Exception as e: + self.error = str(e) + + +class SymbioticScanner: + """Scanner using opengrep-core with cancel support""" + + def __init__(self, config): + self.config = config + self._pseudocode_cache = {} # Cache pseudocode for line mapping + self._cancel_requested = False + self._current_process = None + + def cancel_scan(self): + """Request cancellation of current scan""" + self._cancel_requested = True + if self._current_process: + try: + self._current_process.terminate() + print("[Symbiotic] Scan cancelled by user") + except: + pass + + def is_cancelled(self): + """Check if cancel was requested""" + return self._cancel_requested + + def reset_cancel(self): + """Reset cancel flag for new scan""" + self._cancel_requested = False + self._current_process = None + + def extract_pseudocode(self, ea): + """Extract pseudocode from a function at given address and build line→EA mapping""" + try: + cfunc = ida_hexrays.decompile(ea) + if cfunc: + code = str(cfunc) + code_lines = code.split('\n') + + line_ea_map = {} + try: + sv = cfunc.get_pseudocode() + + for line_idx in range(len(sv)): + sl = sv[line_idx] + line_text = ida_lines.tag_remove(sl.line) + + found_ea = idaapi.BADADDR + for item_ea, items in cfunc.eamap.items(): + if item_ea != idaapi.BADADDR: + for item in items: + if hasattr(item, 'loc') and item.loc: + if item.loc.line == line_idx: + found_ea = item_ea + break + if found_ea != idaapi.BADADDR: + break + + line_ea_map[line_idx] = found_ea + + except Exception as e: + print(f"[Symbiotic] EA mapping error: {e}") + + self._pseudocode_cache[ea] = { + 'lines': code_lines, + 'line_ea_map': line_ea_map, + 'cfunc': cfunc + } + return code + except Exception as e: + print(f"[Symbiotic] Failed to decompile function at {hex(ea)}: {e}") + return None + + def get_pseudocode_line(self, ea, line_number): + """Get specific line from cached pseudocode""" + if ea in self._pseudocode_cache: + cache = self._pseudocode_cache[ea] + lines = cache.get('lines', []) if isinstance(cache, dict) else cache + if 0 < line_number <= len(lines): + return lines[line_number - 1] + return None + + def get_line_ea(self, func_ea, relative_line): + """Get the EA (address) for a specific relative line in a function's pseudocode""" + if func_ea in self._pseudocode_cache: + cache = self._pseudocode_cache[func_ea] + if isinstance(cache, dict): + line_ea_map = cache.get('line_ea_map', {}) + return line_ea_map.get(relative_line, idaapi.BADADDR) + return idaapi.BADADDR + + def extract_function_code(self, ea): + """Extract disassembly code from a function""" + func = ida_funcs.get_func(ea) + if not func: + return None + + lines = [] + for head in idautils.Heads(func.start_ea, func.end_ea): + disasm = idc.GetDisasm(head) + if disasm: + lines.append(disasm) + + return "\n".join(lines) + + def create_temp_file(self, code, filename="code.c"): + """Create a temporary file with the code""" + temp_dir = tempfile.mkdtemp(prefix="symbiotic_") + temp_file = os.path.join(temp_dir, filename) + + with open(temp_file, "w", encoding="utf-8") as f: + f.write(code) + + return temp_dir, temp_file + + def scan_code(self, code, language="c"): + """Scan code using opengrep-core""" + print("[Symbiotic] Preparing opengrep scan...") + + extensions = { + "c": "code.c", + "cpp": "code.cpp", + } + filename = extensions.get(language, "code.c") + + temp_dir, temp_file = self.create_temp_file(code, filename) + + try: + cmd = [self.config.opengrep_path, "-json"] + + if self.config.rules_path and os.path.exists(self.config.rules_path): + cmd.extend(["-rules", self.config.rules_path]) + else: + print("[Symbiotic] ERROR: No rules file configured!") + return {"error": "No rules file configured"} + + cmd.extend(["-lang", language, temp_file]) + + print(f"[Symbiotic] Executing: {' '.join(cmd)}") + + self._current_process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + encoding='utf-8', + errors='ignore' + ) + + try: + stdout, stderr = self._current_process.communicate(timeout=300) + except subprocess.TimeoutExpired: + self._current_process.kill() + return {"error": "Scan timeout (>300s)"} + + if self._cancel_requested: + return {"error": "Scan cancelled by user"} + + output = stdout if stdout else "" + stderr_out = stderr if stderr else "" + + if stderr_out: + print(f"[Symbiotic] Stderr: {stderr_out[:500]}") + + source_lines = code.split('\n') + converted_output = self._convert_opengrep_output(output, source_lines) + + return { + "success": True, + "output": converted_output, + "raw_output": output, + "stderr": stderr, + "temp_dir": temp_dir, + "scanner": "opengrep", + "source_lines": source_lines + } + + except subprocess.TimeoutExpired: + return {"error": "Scan timeout (>300s)"} + except Exception as e: + import traceback + traceback.print_exc() + return {"error": f"Scan failed: {str(e)}"} + + def _convert_opengrep_output(self, raw_output, source_lines=None): + """Convert opengrep JSON output to our expected format""" + try: + if not raw_output or not raw_output.strip(): + print("[Symbiotic] No output from opengrep") + return json.dumps({"fail_results": []}) + + json_start = raw_output.find('{') + if json_start == -1: + print("[Symbiotic] No JSON found in output") + return json.dumps({"fail_results": []}) + + json_output = raw_output[json_start:] + data = json.loads(json_output) + results = data.get("results", []) + print(f"[Symbiotic] Found {len(results)} results in opengrep output") + + fail_results = [] + for r in results: + check_id = r.get("check_id", "UNKNOWN") + extra = r.get("extra", {}) + metadata = extra.get("metadata", {}) + + start_info = r.get("start", {}) + end_info = r.get("end", {}) + start_line = start_info.get("line", 0) + end_line = end_info.get("line", start_line) + + snippet = "" + if source_lines and start_line > 0: + snippet_lines = source_lines[start_line - 1:end_line] + snippet = "\n".join(snippet_lines) + + vuln = { + "rule_id": check_id, + "title": metadata.get("title", extra.get("message", check_id)), + "severity": metadata.get("severity", "MEDIUM").upper() if metadata.get("severity") else "MEDIUM", + "description": extra.get("message", ""), + "snippet": snippet, + "cwe": metadata.get("cwe", ""), + "owasp": metadata.get("owasp", ""), + "impact": metadata.get("impact", ""), + "impact_level": metadata.get("impact_level", "MEDIUM"), + "likelihood_level": metadata.get("likelihood_level", "MEDIUM"), + "confidence_level": metadata.get("confidence_level", "MEDIUM"), + "references": metadata.get("references", []), + "location": { + "start_line": start_line, + "end_line": end_line, + "path": r.get("path", "") + } + } + fail_results.append(vuln) + print(f"[Symbiotic] - {check_id}: {metadata.get('title', 'Unknown')} @ line {start_line}") + + return json.dumps({"fail_results": fail_results}) + except json.JSONDecodeError as e: + print(f"[Symbiotic] JSON parse error: {e}") + print(f"[Symbiotic] Raw output: {raw_output[:1000] if raw_output else 'empty'}") + return json.dumps({"fail_results": []}) + except Exception as e: + print(f"[Symbiotic] Error converting output: {e}") + import traceback + traceback.print_exc() + return json.dumps({"fail_results": []}) + + def scan_function(self, ea): + """Scan a function at given address""" + func_name = idc.get_func_name(ea) + print(f"[Symbiotic] Scanning function: {func_name} at {hex(ea)}") + + code = self.extract_pseudocode(ea) + if code: + print(f"[Symbiotic] Extracted pseudocode ({len(code)} chars)") + result = self.scan_code(code, "c") + else: + print("[Symbiotic] Pseudocode not available, using disassembly") + code = self.extract_function_code(ea) + if code: + result = self.scan_code(code, "c") + else: + return {"error": "Failed to extract code from function"} + + result["function_name"] = func_name + result["address"] = hex(ea) + result["scan_type"] = "single_function" + + self.annotate_findings(result, ea) + + return result + + def scan_function_async(self, ea, callback): + """Scan function in background thread""" + func_name = idc.get_func_name(ea) + print(f"[Symbiotic] Scanning function (async): {func_name} at {hex(ea)}") + + code = self.extract_pseudocode(ea) + if not code: + print("[Symbiotic] Pseudocode not available, using disassembly") + code = self.extract_function_code(ea) + if not code: + callback({"error": "Failed to extract code from function"}) + return None + + print(f"[Symbiotic] Extracted code ({len(code)} chars)") + + def _scan_and_callback(): + result = self.scan_code(code, "c") + result["function_name"] = func_name + result["address"] = hex(ea) + result["scan_type"] = "single_function" + + def _annotate_and_show(): + self.annotate_findings(result, ea) + callback(result) + + ida_kernwin.execute_sync(_annotate_and_show, ida_kernwin.MFF_FAST) + + thread = threading.Thread(target=_scan_and_callback) + thread.start() + return thread + + def scan_all_functions(self): + """Scan all functions in the binary""" + print("\n" + "=" * 70) + print("[Symbiotic] SCAN ALL FUNCTIONS") + print("=" * 70) + + all_funcs = [] + for func_ea in idautils.Functions(): + func = ida_funcs.get_func(func_ea) + if func: + func_name = idc.get_func_name(func_ea) + all_funcs.append((func_ea, func_name)) + + total_funcs = len(all_funcs) + print(f"[Symbiotic] Found {total_funcs} functions to scan") + + all_code = [] + func_line_map = {} + current_line = 1 + + for func_ea, func_name in all_funcs: + code = self.extract_pseudocode(func_ea) + if code: + lines = code.count('\n') + 1 + func_line_map[func_ea] = { + "name": func_name, + "start_line": current_line, + "end_line": current_line + lines - 1 + } + all_code.append(f"// Function: {func_name} @ {hex(func_ea)}") + all_code.append(code) + all_code.append("") + current_line += lines + 2 + + if not all_code: + return {"error": "No functions could be decompiled"} + + combined_code = "\n".join(all_code) + print(f"[Symbiotic] Combined {len(all_code)} functions ({len(combined_code)} chars)") + + result = self.scan_code(combined_code, "c") + result["total_functions"] = total_funcs + result["functions_scanned"] = len(func_line_map) + result["scan_type"] = "all_functions" + result["func_line_map"] = func_line_map + + # Annotate functions + self.annotate_all_functions(result, func_line_map) + + return result + + def scan_all_functions_async(self, callback): + """Scan all functions in background thread""" + print("\n" + "=" * 70) + print("[Symbiotic] SCAN ALL FUNCTIONS (async)") + print("=" * 70) + + all_funcs = [] + for func_ea in idautils.Functions(): + func = ida_funcs.get_func(func_ea) + if func: + func_name = idc.get_func_name(func_ea) + all_funcs.append((func_ea, func_name)) + + total_funcs = len(all_funcs) + print(f"[Symbiotic] Found {total_funcs} functions to scan") + + all_code = [] + func_line_map = {} + current_line = 1 + + for i, (func_ea, func_name) in enumerate(all_funcs): + if i % 5 == 0: + ida_kernwin.replace_wait_box(f"Decompiling function {i+1}/{total_funcs}...") + + code = self.extract_pseudocode(func_ea) + if code: + lines = code.count('\n') + 1 + func_line_map[func_ea] = { + "name": func_name, + "start_line": current_line, + "end_line": current_line + lines - 1 + } + all_code.append(f"// Function: {func_name} @ {hex(func_ea)}") + all_code.append(code) + all_code.append("") + current_line += lines + 2 + + if not all_code: + callback({"error": "No functions could be decompiled"}) + return None + + combined_code = "\n".join(all_code) + print(f"[Symbiotic] Combined {len(func_line_map)} functions ({len(combined_code)} chars)") + + ida_kernwin.replace_wait_box("Running opengrep scan...") + + def _scan_and_callback(): + result = self.scan_code(combined_code, "c") + result["total_functions"] = total_funcs + result["functions_scanned"] = len(func_line_map) + result["scan_type"] = "all_functions" + result["func_line_map"] = func_line_map + + def _annotate_and_show(): + self.annotate_all_functions(result, func_line_map) + callback(result) + + ida_kernwin.execute_sync(_annotate_and_show, ida_kernwin.MFF_FAST) + + thread = threading.Thread(target=_scan_and_callback) + thread.start() + return thread + + def annotate_findings(self, scan_result, func_addr=None): + """Annotate IDA with vulnerability findings""" + try: + output = scan_result.get("output", "") + if not output: + return + + data = json.loads(output) + findings = data.get("fail_results", []) + + if not findings: + print("[Symbiotic] No vulnerabilities to annotate") + return + + for finding in findings: + title = finding.get("title", "Vulnerability") + cwe = finding.get("cwe", "") + + comment = f"[VULN] {title}" + if cwe: + comment += f" ({cwe})" + + if func_addr: + existing = idc.get_func_cmt(func_addr, 0) or "" + if comment not in existing: + new_comment = f"{existing}\n{comment}" if existing else comment + idc.set_func_cmt(func_addr, new_comment, 0) + idc.set_color(func_addr, idc.CIC_FUNC, 0xFF6B6B) + print(f"[Symbiotic] [OK] Annotated function at {hex(func_addr)}") + + print("[Symbiotic] [OK] IDA annotations complete!") + + except Exception as e: + print(f"[Symbiotic] Annotation error: {e}") + + def annotate_all_functions(self, scan_result, func_line_map): + """Annotate all functions with their vulnerabilities""" + try: + output = scan_result.get("output", "") + if not output: + return + + data = json.loads(output) + findings = data.get("fail_results", []) + + if not findings: + return + + func_vulns = {} + for finding in findings: + loc = finding.get("location", {}) + vuln_line = loc.get("start_line", 0) + + for func_ea, info in func_line_map.items(): + if info["start_line"] <= vuln_line <= info["end_line"]: + if func_ea not in func_vulns: + func_vulns[func_ea] = [] + func_vulns[func_ea].append(finding) + break + + for func_ea, vulns in func_vulns.items(): + func_name = func_line_map[func_ea]["name"] + func_start_line = func_line_map[func_ea]["start_line"] + + try: + cfunc = ida_hexrays.decompile(func_ea) + if cfunc: + sv = cfunc.get_pseudocode() + eamap = cfunc.get_eamap() + + for v in vulns: + title = v.get("title", "Vulnerability") + cwe = v.get("cwe", "") + snippet = v.get("snippet", "") + + cmt_text = f"VULN: {title}" + if cwe: + cmt_text += f" ({cwe})" + + target_ea = None + if snippet: + snippet_clean = snippet.strip().split('\n')[0].strip() + for line_idx in range(len(sv)): + line_text = ida_lines.tag_remove(sv[line_idx].line) + if snippet_clean in line_text: + for ea_addr, items in eamap.items(): + if ea_addr != idaapi.BADADDR: + target_ea = ea_addr + break + break + + if target_ea and target_ea in eamap: + nearest_ea = eamap[target_ea][0].ea + + if cfunc.has_orphan_cmts(): + cfunc.del_orphan_cmts() + cfunc.save_user_cmts() + + tl = idaapi.treeloc_t() + tl.ea = nearest_ea + success = False + for itp in range(idaapi.ITP_SEMI, idaapi.ITP_COLON): + tl.itp = itp + cfunc.set_user_cmt(tl, cmt_text) + cfunc.save_user_cmts() + cfunc.refresh_func_ctext() + if not cfunc.has_orphan_cmts(): + success = True + print(f"[Symbiotic] Added comment @ {hex(nearest_ea)}: {cmt_text[:40]}...") + break + cfunc.del_orphan_cmts() + cfunc.save_user_cmts() + + if not success: + print(f"[Symbiotic] Warning: Comment became orphaned") + + except Exception as e: + print(f"[Symbiotic] Inline comment error: {e}") + + comments = [] + for v in vulns: + title = v.get("title", "Vulnerability") + cwe = v.get("cwe", "") + snippet = v.get("snippet", "").strip().split('\n')[0][:60] + comment = f"// [VULN] {title}" + if cwe: + comment += f" ({cwe})" + if snippet: + comment += f" @ {snippet}" + comments.append(comment) + + existing = idc.get_func_cmt(func_ea, 0) or "" + if "[VULN]" in existing: + existing = "\n".join([l for l in existing.split("\n") if "[VULN]" not in l]) + new_comment = existing + "\n" + "\n".join(comments) if existing.strip() else "\n".join(comments) + idc.set_func_cmt(func_ea, new_comment.strip(), 0) + idc.set_color(func_ea, idc.CIC_FUNC, 0xFF6B6B) + + print(f"[Symbiotic] [OK] Annotated {func_name} with {len(vulns)} vuln(s)") + + print(f"[Symbiotic] [OK] Annotated {len(func_vulns)} vulnerable functions!") + + except Exception as e: + print(f"[Symbiotic] Annotation error: {e}") + import traceback + traceback.print_exc() + +``` + +`symbiotic_ida_plugin.py`: + +```py +""" +IDA Security Scanner Plugin +Scans decompiled pseudocode for security vulnerabilities using opengrep-core. +Features AI analysis for explanations, PoC generation, and function analysis. +""" + +import os +import sys + +import idaapi +import ida_hexrays +import ida_kernwin +import ida_funcs +import idc + +PLUGIN_DIR = os.path.dirname(__file__) +PARENT_DIR = os.path.dirname(PLUGIN_DIR) +if PARENT_DIR not in sys.path: + sys.path.insert(0, PARENT_DIR) +if PLUGIN_DIR not in sys.path: + sys.path.insert(0, PLUGIN_DIR) + +from config import SymbioticConfig, configure_symbiotic +from scanner import SymbioticScanner +from viewer import SymbioticResultsViewer + +# Load version from ida-plugin.json +import json +try: + with open(os.path.join(PLUGIN_DIR, "ida-plugin.json"), "r") as f: + VERSION = json.load(f).get("plugin", {}).get("version", "0.0.1") +except: + VERSION = "0.1.2" + + +class ScanCurrentFunctionAction(ida_kernwin.action_handler_t): + """Action handler for scanning current function""" + + def __init__(self, plugin): + ida_kernwin.action_handler_t.__init__(self) + self.plugin = plugin + + def activate(self, ctx): + ea = ida_kernwin.get_screen_ea() + self.plugin.scan_current_function(ea) + return 1 + + def update(self, ctx): + return ida_kernwin.AST_ENABLE_ALWAYS + + +class ScanAllFunctionsAction(ida_kernwin.action_handler_t): + """Action handler for scanning all functions""" + + def __init__(self, plugin): + ida_kernwin.action_handler_t.__init__(self) + self.plugin = plugin + + def activate(self, ctx): + self.plugin.scan_all_functions() + return 1 + + def update(self, ctx): + return ida_kernwin.AST_ENABLE_ALWAYS + + +class ConfigureAction(ida_kernwin.action_handler_t): + """Action handler for configuration""" + + def __init__(self, plugin): + ida_kernwin.action_handler_t.__init__(self) + self.plugin = plugin + + def activate(self, ctx): + configure_symbiotic(self.plugin.config) + return 1 + + def update(self, ctx): + return ida_kernwin.AST_ENABLE_ALWAYS + + +class AskAIAction(ida_kernwin.action_handler_t): + """Action handler for Ask AI about current function""" + + def __init__(self, plugin): + ida_kernwin.action_handler_t.__init__(self) + self.plugin = plugin + + def activate(self, ctx): + self.plugin.ask_ai_about_function() + return 1 + + def update(self, ctx): + return ida_kernwin.AST_ENABLE_ALWAYS + + +class SymbioticPlugin(idaapi.plugin_t): + """Main IDA Plugin class""" + + flags = idaapi.PLUGIN_KEEP + comment = "Symbiotic Security Scanner - Scan code for vulnerabilities" + help = "Use Ctrl+Shift+S to scan current function, Ctrl+Shift+L to scan all" + wanted_name = "Symbiotic" + wanted_hotkey = "" + + def init(self): + """Initialize the plugin""" + print(f"\n[Symbiotic] Security Scanner v{VERSION} loading...") + + self.config = SymbioticConfig() + self.scanner = SymbioticScanner(self.config) + self._scanning = False + + # Check for decompiler + if not ida_hexrays.init_hexrays_plugin(): + print("[Symbiotic] Warning: Hex-Rays decompiler not available") + + # Print status + print(f"[Symbiotic] Opengrep: {self.config.opengrep_path}") + print(f"[Symbiotic] Rules: {self.config.rules_path}") + ai_status = "[OK]" if self.config.is_ai_configured() else "[X] Not set" + print(f"[Symbiotic] Gemini AI: {ai_status}") + status = "[OK] Configured" if self.config.is_configured() else "[X] Not configured" + print(f"[Symbiotic] Status: {status}") + + # Register actions + self._register_actions() + + print(f"[Symbiotic] v{VERSION} loaded successfully!") + print("[Symbiotic] Shortcuts: Ctrl+Shift+S (scan), Ctrl+Shift+L (scan all), Ctrl+Shift+A (ask AI)") + + return idaapi.PLUGIN_KEEP + + def _register_actions(self): + """Register all plugin actions""" + # Scan Current Function + action_desc = ida_kernwin.action_desc_t( + "symbiotic:scan_current", + "Symbiotic: Scan Current Function", + ScanCurrentFunctionAction(self), + "Ctrl+Shift+S", + "Scan the current function for vulnerabilities", + -1 + ) + ida_kernwin.register_action(action_desc) + ida_kernwin.attach_action_to_menu("Edit/Plugins/", "symbiotic:scan_current", ida_kernwin.SETMENU_APP) + + # Scan All Functions + action_desc = ida_kernwin.action_desc_t( + "symbiotic:scan_all", + "Symbiotic: Scan ALL Functions", + ScanAllFunctionsAction(self), + "Ctrl+Shift+L", + "Scan all functions in the binary", + -1 + ) + ida_kernwin.register_action(action_desc) + ida_kernwin.attach_action_to_menu("Edit/Plugins/", "symbiotic:scan_all", ida_kernwin.SETMENU_APP) + + # Configuration + action_desc = ida_kernwin.action_desc_t( + "symbiotic:configure", + "Symbiotic: Configuration", + ConfigureAction(self), + "", + "Configure Symbiotic Scanner", + -1 + ) + ida_kernwin.register_action(action_desc) + ida_kernwin.attach_action_to_menu("Edit/Plugins/", "symbiotic:configure", ida_kernwin.SETMENU_APP) + + # Ask AI about function + action_desc = ida_kernwin.action_desc_t( + "symbiotic:ask_ai", + "Symbiotic: Ask AI About Function", + AskAIAction(self), + "Ctrl+Shift+A", + "Ask AI to analyze the current function", + -1 + ) + ida_kernwin.register_action(action_desc) + ida_kernwin.attach_action_to_menu("Edit/Plugins/", "symbiotic:ask_ai", ida_kernwin.SETMENU_APP) + + + def run(self, arg): + """Run plugin - Show dialog with options""" + msg = f"""Symbiotic Security Scanner v{VERSION} + +Choose an action:""" + + result = ida_kernwin.ask_buttons( + "Scan Current Function", + "Scan ALL Functions", + "Configuration", + ida_kernwin.ASKBTN_BTN2, + msg + ) + + if result == ida_kernwin.ASKBTN_BTN1: + if not self.config.is_configured(): + ida_kernwin.warning("Please configure Symbiotic Scanner first!") + configure_symbiotic(self.config) + else: + ea = ida_kernwin.get_screen_ea() + self.scan_current_function(ea) + + elif result == ida_kernwin.ASKBTN_BTN2: + if not self.config.is_configured(): + ida_kernwin.warning("Please configure Symbiotic Scanner first!") + configure_symbiotic(self.config) + else: + self.scan_all_functions() + + elif result == ida_kernwin.ASKBTN_BTN3: + configure_symbiotic(self.config) + + def scan_current_function(self, ea): + """Scan function at current address (async)""" + func = ida_funcs.get_func(ea) + if not func: + ida_kernwin.warning("No function at current address") + return + + if self._scanning: + ida_kernwin.warning("A scan is already in progress") + return + + func_name = idc.get_func_name(ea) + print(f"\n[Symbiotic] Starting async scan of: {func_name}") + + self._scanning = True + ida_kernwin.show_wait_box(f"Scanning {func_name}...") + + def on_complete(result): + self._scanning = False + ida_kernwin.hide_wait_box() + + if "error" in result: + print(f"[Symbiotic] Scan failed: {result['error']}") + else: + print("[Symbiotic] Scan completed!") + + viewer = SymbioticResultsViewer(result) + viewer.Show("Symbiotic Scan Results") + + self.scanner.scan_function_async(func.start_ea, on_complete) + + def scan_all_functions(self): + """Scan all functions (async)""" + if self._scanning: + ida_kernwin.warning("A scan is already in progress") + return + + print("\n[Symbiotic] Starting async scan of all functions...") + + self._scanning = True + ida_kernwin.show_wait_box("Scanning all functions...") + + def on_complete(result): + self._scanning = False + ida_kernwin.hide_wait_box() + + if "error" in result: + print(f"[Symbiotic] Scan failed: {result['error']}") + else: + print("[Symbiotic] Scan completed!") + + viewer = SymbioticResultsViewer(result) + viewer.Show("Symbiotic Scan Results") + + self.scanner.scan_all_functions_async(on_complete) + + def ask_ai_about_function(self): + """Ask AI to analyze the current function""" + from ai_provider import AIProvider + from ai_prompts import analyze_function + + if not self.config.is_ai_configured(): + ida_kernwin.warning("AI not configured!\n\nAdd AI_MODEL and AI_API_KEY to .env file") + return + + ea = ida_kernwin.get_screen_ea() + func = ida_funcs.get_func(ea) + if not func: + ida_kernwin.warning("No function at current address") + return + + func_name = idc.get_func_name(ea) + print(f"[Symbiotic] Asking AI to analyze: {func_name}") + + # Get pseudocode + try: + cfunc = ida_hexrays.decompile(func.start_ea) + if not cfunc: + ida_kernwin.warning("Failed to decompile function") + return + pseudocode = str(cfunc) + except Exception as e: + ida_kernwin.warning(f"Decompilation failed: {e}") + return + + ida_kernwin.show_wait_box(f"Analyzing {func_name} with AI...") + + try: + provider = AIProvider( + model=self.config.ai_model, + api_key=self.config.ai_api_key, + api_base=self.config.ai_api_base + ) + analysis = analyze_function(provider, pseudocode, func_name) + ida_kernwin.hide_wait_box() + + # Show result + msg = f"AI Analysis of {func_name}\n{'='*50}\n\n{analysis}" + if len(msg) > 2000: + print(f"\n[Symbiotic AI] Analysis:\n{analysis}\n") + ida_kernwin.info(msg[:2000] + "\n\n... (see Output window for full)") + else: + print(f"\n[Symbiotic AI] Analysis:\n{analysis}\n") + ida_kernwin.info(msg) + + except Exception as e: + ida_kernwin.hide_wait_box() + ida_kernwin.warning(f"AI Error: {e}") + + + def term(self): + """Terminate the plugin""" + ida_kernwin.unregister_action("symbiotic:scan_current") + ida_kernwin.unregister_action("symbiotic:scan_all") + ida_kernwin.unregister_action("symbiotic:configure") + ida_kernwin.unregister_action("symbiotic:ask_ai") + + print("[Symbiotic] Plugin unloaded") + + +def PLUGIN_ENTRY(): + return SymbioticPlugin() + +``` + +`viewer.py`: + +```py +""" +Results Viewer Module +HTML-based viewer with navigation, export, filtering for IDA +""" + +import json +import os +import datetime +import idaapi +import idc +import ida_kernwin + +try: + from PySide6.QtWidgets import (QVBoxLayout, QHBoxLayout, QTextBrowser, + QPushButton, QComboBox, QLabel, QFileDialog, QWidget) + from PySide6.QtCore import Qt, QUrl +except ImportError: + try: + from PyQt6.QtWidgets import (QVBoxLayout, QHBoxLayout, QTextBrowser, + QPushButton, QComboBox, QLabel, QFileDialog, QWidget) + from PyQt6.QtCore import Qt, QUrl + except ImportError: + try: + from PyQt5.QtWidgets import (QVBoxLayout, QHBoxLayout, QTextBrowser, + QPushButton, QComboBox, QLabel, QFileDialog, QWidget) + from PyQt5.QtCore import Qt, QUrl + except ImportError: + from PySide2.QtWidgets import (QVBoxLayout, QHBoxLayout, QTextBrowser, + QPushButton, QComboBox, QLabel, QFileDialog, QWidget) + from PySide2.QtCore import Qt, QUrl + + +# Global scan history +_scan_history = [] + + +class SymbioticResultsViewer(idaapi.PluginForm): + """HTML-based viewer for displaying scan results with advanced features""" + + def __init__(self, results, func_line_map=None): + super().__init__() + self.results = results + self.func_line_map = func_line_map or results.get("func_line_map", {}) + self.current_filter = "ALL" + self.current_func_filter = "ALL" + self.current_tag_filter = "ALL" + self.vulns = [] + self.ai_results = {} + self.vuln_tags = {} + + self._load_tags() + self._load_ai_cache() + + _scan_history.append({ + "timestamp": datetime.datetime.now().isoformat(), + "results": results, + "vuln_count": len(self._parse_vulnerabilities()) + }) + + def OnCreate(self, form): + """Called when the form is created""" + self.parent = self.FormToPyQtWidget(form) + + main_layout = QVBoxLayout() + main_layout.setContentsMargins(0, 0, 0, 0) + main_layout.setSpacing(0) + + # Toolbar + toolbar = QHBoxLayout() + toolbar.setContentsMargins(8, 8, 8, 8) + + # Filter dropdown + filter_label = QLabel("Severity:") + filter_label.setStyleSheet("color: #cccccc; padding-right: 5px;") + toolbar.addWidget(filter_label) + + self.filter_combo = QComboBox() + self.filter_combo.addItems(["ALL", "CRITICAL", "HIGH", "MEDIUM", "LOW"]) + self.filter_combo.setStyleSheet(""" + QComboBox { + background-color: #3c3c3c; + color: #cccccc; + border: 1px solid #555555; + padding: 4px 8px; + min-width: 80px; + } + """) + self.filter_combo.currentTextChanged.connect(self._on_filter_changed) + toolbar.addWidget(self.filter_combo) + + # Function filter + func_label = QLabel("Function:") + func_label.setStyleSheet("color: #cccccc; padding-left: 10px; padding-right: 5px;") + toolbar.addWidget(func_label) + + self.func_filter_combo = QComboBox() + self.func_filter_combo.setStyleSheet(""" + QComboBox { + background-color: #3c3c3c; + color: #cccccc; + border: 1px solid #555555; + padding: 4px 8px; + min-width: 120px; + } + """) + self.func_filter_combo.currentTextChanged.connect(self._on_func_filter_changed) + toolbar.addWidget(self.func_filter_combo) + + # Tag filter + tag_label = QLabel("Tag:") + tag_label.setStyleSheet("color: #cccccc; padding-left: 10px; padding-right: 5px;") + toolbar.addWidget(tag_label) + + self.tag_filter_combo = QComboBox() + self.tag_filter_combo.addItems(["ALL", "Untagged", "Confirmed", "False Positive"]) + self.tag_filter_combo.setStyleSheet(""" + QComboBox { + background-color: #3c3c3c; + color: #cccccc; + border: 1px solid #555555; + padding: 4px 8px; + min-width: 100px; + } + """) + self.tag_filter_combo.currentTextChanged.connect(self._on_tag_filter_changed) + toolbar.addWidget(self.tag_filter_combo) + + toolbar.addStretch() + + # Export button + export_btn = QPushButton("Export JSON") + export_btn.setStyleSheet(""" + QPushButton { + background-color: #0e639c; + color: white; + border: none; + padding: 6px 12px; + } + QPushButton:hover { + background-color: #1177bb; + } + """) + export_btn.clicked.connect(self._export_json) + toolbar.addWidget(export_btn) + + # Export HTML button + export_html_btn = QPushButton("Export HTML") + export_html_btn.setStyleSheet(""" + QPushButton { + background-color: #0e639c; + color: white; + border: none; + padding: 6px 12px; + margin-left: 5px; + } + QPushButton:hover { + background-color: #1177bb; + } + """) + export_html_btn.clicked.connect(self._export_html) + toolbar.addWidget(export_html_btn) + + # History button + history_btn = QPushButton(f"History ({len(_scan_history)})") + history_btn.setStyleSheet(""" + QPushButton { + background-color: #3c3c3c; + color: #cccccc; + border: 1px solid #555555; + padding: 6px 12px; + margin-left: 5px; + } + QPushButton:hover { + background-color: #4c4c4c; + } + """) + history_btn.clicked.connect(self._show_history) + toolbar.addWidget(history_btn) + + toolbar_widget = QWidget() + toolbar_widget.setLayout(toolbar) + toolbar_widget.setStyleSheet("background-color: #252526;") + main_layout.addWidget(toolbar_widget) + + # Browser + self.browser = QTextBrowser() + self.browser.setOpenLinks(False) # Prevent default navigation + self.browser.setOpenExternalLinks(False) + self.browser.anchorClicked.connect(self._on_link_clicked) + self.vulns = self._parse_vulnerabilities() + self._populate_func_filter() # Populate function filter dropdown + self.browser.setHtml(self._generate_html()) + + self.browser.setStyleSheet(""" + QTextBrowser { + background-color: #1e1e1e; + color: #cccccc; + border: 0px; + padding: 0px; + margin: 0px; + } + """) + + main_layout.addWidget(self.browser) + self.parent.setLayout(main_layout) + + self._print_console_summary() + + def _refresh_html(self): + """Refresh HTML content while preserving scroll position""" + # Save scroll position + scrollbar = self.browser.verticalScrollBar() + scroll_pos = scrollbar.value() if scrollbar else 0 + + # Update content + self.browser.setHtml(self._generate_html()) + + # Restore scroll position after a short delay (Qt needs time to render) + if scrollbar and scroll_pos > 0: + try: + from PySide6.QtCore import QTimer + except ImportError: + try: + from PyQt6.QtCore import QTimer + except ImportError: + try: + from PyQt5.QtCore import QTimer + except ImportError: + from PySide2.QtCore import QTimer + + def restore_scroll(): + scrollbar.setValue(scroll_pos) + + QTimer.singleShot(50, restore_scroll) + + def _on_filter_changed(self, severity): + """Filter vulnerabilities by severity""" + self.current_filter = severity + self._refresh_html() + + def _on_func_filter_changed(self, func_name): + """Filter vulnerabilities by function""" + self.current_func_filter = func_name + self._refresh_html() + + def _on_tag_filter_changed(self, tag): + """Filter vulnerabilities by tag""" + self.current_tag_filter = tag + self._refresh_html() + + def _get_tags_file(self): + """Get path to persistent tags file""" + idb_path = idc.get_idb_path() if hasattr(idc, 'get_idb_path') else "" + if idb_path: + return idb_path + ".symbiotic_tags.json" + return os.path.expanduser("~/.symbiotic_tags.json") + + def _load_tags(self): + """Load tags from persistent file""" + try: + tags_file = self._get_tags_file() + if os.path.exists(tags_file): + with open(tags_file, "r") as f: + self.vuln_tags = json.load(f) + except: + self.vuln_tags = {} + + def _save_tags(self): + """Save tags to persistent file""" + try: + tags_file = self._get_tags_file() + with open(tags_file, "w") as f: + json.dump(self.vuln_tags, f, indent=2) + except Exception as e: + print(f"[Symbiotic] Error saving tags: {e}") + + def _get_ai_cache_file(self): + """Get path to persistent AI cache file""" + idb_path = idc.get_idb_path() if hasattr(idc, 'get_idb_path') else "" + if idb_path: + return idb_path + ".symbiotic_ai_cache.json" + return os.path.expanduser("~/.symbiotic_ai_cache.json") + + def _load_ai_cache(self): + """Load AI results from persistent cache""" + try: + cache_file = self._get_ai_cache_file() + if os.path.exists(cache_file): + with open(cache_file, "r") as f: + self.ai_results = json.load(f) + print(f"[Symbiotic] Loaded {len(self.ai_results)} cached AI results") + except Exception as e: + print(f"[Symbiotic] Could not load AI cache: {e}") + self.ai_results = {} + + def _save_ai_cache(self): + """Save AI results to persistent cache""" + try: + cache_file = self._get_ai_cache_file() + # Only save completed results (not loading states) + to_save = {} + for key, result in self.ai_results.items(): + content = result.get("content", "") + if not content.startswith("Generating") and not content.startswith("Error"): + to_save[key] = result + + with open(cache_file, "w") as f: + json.dump(to_save, f, indent=2) + except Exception as e: + print(f"[Symbiotic] Error saving AI cache: {e}") + + def _handle_tag(self, vuln_id, tag_type): + """Handle tagging a vulnerability""" + # Don't convert underscores - use ID as-is from URL + current = self.vuln_tags.get(vuln_id) + + # Toggle: if same tag, remove it; otherwise set it + if current == tag_type: + del self.vuln_tags[vuln_id] + print(f"[Symbiotic] Removed tag from {vuln_id}") + else: + self.vuln_tags[vuln_id] = tag_type + print(f"[Symbiotic] Tagged {vuln_id} as {tag_type.upper()}") + + self._save_tags() + self._refresh_html() + + def _populate_func_filter(self): + """Populate function filter dropdown with unique functions""" + functions = set(["ALL"]) + for v in self.vulns: + loc = v.get("location", {}) + line = loc.get("start_line", 0) + if line: + _, func_name = self._get_func_info_for_line(line) + if func_name: + functions.add(func_name) + + # Update dropdown + self.func_filter_combo.blockSignals(True) + self.func_filter_combo.clear() + self.func_filter_combo.addItems(sorted(functions)) + self.func_filter_combo.blockSignals(False) + + def _on_link_clicked(self, url): + """Handle internal links - navigate to function or AI actions""" + url_str = url.toString() + + if url_str.startswith("ida://"): + # Extract address from ida://0x12345 + addr_str = url_str.replace("ida://", "") + try: + addr = int(addr_str, 16) + # Jump to address in IDA + idc.jumpto(addr) + print(f"[Symbiotic] Jumped to {hex(addr)}") + except: + print(f"[Symbiotic] Invalid address: {addr_str}") + + elif url_str.startswith("ai://explain/"): + # AI Explain vulnerability + vuln_id = url_str.replace("ai://explain/", "").replace("_", " ") + self._handle_ai_explain(vuln_id) + + elif url_str.startswith("ai://poc/"): + # AI Generate PoC + vuln_id = url_str.replace("ai://poc/", "").replace("_", " ") + self._handle_ai_poc(vuln_id) + + elif url_str.startswith("ai://toggle/"): + # Toggle (hide) AI result + parts = url_str.replace("ai://toggle/", "").split("/") + if len(parts) == 2: + vuln_id = parts[0].replace("_", " ") + result_type = parts[1] + self._handle_ai_toggle(vuln_id, result_type) + + elif url_str.startswith("tag://confirmed/"): + vuln_id = url_str.replace("tag://confirmed/", "") + self._handle_tag(vuln_id, "confirmed") + + elif url_str.startswith("tag://false_positive/"): + vuln_id = url_str.replace("tag://false_positive/", "") + self._handle_tag(vuln_id, "false_positive") + + elif url_str.startswith("http"): + # Open external link + import webbrowser + webbrowser.open(url_str) + + def _handle_ai_explain(self, vuln_id): + """Call AI to explain a vulnerability (async)""" + import threading + from .config import SymbioticConfig + + config = SymbioticConfig() + if not config.is_ai_configured(): + ida_kernwin.warning("Gemini API not configured!\n\nGo to: Edit > Plugins > Symbiotic Configuration > AI") + return + + vuln = self._find_vuln_by_id(vuln_id) + if not vuln: + ida_kernwin.warning(f"Vulnerability not found: {vuln_id}") + return + + print(f"[Symbiotic] Calling AI to explain: {vuln.get('title', vuln_id)}") + + # Mark as loading - use composite key + storage_key = f"{vuln_id}_explain" + self.ai_results[storage_key] = {"type": "explain", "content": "Generating explanation..."} + self._refresh_html() + + def do_ai_call(): + try: + from .ai_provider import AIProvider + from .ai_prompts import explain_vulnerability + provider = AIProvider(model=config.ai_model, api_key=config.ai_api_key, api_base=config.ai_api_base) + explanation = explain_vulnerability( + provider, + vuln.get("title", "Vulnerability"), + vuln.get("cwe", ""), + vuln.get("snippet", "") + ) + + # Update on main thread via execute_sync + def update_ui(): + self.ai_results[storage_key] = {"type": "explain", "content": explanation} + + # Check if AI detected this as a false positive + if "[LIKELY FALSE POSITIVE]" in explanation.upper() or "LIKELY FALSE POSITIVE" in explanation.upper(): + # Auto-tag as false positive + self.vuln_tags[vuln_id] = "false_positive" + self._save_tags() + print(f"[Symbiotic AI] Auto-tagged as FALSE POSITIVE: {vuln_id}") + elif "[CONFIRMED VULNERABILITY]" in explanation.upper() or "CONFIRMED VULNERABILITY" in explanation.upper(): + # Auto-tag as confirmed + self.vuln_tags[vuln_id] = "confirmed" + self._save_tags() + print(f"[Symbiotic AI] Auto-tagged as CONFIRMED: {vuln_id}") + self._save_ai_cache() # Persist cache + self._refresh_html() + print(f"[Symbiotic AI] Explanation generated") + + idaapi.execute_sync(update_ui, idaapi.MFF_FAST) + + except Exception as e: + def show_error(): + self.ai_results[storage_key] = {"type": "explain", "content": f"Error: {e}"} + self._refresh_html() + idaapi.execute_sync(show_error, idaapi.MFF_FAST) + + thread = threading.Thread(target=do_ai_call, daemon=True) + thread.start() + + def _handle_ai_poc(self, vuln_id): + """Call AI to generate PoC (async)""" + import threading + from .config import SymbioticConfig + + config = SymbioticConfig() + if not config.is_ai_configured(): + ida_kernwin.warning("Gemini API not configured!\n\nGo to: Edit > Plugins > Symbiotic Configuration > AI") + return + + vuln = self._find_vuln_by_id(vuln_id) + if not vuln: + ida_kernwin.warning(f"Vulnerability not found: {vuln_id}") + return + + print(f"[Symbiotic] Generating PoC for: {vuln.get('title', vuln_id)}") + + # Mark as loading - use composite key + storage_key = f"{vuln_id}_poc" + self.ai_results[storage_key] = {"type": "poc", "content": "Generating PoC exploit..."} + self._refresh_html() + + def do_ai_call(): + try: + from .ai_provider import AIProvider + from .ai_prompts import generate_poc + provider = AIProvider(model=config.ai_model, api_key=config.ai_api_key, api_base=config.ai_api_base) + poc = generate_poc( + provider, + vuln.get("title", "Vulnerability"), + vuln.get("cwe", ""), + vuln.get("snippet", ""), + "target_function" + ) + + def update_ui(): + self.ai_results[storage_key] = {"type": "poc", "content": poc} + self._save_ai_cache() # Persist cache + self._refresh_html() + print(f"[Symbiotic AI] PoC generated") + + idaapi.execute_sync(update_ui, idaapi.MFF_FAST) + + except Exception as e: + def show_error(): + self.ai_results[storage_key] = {"type": "poc", "content": f"Error: {e}"} + self._refresh_html() + idaapi.execute_sync(show_error, idaapi.MFF_FAST) + + thread = threading.Thread(target=do_ai_call, daemon=True) + thread.start() + + def _handle_ai_toggle(self, vuln_id, result_type): + """Toggle (collapse/expand) AI result content""" + # Find the result and toggle its hidden state + for vid in [vuln_id, vuln_id.replace(" ", "_"), vuln_id.replace("_", " ")]: + key = f"{vid}_{result_type}" + if key in self.ai_results: + result = self.ai_results[key] + # Toggle hidden state + result["hidden"] = not result.get("hidden", False) + print(f"[Symbiotic] Toggled {result_type}: hidden={result['hidden']}") + break + + # Refresh display + self._refresh_html() + + def _find_vuln_by_id(self, vuln_id): + """Find vulnerability by rule_id""" + for v in self.vulns: + rule_id = v.get("rule_id", "") + # Match with both spaces and underscores + if rule_id.replace(" ", "_") == vuln_id.replace(" ", "_"): + return v + return None + + def _get_ai_result(self, vuln_id, result_type=None): + """Get cached AI result for a vulnerability + + Args: + vuln_id: Vulnerability ID (will try with spaces and underscores) + result_type: 'explain' or 'poc' - required for new composite key lookup + """ + if not result_type: + return None + + # Normalize vuln_id formats + vuln_id_spaces = vuln_id.replace("_", " ") + vuln_id_underscores = vuln_id.replace(" ", "_") + + # Try composite keys with both formats + for vid in [vuln_id, vuln_id_spaces, vuln_id_underscores]: + key = f"{vid}_{result_type}" + result = self.ai_results.get(key) + if result: + return result + + return None + + def _render_ai_content(self, result, accent_color): + """Render AI result content to HTML""" + content = result.get("content", "") + + # Only show loading for actual loading messages (short strings starting with "Generating") + if content.startswith("Generating") and len(content) < 50: + return f'{self._escape_html(content)}' + + return self._markdown_to_html(content, accent_color) + + def _highlight_code(self, code, lang="c"): + """Apply syntax highlighting to code with proper formatting""" + import re + + # Colors for syntax highlighting + colors = { + "keyword": "#c45d97", # Pink - keywords + "type": "#74c1cf", # Cyan - types + "string": "#6ad9a9", # Green - strings + "comment": "#666666", # Gray - comments + "number": "#e5c166", # Yellow - numbers + "default": "#e0e0e0", # White - default + } + + # C/C++ keywords and types + c_keywords = {'if', 'else', 'for', 'while', 'do', 'switch', 'case', 'break', 'continue', + 'return', 'goto', 'typedef', 'struct', 'union', 'enum', 'sizeof', 'void', + 'const', 'static', 'extern', 'volatile', 'register', 'inline', 'auto', + 'default', 'include', 'define', 'ifdef', 'ifndef', 'endif', 'elif', 'pragma'} + c_types = {'int', 'char', 'float', 'double', 'long', 'short', 'unsigned', 'signed', + 'size_t', 'bool', 'NULL', 'true', 'false', 'uint8_t', 'uint16_t', 'uint32_t', + 'uint64_t', 'int8_t', 'int16_t', 'int32_t', 'int64_t', 'FILE', 'void'} + + # Python keywords + py_keywords = {'def', 'class', 'if', 'elif', 'else', 'for', 'while', 'try', 'except', + 'finally', 'with', 'as', 'import', 'from', 'return', 'yield', 'raise', + 'pass', 'break', 'continue', 'and', 'or', 'not', 'in', 'is', 'None', + 'True', 'False', 'lambda', 'global', 'nonlocal', 'assert', 'async', 'await'} + + keywords = py_keywords if lang in ['python', 'py'] else c_keywords + types = set() if lang in ['python', 'py'] else c_types + + def highlight_line(line): + """Highlight a single line of code""" + if not line: + return "" + + # Handle leading whitespace (preserve indentation) + leading_spaces = len(line) - len(line.lstrip(' ')) + indent = ' ' * leading_spaces + line = line[leading_spaces:] + + # Check for C-style comments + if line.strip().startswith('//'): + return f'{indent}{self._escape_html(line)}' + + # Check for Python comments + if lang in ['python', 'py'] and line.strip().startswith('#'): + return f'{indent}{self._escape_html(line)}' + + # Tokenize and highlight + result = [indent] + + # Split by word boundaries but keep delimiters + tokens = re.split(r'(\s+|[^\w]+)', line) + + in_string = False + string_char = None + + for token in tokens: + if not token: + continue + + # Handle strings + if not in_string and (token.startswith('"') or token.startswith("'")): + in_string = True + string_char = token[0] + result.append(f'{self._escape_html(token)}') + if len(token) > 1 and token.endswith(string_char): + in_string = False + result.append('') + continue + + if in_string: + result.append(self._escape_html(token)) + if token.endswith(string_char) and not token.endswith('\\' + string_char): + in_string = False + result.append('') + continue + + escaped = self._escape_html(token) + + # Highlight based on token type + if token in keywords: + result.append(f'{escaped}') + elif token in types: + result.append(f'{escaped}') + elif re.match(r'^0x[0-9a-fA-F]+$', token) or re.match(r'^\d+$', token): + result.append(f'{escaped}') + elif token.isspace(): + result.append(escaped.replace(' ', ' ')) + else: + result.append(f'{escaped}') + + if in_string: + result.append('') + + return ''.join(result) + + # Process line by line + lines = code.split('\n') + highlighted_lines = [highlight_line(line) for line in lines] + + return '
'.join(highlighted_lines) + + def _markdown_to_html(self, text, accent_color="#c45d97"): + """Convert markdown to styled HTML for IDA viewer""" + import re + + # Use accent_color for headings, bullets, inline code + colors = { + "text": "#e0e0e0", + "heading": accent_color, # Use accent color + "code_bg": "#0a0a0f", # Very dark + "code_text": "#74c1cf", # Info cyan + "inline_code": accent_color, # Use accent color + "bold": "#ffffff", + "bullet": accent_color, # Use accent color + "link": "#74c1cf", # Info cyan + "success": "#6ad9a9", + "warning": "#e5c166", + "error": "#e57260", + } + + # IMPORTANT: Process code blocks BEFORE HTML escaping + # to preserve code formatting + def replace_code_block(match): + lang = match.group(1) or "" + code = match.group(2).strip() + + # Apply syntax highlighting (code is not yet escaped) + highlighted = self._highlight_code(code, lang if lang else "c") + + return f'
{highlighted}
' + + # Extract code blocks first, before escaping + text = re.sub(r'```(\w*)\n?(.*?)```', replace_code_block, text, flags=re.DOTALL) + + # Now escape the rest of the HTML (code blocks already processed) + # We need to be careful - code blocks are now HTML, rest is not + # Split by code blocks and escape only non-code parts + html = text + + # Headers - NO extra line breaks (remove
) + # Process #### first (most specific), then ###, ##, # + html = re.sub(r'^#### (.+)$', f'\\1', html, flags=re.MULTILINE) + html = re.sub(r'^### (.+)$', f'\\1', html, flags=re.MULTILINE) + html = re.sub(r'^## (.+)$', f'\\1', html, flags=re.MULTILINE) + html = re.sub(r'^# (.+)$', f'\\1', html, flags=re.MULTILINE) + + # Bold **text** + html = re.sub(r'\*\*(.+?)\*\*', f'\\1', html) + + # Italic *text* + html = re.sub(r'\*(.+?)\*', '\\1', html) + + # Inline code `code` + html = re.sub(r'`([^`]+)`', f'\\1', html) + + # Bullet points + html = re.sub(r'^- (.+)$', f' \\1', html, flags=re.MULTILINE) + html = re.sub(r'^\* (.+)$', f' \\1', html, flags=re.MULTILINE) + + # Numbered lists + html = re.sub(r'^(\d+)\. (.+)$', f'\\1. \\2', html, flags=re.MULTILINE) + + # Line breaks + html = html.replace('\n', '
') + + # Wrap in default text color + return f'{html}' + + def _export_json(self): + """Export results to JSON file""" + filename, _ = QFileDialog.getSaveFileName( + self.parent, "Export Results", + os.path.expanduser("~/symbiotic_results.json"), + "JSON Files (*.json)" + ) + if filename: + # Prepare AI results for export + ai_data = {} + for key, result in self.ai_results.items(): + if not result.get("hidden", False): + content = result.get("content", "") + if not content.startswith("Generating"): + ai_data[key] = {"type": result.get("type", "unknown"), "content": content} + + export_data = { + "timestamp": datetime.datetime.now().isoformat(), + "scanner": self.results.get("scanner", "opengrep"), + "scan_type": self.results.get("scan_type", "unknown"), + "total_functions": self.results.get("total_functions", 0), + "vulnerabilities": self.vulns, + "ai_results": ai_data + } + with open(filename, "w") as f: + json.dump(export_data, f, indent=2) + print(f"[Symbiotic] Results exported to {filename}") + ida_kernwin.info(f"Results exported to:\n{filename}") + + def _export_html(self): + """Export results to HTML file""" + filename, _ = QFileDialog.getSaveFileName( + self.parent, "Export Results", + os.path.expanduser("~/symbiotic_results.html"), + "HTML Files (*.html)" + ) + if filename: + html_content = self._generate_html(for_export=True) + with open(filename, "w") as f: + f.write(html_content) + print(f"[Symbiotic] HTML exported to {filename}") + ida_kernwin.info(f"HTML exported to:\n{filename}") + + def _show_history(self): + """Show scan history with diff comparison""" + if len(_scan_history) < 1: + ida_kernwin.info("No scan history available") + return + + msg = "═══ SCAN HISTORY ═══\n\n" + + for i, h in enumerate(reversed(_scan_history[-10:])): + ts = h["timestamp"][:19].replace("T", " ") + count = h["vuln_count"] + current = " ← CURRENT" if i == 0 else "" + msg += f"{i+1}. {ts} - {count} issue(s){current}\n" + + # Show diff if we have at least 2 scans + if len(_scan_history) >= 2: + current_scan = _scan_history[-1] + previous_scan = _scan_history[-2] + + # Get rule IDs from each scan + current_rules = set() + previous_rules = set() + + try: + current_results = current_scan.get("results", {}).get("output", "") + if current_results: + current_data = json.loads(current_results) + for v in current_data.get("fail_results", []): + current_rules.add(v.get("rule_id", "")) + + previous_results = previous_scan.get("results", {}).get("output", "") + if previous_results: + previous_data = json.loads(previous_results) + for v in previous_data.get("fail_results", []): + previous_rules.add(v.get("rule_id", "")) + + new_vulns = current_rules - previous_rules + fixed_vulns = previous_rules - current_rules + + msg += "\n═══ DIFF vs PREVIOUS ═══\n" + + if new_vulns: + msg += f"\n🔴 NEW ({len(new_vulns)}):\n" + for r in list(new_vulns)[:5]: + msg += f" + {r}\n" + if len(new_vulns) > 5: + msg += f" ... and {len(new_vulns) - 5} more\n" + + if fixed_vulns: + msg += f"\n🟢 FIXED ({len(fixed_vulns)}):\n" + for r in list(fixed_vulns)[:5]: + msg += f" - {r}\n" + if len(fixed_vulns) > 5: + msg += f" ... and {len(fixed_vulns) - 5} more\n" + + if not new_vulns and not fixed_vulns: + msg += "\nNo changes from previous scan.\n" + + except Exception as e: + msg += f"\n(Could not compute diff: {e})\n" + + ida_kernwin.info(msg) + + def _get_func_info_for_line(self, line_num): + """Get function address and name for a given line number""" + for func_ea, info in self.func_line_map.items(): + if info["start_line"] <= line_num <= info["end_line"]: + return func_ea, info.get("name", "Unknown") + return None, None + + def _group_vulnerabilities(self, vulns): + """Group vulnerabilities by rule_id to reduce repetition""" + from collections import OrderedDict + + grouped = OrderedDict() + for v in vulns: + rule_id = v.get("rule_id", "UNKNOWN") + if rule_id not in grouped: + grouped[rule_id] = { + "rule_id": rule_id, + "title": v.get("title", "Vulnerability"), + "severity": v.get("severity", "MEDIUM"), + "description": v.get("description", ""), + "cwe": v.get("cwe", ""), + "impact": v.get("impact", ""), + "references": v.get("references", []), + "impact_level": v.get("impact_level", "MEDIUM"), + "likelihood_level": v.get("likelihood_level", "LOW"), + "confidence_level": v.get("confidence_level", "MEDIUM"), + "occurrences": [] + } + + # Add this occurrence + loc = v.get("location", {}) + line = loc.get("start_line", 0) + func_addr, func_name = self._get_func_info_for_line(line) if line else (None, None) + + grouped[rule_id]["occurrences"].append({ + "line": line, + "snippet": v.get("snippet", ""), + "func_addr": func_addr, + "func_name": func_name + }) + + return list(grouped.values()) + + def _generate_html(self, for_export=False): + """Generate HTML content""" + + # Colors + bg_main = "#1e1e1e" + bg_card = "#252526" + text_white = "#ffffff" + text_gray = "#858585" + text_light = "#cccccc" + brand_pink = "#c45d97" + color_critical = "#ff4040" + color_error = "#e57260" + color_warning = "#e5c166" + color_success = "#6ad9a9" + color_info = "#74c1cf" + color_link = "#3794ff" + + html = f''' + + + + + + +''' + if "error" in self.results: + html += f'Error: {self._escape_html(self.results["error"])}' + html += '' + return html + + scan_type = self.results.get("scan_type", "single_function") + scanner = self.results.get("scanner", "opengrep").upper() + + if scan_type == "all_functions": + total = self.results.get("total_functions", 0) + scanned = self.results.get("functions_scanned", 0) + header_info = f"Scanner: {scanner} | Functions: {scanned}/{total}" + else: + func = self.results.get("function_name", "Unknown") + addr = self.results.get("address", "Unknown") + header_info = f"{self._escape_html(func)} @ {addr} | Scanner: {scanner}" + + # Filter vulnerabilities by severity + filtered_vulns = self.vulns + if self.current_filter != "ALL": + filtered_vulns = [v for v in filtered_vulns if v.get("severity", "").upper() == self.current_filter] + + # Filter by function + if self.current_func_filter != "ALL": + def match_func(v): + loc = v.get("location", {}) + line = loc.get("start_line", 0) + if line: + _, func_name = self._get_func_info_for_line(line) + return func_name == self.current_func_filter + return False + filtered_vulns = [v for v in filtered_vulns if match_func(v)] + + # Filter by tag + if self.current_tag_filter != "ALL": + def match_tag(v): + rule_id = v.get("rule_id", "UNKNOWN") + tag = self.vuln_tags.get(rule_id) + if self.current_tag_filter == "Untagged": + return tag is None + elif self.current_tag_filter == "Confirmed": + return tag == "confirmed" + elif self.current_tag_filter == "False Positive": + return tag == "false_positive" + return True + filtered_vulns = [v for v in filtered_vulns if match_tag(v)] + + # Group vulnerabilities by rule_id + grouped_vulns = self._group_vulnerabilities(filtered_vulns) + + status_html = f'[OK] Scan completed successfully' + + # Build filter info + filter_info = [] + if self.current_filter != "ALL": + filter_info.append(self.current_filter) + if self.current_func_filter != "ALL": + filter_info.append(f"func:{self.current_func_filter}") + if self.current_tag_filter != "ALL": + filter_info.append(f"tag:{self.current_tag_filter}") + filter_str = f" (filtered: {', '.join(filter_info)})" if filter_info else "" + + html += f''' +{header_info}
+{status_html}
+Found {len(self.vulns)} issue(s) in {len(grouped_vulns)} unique rule(s){filter_str} +

+''' + + if grouped_vulns: + for g in grouped_vulns: + html += self._render_grouped_vuln(g, bg_card, text_white, text_gray, text_light, + brand_pink, color_critical, color_error, + color_warning, color_success, color_info, for_export) + else: + html += f''' + + +
+[OK]

+No Issues Found
+No vulnerabilities match the current filter. +
+''' + + html += '' + return html + + def _render_grouped_vuln(self, g, bg_card, text_white, text_gray, text_light, brand_pink, + color_critical, color_error, color_warning, color_success, color_info, for_export=False): + """Render a grouped vulnerability with multiple occurrences""" + rule_id = g.get("rule_id", "UNKNOWN") + title = g.get("title", "Vulnerability") + severity = g.get("severity", "MEDIUM").upper() + desc = g.get("description", "") + cwe = g.get("cwe", "") + impact_desc = g.get("impact", "") + refs = g.get("references", []) + occurrences = g.get("occurrences", []) + + impact_level = g.get("impact_level", "HIGH" if severity in ["CRITICAL", "HIGH"] else "MEDIUM") + likelihood = g.get("likelihood_level", "LOW") + confidence = g.get("confidence_level", "MEDIUM") + + sev_styles = { + "CRITICAL": (color_critical, "#ffffff"), + "HIGH": (color_error, "#ffffff"), + "MEDIUM": (color_warning, "#1e1e1e"), + "LOW": (color_success, "#1e1e1e"), + } + sev_bg, sev_fg = sev_styles.get(severity, ("#666666", "#ffffff")) + + occ_count = len(occurrences) + occ_text = f"{occ_count} occurrence{'s' if occ_count > 1 else ''}" + + + # Tag buttons (only when not exporting) + vuln_id_safe = rule_id.replace(" ", "_") + + # Get current tag for this vuln (use safe ID with underscores) + current_tag = self.vuln_tags.get(vuln_id_safe) + tag_badge = "" + if current_tag == "confirmed": + tag_badge = f' ✓ CONFIRMED' + elif current_tag == "false_positive": + tag_badge = f' ✗ FALSE POSITIVE' + + tag_buttons = "" + if not for_export: + confirm_style = f'color:{color_success}; font-weight:bold;' if current_tag == "confirmed" else 'color:#666;' + fp_style = f'color:{color_error}; font-weight:bold;' if current_tag == "false_positive" else 'color:#666;' + tag_buttons = f'''''' + + html = f''' +
+ +
+{tag_buttons} +{self._escape_html(rule_id)} | {occ_text}{tag_badge}
+{self._escape_html(title)}

+ + + + + +
{severity}Impact: {impact_level}Likelihood: {likelihood}Confidence: {confidence}
+''' + + if cwe: + html += f'''

+ + +
{self._escape_html(cwe)}
+''' + + if desc: + html += f'''

+Description
+{self._escape_html(desc[:700])} +''' + + if impact_desc: + html += f'''

+Impact
+{self._escape_html(impact_desc[:500])} +''' + + # Occurrences section - show all locations with jump links + if occurrences: + html += f'''

+Occurrences ({occ_count})
+ +''' + for i, occ in enumerate(occurrences): + line = occ.get("line", 0) + func_name = occ.get("func_name", "") + func_addr = occ.get("func_addr") + snippet = occ.get("snippet", "") + + # Jump link + jump_link = "" + if func_addr and not for_export: + jump_link = f'[Jump]' + + func_display = f' in {self._escape_html(func_name)}' if func_name else "" + + # Alternating row colors + row_bg = "#1e1e1e" if i % 2 == 0 else "#252526" + + html += f''' + + +''' + html += '
+Line {line}{func_display} {jump_link}
+{self._escape_html(snippet[:150])} +
' + + if refs: + html += f'

Resources
' + for r in refs[:3]: + html += f'{self._escape_html(r)}
' + + # AI Action buttons + if not for_export: + vuln_id = rule_id.replace(" ", "_") + html += f'''

+ + + +
+Explain with AI + +Generate PoC +
+''' + + # AI Results section (same as before) + explain_result = self._get_ai_result(rule_id, "explain") + poc_result = self._get_ai_result(rule_id, "poc") + has_explain = explain_result is not None + has_poc = poc_result is not None + + if has_explain or has_poc: + explain_hidden = explain_result.get("hidden", False) if has_explain else True + poc_hidden = poc_result.get("hidden", False) if has_poc else True + + html += '
' + + if has_explain: + content = explain_result.get("content", "") + is_loading = content.startswith("Generating") and len(content) < 50 + tab_color = "#e5c166" if is_loading else "#c45d97" + status = " (loading...)" if is_loading else "" + toggle_text = "[show]" if explain_hidden else "[hide]" + toggle_link = f'{toggle_text}' if not is_loading else "" + width = "50%" if has_poc else "100%" + html += f'''''' + + if has_poc: + content = poc_result.get("content", "") + is_loading = content.startswith("Generating") and len(content) < 50 + tab_color = "#e5c166" if is_loading else "#74c1cf" + status = " (loading...)" if is_loading else "" + toggle_text = "[show]" if poc_hidden else "[hide]" + toggle_link = f'{toggle_text}' if not is_loading else "" + width = "50%" if has_explain else "100%" + html += f'''''' + + html += '
+AI Explanation{status}{toggle_link} + +Generated PoC{status}{toggle_link} +
' + + show_explain = has_explain and not explain_hidden + show_poc = has_poc and not poc_hidden + + if show_explain or show_poc: + html += '' + + if has_explain and has_poc: + if show_explain: + explain_html = self._render_ai_content(explain_result, "#c45d97") + html += f'' + else: + html += '' + + if show_poc: + poc_html = self._render_ai_content(poc_result, "#74c1cf") + html += f'' + else: + html += '' + else: + if show_explain: + explain_html = self._render_ai_content(explain_result, "#c45d97") + html += f'' + elif show_poc: + poc_html = self._render_ai_content(poc_result, "#74c1cf") + html += f'' + + html += '
{explain_html}{poc_html}{explain_html}{poc_html}
' + + html += '
' + return html + + def _render_vuln(self, v, bg_card, text_white, text_gray, text_light, brand_pink, + color_critical, color_error, color_warning, color_success, color_info, for_export=False): + """Render a single vulnerability with navigation link""" + rule_id = v.get("rule_id", "UNKNOWN") + title = v.get("title", "Vulnerability") + severity = v.get("severity", "MEDIUM").upper() + desc = v.get("description", "") + snippet = v.get("snippet", "") + cwe = v.get("cwe", "") + impact_desc = v.get("impact", "") + refs = v.get("references", []) + loc = v.get("location", {}) + line = loc.get("start_line", 0) + + # Get function address and name for navigation + func_addr, func_name = self._get_func_info_for_line(line) if line else (None, None) + + impact_level = v.get("impact_level", "HIGH" if severity in ["CRITICAL", "HIGH"] else "MEDIUM") + likelihood = v.get("likelihood_level", "LOW") + confidence = v.get("confidence_level", "MEDIUM") + + sev_styles = { + "CRITICAL": (color_critical, "#ffffff"), + "HIGH": (color_error, "#ffffff"), + "MEDIUM": (color_warning, "#1e1e1e"), + "LOW": (color_success, "#1e1e1e"), + } + sev_bg, sev_fg = sev_styles.get(severity, ("#666666", "#ffffff")) + + # Navigation link - put on separate line, right-aligned + nav_link = "" + if func_addr and not for_export: + nav_link = f'' + + # Function name display + func_display = f" in {self._escape_html(func_name)}" if func_name else "" + + html = f''' +
+ +
+{nav_link} +{self._escape_html(rule_id)} | Line {line}{func_display}
+{self._escape_html(title)}

+ + + + + +
{severity}Impact: {impact_level}Likelihood: {likelihood}Confidence: {confidence}
+''' + + if cwe: + html += f'''

+ + +
{self._escape_html(cwe)}
+''' + + if desc: + html += f'''

+Description
+{self._escape_html(desc[:700])} +''' + + if impact_desc: + html += f'''

+Impact
+{self._escape_html(impact_desc[:500])} +''' + + if snippet: + html += f'''

+Vulnerable code example
+ + +
{self._escape_html(snippet)}
+''' + + if refs: + html += f'

Resources
' + for r in refs[:3]: + html += f'{self._escape_html(r)}
' + + # AI Action buttons (VSCode-style) + if not for_export: + vuln_id = rule_id.replace(" ", "_") + html += f'''

+ + + +
+Explain with AI + +Generate PoC +
+''' + + # Check if there are AI results for this vuln + explain_result = self._get_ai_result(vuln_id, "explain") + poc_result = self._get_ai_result(vuln_id, "poc") + + has_explain = explain_result is not None + has_poc = poc_result is not None + + if has_explain or has_poc: + # Use fixed table layout for aligned 50/50 columns + html += '
' + + # Determine if content is hidden + explain_hidden = explain_result.get("hidden", False) if has_explain else True + poc_hidden = poc_result.get("hidden", False) if has_poc else True + + # Tab headers - always 50% each when both exist + if has_explain: + content = explain_result.get("content", "") + is_loading = content.startswith("Generating") and len(content) < 50 + tab_color = "#e5c166" if is_loading else "#c45d97" + status = " (loading...)" if is_loading else "" + toggle_text = "[show]" if explain_hidden else "[hide]" + toggle_link = f'{toggle_text}' if not is_loading else "" + width = "50%" if has_poc else "100%" + html += f'''''' + + if has_poc: + content = poc_result.get("content", "") + is_loading = content.startswith("Generating") and len(content) < 50 + tab_color = "#e5c166" if is_loading else "#74c1cf" + status = " (loading...)" if is_loading else "" + toggle_text = "[show]" if poc_hidden else "[hide]" + toggle_link = f'{toggle_text}' if not is_loading else "" + width = "50%" if has_explain else "100%" + html += f'''''' + + html += '
+AI Explanation{status}{toggle_link} + +Generated PoC{status}{toggle_link} +
' + + # Content area - only show if not hidden + show_explain_content = has_explain and not explain_hidden + show_poc_content = has_poc and not poc_hidden + + if show_explain_content or show_poc_content: + html += '' + + if has_explain and has_poc: + # Both exist - show columns based on hidden state + if show_explain_content: + explain_html = self._render_ai_content(explain_result, "#c45d97") + html += f'''''' + else: + html += '' # Empty placeholder for alignment + + if show_poc_content: + poc_html = self._render_ai_content(poc_result, "#74c1cf") + html += f'''''' + else: + html += '' # Empty placeholder for alignment + else: + # Single result - full width + if show_explain_content: + explain_html = self._render_ai_content(explain_result, "#c45d97") + html += f'''''' + elif show_poc_content: + poc_html = self._render_ai_content(poc_result, "#74c1cf") + html += f'''''' + + html += '
+{explain_html} + +{poc_html} + +{explain_html} + +{poc_html} +
' + + html += '
' + return html + + def _parse_vulnerabilities(self): + """Parse vulnerabilities from scan output""" + output = self.results.get("output", "") + if not output: + return [] + try: + data = json.loads(output) + return data.get("fail_results", []) + except: + return [] + + def _escape_html(self, text): + """Escape HTML special characters""" + if not text: + return "" + text = str(text) + return text.replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """) + + def _print_console_summary(self): + """Print summary to IDA console""" + print(f"\n[Symbiotic] Scan Results: {len(self.vulns)} issue(s) found") + for v in self.vulns[:5]: + title = v.get("title", "Unknown") + severity = v.get("severity", "?") + print(f" - [{severity}] {title}") + if len(self.vulns) > 5: + print(f" ... and {len(self.vulns) - 5} more") + + def OnClose(self, form): + pass + + def Show(self, title="Symbiotic Scan Results"): + return idaapi.PluginForm.Show(self, title, options=idaapi.PluginForm.WOPN_PERSIST) + +``` \ No newline at end of file diff --git a/archive/a1ext/auto_re.txt b/archive/a1ext/auto_re.txt new file mode 100644 index 00000000..a8a3a8c5 --- /dev/null +++ b/archive/a1ext/auto_re.txt @@ -0,0 +1,957 @@ +Project Path: arc_a1ext_auto_re_rc0lblvb + +Source Tree: + +```txt +arc_a1ext_auto_re_rc0lblvb +├── README.md +├── auto_re.py +├── docs +│ ├── auto_rename_dst.png +│ ├── auto_rename_src.png +│ ├── function_rename.png +│ ├── tags_in_unexplored_code.png +│ ├── tags_view_0.png +│ └── tags_view_1.png +└── ida-plugin.json + +``` + +`README.md`: + +```md +AutoRE +=== + + +Features +======== + +## 1. Auto-renaming dummy-named functions, which have one API call or jump to the imported API + +### Before +![auto_rename_src.png](docs/auto_rename_src.png) + +### After +![auto_rename_dst.png](docs/auto_rename_dst.png) + + +## 2. Assigning TAGS to functions accordingly to called API-indicators inside + +* Sets tags as repeatable function comments and displays TAG tree in the separate view + + +Some screenshots of TAGS view: + +![tags_view_0.png](docs/tags_view_0.png) + +![tags_view_1.png](docs/tags_view_1.png) + +How TAGs look in unexplored code: +![tags_in_unexplored_code.png](docs/tags_in_unexplored_code.png) + + +You can easily rename function using its context menu or just pressing `n` hotkey: + +![function_rename.png](docs/function_rename.png) + +# Installation + +Just copy `auto_re.py` to the `IDA\plugins` directory and it will be available through `Edit -> Plugins -> Auto RE` menu +``` + +`auto_re.py`: + +```py +# -*- coding: utf-8 -* +__author__ = 'Trafimchuk Aliaksandr' +__version__ = '2.2' + +from collections import defaultdict +import idaapi +from idautils import FuncItems, CodeRefsTo +from idaapi import o_reg, o_imm, o_far, o_near, o_mem, o_displ +import os +import re +import sys +import traceback + + +HAS_PYSIDE = idaapi.IDA_SDK_VERSION < 690 +if HAS_PYSIDE: + from PySide import QtGui, QtCore + from PySide.QtGui import QTreeView, QVBoxLayout, QLineEdit, QMenu, QInputDialog, QAction, QTabWidget +else: + if idaapi.IDA_SDK_VERSION >= 920: + from PySide6 import QtGui, QtCore + from PySide6.QtGui import QAction + from PySide6.QtWidgets import QTreeView, QVBoxLayout, QLineEdit, QMenu, QInputDialog, QTabWidget + else: + from PyQt5 import QtGui, QtCore + from PyQt5.QtWidgets import QTreeView, QVBoxLayout, QLineEdit, QMenu, QInputDialog, QAction, QTabWidget + + +try: + # Python 2. + xrange +except NameError: + # Python 3. + xrange = range + + +# enable to allow PyCharm remote debug +RDEBUG = False +# adjust this value to be a full path to a debug egg +RDEBUG_EGG = r'c:\Program Files\JetBrains\PyCharm 2017.1.4\debug-eggs\pycharm-debug.egg' +RDEBUG_HOST = 'localhost' +RDEBUG_PORT = 12321 + + +TAGS_IGNORE_LIST = { + 'OpenProcessToken', + 'DisconnectNamedPipe' +} + +IGNORE_CALL_LIST = { + 'RtlNtStatusToDosError', + 'GetLastError', + 'SetLastError' +} + +TAGS = { + 'net': ['WSAStartup', 'socket', 'recv', 'recvfrom', 'send', 'sendto', 'acccept', 'bind', 'listen', 'select', + 'setsockopt', 'ioctlsocket', 'closesocket', 'WSAAccept', 'WSARecv', 'WSARecvFrom', 'WSASend', 'WSASendTo', + 'WSASocket', 'WSAConnect', 'ConnectEx', 'TransmitFile', 'HTTPOpenRequest', 'HTTPSendRequest', + 'URLDownloadToFile', 'InternetCrackUrl', 'InternetOpen', 'InternetOpen', 'InternetConnect', + 'InternetOpenUrl', 'InternetQueryOption', 'InternetSetOption', 'InternetReadFile', 'InternetWriteFile', + 'InternetGetConnectedState', 'InternetSetStatusCallback', 'DnsQuery', 'getaddrinfo', 'GetAddrInfo', + 'GetAdaptersInfo', 'GetAdaptersAddresses', 'HttpQueryInfo', 'ObtainUserAgentString', 'WNetGetProviderName', + 'GetBestInterfaceEx', 'gethostbyname', 'getsockname', 'connect', 'WinHttpOpen', 'WinHttpSetTimeouts', + 'WinHttpSendRequest', 'WinHttpConnect', 'WinHttpCrackUrl', 'WinHttpReadData', 'WinHttpOpenRequest', + 'WinHttpReceiveResponse', 'WinHttpQueryHeaders', 'HttpSendRequestW', 'HttpSendRequestA', 'HttpAddRequestHeadersW', + 'HttpAddRequestHeadersA', 'HttpOpenRequestW', 'HttpOpenRequestA', 'NetServerGetInfo', 'NetApiBufferFree', 'NetWkstaGetInfo', + 'getnameinfo', 'getpeername', 'socketpair'], + 'spawn': ['CreateProcess', 'ShellExecute', 'ShellExecuteEx', 'system', 'CreateProcessInternal', 'NtCreateProcess', + 'ZwCreateProcess', 'NtCreateProcessEx', 'ZwCreateProcessEx', 'NtCreateUserProcess', 'ZwCreateUserProcess', + 'RtlCreateUserProcess', 'NtCreateSection', 'ZwCreateSection', 'NtOpenSection', 'ZwOpenSection', + 'NtAllocateVirtualMemory', 'ZwAllocateVirtualMemory', 'NtWriteVirtualMemory', 'ZwWriteVirtualMemory', + 'NtMapViewOfSection', 'ZwMapViewOfSection', 'OpenSCManager', 'CreateService', 'OpenService', + 'StartService', 'ControlService', 'ShellExecuteExA', 'ShellExecuteExW', 'execve', 'execvp', 'fork', 'popen', 'execl', + 'posix_spawn'], + 'inject': ['OpenProcess-disabled', 'ZwOpenProcess', 'NtOpenProcess', 'WriteProcessMemory', 'NtWriteVirtualMemory', + 'ZwWriteVirtualMemory', 'CreateRemoteThread', 'QueueUserAPC', 'ZwUnmapViewOfSection', 'NtUnmapViewOfSection'], + 'com': ['CoCreateInstance', 'CoInitializeSecurity', 'CoGetClassObject', 'OleConvertOLESTREAMToIStorage', 'CreateBindCtx', + 'CoSetProxyBlanket', 'VariantClear'], + 'crypto': ['CryptAcquireContext', 'CryptProtectData', 'CryptUnprotectData', 'CryptProtectMemory', + 'CryptUnprotectMemory', 'CryptDecrypt', 'CryptEncrypt', 'CryptHashData', 'CryptDecodeMessage', + 'CryptDecryptMessage', 'CryptEncryptMessage', 'CryptHashMessage', 'CryptExportKey', 'CryptGenKey', + 'CryptCreateHash', 'CryptDecodeObjectEx', 'EncryptMessage', 'DecryptMessage'], + 'kbd': ['SendInput', 'VkKeyScanA', 'VkKeyScanW'], + 'file': ['_open64', 'open64', 'open', 'open64', 'fopen', 'fread', 'fclose', 'fwrite', 'flock', 'read', 'write', + 'fstat', 'lstat', 'stat', 'chmod', 'chown', 'lchown', 'link', 'symlink', 'readdir', 'readdir64', 'sync', 'ftell', 'opendir'], + 'reg': ['RegOpenKeyExW', 'RegQueryValueExW', 'RegSetValueExW', 'RegCreateKeyExW', 'RegDeleteValueW', 'RegEnumKeyW', 'RegCloseKey', + 'RegQueryInfoKeyW', 'RegOpenKeyExA', 'RegQueryValueExA', 'RegSetValueExA', 'RegCreateKeyExA', 'RegDeleteValueA', + 'RegEnumKeyA', 'RegQueryInfoKeyA'], + 'dev': ['DeviceIoControl', 'ioctl'], + 'wow': ['Wow64DisableWow64FsRedirection', 'Wow64RevertWow64FsRedirection'], + 'native': ['syscall'], + 'mem': ['memcpy', 'memset', 'memmove', 'shmget', 'mmap', 'bcopy', 'munmap', 'strncpy'], + 'priv': ['geteuid', 'getuid', 'getgid', 'setreuid', 'setregid', 'getresuid', 'seteuid', 'getlogin_r', 'pam_open_session'], + 'cmp': ['memcmp', 'strcmp', 'strncmp', 'strcasecmp'], + 'fmt': ['vprintf', 'vsnprintf', 'sprintf', 'ssprintf', 'vfprintf', 'spprintf'], + 'parsing': ['sscanf', 'strtok', 'strtol', 'strtoul'], + 'io': ['mkfifo'], + 'ldr': ['LoadLibrary', 'dlopen', 'LdrLoadDLL', 'LdrLoadDriver'], +} + +STRICT_TAG_NAME_CHECKING = {'file'} + +BLACKLIST = frozenset([ + '@__security_check_cookie@4', + '__SEH_prolog4', + '__SEH_epilog4', + 'throw_system_error', + 'chrono::system_clock', + 'QObject::connect', +]) +REPLACEMENTS = [ + ('??3@YAXPAX@Z', 'alloc'), + ('?', '') +] + +def inf_is_64bit(): + return (idaapi.inf_is_64bit if idaapi.IDA_SDK_VERSION >= 900 else idaapi.cvar.inf.is_64bit)() + + +def get_addr_width(): + return '16' if inf_is_64bit() else '8' + + +def decode_insn(ea): + if idaapi.IDA_SDK_VERSION >= 700 and sys.maxsize > 2**32: + insn = idaapi.insn_t() + if idaapi.decode_insn(insn, ea) > 0: + return insn + else: + if idaapi.decode_insn(ea): + return idaapi.cmd.copy() + + +def force_name(ea, new_name): + if not ea or ea == idaapi.BADADDR: + return + if idaapi.IDA_SDK_VERSION >= 700: + return idaapi.force_name(ea, new_name, idaapi.SN_NOCHECK) + return idaapi.do_name_anyway(ea, new_name, 0) + + +class AutoReIDPHooks(idaapi.IDP_Hooks): + """ + Hooks to keep view updated if some function is updated + """ + def __init__(self, view, *args): + super(AutoReIDPHooks, self).__init__(*args) + self._view = view + + def __on_rename(self, ea, new_name): + if not self._view: + return + items = self._view._model.findItems(('%0' + get_addr_width() + 'X') % ea, QtCore.Qt.MatchRecursive) + if len(items) != 1: + return + + item = items[0] + index = self._view._model.indexFromItem(item) + if not index.isValid(): + return + + name_index = index.sibling(index.row(), 1) + if not name_index.isValid(): + return + + self._view._model.setData(name_index, new_name) + + def ev_rename(self, ea, new_name): + """ callback for IDA >= 700 """ + self.__on_rename(ea, new_name) + return super(AutoReIDPHooks, self).ev_rename(ea, new_name) + + def rename(self, ea, new_name): + """ callback for IDA < 700 """ + self.__on_rename(ea, new_name) + return super(AutoReIDPHooks, self).rename(ea, new_name) + + +class AutoREView(idaapi.PluginForm): + ADDR_ROLE = QtCore.Qt.UserRole + 1 + + OPT_FORM_PERSIST = idaapi.PluginForm.FORM_PERSIST if hasattr(idaapi.PluginForm, 'FORM_PERSIST') else idaapi.PluginForm.WOPN_PERSIST + OPT_FORM_NO_CONTEXT = idaapi.PluginForm.FORM_NO_CONTEXT if hasattr(idaapi.PluginForm, 'FORM_NO_CONTEXT') else idaapi.PluginForm.WCLS_NO_CONTEXT + + def __init__(self, data): + super(AutoREView, self).__init__() + self._data = data + self.tv = None + self._model = None + self._idp_hooks = None + + def Show(self): + return idaapi.PluginForm.Show(self, 'AutoRE', options=self.OPT_FORM_PERSIST) + + def _get_parent_widget(self, form): + if HAS_PYSIDE: + return self.FormToPySideWidget(form) + return self.FormToPyQtWidget(form) + + def OnCreate(self, form): + self.parent = self._get_parent_widget(form) + + self._idp_hooks = AutoReIDPHooks(self) + if not self._idp_hooks.hook(): + print('IDP_Hooks.hook() failed') + + self.tv = QTreeView() + self.tv.setExpandsOnDoubleClick(False) + + root_layout = QVBoxLayout(self.parent) + # self.le_filter = QLineEdit(self.parent) + + # root_layout.addWidget(self.le_filter) + root_layout.addWidget(self.tv) + + self.parent.setLayout(root_layout) + + self._model = QtGui.QStandardItemModel() + self._init_model() + self.tv.setModel(self._model) + + self.tv.setColumnWidth(0, 200) + self.tv.setColumnWidth(1, 300) + self.tv.header().setStretchLastSection(True) + + self.tv.expandAll() + + self.tv.doubleClicked.connect(self.on_navigate_to_method_requested) + # self.le_filter.textChanged.connect(self.on_filter_text_changed) + self.tv.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) + self.tv.customContextMenuRequested.connect(self._tree_customContextMenuRequesssted) + + rename_action = QAction('Rename...', self.tv) + rename_action.setShortcut('n') + rename_action.triggered.connect(self._tv_rename_action_triggered) + self.tv.addAction(rename_action) + + def _tree_customContextMenuRequesssted(self, pos): + idx = self.tv.indexAt(pos) + if not idx.isValid(): + return + + addr = idx.data(role=self.ADDR_ROLE) + if not addr: + return + + name_idx = idx.sibling(idx.row(), 1) + old_name = name_idx.data() + + menu = QMenu() + rename_action = menu.addAction('Rename `%s`...' % old_name) + rename_action.setShortcut('n') + action = menu.exec_(self.tv.mapToGlobal(pos)) + if action == rename_action: + return self._rename_ea_requested(addr, name_idx) + + def _tv_rename_action_triggered(self): + selected = self.tv.selectionModel().selectedIndexes() + if not selected: + return + + idx = selected[0] + if not idx.isValid(): + return + + addr = idx.data(role=self.ADDR_ROLE) + if not addr: + return + + name_idx = idx.sibling(idx.row(), 1) + if not name_idx.isValid(): + return + + return self._rename_ea_requested(addr, name_idx) + + def _rename_ea_requested(self, addr, name_idx): + old_name = name_idx.data() + + if idaapi.IDA_SDK_VERSION >= 700: + new_name = idaapi.ask_str(str(old_name), 0, 'New name:') + else: + new_name = idaapi.askstr(0, str(old_name), 'New name:') + + if new_name is None: + return + + force_name(addr, new_name) + renamed_name = idaapi.get_ea_name(addr) + name_idx.model().setData(name_idx, renamed_name) + + def OnClose(self, form): + if self._idp_hooks: + self._idp_hooks.unhook() + + def _tv_init_header(self, model): + item_header = QtGui.QStandardItem("EA") + item_header.setToolTip("Address") + model.setHorizontalHeaderItem(0, item_header) + + item_header = QtGui.QStandardItem("Function name") + model.setHorizontalHeaderItem(1, item_header) + + item_header = QtGui.QStandardItem("API called") + model.setHorizontalHeaderItem(2, item_header) + + # noinspection PyMethodMayBeStatic + def _tv_make_tag_item(self, name): + rv = QtGui.QStandardItem(name) + + rv.setEditable(False) + return [rv, QtGui.QStandardItem(), QtGui.QStandardItem()] + + def _tv_make_ref_item(self, tag, ref): + ea_item = QtGui.QStandardItem(('%0' + get_addr_width() + 'X') % ref['ea']) + ea_item.setEditable(False) + ea_item.setData(ref['ea'], self.ADDR_ROLE) + + name_item = QtGui.QStandardItem(ref['name']) + name_item.setEditable(False) + name_item.setData(ref['ea'], self.ADDR_ROLE) + + apis = ', '.join(ref['tags'][tag]) + api_name = QtGui.QStandardItem(apis) + api_name.setEditable(False) + api_name.setData(ref['ea'], self.ADDR_ROLE) + api_name.setToolTip(apis) + + return [ea_item, name_item, api_name] + + def _init_model(self): + self._model.clear() + + root_node = self._model.invisibleRootItem() + self._tv_init_header(self._model) + + for tag, refs in self._data.items(): + item_tag_list = self._tv_make_tag_item(tag) + item_tag = item_tag_list[0] + + root_node.appendRow(item_tag_list) + + for ref in refs: + ref_item_list = self._tv_make_ref_item(tag, ref) + + item_tag.appendRow(ref_item_list) + + def on_navigate_to_method_requested(self, index): + addr = index.data(role=self.ADDR_ROLE) + if addr is not None: + idaapi.jumpto(addr) + + # def on_filter_text_changed(self, text): + # print('on_text_changed: %s' % text) + + +class auto_re_t(idaapi.plugin_t): + flags = idaapi.PLUGIN_UNL + comment = "" + + help = "" + wanted_name = "Auto RE" + wanted_hotkey = "Ctrl+Shift+M" + + _PREFIX_NAME = 'au_re_' + _MIN_MAX_MATH_OPS_TO_ALLOW_RENAME = 10 + + _CALLEE_NODE_NAMES = { + idaapi.PLFM_MIPS: '$ mips', + idaapi.PLFM_ARM: '$ arm' + } + _DEFAULT_CALLEE_NODE_NAME = '$ vmm functions' + + _JMP_TYPES = {idaapi.NN_jmp, idaapi.NN_jmpni, idaapi.NN_jmpfi, idaapi.NN_jmpshort} + + def __init__(self): + super(auto_re_t, self).__init__() + self._data = None + self.view = None + + def init(self): + # self._cfg = None + self.view = None + # self._load_config() + + return idaapi.PLUGIN_OK + + # def _load_config(self): + # self._cfg = {'auto_rename': False} + + # def _store_config(self, cfg): + # pass + + def _handle_tags(self, fn, fn_an, known_refs): + if known_refs: + known_refs = dict(known_refs) + for k, names in known_refs.items(): + existing = set(fn_an['tags'][k]) + new = set(names) - existing + if new: + fn_an['tags'][k] += list(new) + + tags = dict(fn_an['tags']) + if not tags: + return + print('fn: %#08x tags: %s' % (self.start_ea_of(fn), tags)) + cmt = idaapi.get_func_cmt(fn, True) + if cmt: + cmt += '\n' + s = str(tags.keys()) + name = idaapi.get_long_name(self.start_ea_of(fn)) + item = {'ea': self.start_ea_of(fn), 'name': name, 'tags': tags} + if not cmt or s not in cmt: + idaapi.set_func_cmt(fn, '%sTAGS: %s' % (cmt or '', s), True) + # self.mark_position(self.start_ea_of(fn), 'TAGS: %s' % s) + for tag in tags: + if tag not in self._data: + self._data[tag] = list() + self._data[tag].append(item) + + def _handle_calls(self, fn, fn_an): + num_calls = len(fn_an['calls']) + if num_calls != 1: + return + + dis = fn_an['calls'][0] + if dis.Op1.type not in (o_imm, o_far, o_near, o_mem): + return + + ea = dis.Op1.value + if not ea and dis.Op1.addr: + ea = dis.Op1.addr + + if idaapi.has_dummy_name(self.get_flags_at(ea)): + return + + # TODO: check is there jmp, push+retn then don't rename the func + if fn_an['strange_flow']: + return + + possible_name = idaapi.get_ea_name(ea) + if not possible_name or possible_name in BLACKLIST: + return + + normalized = self.normalize_name(possible_name) + + # if self._cfg.get('auto_rename'): + if len(fn_an['math']) < self._MIN_MAX_MATH_OPS_TO_ALLOW_RENAME: + force_name(self.start_ea_of(fn), normalized) + # TODO: add an API to the view + print('fn: %#08x: %d calls, %d math%s possible name: %s, normalized: %s' % ( + self.start_ea_of(fn), len(fn_an['calls']), len(fn_an['math']), 'has bads' if fn_an['has_bads'] else '', + possible_name, normalized)) + + # noinspection PyMethodMayBeStatic + def _check_is_jmp_wrapper(self, dis): + # checks instructions like `jmp API` + if dis.itype not in self._JMP_TYPES: + return + + # handle call wrappers like jmp GetProcAddress + if dis.Op1.type == idaapi.o_mem and dis.Op1.addr: + # TODO: check is there better way to determine is the function a wrapper + v = dis.Op1.addr + flags = self.get_flags_at(v) + if v and dis.itype == idaapi.NN_jmpni and self.is_data(flags) and self.__is_ptr_val(flags): + v = self.__get_ptr_val(v) + return v + + # noinspection PyMethodMayBeStatic + def _check_is_push_retn_wrapper(self, dis0, dis1): + """ + Checks for sequence of push IMM32/retn + :param dis0: the first insn + :param dis1: the second insn + :return: value of IMM32 + """ + if dis0.itype != idaapi.NN_push or dis0.Op1.type != idaapi.o_imm or not dis0.Op1.value: + return + + if dis1.itype not in (idaapi.NN_retn,): + return + + return dis0.Op1.value + + def _check_is_mipsl_jmp(self, dis0, dis1, rest_items): + """ + Checks for sequence like: + .plt:009F63E0 lui $t7, 0xA1 + .plt:009F63E4 lw $t9, off_A08884 + .plt:009F63E8 jr $t9 + """ + + if (not dis0 or dis0.itype != idaapi.NN_popaw or dis0.Op1.type != idaapi.o_reg or + dis0.Op2.type != idaapi.o_imm or not dis0.Op2.value): + return + if (not dis1 or dis1.itype != idaapi.NN_or or dis1.Op1.type != idaapi.o_reg or + dis1.Op2.type != idaapi.o_mem or not dis1.Op2.addr or + not idaapi.is_data(idaapi.get_flags(dis1.Op2.addr))): + return + if not rest_items: return + + dis2 = decode_insn(rest_items[0]) + if not dis2 or dis2.itype != idaapi.NN_loopwne or dis2.Op1.type != idaapi.o_reg: + return + + addr = dis1.Op2.addr + seg = idaapi.getseg(addr) + if not seg: return # TODO: check if imagebase check is required + if idaapi.get_segm_name(seg) not in ('.got.plt',): return + + offs = idaapi.get_dword(addr) + if not offs: + offs = idaapi.get_word(addr + idaapi.get_imagebase()) + if not offs: return + + offs_seg = idaapi.getseg(offs) + if not offs_seg: + offs_seg = idaapi.getseg(offs + idaapi.get_imagebase()) + if not offs_seg or idaapi.get_segm_name(offs_seg) not in ('extern',): + return + + return offs + + def _check_is_armle_jmp(self, dis0, dis1, rest_items): + """ + Check for sequence like: + ADDRL R12, 0x606A4 + LDR PC, [R12, #(sprintf_ptr - 0x606A4)]! + + # and + .plt:0000B0B4 ADR R12, 0xB0BC ; Load address + .plt:0000B0B8 ADD R12, R12, #0x55000 ; Rd = Op1 + Op2 + .plt:0000B0BC LDR PC, [R12,#(XXXX_ptr - 0x600BC)]! ; Indirect Jump + """ + if not dis0 or not dis1: return + + if dis0.itype not in (idaapi.ARM_adrl, idaapi.ARM_adr) or dis0.Op1.type != idaapi.o_reg or dis0.Op2.type != idaapi.o_imm: + return + + r = dis0.Op1.reg + addr = dis0.Op2.value + + if dis1.itype == idaapi.ARM_add and rest_items and dis1.Op1.type == idaapi.o_reg \ + and dis1.Op2.type == idaapi.o_reg and dis1.Op3.type == idaapi.o_imm and dis1.Op1.reg == r and dis1.Op2.reg == r: + + addr += dis1.Op3.value + dis1 = decode_insn(rest_items[0]) # ldr should be the third instruction + + if dis1.itype != idaapi.ARM_ldrpc or dis1.Op1.type != idaapi.o_reg or dis1.Op2.type != idaapi.o_displ or \ + dis1.Op2.reg != r or not dis1.Op2.addr: + return + + addr = dis1.Op2.addr + addr + seg = idaapi.getseg(addr) + if not seg: return + if idaapi.get_segm_name(seg) not in ('.got',): return + + offs = idaapi.get_dword(addr) + if not offs: + offs = idaapi.get_word(addr + idaapi.get_imagebase()) + if not offs: return + + offs_seg = idaapi.getseg(offs) + if not offs_seg: + offs_seg = idaapi.getseg(offs + idaapi.get_imagebase()) + if not offs_seg or idaapi.get_segm_name(offs_seg) not in ('extern',): + return + + return offs + + def _preprocess_api_wrappers(self, fnqty): + rv = defaultdict(dict) + + for i in xrange(fnqty): + fn = idaapi.getn_func(i) + items = list(FuncItems(self.start_ea_of(fn))) + if not (0 < len(items) <= 4): + continue + + dis0 = decode_insn(items[0]) + if dis0 is None: + continue + addr = self._check_is_jmp_wrapper(dis0) + + if not addr and len(items) > 1: + dis1 = decode_insn(items[1]) + if dis1 is not None: + addr = self._check_is_push_retn_wrapper(dis0, dis1) + if not addr: + addr = self._check_is_mipsl_jmp(dis0, dis1, items[2:]) + if not addr: + addr = self._check_is_armle_jmp(dis0, dis1, items[2:]) + + if not addr: + continue + + name = idaapi.get_long_name(addr) + name = name.replace(idaapi.FUNC_IMPORT_PREFIX, '') + if not name or any(x in name for x in BLACKLIST): + continue + + imp_stripped_name = name.lstrip('_') + + for tag, names in TAGS.items(): + for tag_api in names: + if tag in STRICT_TAG_NAME_CHECKING: + match = tag_api in (name, imp_stripped_name) + else: + match = tag_api in name + if not match: + continue + + p = name.find(tag_api) + if p == -1: + continue + after_name = name[p+len(tag_api):] + if after_name and after_name[0].isalpha(): + # not fully matching the api name, skip it + continue + pre_name = name[p-1:p] if p > 0 else None + if pre_name and pre_name[0].isalpha(): + # not fully matching the api name, skip it + continue + + refs = list(CodeRefsTo(self.start_ea_of(fn), 1)) + + for ref in refs: + ref_fn = idaapi.get_func(ref) + if not ref_fn: + # idaapi.msg('AutoRE: there is no func for ref: %08x for api: %s' % (ref, name)) + continue + if tag not in rv[self.start_ea_of(ref_fn)]: + rv[self.start_ea_of(ref_fn)][tag] = list() + if name not in rv[self.start_ea_of(ref_fn)][tag]: + rv[self.start_ea_of(ref_fn)][tag].append(name) + return dict(rv) + + def run(self, arg): + if RDEBUG and RDEBUG_EGG: + if not os.path.isfile(RDEBUG_EGG): + idaapi.msg('AutoRE: Remote debug is enabled, but I cannot find the debug egg: %s' % RDEBUG_EGG) + else: + import sys + + if RDEBUG_EGG not in sys.path: + sys.path.append(RDEBUG_EGG) + + import pydevd + pydevd.settrace(RDEBUG_HOST, port=RDEBUG_PORT, stdoutToServer=True, stderrToServer=True) + + + try: + self._data = dict() + count = idaapi.get_func_qty() + + # pre-process of api wrapper functions + known_refs_tags = self._preprocess_api_wrappers(count) + + for i in xrange(count): + fn = idaapi.getn_func(i) + fn_an = self.analyze_func(fn) + + # if fn_an['math']: + # print('fn: %#08x has math' % self.start_ea_of(fn)) + + if idaapi.has_dummy_name(self.get_flags_at(self.start_ea_of(fn))): + self._handle_calls(fn, fn_an) + + known_refs = known_refs_tags.get(self.start_ea_of(fn)) + self._handle_tags(fn, fn_an, known_refs) + + if self.view: + self.view.Close(AutoREView.OPT_FORM_NO_CONTEXT) + self.view = AutoREView(self._data) + self.view.Show() + except: + idaapi.msg('AutoRE: error: %s\n' % traceback.format_exc()) + + def term(self): + self._data = None + + @classmethod + def disasm_func(cls, fn): + rv = list() + items = list(FuncItems(cls.start_ea_of(fn))) + for item_ea in items: + obj = {'ea': item_ea, 'fn_ea': cls.start_ea_of(fn), 'dis': None} + insn = decode_insn(item_ea) + if insn is not None: + obj['dis'] = insn + rv.append(obj) + return rv + + @classmethod + def get_callee_netnode(cls): + node_name = cls._CALLEE_NODE_NAMES.get(idaapi.ph.id, cls._DEFAULT_CALLEE_NODE_NAME) + n = idaapi.netnode(node_name) + return n + + @classmethod + def get_callee(cls, ea): + n = cls.get_callee_netnode() + v = n.altval(ea) + v -= 1 + if v == idaapi.BADNODE: + return + return v + + @classmethod + def _analysis_handle_call_insn(cls, dis, rv): + rv['calls'].append(dis) + if dis.Op1.type != o_mem or not dis.Op1.addr: + callee = cls.get_callee(dis.ip) + if not callee: + return + else: + callee = dis.Op1.addr + + cls._apply_tag_on_callee(callee, rv, is_call=True) + + @classmethod + def _apply_tag_on_callee(cls, callee_ea, rv, is_call=False): + name = idaapi.get_ea_name(callee_ea) + name = name.replace(idaapi.FUNC_IMPORT_PREFIX, '') + + if '@' in name: + name = name.split('@')[0] + + if not name: + return + + if name in IGNORE_CALL_LIST: + if is_call: + rv['calls'].pop() + return + + if name in TAGS_IGNORE_LIST: + return + + for tag, names in TAGS.items(): + for tag_api in names: + if tag in STRICT_TAG_NAME_CHECKING: + match = tag_api in (name, name.lstrip('_')) + else: + match = tag_api in name + if not match or name in rv['tags'][tag]: + continue + + # print('%#08x: %s, tag: %s' % (dis.ea, name, tag)) + rv['tags'][tag].append(name) + break + + @classmethod + def __is_ptr_val(cls, flags): + if idaapi.IDA_SDK_VERSION >= 700: + return (idaapi.is_qword if inf_is_64bit() else idaapi.is_dword)(flags) + return (idaapi.isQwrd if inf_is_64bit() else idaapi.isDwrd)(flags) + + @classmethod + def __get_ptr_val(cls, ea): + if inf_is_64bit(): + return idaapi.get_qword(ea) + + return (idaapi.get_dword if idaapi.IDA_SDK_VERSION >= 700 else idaapi.get_long)(ea) + + @classmethod + def start_ea_of(cls, o): + return getattr(o, 'start_ea' if idaapi.IDA_SDK_VERSION >= 700 else 'startEA') + + @classmethod + def end_ea_of(cls, o): + return getattr(o, 'end_ea' if idaapi.IDA_SDK_VERSION >= 700 else 'endEA') + + @classmethod + def get_flags_at(cls, ea): + return getattr(idaapi, 'get_flags' if idaapi.IDA_SDK_VERSION >= 700 else 'getFlags')(ea) + + @classmethod + def is_data(cls, flags): + return getattr(idaapi, 'is_data' if idaapi.IDA_SDK_VERSION >= 700 else 'isData')(flags) + + @classmethod + def analyze_func(cls, fn): + rv = { + 'fn': fn, + 'calls': [], + 'math': [], + 'has_bads': False, + 'strange_flow': False, + 'tags': defaultdict(list) + } + items = cls.disasm_func(fn) + items_set = set(map(lambda x: x['ea'], items)) + + for item in items: + dis = item['dis'] + if dis is None: + rv['has_bads'] = True + continue + + if dis.itype in (idaapi.NN_call, idaapi.NN_callfi, idaapi.NN_callni): + cls._analysis_handle_call_insn(dis, rv) + elif dis.itype == idaapi.NN_xor: + if dis.Op1.type == o_reg and dis.Op2.type == o_reg and dis.Op1.reg == dis.Op2.reg: + continue + rv['math'].append(dis) + elif dis.itype in (idaapi.NN_shr, idaapi.NN_shl, idaapi.NN_sal, idaapi.NN_sar, idaapi.NN_ror, + idaapi.NN_rol, idaapi.NN_rcl, idaapi.NN_rcl): + # TODO + rv['math'].append(dis) + elif dis.itype in cls._JMP_TYPES: + if dis.Op1.type not in (o_far, o_near, o_mem, o_displ): + continue + + if dis.Op1.type == o_displ: + rv['strange_flow'] = True + continue + + ea = dis.Op1.value + if not ea and dis.Op1.addr: + ea = dis.Op1.addr + if ea not in items_set: + rv['strange_flow'] = True + + # flags = self.get_flags_at(ea) + # if dis.itype == idaapi.NN_jmpni and dis.Op1.type == o_mem and ea and self.is_data(flags): + # if cls.__is_ptr_val(flags): + # val = cls.__get_ptr_val(ea) + # if val: + # cls._apply_tag_on_callee(val, rv, is_call=False) + + return rv + + @classmethod + def normalize_name(cls, n): + for repl in REPLACEMENTS: + n = n.replace(*repl) + if '@' in n: + n = n.split('@')[0] + if len(n) < 3: + return '' + if not n.startswith(cls._PREFIX_NAME): + n = cls._PREFIX_NAME + n + return n + + # @classmethod + # def mark_position(cls, ea, name, slot=[0]): + # curloc = idaapi.curloc() + # curloc.ea = ea + # curloc.lnnum = 0 + # curloc.x = 0 + # curloc.y = 0 + # slot[0] += 1 + # curloc.mark(slot[0], name, name) + + +# noinspection PyPep8Naming +def PLUGIN_ENTRY(): + return auto_re_t() + +``` + +`ida-plugin.json`: + +```json +{ + "IDAMetadataDescriptorVersion": 1, + "plugin": { + "name": "AutoRE", + "version": "2.2.0", + "entryPoint": "auto_re.py", + "description": "IDA PRO auto-renaming plugin with tagging support", + "urls": { + "repository": "https://github.com/a1ext/auto_re" + }, + "authors": [{ + "name": "Aliaksandr Trafimchuk", + "email": "a13x4nd3r.t@gmail.com" + }], + "logoPath": ".img/Logo.png", + "keywords": [ + "automatic", + "renaming", + "tagging", + "AutoRE", + "pwn" + ] + } +} + +``` \ No newline at end of file diff --git a/archive/allthingsida/idasql.txt b/archive/allthingsida/idasql.txt new file mode 100644 index 00000000..fe614102 --- /dev/null +++ b/archive/allthingsida/idasql.txt @@ -0,0 +1,22714 @@ +Project Path: arc_allthingsida_idasql_2s2hgymb + +Source Tree: + +```txt +arc_allthingsida_idasql_2s2hgymb +├── CMakeLists.txt +├── CMakePresets.json +├── LICENSE +├── README.md +├── assets +│ ├── idasql_cli_agent_2.jpg +│ ├── idasql_cli_agent_3.jpg +│ ├── idasql_cli_agent_help.jpg +│ ├── idasql_cli_agent_prompt_1.jpg +│ ├── idasql_cli_handoff_1.jpg +│ ├── idasql_cli_interactive_1.jpg +│ ├── idasql_cli_one_query.jpg +│ ├── idasql_cli_one_query_2.jpg +│ ├── idasql_help.jpg +│ ├── idasql_plugin_agent_1.jpg +│ ├── idasql_plugin_agent_2.jpg +│ ├── idasql_plugin_agent_3.jpg +│ ├── idasql_plugin_agent_4.jpg +│ ├── idasql_plugin_cli_select.jpg +│ └── idasql_plugin_handoff_1.jpg +├── benchmark.md +├── examples +│ ├── CMakeLists.txt +│ ├── agent_session_strings.md +│ ├── example_basic.cpp +│ ├── example_breakpoints.cpp +│ ├── example_custom_vtable.cpp +│ ├── example_decompiler.cpp +│ ├── example_functions.cpp +│ ├── example_instructions.cpp +│ ├── example_jump_entities.cpp +│ ├── example_jump_search.cpp +│ ├── example_plugin_style.cpp +│ └── example_strings.cpp +├── ida-plugin.json +├── prompts +│ └── idasql_agent.md +├── scripts +│ └── embed_prompt.py +└── src + ├── CMakeLists.txt + ├── cli + │ ├── CMakeLists.txt + │ ├── idasql.manifest + │ └── main.cpp + ├── common + │ ├── agent_settings.hpp + │ ├── ai_agent.cpp + │ ├── ai_agent.hpp + │ ├── http_server.cpp + │ ├── http_server.hpp + │ ├── idasql_agent_prompt.hpp + │ ├── idasql_cli.hpp + │ ├── idasql_commands.hpp + │ ├── idasql_version.hpp + │ ├── json_utils.hpp + │ ├── mcp_server.cpp + │ ├── mcp_server.hpp + │ ├── plugin_control.hpp + │ └── session_handler.hpp + ├── lib + │ ├── CMakeLists.txt + │ └── include + │ └── idasql + │ ├── database.hpp + │ ├── decompiler.hpp + │ ├── disassembly.hpp + │ ├── entities.hpp + │ ├── entities_dbg.hpp + │ ├── entities_ext.hpp + │ ├── entities_search.hpp + │ ├── entities_types.hpp + │ ├── functions.hpp + │ ├── idasql.hpp + │ ├── metadata.hpp + │ ├── platform.hpp + │ ├── platform_undef.hpp + │ ├── search_bytes.hpp + │ ├── vtable.hpp + │ └── vtable_policy.hpp + └── plugin + ├── CMakeLists.txt + └── main.cpp + +``` + +`CMakeLists.txt`: + +```txt +cmake_minimum_required(VERSION 3.20) +project(idasql VERSION 0.0.7 LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Silence CMake policy warnings from dependencies +cmake_policy(SET CMP0135 NEW) # Use extraction time for DOWNLOAD_EXTRACT_TIMESTAMP +set(CMAKE_POLICY_DEFAULT_CMP0169 OLD CACHE STRING "" FORCE) # Allow FetchContent_Populate() in dependencies + +include(FetchContent) + +# Output directories (only set if building standalone) +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +endif() + +# ============================================================================ +# IDA SDK (required for CLI and plugin) +# ============================================================================ + +include($ENV{IDASDK}/src/cmake/bootstrap.cmake) +find_package(idasdk REQUIRED) + +# ============================================================================ +# HTTP Server Support - must be set BEFORE libxsql +# ============================================================================ + +set(XSQL_WITH_THINCLIENT ON CACHE BOOL "" FORCE) + +# idasql uses only plain HTTP (localhost); prevent cpp-httplib from opportunistically linking OpenSSL +set(HTTPLIB_USE_OPENSSL_IF_AVAILABLE OFF CACHE BOOL "" FORCE) + +# ============================================================================ +# libxsql dependency +# ============================================================================ + +if(TARGET xsql::xsql) + # Monorepo: already available from parent + message(STATUS "idasql: Using xsql::xsql from parent project") +elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../libxsql/CMakeLists.txt") + # Monorepo: sibling directory (private test infrastructure) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../libxsql ${CMAKE_CURRENT_BINARY_DIR}/libxsql) + message(STATUS "idasql: Added libxsql from sibling directory") +else() + # Standalone: FetchContent from GitHub + message(STATUS "idasql: Fetching libxsql from GitHub...") + FetchContent_Declare(libxsql + GIT_REPOSITORY https://github.com/0xeb/libxsql.git + GIT_TAG main + GIT_SHALLOW TRUE + ) + FetchContent_MakeAvailable(libxsql) +endif() + +# Include directories +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/include) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/lib/include) + +# ============================================================================ +# AI Agent Support (libagents - optional) +# ============================================================================ + +option(IDASQL_WITH_AI_AGENT "Build with AI agent support (libagents)" ON) + +if(IDASQL_WITH_AI_AGENT) + message(STATUS "idasql: Building with AI agent support via libagents") + + # libagents build options + set(LIBAGENTS_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(LIBAGENTS_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + set(LIBAGENTS_BUILD_CLI OFF CACHE BOOL "" FORCE) + set(LIBAGENTS_BUILD_COPILOT ON CACHE BOOL "" FORCE) + + # FetchContent from GitHub (with recursive submodules for fastmcpp, SDKs) + message(STATUS "idasql: Fetching libagents from GitHub...") + FetchContent_Declare(libagents + GIT_REPOSITORY https://github.com/0xeb/libagents.git + GIT_TAG ae162192d49126da6328254aab73417d673379cc + GIT_SUBMODULES_RECURSE TRUE + ) + FetchContent_MakeAvailable(libagents) + + # ======================================================================== + # Build speed: enable MSVC parallel compilation for dependency targets. + # IDA SDK's /MP only applies to ida_add_plugin/ida_add_idalib targets; + # FetchContent dependencies compile their files sequentially without this. + # ======================================================================== + if(MSVC) + foreach(_dep_target fastmcpp_core copilot_sdk_cpp claude_sdk libagents) + if(TARGET ${_dep_target}) + target_compile_options(${_dep_target} PRIVATE /MP) + endif() + endforeach() + endif() + + # Build speed: merge translation units to reduce header re-parsing. + # Note: fastmcpp_core is excluded — its server files use duplicate + # anonymous-namespace symbols that conflict under unity builds. + foreach(_dep_target copilot_sdk_cpp claude_sdk libagents) + if(TARGET ${_dep_target}) + set_target_properties(${_dep_target} PROPERTIES UNITY_BUILD ON) + endif() + endforeach() + + # Exclude unused targets from default build (e.g. claude-sdk debug tool) + foreach(_unused_target debug) + if(TARGET ${_unused_target}) + set_target_properties(${_unused_target} PROPERTIES EXCLUDE_FROM_ALL TRUE) + endif() + endforeach() + + # Generate prompt header if needed (conditional on source file age) + find_package(Python3 COMPONENTS Interpreter) + if(Python3_FOUND) + set(PROMPT_MD "${CMAKE_CURRENT_SOURCE_DIR}/prompts/idasql_agent.md") + set(PROMPT_HPP "${CMAKE_CURRENT_SOURCE_DIR}/src/common/idasql_agent_prompt.hpp") + + add_custom_command( + OUTPUT ${PROMPT_HPP} + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_CURRENT_SOURCE_DIR}/scripts/embed_prompt.py + ${PROMPT_MD} ${PROMPT_HPP} + DEPENDS ${PROMPT_MD} + COMMENT "Checking/regenerating idasql_agent_prompt.hpp" + ) + add_custom_target(generate_prompt DEPENDS ${PROMPT_HPP}) + endif() +endif() + +# Add subdirectories +add_subdirectory(src) + +# Examples (optional) +option(IDASQL_BUILD_EXAMPLES "Build IDASQL example programs" ON) +if(IDASQL_BUILD_EXAMPLES AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples") + add_subdirectory(examples) +endif() + +``` + +`CMakePresets.json`: + +```json +{ + "version": 6, + "configurePresets": [ + { + "name": "release", + "displayName": "Release", + "generator": "Visual Studio 17 2022", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "IDASQL_WITH_AI_AGENT": "ON" + } + }, + { + "name": "release-no-agent", + "displayName": "Release (no AI agent)", + "description": "Build without AI agent support to reduce binary size. Excludes libagents, fastmcpp, claude_sdk, copilot_sdk_cpp, easywsclient, tiny-process-library.", + "generator": "Visual Studio 17 2022", + "binaryDir": "${sourceDir}/build-no-agent", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "IDASQL_WITH_AI_AGENT": "OFF" + } + } + ] +} + +``` + +`LICENSE`: + +``` +MIT License + +Copyright (c) 2024-2026 Elias Bachaalany + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +`README.md`: + +```md +# IDASQL + +Talk to your IDA database. + +``` +you: what's the most called function? +agent: The most called function is `__security_check_cookie` with 1,247 callers. + This is typical for MSVC-compiled binaries - it's the stack canary check. + +you: find functions that reference "password" strings +agent: Found 3 functions referencing password-related strings: + - validate_credentials (0x401240) - references "password incorrect" + - parse_config (0x402890) - references "password_hash" + - debug_dump (0x405120) - references "password: %s" + +you: decompile validate_credentials +agent: [displays pseudocode] +``` + +Ask complex questions that would take hours to script: + +``` +you: find functions that make at most 3 calls, where those callees + don't go deeper than 5 levels in the call graph + +agent: Looking for shallow functions with bounded call depth... + + Found 47 functions matching your criteria: + - init_config (0x401000) - 2 calls, max depth 3 + - validate_checksum (0x401200) - 3 calls, max depth 4 + - parse_header (0x401400) - 1 call, max depth 2 + ... + + These are good candidates for isolated analysis - simple entry + points that don't spiral into deep call chains. +``` + +IDASQL exposes IDA Pro databases as SQL tables with a built-in AI agent. Ask questions in plain English. The agent writes SQL, runs queries, and explains results. + +Works as a **standalone CLI** (query `.i64` files directly) or as an **IDA plugin** (query the open database). No scripting. No IDAPython. Just ask. + +**No indexing required.** IDA already has everything indexed. Queries run instantly against the live database. + +## Features + +- **AI Agent** - Natural language queries with Claude or GitHub Copilot +- **SQL Interface** - Full SQL access to functions, strings, imports, xrefs, instructions, types +- **Unified Entity Search** - `jump_entities()` searches everything: functions, types, members, enums (think "Jump Anywhere") +- **Standalone CLI** - Query `.i64` files without opening IDA GUI +- **IDA Plugin** - SQL/AI interface inside IDA's command line +- **Remote Server** - Query IDA from external tools, scripts, or coding agents +- **Zero Setup** - Uses your existing Claude Code or Copilot authentication + +## Screenshots + +### CLI - Single Query + +Run one query and exit. Useful for scripts and pipelines. + +```bash +idasql -s database.i64 -q "SELECT * FROM funcs LIMIT 5" +``` + +![CLI Single Query](assets/idasql_cli_one_query.jpg) + +### CLI - Interactive Mode + +Launch a REPL for exploratory analysis. Type SQL directly at the prompt. + +```bash +idasql -s database.i64 -i +``` + +``` +idasql> SELECT COUNT(*) FROM strings; +idasql> SELECT name FROM funcs WHERE size > 1000; +idasql> .tables -- list available tables +idasql> .schema funcs -- show table schema +idasql> .http start -- start HTTP server from REPL +idasql> .mcp start -- start MCP server from REPL +idasql> .quit -- exit +``` + +![CLI Interactive](assets/idasql_cli_interactive_1.jpg) + +### CLI - AI Agent + +Talk to the database in plain English. The agent translates to SQL, runs queries, and explains results. + +```bash +idasql -s database.i64 -i --agent +``` + +Ask questions naturally: +``` +idasql> how many functions are there? +idasql> which function is called the most? +idasql> find strings that look like URLs +idasql> what imports are related to file operations? +idasql> show me the largest functions +``` + +![CLI Agent](assets/idasql_cli_agent_2.jpg) + +The agent can generate visualizations like call graphs: + +``` +idasql> draw a call graph for the main function +idasql> visualize the call hierarchy of CreateFileW +``` + +![CLI Agent Call Graph](assets/idasql_cli_agent_3.jpg) + +### IDA Plugin + +Select `idasql` from the CLI dropdown at the bottom of IDA: + +![Plugin CLI Select](assets/idasql_plugin_cli_select.jpg) + +Type SQL or natural language questions directly. The agent has full access to IDA's capabilities: + +``` +idasql> what's the busiest function? +idasql> find functions that reference "error" +idasql> which structures have the most members? +``` + +![Plugin Agent](assets/idasql_plugin_agent_1.jpg) + +The agent can invoke the decompiler, analyze types, and trace cross-references: + +``` +idasql> decompile the free_base function +idasql> what does the _CONTEXT structure look like? +idasql> who calls VirtualAlloc and what do they do with it? +``` + +![Plugin Decompile](assets/idasql_plugin_agent_2.jpg) + +### Remote Server + +The plugin runs a TCP server. Query IDA from scripts, other tools, or coding agents: + +```bash +idasql --remote localhost:13337 --token -q "SELECT COUNT(*) FROM funcs" +``` + +![Remote Query](assets/idasql_cli_handoff_1.jpg) + +## Quick Start + +### CLI + +```bash +# Single query +idasql -s database.i64 -q "SELECT name, address FROM funcs LIMIT 10" + +# Interactive mode +idasql -s database.i64 -i + +# AI agent mode +idasql -s database.i64 -i --agent + +# One-shot natural language query +idasql -s database.i64 --prompt "find the largest function" + +# Run SQL script +idasql -s database.i64 -f queries.sql + +# Export all tables +idasql -s database.i64 --export dump.sql +``` + +### IDA Plugin + +1. Build and install the plugin +2. Open a database in IDA +3. Select `idasql` from the command interpreter dropdown +4. Type SQL or natural language (in agent mode) + +```sql +SELECT name, printf('0x%X', address) as addr FROM funcs WHERE size > 1000; +``` + +## Available Tables + +| Table | Description | +|-------|-------------| +| `funcs` | Functions - name, address, size, end address, flags (INSERT/UPDATE/DELETE) | +| `segments` | Segments - name, start/end address, permissions, class (UPDATE/DELETE) | +| `names` | Named locations - address, name, flags (INSERT/UPDATE/DELETE) | +| `imports` | Imports - module, name, address, ordinal | +| `exports` | Exports - name, address, ordinal | +| `strings` | Strings - address, content, length, type | +| `xrefs` | Cross-references - from/to address, type, is_code | +| `instructions` | Disassembly - address, mnemonic, operands, itype, func_addr (DELETE) | +| `blocks` | Basic blocks - start/end address, func_ea, size | +| `types` | Type library - structs, unions, enums with members (INSERT/UPDATE/DELETE) | +| `breakpoints` | Breakpoints - address, type, enabled, condition (full CRUD) | +| `jump_entities(pattern, mode)` | Unified search across all entities (see below) | + +### Unified Entity Search + +`jump_entities` is a table-valued function that searches across functions, types, struct members, enum values, and names. It works like IDA's "Jump to address" (G) but returns SQL rows. + +```sql +-- Search anything starting with "Create" +SELECT name, kind, printf('0x%X', address) as addr +FROM jump_entities('Create', 'prefix') +LIMIT 20; + +-- Search anywhere in name (slower but thorough) +SELECT name, kind, full_name +FROM jump_entities('File', 'contains') +WHERE kind IN ('function', 'import') +LIMIT 20; + +-- Find struct members +SELECT name, parent_name, full_name +FROM jump_entities('dw', 'prefix') +WHERE kind = 'member'; +``` + +## Query Examples + +### Function Analysis + +```sql +-- Functions with most incoming calls +SELECT f.name, COUNT(*) as callers +FROM funcs f +JOIN xrefs x ON f.address = x.to_ea +WHERE x.is_code = 1 +GROUP BY f.address +ORDER BY callers DESC LIMIT 10; + +-- Leaf functions (make no calls) +SELECT name, size FROM funcs f +WHERE NOT EXISTS ( + SELECT 1 FROM instructions i + WHERE i.func_addr = f.address AND i.mnemonic = 'call' +) +ORDER BY size DESC LIMIT 10; + +-- Orphan functions (no callers) +SELECT name, printf('0x%X', address) as addr FROM funcs f +WHERE NOT EXISTS ( + SELECT 1 FROM xrefs x WHERE x.to_ea = f.address AND x.is_code = 1 +); + +-- Function size distribution +SELECT + CASE + WHEN size < 64 THEN 'small (<64)' + WHEN size < 256 THEN 'medium (64-256)' + WHEN size < 1024 THEN 'large (256-1K)' + ELSE 'huge (>1K)' + END as category, + COUNT(*) as count +FROM funcs GROUP BY category; +``` + +### String Analysis + +```sql +-- Strings with most references +SELECT s.content, COUNT(x.from_ea) as refs +FROM strings s +JOIN xrefs x ON s.address = x.to_ea +GROUP BY s.address +ORDER BY refs DESC LIMIT 10; + +-- Functions using most strings +SELECT func_at(x.from_ea) as func, COUNT(DISTINCT s.address) as str_count +FROM strings s +JOIN xrefs x ON s.address = x.to_ea +GROUP BY func_at(x.from_ea) +ORDER BY str_count DESC LIMIT 10; + +-- URL and path strings +SELECT printf('0x%X', address) as addr, content FROM strings +WHERE content LIKE 'http%' + OR content LIKE '%.exe%' + OR content LIKE '%.dll%' + OR content LIKE 'C:\\%'; +``` + +### Instruction Patterns + +```sql +-- Most common call targets +SELECT operand0 as target, COUNT(*) as count +FROM instructions +WHERE mnemonic = 'call' +GROUP BY operand0 +ORDER BY count DESC LIMIT 15; + +-- Jump instruction distribution +SELECT mnemonic, COUNT(*) as count +FROM instructions +WHERE mnemonic LIKE 'j%' +GROUP BY mnemonic +ORDER BY count DESC; + +-- Functions with unusual push/pop ratio (potential obfuscation) +SELECT func_at(func_addr) as name, + SUM(CASE WHEN mnemonic = 'push' THEN 1 ELSE 0 END) as pushes, + SUM(CASE WHEN mnemonic = 'pop' THEN 1 ELSE 0 END) as pops +FROM instructions +GROUP BY func_addr +HAVING pushes > 20 AND ABS(pushes - pops) > 5; +``` + +### Breakpoint Management + +The `breakpoints` table supports full CRUD: SELECT, INSERT, UPDATE, DELETE. Breakpoints persist in the IDB even without an active debugger session. + +```sql +-- List all breakpoints +SELECT printf('0x%08X', address) as addr, type_name, enabled, condition +FROM breakpoints; + +-- Add a software breakpoint +INSERT INTO breakpoints (address) VALUES (0x401000); + +-- Add a hardware write watchpoint (type=1, size=4) +INSERT INTO breakpoints (address, type, size) VALUES (0x402000, 1, 4); + +-- Add a conditional breakpoint +INSERT INTO breakpoints (address, condition) VALUES (0x401000, 'eax == 0'); + +-- Disable a breakpoint +UPDATE breakpoints SET enabled = 0 WHERE address = 0x401000; + +-- Update condition +UPDATE breakpoints SET condition = 'ecx > 5' WHERE address = 0x401000; + +-- Delete a breakpoint +DELETE FROM breakpoints WHERE address = 0x401000; + +-- Join with functions to see which functions have breakpoints +SELECT b.address, f.name, b.type_name, b.enabled +FROM breakpoints b +JOIN funcs f ON b.address >= f.address AND b.address < f.end_ea; +``` + +**Breakpoint types:** `0` = software, `1` = hardware write, `2` = hardware read, `3` = hardware rdwr, `4` = hardware exec + +**Writable columns:** `enabled`, `type`, `size`, `flags`, `pass_count`, `condition`, `group` + +### Database Modification + +Several tables support INSERT, UPDATE, and DELETE operations: + +| Table | INSERT | UPDATE | DELETE | +|-------|--------|--------|--------| +| `breakpoints` | Yes | Yes | Yes | +| `funcs` | Yes | `name`, `flags` | Yes | +| `names` | Yes | `name` | Yes | +| `comments` | Yes | `comment`, `rpt_comment` | Yes | +| `bookmarks` | Yes | `description` | Yes | +| `segments` | — | `name`, `class`, `perm` | Yes | +| `instructions` | — | — | Yes | +| `types` | Yes | Yes | Yes | +| `types_members` | Yes | Yes | Yes | +| `types_enum_values` | Yes | Yes | Yes | + +```sql +-- Create a function at an address (IDA auto-detects boundaries) +INSERT INTO funcs (address) VALUES (0x401000); + +-- Create a function with explicit end address and name +INSERT INTO funcs (address, name, end_ea) VALUES (0x401000, 'my_func', 0x401050); + +-- Set a name at an address +INSERT INTO names (address, name) VALUES (0x401000, 'main'); + +-- Add a comment +INSERT INTO comments (address, comment) VALUES (0x401000, 'entry point'); + +-- Add both regular and repeatable comments +INSERT INTO comments (address, comment, rpt_comment) VALUES (0x401000, 'regular', 'repeatable'); + +-- Add a bookmark (slot auto-assigned) +INSERT INTO bookmarks (address, description) VALUES (0x401000, 'interesting function'); + +-- Add a bookmark at a specific slot +INSERT INTO bookmarks (slot, address, description) VALUES (5, 0x401000, 'slot 5 bookmark'); + +-- Rename a segment +UPDATE segments SET name = '.mytext' WHERE start_ea = 0x401000; + +-- Change segment permissions (R=4, W=2, X=1) +UPDATE segments SET perm = 5 WHERE name = '.text'; + +-- Delete a segment +DELETE FROM segments WHERE name = '.rdata'; + +-- Delete an instruction (convert to unexplored bytes) +DELETE FROM instructions WHERE address = 0x401000; + +-- Create a new struct type +INSERT INTO types (name, kind) VALUES ('my_struct', 'struct'); + +-- Create an enum type +INSERT INTO types (name, kind) VALUES ('my_flags', 'enum'); + +-- Add a member to a struct +INSERT INTO types_members (type_ordinal, member_name, member_type) VALUES (42, 'field1', 'int'); + +-- Add an enum value +INSERT INTO types_enum_values (type_ordinal, value_name, value) VALUES (15, 'FLAG_ACTIVE', 1); +``` + +## AI Agent + +The agent translates natural language to SQL, executes queries, and explains results. It has access to all tables and can perform multi-step analysis. + +``` +idasql> what functions have the most cross-references? +idasql> find strings that look like file paths +idasql> show me the call hierarchy of main +idasql> which imports are related to networking? +``` + +See an [example session](examples/agent_session_strings.md) analyzing string references in a binary. + +### Prerequisites for AI Features + +The AI agent requires one of these CLI tools installed and authenticated: + +| Provider | CLI Tool | Install | Login | +|----------|----------|---------|-------| +| Claude (default) | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | `npm install -g @anthropic-ai/claude-code` | Run `claude`, then `/login` | +| GitHub Copilot | [Copilot CLI](https://github.com/features/copilot/cli/) | `npm install -g @github/copilot` | Run `copilot`, then `/login` | + +**Important:** You must be logged in before using AI features. No API keys needed for basic usage. + +### Provider Configuration + +``` +.agent provider claude # Claude (default) +.agent provider copilot # GitHub Copilot +``` + +### Bring Your Own Key (BYOK) + +For direct API access or local inference, enable BYOK mode: + +``` +.agent byok enable +.agent byok type anthropic # or: openai, azure +.agent byok key sk-ant-... +.agent byok model claude-sonnet-4-20250514 +.agent byok endpoint https://api.anthropic.com # optional +``` + +With `copilot` provider and BYOK, you can point to local inference servers (Ollama, LM Studio, vLLM) using OpenAI-compatible endpoints: + +``` +.agent provider copilot +.agent byok enable +.agent byok type openai +.agent byok endpoint http://localhost:11434/v1 +.agent byok model llama3 +.agent byok key unused +``` + +Settings persist in `~/.idasql/agent_settings.json`. + +## Building + +### Prerequisites + +- CMake 3.20+ +- C++17 compiler +- IDA SDK 9.0+ (set `IDASDK` environment variable) + +### CLI + +```bash +cmake -S src/cli -B build/cli +cmake --build build/cli --config Release +``` + +### Plugin + +```bash +cmake -S src/plugin -B build/plugin -DIDASQL_WITH_AI_AGENT=ON +cmake --build build/plugin --config Release +``` + +Output: `$IDASDK/bin/plugins/idasql_plugin.dll` + +### Tests + +```bash +cmake -S tests -B build/tests +cmake --build build/tests --config Release +ctest --test-dir build/tests -C Release +``` + +## Remote Server Protocol + +The plugin listens on `127.0.0.1:13337` by default. Protocol is length-prefixed JSON over TCP. No MCP, no complex handshakes. A thin CLI client is all you need. + +Set `IDASQL_TOKEN` environment variable or let the plugin generate one (printed to IDA Output window on startup). + +```bash +# From CLI +idasql --remote localhost:13337 --token -q "SELECT * FROM funcs LIMIT 5" +idasql --remote localhost:13337 --token -i # Interactive +``` + +## HTTP REST API + +Stateless HTTP server for simple integration. No protocol overhead. + +```bash +idasql -s database.i64 --http 8081 +``` + +```bash +curl http://localhost:8081/status +curl -X POST http://localhost:8081/query -d "SELECT name FROM funcs LIMIT 5" +``` + +For multiple databases, run separate instances: + +```bash +idasql -s malware.i64 --http 8081 +idasql -s kernel.i64 --http 8082 +``` + +Endpoints: `/status`, `/help`, `/query`, `/shutdown` + +### HTTP Server from REPL + +Start an HTTP server interactively from the REPL or IDA plugin CLI: + +``` +idasql -s database.i64 -i +idasql> .http start +HTTP server started on port 8142 +URL: http://127.0.0.1:8142 +... +Press Ctrl+C to stop and return to REPL. +``` + +In IDA plugin (non-blocking): +``` +idasql> .http start +HTTP server started on port 8142 +idasql> .http stop +HTTP server stopped +``` + +The server uses a random port (8100-8199) to avoid conflicts with `--http`. + +## MCP Server + +For MCP-compatible clients (Claude Desktop, etc.): + +```bash +# Standalone mode +idasql -s database.i64 --mcp +idasql -s database.i64 --mcp 9500 # specific port + +# Or in interactive mode +idasql -s database.i64 -i +.mcp start +``` + +Configure your MCP client: + +```json +{ + "mcpServers": { + "idasql": { "url": "http://127.0.0.1:/sse" } + } +} +``` + +Tools: `idasql_query` (direct SQL), `idasql_agent` (natural language) + +## Integration with Coding Agents + +The CLI is designed for integration with coding agents (Claude Code, Cursor, Aider, Cline, etc.). Agents can query IDA databases directly without writing IDAPython or understanding IDA's API. + +### Setup + +1. Open your target binary in IDA Pro (plugin loads automatically) +2. Note the auth token printed in IDA's Output window: + ``` + IDASQL: Auth token generated. Token: 2af05f7152ccc6dd66894c1243f72a2a + IDASQL: Server listening on 127.0.0.1:13337 + ``` +3. The CLI acts as a thin client to query the running IDA instance + +### Instructing an Agent + +When working with a coding agent on reverse engineering tasks, provide these instructions: + +``` +IDASQL is running on localhost:13337 with token . + +To query the IDA database, use the idasql CLI: + + idasql --remote localhost:13337 --token -q "SQL QUERY" + +Available tables: funcs, segments, names, imports, exports, strings, xrefs, instructions, blocks, types + +Example queries: + # List functions + idasql --remote localhost:13337 --token -q "SELECT name, printf('0x%X', address) as addr FROM funcs LIMIT 20" + + # Find strings containing a keyword + idasql --remote localhost:13337 --token -q "SELECT * FROM strings WHERE content LIKE '%error%'" + + # Find callers of a function + idasql --remote localhost:13337 --token -q "SELECT printf('0x%X', from_ea) as caller FROM xrefs WHERE to_ea = 0x401000" + + # Search for any identifier + idasql --remote localhost:13337 --token -q "SELECT name, kind, address FROM jump_entities('CreateFile', 'prefix') LIMIT 10" +``` + +### Agent Workflow Example + +``` +User: "Find all functions that call CreateFileW and check if they handle errors" + +Agent thinks: I'll query IDASQL to find the callers + +Agent runs: +$ idasql --remote localhost:13337 --token abc123 -q " + SELECT DISTINCT func_at(x.from_ea) as caller, printf('0x%X', x.from_ea) as call_site + FROM xrefs x + JOIN imports i ON x.to_ea = i.address + WHERE i.name = 'CreateFileW' +" + +Agent receives: +| caller | call_site | +|---------------------|------------| +| ReadConfigFile | 0x401234 | +| SaveDocument | 0x401890 | +| ... | ... | + +Agent then queries for error handling patterns in those functions... +``` + +The agent never needs to write IDAPython. SQL queries are self-contained and portable. + +## Claude Code Plugin + +IDASQL is available as a Claude Code plugin, allowing Claude to query IDA databases directly within your coding workflow. + +### Prerequisites + +1. **IDA Pro** installed with `ida.exe` directory in your PATH +2. **idasql.exe** downloaded from [Releases](https://github.com/allthingsida/idasql/releases) and placed next to `ida.exe` +3. Verify setup: `idasql --version` should work from command line + +### Installation + +```bash +# Add the marketplace (one-time) +/plugin marketplace add 0xeb/anthropic-xsql-tools-plugin + +# Install idasql plugin +/plugin install idasql@0xeb-tools +``` + +### Usage + +Once installed, the skill is automatically available: + +``` +"Using idasql, count functions in myfile.i64" +"Using idasql, decompile main in test.i64" +"Using idasql, find strings containing 'password'" +``` + +### Updating + +```bash +/plugin update idasql +``` + +### Troubleshooting + +**SSH Permission Denied** + +If you see `git@github.com: Permission denied (publickey)` during install, configure git to use HTTPS: + +```bash +git config --global url."https://github.com/".insteadOf "git@github.com:" +``` + +## Built With + +- **[libxsql](https://github.com/0xeb/libxsql)** - Header-only C++17 library for exposing C++ data structures as SQLite virtual tables. Provides the fluent builder API for defining tables, constraint pushdown, and the socket server/client protocol. + +- **[libagents](https://github.com/0xeb/libagents)** - C++ library for building AI agents with tool use. Powers the natural language interface with support for Claude (Anthropic) and GitHub Copilot providers. + +## Author + +**Elias Bachaalany** ([@0xeb](https://github.com/0xeb)) + +## License + +MIT License - see [LICENSE](LICENSE) for details. + +``` + +`benchmark.md`: + +```md +# Build Performance Benchmark + +## Environment + +- **OS**: Windows 11 +- **CPU**: 24 logical processors +- **Compiler**: MSVC (Visual Studio 17.14) +- **CMake**: 4.0.3 +- **Config**: Release +- **Features**: All ON (AI Agent, HTTP, Plugin, CLI, Examples) + +## Results (clean rebuild, deps already fetched) + +| Branch | Build Time | Speedup | +|--------|-----------|---------| +| `main` (baseline) | ~280 sec (4.7 min) | — | +| `build/faster-windows-build` | ~175 sec (2.9 min) | **~38% faster** | + +## Changes Applied + +### 1. MSVC `/MP` on dependency targets (~80-85 sec saved) +Dependency libraries (fastmcpp_core, copilot_sdk_cpp, claude_sdk, libagents) were compiling +files sequentially because IDA SDK's `/MP` flag only applies to `ida_add_plugin`/`ida_add_idalib` +targets. Adding `/MP` enables multi-processor compilation within each dependency target. + +### 2. Unity build on compatible dependency targets (~20-30 sec saved) +Enabled `UNITY_BUILD` for copilot_sdk_cpp, claude_sdk, and libagents. This merges +translation units to reduce redundant header parsing. + +**Note**: fastmcpp_core is excluded because its server files (stdio_server.cpp, +sse_server.cpp, streamable_http_server.cpp) use duplicate anonymous-namespace symbols +that conflict under unity builds. + +### 3. Pinned dependency versions with `GIT_SHALLOW` (~5-30 sec saved on configure) +Changed `GIT_TAG main` to specific commit SHAs and added `GIT_SHALLOW TRUE` for both +libxsql and libagents. This avoids re-fetching on every configure and reduces clone size. + +### 4. Excluded unused targets +The `debug` tool from claude-agent-sdk-cpp is excluded from the default build via +`EXCLUDE_FROM_ALL`. + +## Not Yet Applied (potential further improvements) + +- **PCH for idasql targets**: Precompiled headers for CLI/plugin/examples could save + another ~50-60 sec by eliminating redundant IDA SDK + xsql header parsing. +- **libxsql partial de-header-only**: Moving non-template code to .cpp files would + reduce per-TU compile cost. Requires upstream libxsql changes. + +``` + +`examples/CMakeLists.txt`: + +```txt +# examples/CMakeLists.txt - IDASQL Example Programs +# +# Standalone build: +# cmake -B build +# cmake --build build --config Release +# +# Run: +# Windows: set PATH=%IDASDK%\src\bin;%PATH% +# build\Release\example_basic.exe database.i64 +# Linux: LD_LIBRARY_PATH=$IDASDK/src/bin ./build/example_basic database.i64 + +# Standalone mode: set up project and dependencies +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + cmake_minimum_required(VERSION 3.20) + project(idasql_examples VERSION 1.0 LANGUAGES C CXX) + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + + # Include IDA SDK bootstrap + include($ENV{IDASDK}/src/cmake/bootstrap.cmake) + find_package(idasdk REQUIRED) +endif() + +# ============================================================================ +# xsql dependency (provides SQLite) +# ============================================================================ + +if(TARGET xsql::xsql) + message(STATUS "idasql_examples: Using xsql::xsql from parent project") +elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../external/libxsql/CMakeLists.txt") + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../external/libxsql ${CMAKE_CURRENT_BINARY_DIR}/libxsql) + message(STATUS "idasql_examples: Added libxsql from external/libxsql") +else() + message(FATAL_ERROR "libxsql not found. Initialize submodule: git submodule update --init external/libxsql") +endif() + +# libidasql interface library (headers only) +add_library(idasql_lib INTERFACE) +target_include_directories(idasql_lib INTERFACE + ${CMAKE_CURRENT_SOURCE_DIR}/../src/lib/include +) + +# Helper function to create examples +function(add_idasql_example name) + ida_add_idalib(${name} + TYPE EXECUTABLE + SOURCES ${name}.cpp + ) + target_link_libraries(${name} PRIVATE idasql_lib xsql::xsql) + target_compile_definitions(${name} PRIVATE USE_IDA_SDK USE_HEXRAYS) + + # Platform-specific settings + if(MSVC) + file(TO_NATIVE_PATH "${IDABIN}" IDABIN_NATIVE) + set_target_properties(${name} PROPERTIES + VS_DEBUGGER_ENVIRONMENT "PATH=${IDABIN_NATIVE};%PATH%" + ) + elseif(APPLE) + set_target_properties(${name} PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "@executable_path;$ENV{IDASDK}/src/bin" + ) + elseif(UNIX) + set_target_properties(${name} PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "$ORIGIN;$ENV{IDASDK}/src/bin" + ) + endif() +endfunction() + +# Example executables +add_idasql_example(example_basic) +add_idasql_example(example_functions) +add_idasql_example(example_strings) +add_idasql_example(example_instructions) +add_idasql_example(example_decompiler) +add_idasql_example(example_custom_vtable) +add_idasql_example(example_plugin_style) +add_idasql_example(example_jump_search) +add_idasql_example(example_jump_entities) +add_idasql_example(example_breakpoints) + +``` + +`examples/agent_session_strings.md`: + +```md +# IDASQL Agent Session: String Analysis + +This transcript demonstrates IDASQL's AI agent analyzing string references in a binary. + +--- + +``` +IDASQL: Query engine initialized +IDASQL: Auth token generated (set IDASQL_TOKEN to override). Token: 6ce6213e59626ebadc269aebee634240 +IDASQL CLI: Installed (AI agent: enabled) +----------------------------------------------------------------------------------------- +Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)] +IDAPython 64-bit v9.2.0 (c) The IDAPython Team +----------------------------------------------------------------------------------------- +``` + +**User:** Which function refers to strings the most? + +**Agent:** The function that refers to strings the most is **`main`** with **13 string references**. + +Here's a summary of the top string-referencing functions: + +| Function | String References | +|----------|-------------------| +| `main` | 13 | +| `__acrt_fltout` | 4 | +| `unknown_libname_26` | 2 | +| `__vcrt_InitializeCriticalSectionEx` | 2 | +| `__vcrt_FlsSetValue` | 2 | +| `__vcrt_FlsGetValue` | 2 | +| `__vcrt_FlsFree` | 2 | +| `__vcrt_FlsAlloc` | 2 | + +The `main` function stands out significantly with more than 3x the string references of the next highest function, suggesting it likely contains significant user-facing logic with messages, prompts, or configuration strings. + +--- + +**User:** What string does "main" refer to? + +**Agent:** The `main` function refers to these 13 strings: + +| String | Purpose | +|--------|---------| +| `--pause` | Command-line argument | +| `-monoff` | Monitor off command | +| `-abortshutdown` | Abort shutdown command | +| `-play` | Play command | +| `-lock` | Lock workstation command | +| `-hibernate` | Hibernate command | +| `-logoff` | Log off command | +| `-mute` | Mute audio command | +| `-reboot` | Reboot command | +| `-blank` | Blank screen command | +| `-eject` | Eject media command | +| `-close` | Close command | +| `Invalid usage!\n` | Error message | + +This reveals that the binary is a **command-line utility** for system control operations. It parses various command-line arguments to perform actions like: +- Power management: hibernate, reboot, shutdown abort +- Display control: monitor off, blank screen +- Session control: lock, logoff +- Media control: play, mute, eject + +The `Invalid usage!` string is displayed when none of the valid arguments are provided. + +--- + +**User:** List all those strings used in main in a nice table and their addresses + +**Agent:** Here are all the strings used in `main`: + +| String Address | Reference Address | String Content | +|----------------|-------------------|----------------| +| 0x140017410 | 0x140001064 | `--pause` | +| 0x140017418 | 0x1400010B2 | `-monoff` | +| 0x140017420 | 0x1400010DA | `-abortshutdown` | +| 0x140017430 | 0x140001103 | `-play` | +| 0x140017438 | 0x14000112A | `-lock` | +| 0x140017440 | 0x14000114E | `-hibernate` | +| 0x140017450 | 0x140001172 | `-logoff` | +| 0x140017458 | 0x140001196 | `-mute` | +| 0x140017460 | 0x1400011CB | `-reboot` | +| 0x140017468 | 0x1400011EF | `-blank` | +| 0x140017470 | 0x140001215 | `-eject` | +| 0x140017478 | 0x14000123D | `-close` | +| 0x140017480 | 0x140001264 | `Invalid usage!` | + +The strings are stored sequentially in the `.rdata` section starting at `0x140017410`, and are referenced in order within the `main` function from `0x140001064` to `0x140001264`. + +``` + +`examples/example_basic.cpp`: + +```cpp +/** + * example_basic.cpp - Basic IDASQL usage with Session + * + * Demonstrates: + * - Opening an IDA database with Session + * - Running queries with query() and getting results + * - Using scalar() for single values + * - Iterating over result rows + * + * This is the pattern for standalone CLI tools that manage the IDA lifecycle. + * + * Build & Run: + * cmake -B build && cmake --build build --config Release + * set PATH=%IDASDK%\bin;%PATH% + * build\Release\example_basic.exe database.i64 + */ + +#include +#include + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } + + // ========================================================================== + // Open the IDA database using Session + // ========================================================================== + + idasql::Session session; + + std::cout << "Opening: " << argv[1] << "...\n"; + if (!session.open(argv[1])) { + std::cerr << "Error: " << session.error() << "\n"; + return 1; + } + + std::cout << "\n" << session.info() << "\n"; + + // ========================================================================== + // Example 1: Get a single value with scalar() + // ========================================================================== + + std::cout << "=== Scalar Queries ===\n"; + + std::string func_count = session.scalar("SELECT COUNT(*) FROM funcs"); + std::cout << "Total functions: " << func_count << "\n"; + + std::string segment_count = session.scalar("SELECT COUNT(*) FROM segments"); + std::cout << "Total segments: " << segment_count << "\n"; + + // ========================================================================== + // Example 2: Query with result set + // ========================================================================== + + std::cout << "\n=== Top 5 Largest Functions ===\n"; + + auto result = session.query( + "SELECT printf('0x%08X', address) as addr, name, size " + "FROM funcs ORDER BY size DESC LIMIT 5" + ); + + if (result.success) { + // Print column headers + for (const auto& col : result.columns) { + std::cout << col << "\t"; + } + std::cout << "\n" << std::string(50, '-') << "\n"; + + // Print rows + for (const auto& row : result) { + std::cout << row[0] << "\t" << row[1] << "\t" << row[2] << "\n"; + } + std::cout << "\n(" << result.row_count() << " rows)\n"; + } else { + std::cerr << "Query failed: " << result.error << "\n"; + } + + // ========================================================================== + // Example 3: Segments listing + // ========================================================================== + + std::cout << "\n=== Segments ===\n"; + + auto segments = session.query( + "SELECT name, printf('0x%X', start_ea) as start, " + " printf('0x%X', end_ea) as end, perm " + "FROM segments" + ); + + for (const auto& row : segments) { + std::cout << row[0] << ": " << row[1] << " - " << row[2] + << " (perm: " << row[3] << ")\n"; + } + + // ========================================================================== + // Example 4: Using SQL functions + // ========================================================================== + + std::cout << "\n=== SQL Functions ===\n"; + + // Get function at specific index + auto first_func = session.query( + "SELECT printf('0x%X', func_at_index(0)) as addr, " + " func_at(func_at_index(0)) as name" + ); + if (!first_func.empty()) { + std::cout << "First function: " << first_func.rows[0][1] + << " at " << first_func.rows[0][0] << "\n"; + } + + // Cleanup (optional - destructor handles it) + session.close(); + + std::cout << "\nDone.\n"; + return 0; +} + +``` + +`examples/example_breakpoints.cpp`: + +```cpp +/** + * example_breakpoints.cpp - Breakpoint management with IDASQL + * + * Demonstrates: + * - Querying the breakpoints table + * - Adding software and hardware breakpoints via INSERT + * - Disabling breakpoints via UPDATE + * - Deleting breakpoints via DELETE + * - Joining breakpoints with funcs + * + * Breakpoints persist in the IDB even without an active debugger session. + * + * Build & Run: + * cmake -B build && cmake --build build --config Release + * set PATH=%IDASDK%\bin;%PATH% + * build\Release\example_breakpoints.exe database.i64 + */ + +#include +#include +#include + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } + + idasql::Session session; + + std::cout << "Opening: " << argv[1] << "...\n"; + if (!session.open(argv[1])) { + std::cerr << "Error: " << session.error() << "\n"; + return 1; + } + + // ========================================================================= + // 1. List existing breakpoints + // ========================================================================= + + std::cout << "=== Existing Breakpoints ===\n"; + + auto existing = session.query( + "SELECT printf('0x%08X', address) as addr, type_name, enabled, " + " loc_type_name, condition, \"group\" " + "FROM breakpoints" + ); + + if (existing.empty()) { + std::cout << "(none)\n"; + } else { + for (const auto& row : existing) { + std::cout << row[0] << " " << std::setw(16) << row[1] + << " enabled=" << row[2] + << " loc=" << row[3] << "\n"; + } + } + + // ========================================================================= + // 2. Add two breakpoints at the first two function addresses + // ========================================================================= + + std::cout << "\n=== Adding Breakpoints ===\n"; + + // Get two function addresses to use + auto funcs = session.query( + "SELECT address, name FROM funcs ORDER BY address LIMIT 2" + ); + + if (funcs.row_count() < 2) { + std::cerr << "Need at least 2 functions in database\n"; + return 1; + } + + std::string addr1 = funcs.rows[0][0]; + std::string name1 = funcs.rows[0][1]; + std::string addr2 = funcs.rows[1][0]; + std::string name2 = funcs.rows[1][1]; + + // Insert a software breakpoint at the first function + auto r1 = session.query( + "INSERT INTO breakpoints (address) VALUES (" + addr1 + ")" + ); + std::cout << "Added software breakpoint at " << name1 << "\n"; + + // Insert a hardware write watchpoint at the second function + auto r2 = session.query( + "INSERT INTO breakpoints (address, type, size) VALUES (" + addr2 + ", 1, 4)" + ); + std::cout << "Added hardware watchpoint at " << name2 << "\n"; + + // Show what we have + auto after_add = session.query( + "SELECT printf('0x%08X', address) as addr, type_name, enabled, size " + "FROM breakpoints" + ); + + std::cout << "\nBreakpoints after adding:\n"; + std::cout << std::left + << std::setw(14) << "Address" + << std::setw(18) << "Type" + << std::setw(10) << "Enabled" + << std::setw(6) << "Size" << "\n"; + std::cout << std::string(48, '-') << "\n"; + + for (const auto& row : after_add) { + std::cout << std::setw(14) << row[0] + << std::setw(18) << row[1] + << std::setw(10) << row[2] + << std::setw(6) << row[3] << "\n"; + } + + // ========================================================================= + // 3. Disable the second breakpoint + // ========================================================================= + + std::cout << "\n=== Disabling Second Breakpoint ===\n"; + + session.query( + "UPDATE breakpoints SET enabled = 0 WHERE address = " + addr2 + ); + + auto after_disable = session.query( + "SELECT printf('0x%08X', address) as addr, enabled, type_name " + "FROM breakpoints" + ); + + for (const auto& row : after_disable) { + std::cout << row[0] << " enabled=" << row[1] + << " " << row[2] << "\n"; + } + + // ========================================================================= + // 4. Delete the first breakpoint + // ========================================================================= + + std::cout << "\n=== Deleting First Breakpoint ===\n"; + + session.query( + "DELETE FROM breakpoints WHERE address = " + addr1 + ); + + auto after_delete = session.query( + "SELECT printf('0x%08X', address) as addr, enabled, type_name, size " + "FROM breakpoints" + ); + + std::cout << "Remaining breakpoints: " << after_delete.row_count() << "\n"; + for (const auto& row : after_delete) { + std::cout << row[0] << " enabled=" << row[1] + << " " << row[2] << " size=" << row[3] << "\n"; + } + + // ========================================================================= + // 5. Clean up + // ========================================================================= + + session.query("DELETE FROM breakpoints WHERE address = " + addr2); + std::cout << "\nCleaned up. Final count: " + << session.scalar("SELECT COUNT(*) FROM breakpoints") + << " breakpoints.\n"; + + std::cout << "\nDone.\n"; + return 0; +} + +``` + +`examples/example_custom_vtable.cpp`: + +```cpp +/** + * example_custom_vtable.cpp - Creating custom virtual tables + * + * This example shows how to expose your own data as SQL tables. + * We'll create a simple "user_functions" table that mirrors the built-in + * funcs table, demonstrating the pattern you'd use for any custom data. + * + * Key concepts: + * 1. Use VTableBuilder fluent API to define columns + * 2. Register the module, then create the table instance + * 3. Query with standard SQL + */ + +#include +#include + +#include +#include + +// IDA SDK +#include +#include + +// ============================================================================= +// Step 1: Define your table using the fluent API +// ============================================================================= + +idasql::VTableDef make_user_functions_table() { + return idasql::table("user_functions") + // How many rows? + .count([]() -> size_t { + return get_func_qty(); + }) + // Column definitions - each takes a lambda (row_index) -> value + .column_int64("address", [](size_t i) -> int64_t { + func_t* f = getn_func(i); + return f ? f->start_ea : BADADDR; + }) + .column_text("name", [](size_t i) -> std::string { + func_t* f = getn_func(i); + if (!f) return ""; + qstring name; + get_func_name(&name, f->start_ea); + return std::string(name.c_str()); + }) + .column_int64("size", [](size_t i) -> int64_t { + func_t* f = getn_func(i); + return f ? f->size() : 0; + }) + .column_int("flags", [](size_t i) -> int { + func_t* f = getn_func(i); + return f ? f->flags : 0; + }) + .build(); +} + +// ============================================================================= +// Main +// ============================================================================= + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } + + // Open the IDA database + idasql::Session session; + if (!session.open(argv[1])) { + std::cerr << "Error: " << session.error() << "\n"; + return 1; + } + + // Get SQLite handle for registration + sqlite3* db = session.handle(); + + // ========================================================================= + // Step 2: Register your custom table + // ========================================================================= + + // Create the table definition (must outlive the registration) + static auto user_funcs_def = make_user_functions_table(); + + // Register the vtable module with SQLite + idasql::register_vtable(db, "user_functions_module", &user_funcs_def); + + // Create the actual table instance + idasql::create_vtable(db, "user_functions", "user_functions_module"); + + std::cout << "Registered custom table: user_functions\n\n"; + + // ========================================================================= + // Step 3: Query your custom table with SQL + // ========================================================================= + + std::cout << "=== Query: user_functions (Top 10 by size) ===\n\n"; + + auto result = session.query( + "SELECT printf('0x%X', address) as addr, name, size, flags " + "FROM user_functions " + "ORDER BY size DESC " + "LIMIT 10" + ); + + // Print results + std::cout << std::left + << std::setw(14) << "Address" + << std::setw(35) << "Name" + << std::setw(10) << "Size" + << "Flags\n"; + std::cout << std::string(65, '-') << "\n"; + + for (const auto& row : result) { + std::cout << std::setw(14) << row[0] + << std::setw(35) << row[1].substr(0, 33) + << std::setw(10) << row[2] + << row[3] << "\n"; + } + + // ========================================================================= + // Bonus: Join custom table with built-in tables + // ========================================================================= + + std::cout << "\n=== Join: user_functions + xrefs (most called) ===\n\n"; + + auto most_called = session.query( + "SELECT uf.name, COUNT(x.from_ea) as call_count " + "FROM user_functions uf " + "JOIN xrefs x ON uf.address = x.to_ea " + "WHERE x.type = 17 " // Code call xref + "GROUP BY uf.address " + "ORDER BY call_count DESC " + "LIMIT 10" + ); + + for (const auto& row : most_called) { + std::cout << std::setw(40) << row[0] + << " called " << row[1] << " times\n"; + } + + std::cout << "\nDone.\n"; + return 0; +} + +``` + +`examples/example_decompiler.cpp`: + +```cpp +/** + * example_decompiler.cpp - Hex-Rays decompiler analysis with IDASQL + * + * Demonstrates: + * - Querying pseudocode table (line-by-line access) + * - Querying ctree_lvars table (local variables) + * - Using decompile() SQL function (full text) + * - Finding patterns in decompiled code + * + * Requires: Hex-Rays decompiler license + */ + +#include +#include +#include + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n"; + std::cerr << "\nNote: Requires Hex-Rays decompiler license.\n"; + return 1; + } + + idasql::Session session; + if (!session.open(argv[1])) { + std::cerr << "Error: " << session.error() << "\n"; + return 1; + } + + // ========================================================================= + // Decompiler availability check + // ========================================================================= + + std::cout << "=== Decompiler Analysis ===\n\n"; + + // Try to decompile a function to check if Hex-Rays is available + auto test = session.query("SELECT decompile(func_at_index(0)) as code"); + if (!test.success || test.empty() || test.rows[0][0].find("Decompiler") != std::string::npos) { + std::cerr << "Warning: Hex-Rays decompiler may not be available.\n"; + std::cerr << "Some queries may fail or return empty results.\n\n"; + } + + // ========================================================================= + // Functions by pseudocode line count + // ========================================================================= + + std::cout << "=== Functions by Pseudocode Complexity ===\n"; + + auto complex = session.query( + "SELECT " + " func_at(func_addr) as name, " + " COUNT(*) as lines " + "FROM pseudocode " + "GROUP BY func_addr " + "ORDER BY lines DESC " + "LIMIT 10" + ); + + std::cout << std::setw(40) << "Function" << "Lines\n"; + std::cout << std::string(50, '-') << "\n"; + for (const auto& row : complex) { + std::cout << std::setw(40) << row[0] << row[1] << "\n"; + } + + // ========================================================================= + // Functions with most local variables + // ========================================================================= + + std::cout << "\n=== Functions with Most Local Variables ===\n"; + + auto most_vars = session.query( + "SELECT " + " func_at(func_addr) as name, " + " COUNT(*) as total_vars, " + " SUM(CASE WHEN is_arg = 1 THEN 1 ELSE 0 END) as args, " + " SUM(CASE WHEN is_arg = 0 THEN 1 ELSE 0 END) as locals " + "FROM ctree_lvars " + "GROUP BY func_addr " + "ORDER BY total_vars DESC " + "LIMIT 10" + ); + + std::cout << std::setw(35) << "Function" + << std::setw(8) << "Total" + << std::setw(8) << "Args" + << "Locals\n"; + std::cout << std::string(60, '-') << "\n"; + for (const auto& row : most_vars) { + std::cout << std::setw(35) << row[0] + << std::setw(8) << row[1] + << std::setw(8) << row[2] + << row[3] << "\n"; + } + + // ========================================================================= + // Variable type analysis + // ========================================================================= + + std::cout << "\n=== Most Common Variable Types ===\n"; + + auto var_types = session.query( + "SELECT type, COUNT(*) as count " + "FROM ctree_lvars " + "WHERE type != '' " + "GROUP BY type " + "ORDER BY count DESC " + "LIMIT 15" + ); + + for (const auto& row : var_types) { + std::cout << std::setw(30) << row[0] << " - " << row[1] << " occurrences\n"; + } + + // ========================================================================= + // Show pseudocode for a specific function + // ========================================================================= + + std::cout << "\n=== Pseudocode for Largest Function (first 30 lines) ===\n"; + + auto largest = session.scalar("SELECT address FROM funcs ORDER BY size DESC LIMIT 1"); + std::string pseudocode_sql = + "SELECT line " + "FROM pseudocode " + "WHERE func_addr = " + largest + " " + "ORDER BY line_num " + "LIMIT 30"; + auto pseudocode = session.query(pseudocode_sql.c_str()); + + for (const auto& row : pseudocode) { + std::cout << row[0] << "\n"; + } + + // ========================================================================= + // Local variables for a function + // ========================================================================= + + std::cout << "\n=== Variables in Largest Function ===\n"; + + std::string vars_sql = + "SELECT name, type, size, " + " CASE WHEN is_arg = 1 THEN 'arg' ELSE 'local' END as kind " + "FROM ctree_lvars " + "WHERE func_addr = " + largest + " " + "ORDER BY is_arg DESC, idx"; + auto vars = session.query(vars_sql.c_str()); + + std::cout << std::setw(20) << "Name" + << std::setw(25) << "Type" + << std::setw(8) << "Size" + << "Kind\n"; + std::cout << std::string(60, '-') << "\n"; + for (const auto& row : vars) { + std::cout << std::setw(20) << row[0] + << std::setw(25) << row[1] + << std::setw(8) << row[2] + << row[3] << "\n"; + } + + // ========================================================================= + // Full decompilation with decompile() function + // ========================================================================= + + std::cout << "\n=== Using decompile() SQL Function ===\n"; + + // Find main function + auto main_addr = session.scalar( + "SELECT address FROM funcs WHERE name LIKE '%main%' LIMIT 1" + ); + + if (!main_addr.empty()) { + std::cout << "Decompiling main function:\n\n"; + auto code = session.scalar("SELECT decompile(" + main_addr + ")"); + std::cout << code << "\n"; + } else { + std::cout << "No 'main' function found.\n"; + } + + // ========================================================================= + // Search pseudocode for patterns + // ========================================================================= + + std::cout << "\n=== Lines Containing 'if' Statements ===\n"; + + auto if_lines = session.query( + "SELECT func_at(func_addr) as func, line " + "FROM pseudocode " + "WHERE line LIKE '%if (%' " + "LIMIT 10" + ); + + for (const auto& row : if_lines) { + std::cout << "[" << row[0] << "] " << row[1] << "\n"; + } + + std::cout << "\nDone.\n"; + return 0; +} + +``` + +`examples/example_functions.cpp`: + +```cpp +/** + * example_functions.cpp - Function analysis with IDASQL + * + * Demonstrates: + * - Querying the funcs table + * - Using xrefs for call graph analysis + * - Using blocks for CFG analysis + * - Combining tables with JOINs + */ + +#include +#include +#include + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } + + idasql::Session session; + if (!session.open(argv[1])) { + std::cerr << "Error: " << session.error() << "\n"; + return 1; + } + + // ========================================================================= + // Function size distribution + // ========================================================================= + + std::cout << "=== Function Size Distribution ===\n"; + + auto dist = session.query( + "SELECT " + " CASE " + " WHEN size < 16 THEN '1. tiny (<16)' " + " WHEN size < 64 THEN '2. small (16-64)' " + " WHEN size < 256 THEN '3. medium (64-256)' " + " WHEN size < 1024 THEN '4. large (256-1K)' " + " ELSE '5. huge (>1K)' " + " END as category, " + " COUNT(*) as count, " + " SUM(size) as total_bytes " + "FROM funcs " + "GROUP BY category " + "ORDER BY category" + ); + + std::cout << std::left << std::setw(20) << "Category" + << std::setw(10) << "Count" + << std::setw(15) << "Total Bytes" << "\n"; + std::cout << std::string(45, '-') << "\n"; + + for (const auto& row : dist) { + std::cout << std::setw(20) << row[0] + << std::setw(10) << row[1] + << std::setw(15) << row[2] << "\n"; + } + + // ========================================================================= + // Most called functions (incoming xrefs) + // ========================================================================= + + std::cout << "\n=== Top 10 Most Called Functions ===\n"; + + auto most_called = session.query( + "SELECT f.name, COUNT(*) as callers " + "FROM funcs f " + "JOIN xrefs x ON f.address = x.to_ea " + "WHERE x.is_code = 1 " + "GROUP BY f.address " + "ORDER BY callers DESC " + "LIMIT 10" + ); + + for (const auto& row : most_called) { + std::cout << std::setw(40) << row[0] << " - " << row[1] << " callers\n"; + } + + // ========================================================================= + // Functions making most calls (outgoing) + // ========================================================================= + + std::cout << "\n=== Top 10 Functions Making Most Calls ===\n"; + + auto most_calls = session.query( + "SELECT func_at(func_addr) as name, COUNT(*) as calls " + "FROM instructions " + "WHERE mnemonic = 'call' " + "GROUP BY func_addr " + "ORDER BY calls DESC " + "LIMIT 10" + ); + + for (const auto& row : most_calls) { + std::cout << std::setw(40) << row[0] << " - " << row[1] << " calls\n"; + } + + // ========================================================================= + // Functions with most basic blocks (complex CFG) + // ========================================================================= + + std::cout << "\n=== Top 10 Functions by Basic Block Count ===\n"; + + auto complex = session.query( + "SELECT " + " (SELECT name FROM funcs WHERE address = b.func_ea) as name, " + " COUNT(*) as blocks, " + " SUM(b.size) as total_size " + "FROM blocks b " + "GROUP BY b.func_ea " + "ORDER BY blocks DESC " + "LIMIT 10" + ); + + std::cout << std::setw(40) << "Function" + << std::setw(10) << "Blocks" + << std::setw(12) << "Size" << "\n"; + std::cout << std::string(62, '-') << "\n"; + + for (const auto& row : complex) { + std::cout << std::setw(40) << row[0] + << std::setw(10) << row[1] + << std::setw(12) << row[2] << "\n"; + } + + // ========================================================================= + // "Leaf" functions (no outgoing calls) + // ========================================================================= + + std::cout << "\n=== Leaf Functions (no calls, first 10) ===\n"; + + auto leaves = session.query( + "SELECT f.name, f.size " + "FROM funcs f " + "WHERE NOT EXISTS (" + " SELECT 1 FROM instructions i " + " WHERE i.func_addr = f.address AND i.mnemonic = 'call'" + ") " + "ORDER BY f.size DESC " + "LIMIT 10" + ); + + for (const auto& row : leaves) { + std::cout << std::setw(40) << row[0] << " (" << row[1] << " bytes)\n"; + } + + // ========================================================================= + // Orphan functions (no incoming xrefs) + // ========================================================================= + + std::cout << "\n=== Orphan Functions (no callers, first 10) ===\n"; + + auto orphans = session.query( + "SELECT f.name, printf('0x%X', f.address) as addr " + "FROM funcs f " + "WHERE NOT EXISTS (" + " SELECT 1 FROM xrefs x WHERE x.to_ea = f.address AND x.is_code = 1" + ") " + "LIMIT 10" + ); + + for (const auto& row : orphans) { + std::cout << row[0] << " at " << row[1] << "\n"; + } + + std::cout << "\nDone.\n"; + return 0; +} + +``` + +`examples/example_instructions.cpp`: + +```cpp +/** + * example_instructions.cpp - Instruction analysis with IDASQL + * + * Demonstrates: + * - Querying the instructions table + * - Mnemonic distribution analysis + * - Finding specific instruction patterns + * - Using itype for instruction classification + */ + +#include +#include +#include + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } + + idasql::Session session; + if (!session.open(argv[1])) { + std::cerr << "Error: " << session.error() << "\n"; + return 1; + } + + // ========================================================================= + // Instruction statistics + // ========================================================================= + + std::cout << "=== Instruction Statistics ===\n"; + + // Get stats for the largest function + auto largest_func = session.scalar("SELECT address FROM funcs ORDER BY size DESC LIMIT 1"); + std::cout << "Analyzing largest function at 0x" << std::hex << std::stoull(largest_func) << std::dec << "\n\n"; + + // ========================================================================= + // Mnemonic distribution + // ========================================================================= + + std::cout << "=== Top 20 Most Common Instructions ===\n"; + + auto mnemonics = session.query( + "SELECT mnemonic, COUNT(*) as count " + "FROM instructions " + "GROUP BY mnemonic " + "ORDER BY count DESC " + "LIMIT 20" + ); + + std::cout << std::left << std::setw(15) << "Mnemonic" << std::setw(10) << "Count" << "\n"; + std::cout << std::string(25, '-') << "\n"; + + for (const auto& row : mnemonics) { + std::cout << std::setw(15) << row[0] << std::setw(10) << row[1] << "\n"; + } + + // ========================================================================= + // Call targets analysis + // ========================================================================= + + std::cout << "\n=== Most Common Call Targets ===\n"; + + auto calls = session.query( + "SELECT operand0, COUNT(*) as count " + "FROM instructions " + "WHERE mnemonic = 'call' " + "GROUP BY operand0 " + "ORDER BY count DESC " + "LIMIT 15" + ); + + for (const auto& row : calls) { + std::cout << std::setw(40) << row[0] << " - called " << row[1] << " times\n"; + } + + // ========================================================================= + // Functions with most NOP instructions (padding/alignment) + // ========================================================================= + + std::cout << "\n=== Functions with Most NOPs ===\n"; + + auto nops = session.query( + "SELECT func_at(func_addr) as name, COUNT(*) as nop_count " + "FROM instructions " + "WHERE mnemonic = 'nop' " + "GROUP BY func_addr " + "HAVING nop_count > 5 " + "ORDER BY nop_count DESC " + "LIMIT 10" + ); + + for (const auto& row : nops) { + std::cout << std::setw(40) << row[0] << " - " << row[1] << " NOPs\n"; + } + + // ========================================================================= + // Jump instruction analysis + // ========================================================================= + + std::cout << "\n=== Jump Instruction Distribution ===\n"; + + auto jumps = session.query( + "SELECT mnemonic, COUNT(*) as count " + "FROM instructions " + "WHERE mnemonic LIKE 'j%' " // All jump variants + "GROUP BY mnemonic " + "ORDER BY count DESC" + ); + + for (const auto& row : jumps) { + std::cout << std::setw(10) << row[0] << " - " << row[1] << "\n"; + } + + // ========================================================================= + // Suspicious patterns (potential obfuscation) + // ========================================================================= + + std::cout << "\n=== Potential Obfuscation Patterns ===\n"; + + // Functions with unusual push/pop ratio + auto unusual = session.query( + "SELECT " + " func_at(func_addr) as name, " + " SUM(CASE WHEN mnemonic = 'push' THEN 1 ELSE 0 END) as pushes, " + " SUM(CASE WHEN mnemonic = 'pop' THEN 1 ELSE 0 END) as pops " + "FROM instructions " + "GROUP BY func_addr " + "HAVING pushes > 20 AND ABS(pushes - pops) > 5 " + "ORDER BY pushes DESC " + "LIMIT 10" + ); + + std::cout << std::setw(40) << "Function" << std::setw(10) << "Pushes" << "Pops\n"; + std::cout << std::string(60, '-') << "\n"; + for (const auto& row : unusual) { + std::cout << std::setw(40) << row[0] << std::setw(10) << row[1] << row[2] << "\n"; + } + + // ========================================================================= + // Instruction type (itype) analysis + // ========================================================================= + + std::cout << "\n=== Instruction Types (itype) ===\n"; + std::cout << "(itype 16/17/18 = call variants, 56-111 = jumps)\n\n"; + + auto itypes = session.query( + "SELECT itype, mnemonic, COUNT(*) as count " + "FROM instructions " + "WHERE itype IN (16, 17, 18, 56, 57, 58, 59, 60) " // calls and common jumps + "GROUP BY itype " + "ORDER BY count DESC" + ); + + std::cout << std::setw(8) << "itype" << std::setw(12) << "Mnemonic" << "Count\n"; + std::cout << std::string(30, '-') << "\n"; + for (const auto& row : itypes) { + std::cout << std::setw(8) << row[0] << std::setw(12) << row[1] << row[2] << "\n"; + } + + // ========================================================================= + // Instructions in a specific function + // ========================================================================= + + std::cout << "\n=== Instructions in Largest Function ===\n"; + + auto func_insns = session.query( + "SELECT mnemonic, COUNT(*) as count " + "FROM instructions " + "WHERE func_addr = (SELECT address FROM funcs ORDER BY size DESC LIMIT 1) " + "GROUP BY mnemonic " + "ORDER BY count DESC " + "LIMIT 10" + ); + + for (const auto& row : func_insns) { + std::cout << std::setw(12) << row[0] << " - " << row[1] << "\n"; + } + + std::cout << "\nDone.\n"; + return 0; +} + +``` + +`examples/example_jump_entities.cpp`: + +```cpp +/** + * example_jump_entities.cpp - "Jump to Anything" with table-valued function + * + * Demonstrates the jump_entities virtual table for unified entity search. + * Unlike jump_search() which returns JSON, jump_entities returns proper + * table rows that can be composed with full SQL. + * + * Demonstrates: + * - Basic entity search with prefix/contains modes + * - Filtering by entity kind (function, struct, member, etc.) + * - JOINs with other tables for enriched data + * - Pagination for virtual scrolling + * - Aggregations and grouping + */ + +#include +#include +#include + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " [search_prefix]\n"; + return 1; + } + + idasql::Session session; + if (!session.open(argv[1])) { + std::cerr << "Error: " << session.error() << "\n"; + return 1; + } + + std::string prefix = (argc >= 3) ? argv[2] : "sub"; + + // ========================================================================= + // Basic Usage - Function Call Syntax + // ========================================================================= + + std::cout << "=== Basic Search: '" << prefix << "' (prefix mode) ===\n\n"; + + auto result = session.query( + "SELECT name, kind, address, ordinal, parent_name, full_name " + "FROM jump_entities('" + prefix + "', 'prefix') " + "LIMIT 10" + ); + + std::cout << std::left + << std::setw(30) << "Name" + << std::setw(12) << "Kind" + << std::setw(12) << "Address" + << "Full Name\n"; + std::cout << std::string(70, '-') << "\n"; + + for (const auto& row : result) { + std::string addr = row[2].empty() ? "-" : row[2]; + std::cout << std::setw(30) << row[0] + << std::setw(12) << row[1] + << std::setw(12) << addr + << row[5] << "\n"; + } + + // ========================================================================= + // Contains Mode - Search Anywhere in Name + // ========================================================================= + + std::cout << "\n=== Contains Mode: 'main' ===\n\n"; + + auto contains = session.query( + "SELECT name, kind, full_name " + "FROM jump_entities('main', 'contains') " + "LIMIT 10" + ); + + for (const auto& row : contains) { + std::cout << std::setw(40) << row[0] + << std::setw(12) << row[1] + << row[2] << "\n"; + } + + // ========================================================================= + // Filter by Kind - Only Functions + // ========================================================================= + + std::cout << "\n=== Functions Only ===\n\n"; + + auto funcs_only = session.query( + "SELECT name, address " + "FROM jump_entities('" + prefix + "', 'prefix') " + "WHERE kind = 'function' " + "LIMIT 10" + ); + + for (const auto& row : funcs_only) { + std::cout << std::setw(30) << row[0] << " @ " << row[1] << "\n"; + } + + // ========================================================================= + // Filter by Kind - Only Types (Structs, Unions, Enums) + // ========================================================================= + + std::cout << "\n=== Types Only (struct/union/enum starting with '_') ===\n\n"; + + auto types = session.query( + "SELECT name, kind, ordinal " + "FROM jump_entities('_', 'prefix') " + "WHERE kind IN ('struct', 'union', 'enum') " + "LIMIT 10" + ); + + for (const auto& row : types) { + std::cout << std::setw(35) << row[0] + << std::setw(10) << row[1] + << "ordinal: " << row[2] << "\n"; + } + + // ========================================================================= + // Members with Parent Info + // ========================================================================= + + std::cout << "\n=== Members (showing parent.member) ===\n\n"; + + auto members = session.query( + "SELECT name, parent_name, full_name " + "FROM jump_entities('e', 'prefix') " + "WHERE kind IN ('member', 'enum_member') " + "LIMIT 10" + ); + + for (const auto& row : members) { + std::cout << std::setw(25) << row[0] + << " in " << std::setw(25) << row[1] + << " (" << row[2] << ")\n"; + } + + // ========================================================================= + // JOIN with funcs Table - Get Function Sizes + // ========================================================================= + + std::cout << "\n=== JOIN with funcs Table (function sizes) ===\n\n"; + + auto with_size = session.query( + "SELECT j.name, f.size, f.address " + "FROM jump_entities('" + prefix + "', 'prefix') j " + "LEFT JOIN funcs f ON j.address = f.address " + "WHERE j.kind = 'function' " + "ORDER BY f.size DESC " + "LIMIT 10" + ); + + std::cout << std::setw(30) << "Function" + << std::setw(10) << "Size" + << "Address\n"; + std::cout << std::string(55, '-') << "\n"; + + for (const auto& row : with_size) { + std::cout << std::setw(30) << row[0] + << std::setw(10) << row[1] + << row[2] << "\n"; + } + + // ========================================================================= + // Aggregation - Count by Kind + // ========================================================================= + + std::cout << "\n=== Entity Count by Kind (prefix '" << prefix << "') ===\n\n"; + + auto by_kind = session.query( + "SELECT kind, COUNT(*) as count " + "FROM jump_entities('" + prefix + "', 'prefix') " + "GROUP BY kind " + "ORDER BY count DESC" + ); + + for (const auto& row : by_kind) { + std::cout << std::setw(15) << row[0] << ": " << row[1] << "\n"; + } + + // ========================================================================= + // Pagination Demo + // ========================================================================= + + std::cout << "\n=== Pagination Demo ===\n\n"; + + // Count total + auto total = session.query( + "SELECT COUNT(*) FROM jump_entities('" + prefix + "', 'prefix')" + ); + std::cout << "Total matches: " << total.scalar() << "\n\n"; + + // Page 1 + std::cout << "Page 1 (items 1-3):\n"; + auto page1 = session.query( + "SELECT name, kind FROM jump_entities('" + prefix + "', 'prefix') LIMIT 3 OFFSET 0" + ); + for (const auto& row : page1) { + std::cout << " " << row[0] << " (" << row[1] << ")\n"; + } + + // Page 2 + std::cout << "\nPage 2 (items 4-6):\n"; + auto page2 = session.query( + "SELECT name, kind FROM jump_entities('" + prefix + "', 'prefix') LIMIT 3 OFFSET 3" + ); + for (const auto& row : page2) { + std::cout << " " << row[0] << " (" << row[1] << ")\n"; + } + + // ========================================================================= + // Case-Insensitive Search Demo + // ========================================================================= + + std::cout << "\n=== Case-Insensitive Search ===\n\n"; + + auto upper = session.query( + "SELECT COUNT(*) FROM jump_entities('SUB', 'prefix')" + ); + auto lower = session.query( + "SELECT COUNT(*) FROM jump_entities('sub', 'prefix')" + ); + + std::cout << "Search 'SUB': " << upper.scalar() << " results\n"; + std::cout << "Search 'sub': " << lower.scalar() << " results\n"; + std::cout << "(Both should match the same entities)\n"; + + // ========================================================================= + // Complex Query - Subquery + // ========================================================================= + + std::cout << "\n=== Complex Query - Find Types with Many Members ===\n\n"; + + auto complex = session.query( + "SELECT parent_name, COUNT(*) as member_count " + "FROM jump_entities('', 'contains') " + "WHERE kind = 'member' AND parent_name IS NOT NULL " + "GROUP BY parent_name " + "HAVING COUNT(*) > 2 " + "ORDER BY member_count DESC " + "LIMIT 5" + ); + + // Note: empty pattern returns no results, this is just to show the syntax + // In practice you'd use a real pattern + if (complex.row_count() > 0) { + for (const auto& row : complex) { + std::cout << std::setw(30) << row[0] << ": " << row[1] << " members\n"; + } + } else { + std::cout << "(No results - empty pattern returns no results)\n"; + std::cout << "Try with a real pattern like: SELECT parent_name, COUNT(*) ...\n"; + std::cout << " FROM jump_entities('e', 'prefix') WHERE kind = 'member' ...\n"; + } + + std::cout << "\nDone.\n"; + return 0; +} + +``` + +`examples/example_jump_search.cpp`: + +```cpp +/** + * example_jump_search.cpp - "Jump to Anything" unified entity search + * + * Demonstrates: + * - Using jump_search() for unified entity search + * - Using jump_query() to get the generated SQL + * - Prefix search vs Contains search modes + * - Pagination for virtual scrolling + */ + +#include +#include +#include + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " [search_prefix]\n"; + return 1; + } + + idasql::Session session; + if (!session.open(argv[1])) { + std::cerr << "Error: " << session.error() << "\n"; + return 1; + } + + std::string prefix = (argc >= 3) ? argv[2] : "main"; + + // ========================================================================= + // Basic jump_search usage (returns JSON) + // ========================================================================= + + std::cout << "=== Jump Search: '" << prefix << "' (prefix mode) ===\n\n"; + + auto result = session.query( + "SELECT jump_search('" + prefix + "', 'prefix', 10, 0)" + ); + + if (result.row_count() > 0) { + std::cout << "JSON result:\n" << result.scalar() << "\n\n"; + } + + // ========================================================================= + // Execute the generated SQL directly for structured results + // ========================================================================= + + std::cout << "=== Structured Results (first 10) ===\n\n"; + + // Get the generated query + auto query_result = session.query( + "SELECT jump_query('" + prefix + "', 'prefix', 10, 0)" + ); + std::string sql = query_result.scalar(); + + // Execute it directly + auto entities = session.query(sql); + + std::cout << std::left + << std::setw(12) << "Kind" + << std::setw(35) << "Name" + << std::setw(18) << "Address" + << "Full Name\n"; + std::cout << std::string(80, '-') << "\n"; + + for (const auto& row : entities) { + std::string addr = row[2].empty() ? "-" : ("0x" + row[2]); + std::cout << std::setw(12) << row[1] // kind + << std::setw(35) << row[0] // name + << std::setw(18) << addr // address + << row[5] << "\n"; // full_name + } + + // ========================================================================= + // Pagination demo + // ========================================================================= + + std::cout << "\n=== Pagination: Page 1 vs Page 2 ===\n"; + + // Count total matches + auto count_sql = + "SELECT COUNT(*) FROM (" + + session.query("SELECT jump_query('" + prefix + "', 'prefix', 99999, 0)").scalar() + + ")"; + auto count_result = session.query(count_sql); + std::cout << "Total matches: " << count_result.scalar() << "\n\n"; + + // Page 1 + auto page1 = session.query( + "SELECT jump_search('" + prefix + "', 'prefix', 5, 0)" + ); + std::cout << "Page 1 (offset 0, limit 5):\n" << page1.scalar() << "\n\n"; + + // Page 2 + auto page2 = session.query( + "SELECT jump_search('" + prefix + "', 'prefix', 5, 5)" + ); + std::cout << "Page 2 (offset 5, limit 5):\n" << page2.scalar() << "\n\n"; + + // ========================================================================= + // Contains mode (searches anywhere in name) + // ========================================================================= + + std::cout << "=== Contains Mode vs Prefix Mode ===\n\n"; + + // Prefix mode - only matches at start + auto prefix_results = session.query( + "SELECT jump_search('" + prefix + "', 'prefix', 50, 0)" + ); + + // Contains mode - matches anywhere + auto contains_results = session.query( + "SELECT jump_search('" + prefix + "', 'contains', 50, 0)" + ); + + // Count occurrences in each result + auto count_json = [](const std::string& json) { + if (json == "[]") return 0; + int count = 0; + for (char c : json) if (c == '{') count++; + return count; + }; + + std::cout << "Prefix mode matches: " << count_json(prefix_results.scalar()) << "\n"; + std::cout << "Contains mode matches: " << count_json(contains_results.scalar()) << "\n"; + + // ========================================================================= + // Search different entity types + // ========================================================================= + + std::cout << "\n=== Search by Entity Type ===\n\n"; + + // Find some struct names + auto structs = session.query( + "SELECT name FROM types WHERE is_struct = 1 LIMIT 1" + ); + if (structs.row_count() > 0) { + std::string struct_prefix = structs.scalar().substr(0, 4); + auto struct_search = session.query( + "SELECT jump_search('" + struct_prefix + "', 'prefix', 10, 0)" + ); + std::cout << "Struct search ('" << struct_prefix << "'): " + << count_json(struct_search.scalar()) << " results\n"; + } + + // Find some enum names + auto enums = session.query( + "SELECT name FROM types WHERE is_enum = 1 LIMIT 1" + ); + if (enums.row_count() > 0) { + std::string enum_prefix = enums.scalar().substr(0, 4); + auto enum_search = session.query( + "SELECT jump_search('" + enum_prefix + "', 'prefix', 10, 0)" + ); + std::cout << "Enum search ('" << enum_prefix << "'): " + << count_json(enum_search.scalar()) << " results\n"; + } + + // ========================================================================= + // Show the generated SQL + // ========================================================================= + + std::cout << "\n=== Generated SQL Query ===\n\n"; + std::cout << sql << "\n"; + + std::cout << "\nDone.\n"; + return 0; +} + +``` + +`examples/example_plugin_style.cpp`: + +```cpp +/** + * example_plugin_style.cpp - IDASQL usage when IDA is already running + * + * Demonstrates the recommended patterns for: + * - IDA plugins + * - IDAPython scripts (via C++ extension) + * - Any code where IDA is already initialized + * + * Key insight: IDA is singleton, so you don't "open" a database - it's already open. + * Just create a QueryEngine or use the free functions. + * + * NOTE: This example uses Session to simulate IDA being open for standalone testing. + * In a real plugin, you'd skip that and just use QueryEngine/free functions. + */ + +#include +#include + +// Simulates what your plugin code would look like +void plugin_main() { + // ========================================================================= + // OPTION 1: Free functions (simplest - recommended for one-off queries) + // ========================================================================= + + std::cout << "=== Using Free Functions ===\n"; + + // Quick one-liner queries + auto funcs = idasql::query("SELECT name, size FROM funcs ORDER BY size DESC LIMIT 5"); + std::cout << "Largest functions:\n"; + for (const auto& row : funcs) { + std::cout << " " << row[0] << " (" << row[1] << " bytes)\n"; + } + + // Scalar for single values + std::string count = idasql::scalar("SELECT COUNT(*) FROM funcs"); + std::cout << "\nTotal functions: " << count << "\n"; + + // Execute without results (for comments_live UPDATE, etc.) + // idasql::execute("UPDATE comments_live SET comment = 'test' WHERE address = 0x401000"); + + // ========================================================================= + // OPTION 2: QueryEngine instance (for multiple related queries) + // ========================================================================= + + std::cout << "\n=== Using QueryEngine Instance ===\n"; + + idasql::QueryEngine qe; + + // Multiple queries sharing same engine + auto imports = qe.query("SELECT module, COUNT(*) as cnt FROM imports GROUP BY module ORDER BY cnt DESC LIMIT 3"); + std::cout << "Top imported modules:\n"; + for (const auto& row : imports) { + std::cout << " " << row[0] << ": " << row[1] << " imports\n"; + } + + auto strings = qe.query("SELECT content FROM strings WHERE content LIKE '%error%' LIMIT 3"); + std::cout << "\nStrings containing 'error':\n"; + for (const auto& row : strings) { + std::cout << " \"" << row[0] << "\"\n"; + } + + // ========================================================================= + // OPTION 3: Callback-based streaming (for large result sets) + // ========================================================================= + + std::cout << "\n=== Using Callback for Streaming ===\n"; + + struct Counter { + int count = 0; + int total_size = 0; + } counter; + + qe.exec( + "SELECT size FROM funcs", + [](void* data, int argc, char** argv, char**) -> int { + auto* c = static_cast(data); + c->count++; + if (argv[0]) c->total_size += std::atoi(argv[0]); + return 0; // Continue + }, + &counter + ); + + std::cout << "Processed " << counter.count << " functions\n"; + std::cout << "Total code size: " << counter.total_size << " bytes\n"; + + // ========================================================================= + // Advanced: Raw SQLite handle access + // ========================================================================= + + std::cout << "\n=== Raw SQLite Handle ===\n"; + + sqlite3* db = qe.handle(); + std::cout << "SQLite handle: " << (db ? "valid" : "null") << "\n"; + + // You can use db with any SQLite C API directly + // sqlite3_prepare_v2, sqlite3_bind, etc. for prepared statements +} + +int main(int argc, char* argv[]) { + // For standalone testing, we need to open IDA first + // In a real plugin, IDA would already be open - skip this part + + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n\n"; + std::cerr << "NOTE: In a real IDA plugin, you wouldn't need to open anything.\n"; + std::cerr << " This example uses Session just to simulate IDA being open.\n"; + return 1; + } + + // Simulate IDA being open (in a plugin, this is already done) + idasql::Session session; + if (!session.open(argv[1])) { + std::cerr << "Error: " << session.error() << "\n"; + return 1; + } + + std::cout << "Database loaded. Simulating plugin environment...\n\n"; + + // This is what your actual plugin code would look like + plugin_main(); + + return 0; +} + +``` + +`examples/example_strings.cpp`: + +```cpp +/** + * example_strings.cpp - String analysis with IDASQL + * + * Demonstrates: + * - Querying the strings table + * - Pattern matching with LIKE + * - Finding xrefs to strings + * - String statistics + */ + +#include +#include +#include + +int main(int argc, char* argv[]) { + if (argc < 2) { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } + + idasql::Session session; + if (!session.open(argv[1])) { + std::cerr << "Error: " << session.error() << "\n"; + return 1; + } + + // ========================================================================= + // String statistics + // ========================================================================= + + std::cout << "=== String Statistics ===\n"; + + std::cout << "Total strings: " << session.scalar("SELECT COUNT(*) FROM strings") << "\n"; + std::cout << "ASCII strings: " << session.scalar("SELECT COUNT(*) FROM strings WHERE type = 0") << "\n"; + std::cout << "Unicode strings: " << session.scalar("SELECT COUNT(*) FROM strings WHERE type = 1") << "\n"; + + auto avg_len = session.scalar("SELECT AVG(length) FROM strings"); + std::cout << "Average length: " << avg_len << " chars\n"; + + // ========================================================================= + // Longest strings + // ========================================================================= + + std::cout << "\n=== Top 10 Longest Strings ===\n"; + + auto longest = session.query( + "SELECT printf('0x%X', address) as addr, length, " + " SUBSTR(content, 1, 60) as preview " + "FROM strings " + "ORDER BY length DESC " + "LIMIT 10" + ); + + for (const auto& row : longest) { + std::cout << row[0] << " [" << row[1] << "] \"" << row[2]; + if (std::stoi(row[1]) > 60) std::cout << "..."; + std::cout << "\"\n"; + } + + // ========================================================================= + // Search for interesting strings + // ========================================================================= + + std::cout << "\n=== Error/Warning Strings ===\n"; + + auto errors = session.query( + "SELECT printf('0x%X', address) as addr, content " + "FROM strings " + "WHERE content LIKE '%error%' " + " OR content LIKE '%fail%' " + " OR content LIKE '%warning%' " + " OR content LIKE '%exception%' " + "LIMIT 15" + ); + + for (const auto& row : errors) { + std::cout << row[0] << ": \"" << row[1] << "\"\n"; + } + + // ========================================================================= + // URL/Path strings + // ========================================================================= + + std::cout << "\n=== URL/Path Strings ===\n"; + + auto urls = session.query( + "SELECT printf('0x%X', address) as addr, content " + "FROM strings " + "WHERE content LIKE 'http%' " + " OR content LIKE 'https%' " + " OR content LIKE '%.exe%' " + " OR content LIKE '%.dll%' " + " OR content LIKE 'C:\\\\%' " + "LIMIT 15" + ); + + for (const auto& row : urls) { + std::cout << row[0] << ": \"" << row[1] << "\"\n"; + } + + // ========================================================================= + // Strings with most xrefs (most used) + // ========================================================================= + + std::cout << "\n=== Most Referenced Strings (Top 10) ===\n"; + + auto most_used = session.query( + "SELECT s.content, COUNT(x.from_ea) as refs " + "FROM strings s " + "LEFT JOIN xrefs x ON s.address = x.to_ea " + "GROUP BY s.address " + "HAVING refs > 0 " + "ORDER BY refs DESC " + "LIMIT 10" + ); + + for (const auto& row : most_used) { + std::cout << std::setw(5) << row[1] << " refs: \"" + << row[0].substr(0, 50); + if (row[0].length() > 50) std::cout << "..."; + std::cout << "\"\n"; + } + + // ========================================================================= + // Strings by function + // ========================================================================= + + std::cout << "\n=== Functions Using Most Strings (Top 10) ===\n"; + + auto by_func = session.query( + "SELECT func_at(x.from_ea) as func_name, COUNT(DISTINCT s.address) as str_count " + "FROM strings s " + "JOIN xrefs x ON s.address = x.to_ea " + "WHERE func_at(x.from_ea) IS NOT NULL " + "GROUP BY func_at(x.from_ea) " + "ORDER BY str_count DESC " + "LIMIT 10" + ); + + for (const auto& row : by_func) { + std::cout << std::setw(40) << row[0] << " - " << row[1] << " strings\n"; + } + + // ========================================================================= + // Format strings (potential printf-like usage) + // ========================================================================= + + std::cout << "\n=== Format Strings (contain %s, %d, etc.) ===\n"; + + auto formats = session.query( + "SELECT printf('0x%X', address) as addr, content " + "FROM strings " + "WHERE content LIKE '%\\%s%' ESCAPE '\\' " + " OR content LIKE '%\\%d%' ESCAPE '\\' " + " OR content LIKE '%\\%x%' ESCAPE '\\' " + " OR content LIKE '%\\%p%' ESCAPE '\\' " + "LIMIT 10" + ); + + for (const auto& row : formats) { + std::cout << row[0] << ": \"" << row[1] << "\"\n"; + } + + std::cout << "\nDone.\n"; + return 0; +} + +``` + +`ida-plugin.json`: + +```json +{ + "IDAMetadataDescriptorVersion": 1, + "plugin": { + "name": "IDASQL", + "version": "0.0.8", + "entryPoint": "idasql", + "description": "SQL interface for IDA databases. Query functions, xrefs, strings, types, and more using SQL. Supports local CLI, HTTP REST server, and optional AI agent integration.", + "urls": { + "repository": "https://github.com/allthingsida/idasql" + }, + "authors": [ + { + "name": "Elias Bachaalany", + "email": "elias.bachaalany@gmail.com" + } + ], + "categories": [ + "api-scripting-and-automation", + "collaboration-and-productivity" + ], + "idaVersions": ">=9.0", + "platforms": [ + "windows-x86_64", + "linux-x86_64", + "macos-aarch64" + ], + "license": "MIT" + } +} + +``` + +`prompts/idasql_agent.md`: + +```md +# IDASQL Agent Guide + +A comprehensive reference for AI agents to effectively use IDASQL - an SQL interface for reverse engineering binary analysis with IDA Pro. + +--- + +## What is IDA and Why SQL? + +**IDA Pro** is the industry-standard disassembler and reverse engineering tool. It analyzes compiled binaries (executables, DLLs, firmware) and produces: +- **Disassembly** - Human-readable assembly code +- **Functions** - Detected code boundaries with names +- **Cross-references** - Who calls what, who references what data +- **Types** - Structures, enums, function prototypes +- **Decompilation** - C-like pseudocode (with Hex-Rays plugin) + +**IDASQL** exposes all this analysis data through SQL virtual tables, enabling: +- Complex queries across multiple data types (JOINs) +- Aggregations and statistics (COUNT, GROUP BY) +- Pattern detection across the entire binary +- Scriptable analysis without writing IDA plugins or IDAPython scripts + +--- + +## Core Concepts for Binary Analysis + +### Addresses (ea_t) +Everything in a binary has an **address** - a memory location where code or data lives. IDA uses `ea_t` (effective address) as unsigned 64-bit integers. SQL shows these as integers; use `printf('0x%X', address)` for hex display. + +### Functions +IDA groups code into **functions** with: +- `address` / `start_ea` - Where the function begins +- `end_ea` - Where it ends +- `name` - Assigned or auto-generated name (e.g., `main`, `sub_401000`) +- `size` - Total bytes in the function + +### Cross-References (xrefs) +Binary analysis is about understanding **relationships**: +- **Code xrefs** - Function calls, jumps between code +- **Data xrefs** - Code reading/writing data locations +- `from_ea` → `to_ea` represents "address X references address Y" + +### Segments +Memory is divided into **segments** with different purposes: +- `.text` - Executable code (typically) +- `.data` - Initialized global data +- `.rdata` - Read-only data (strings, constants) +- `.bss` - Uninitialized data + +Of course, segment names and types can vary. You may query the `segments` table to understand memory layout. + +### Basic Blocks +Within a function, **basic blocks** are straight-line code sequences: +- No branches in the middle +- Single entry, single exit +- Useful for control flow analysis + +### Decompilation (Hex-Rays) +The **Hex-Rays decompiler** converts assembly to C-like **pseudocode**: +- **ctree** - The Abstract Syntax Tree of decompiled code +- **lvars** - Local variables detected by the decompiler +- Much easier to analyze than raw assembly + +--- + +## Command-Line Interface + +IDASQL provides SQL access to IDA databases via command line or as a server. + +### Invocation Modes + +**1. Single Query (Local)** +```bash +idasql -s database.i64 -q "SELECT * FROM funcs LIMIT 10" +idasql -s database.i64 -c "SELECT COUNT(*) FROM funcs" # -c is alias for -q +``` + +**2. SQL File Execution** +```bash +idasql -s database.i64 -f analysis.sql +``` + +**3. Interactive REPL** +```bash +idasql -s database.i64 -i +``` + +**4. Remote Mode** (connect to running server) +```bash +idasql --remote localhost:8080 -q "SELECT * FROM funcs" +idasql --remote localhost:8080 -i # Remote interactive +``` + +**5. HTTP Server Mode** +```bash +idasql -s database.i64 --http 8080 +# Then query via: curl -X POST http://localhost:8080/query -d "SELECT * FROM funcs" +``` + +**6. Export Mode** +```bash +idasql -s database.i64 --export dump.sql +idasql -s database.i64 --export dump.sql --export-tables=funcs,segments +``` + +### CLI Options + +| Option | Description | +|--------|-------------| +| `-s ` | IDA database file (.idb/.i64) | +| `--remote ` | Connect to IDASQL server | +| `--token ` | Auth token for remote/server mode | +| `-q ` | Execute single SQL query | +| `-c ` | Alias for -q (Python-style) | +| `-f ` | Execute SQL from file | +| `-i` | Interactive REPL mode | +| `-w, --write` | Save database changes on exit | +| `--export ` | Export tables to SQL file | +| `--export-tables=X` | Tables to export: `*` (all) or `table1,table2,...` | +| `--http [port]` | Start HTTP REST server (default: 8080) | +| `--bind ` | Bind address for server (default: 127.0.0.1) | +| `-h, --help` | Show help | + +### REPL Commands + +| Command | Description | +|---------|-------------| +| `.tables` | List all virtual tables | +| `.schema [table]` | Show table schema | +| `.info` | Show database metadata | +| `.clear` | Clear session | +| `.quit` / `.exit` | Exit REPL | +| `.help` | Show available commands | +| `.http start` | Start HTTP server on random port | +| `.http stop` | Stop HTTP server | +| `.http status` | Show HTTP server status | +| `.agent` | Start AI agent mode | + +### Performance Strategy + +**Single queries:** Use `-q` directly. +```bash +idasql -s database.i64 -q "SELECT COUNT(*) FROM funcs" +``` + +**Multiple queries / exploration:** Start a server once, then query as a client. + +Opening an IDA database has startup overhead (idalib initialization, auto-analysis). If you plan to run many queries—exploring the database, experimenting with different queries, or iterating on analysis—avoid re-opening the database each time. + +**Recommended workflow for iterative analysis:** +```bash +# Terminal 1: Start server (opens database once) +idasql -s database.i64 --http 8080 + +# Terminal 2: Query repeatedly via remote client (instant responses) +idasql --remote localhost:8080 -q "SELECT * FROM funcs LIMIT 5" +idasql --remote localhost:8080 -q "SELECT * FROM strings WHERE content LIKE '%error%'" +idasql --remote localhost:8080 -q "SELECT name, size FROM funcs ORDER BY size DESC" +# ... as many queries as needed, no startup cost +``` + +Or use interactive mode on the remote connection: +```bash +idasql --remote localhost:8080 -i +idasql> SELECT COUNT(*) FROM funcs; +idasql> SELECT * FROM xrefs WHERE to_ea = 0x401000; +idasql> .quit +``` + +This approach is significantly faster for iterative analysis since the database remains open and queries go directly through the already-initialized session. + +--- + +## Tables Reference + +### Debugger Tables (Full CRUD) + +#### breakpoints +Debugger breakpoints. Supports full CRUD (SELECT, INSERT, UPDATE, DELETE). Breakpoints persist in the IDB even without an active debugger session. + +| Column | Type | RW | Description | +|--------|------|----|-------------| +| `address` | INT | R | Breakpoint address | +| `enabled` | INT | RW | 1=enabled, 0=disabled | +| `type` | INT | RW | Breakpoint type (0=software, 1=hw_write, 2=hw_read, 3=hw_rdwr, 4=hw_exec) | +| `type_name` | TEXT | R | Type name (software, hardware_write, etc.) | +| `size` | INT | RW | Breakpoint size (for hardware breakpoints) | +| `flags` | INT | RW | Breakpoint flags | +| `pass_count` | INT | RW | Pass count before trigger | +| `condition` | TEXT | RW | Condition expression | +| `loc_type` | INT | R | Location type code | +| `loc_type_name` | TEXT | R | Location type (absolute, relative, symbolic, source) | +| `module` | TEXT | R | Module path (relative breakpoints) | +| `symbol` | TEXT | R | Symbol name (symbolic breakpoints) | +| `offset` | INT | R | Offset (relative/symbolic) | +| `source_file` | TEXT | R | Source file (source breakpoints) | +| `source_line` | INT | R | Source line number | +| `is_hardware` | INT | R | 1=hardware breakpoint | +| `is_active` | INT | R | 1=currently active | +| `group` | TEXT | RW | Breakpoint group name | +| `bptid` | INT | R | Breakpoint ID | + +```sql +-- List all breakpoints +SELECT printf('0x%08X', address) as addr, type_name, enabled, condition +FROM breakpoints; + +-- Add software breakpoint +INSERT INTO breakpoints (address) VALUES (0x401000); + +-- Add hardware write watchpoint +INSERT INTO breakpoints (address, type, size) VALUES (0x402000, 1, 4); + +-- Add conditional breakpoint +INSERT INTO breakpoints (address, condition) VALUES (0x401000, 'eax == 0'); + +-- Disable a breakpoint +UPDATE breakpoints SET enabled = 0 WHERE address = 0x401000; + +-- Delete a breakpoint +DELETE FROM breakpoints WHERE address = 0x401000; + +-- Find which functions have breakpoints +SELECT b.address, f.name, b.type_name, b.enabled +FROM breakpoints b +JOIN funcs f ON b.address >= f.address AND b.address < f.end_ea; +``` + +### Entity Tables + +#### funcs +All detected functions in the binary with prototype information. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Function start address | +| `name` | TEXT | Function name | +| `size` | INT | Function size in bytes | +| `end_ea` | INT | Function end address | +| `flags` | INT | Function flags | + +**Prototype columns** (populated when type info available): + +| Column | Type | Description | +|--------|------|-------------| +| `return_type` | TEXT | Return type string (e.g., "int", "void *") | +| `return_is_ptr` | INT | 1 if return type is pointer | +| `return_is_int` | INT | 1 if return type is exactly int | +| `return_is_integral` | INT | 1 if return type is int-like (int, long, DWORD, BOOL) | +| `return_is_void` | INT | 1 if return type is void | +| `arg_count` | INT | Number of function arguments | +| `calling_conv` | TEXT | Calling convention (cdecl, stdcall, fastcall, etc.) | + +```sql +-- 10 largest functions +SELECT name, size FROM funcs ORDER BY size DESC LIMIT 10; + +-- Functions starting with "sub_" (auto-named, not analyzed) +SELECT name, printf('0x%X', address) as addr FROM funcs WHERE name LIKE 'sub_%'; + +-- Functions returning integers with 3+ arguments +SELECT name, return_type, arg_count FROM funcs +WHERE return_is_integral = 1 AND arg_count >= 3; + +-- Void functions (side effects, callbacks) +SELECT name, arg_count FROM funcs WHERE return_is_void = 1; + +-- Pointer-returning functions (factories, allocators) +SELECT name, return_type FROM funcs WHERE return_is_ptr = 1; + +-- Simple getter functions (no args, returns value) +SELECT name, return_type FROM funcs +WHERE arg_count = 0 AND return_is_void = 0; + +-- Functions by calling convention +SELECT calling_conv, COUNT(*) as count FROM funcs +WHERE calling_conv IS NOT NULL AND calling_conv != '' +GROUP BY calling_conv ORDER BY count DESC; +``` + +#### segments +Memory segments. Supports UPDATE (`name`, `class`, `perm`) and DELETE. + +| Column | Type | RW | Description | +|--------|------|----|-------------| +| `start_ea` | INT | R | Segment start | +| `end_ea` | INT | R | Segment end | +| `name` | TEXT | RW | Segment name (.text, .data, etc.) | +| `class` | TEXT | RW | Segment class (CODE, DATA) | +| `perm` | INT | RW | Permissions (R=4, W=2, X=1) | + +```sql +-- Find executable segments +SELECT name, printf('0x%X', start_ea) as start FROM segments WHERE perm & 1 = 1; + +-- Rename a segment +UPDATE segments SET name = '.mytext' WHERE start_ea = 0x401000; + +-- Change segment permissions to read+exec +UPDATE segments SET perm = 5 WHERE name = '.text'; + +-- Delete a segment +DELETE FROM segments WHERE name = '.rdata'; +``` + +#### names +All named locations (functions, labels, data). + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Address | +| `name` | TEXT | Name | + +#### entries +Entry points (exports, program entry). + +| Column | Type | Description | +|--------|------|-------------| +| `ordinal` | INT | Export ordinal | +| `address` | INT | Entry address | +| `name` | TEXT | Entry name | + +#### imports +Imported functions from external libraries. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Import address (IAT entry) | +| `name` | TEXT | Import name | +| `module` | TEXT | Module/DLL name | +| `ordinal` | INT | Import ordinal | + +```sql +-- Imports from kernel32.dll +SELECT name FROM imports WHERE module LIKE '%kernel32%'; +``` + +#### strings +String literals found in the binary. IDA maintains a cached string list that can be configured. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | String address | +| `length` | INT | String length | +| `type` | INT | String type (raw encoding bits) | +| `type_name` | TEXT | Type name: ascii, utf16, utf32 | +| `width` | INT | Char width (0=1-byte, 1=2-byte, 2=4-byte) | +| `width_name` | TEXT | Width name: 1-byte, 2-byte, 4-byte | +| `layout` | INT | String layout (0=null-terminated, 1-3=pascal) | +| `layout_name` | TEXT | Layout name: termchr, pascal1, pascal2, pascal4 | +| `encoding` | INT | Encoding index (0=default) | +| `content` | TEXT | String content | + +**String Type Encoding:** +IDA stores string type as a 32-bit value: +- Bits 0-1: Width (0=1B/ASCII, 1=2B/UTF-16, 2=4B/UTF-32) +- Bits 2-7: Layout (0=TERMCHR, 1=PASCAL1, 2=PASCAL2, 3=PASCAL4) +- Bits 8-15: term1 (first termination character) +- Bits 16-23: term2 (second termination character) +- Bits 24-31: encoding index + +```sql +-- Find error messages +SELECT content, printf('0x%X', address) as addr FROM strings WHERE content LIKE '%error%'; + +-- ASCII strings only +SELECT * FROM strings WHERE type_name = 'ascii'; + +-- UTF-16 strings (common in Windows) +SELECT * FROM strings WHERE type_name = 'utf16'; + +-- Count strings by type +SELECT type_name, layout_name, COUNT(*) as count +FROM strings GROUP BY type_name, layout_name ORDER BY count DESC; +``` + +**Important:** For new analysis (exe/dll), strings are auto-built. For existing databases (i64/idb), strings are already saved. If you see 0 strings unexpectedly, run `SELECT rebuild_strings()` once to rebuild the list. See String List Functions section below. + +#### xrefs +Cross-references - the most important table for understanding code relationships. + +| Column | Type | Description | +|--------|------|-------------| +| `from_ea` | INT | Source address (who references) | +| `to_ea` | INT | Target address (what is referenced) | +| `type` | INT | Xref type code | +| `is_code` | INT | 1=code xref (call/jump), 0=data xref | + +```sql +-- Who calls function at 0x401000? +SELECT printf('0x%X', from_ea) as caller FROM xrefs WHERE to_ea = 0x401000 AND is_code = 1; + +-- What does function at 0x401000 reference? +SELECT printf('0x%X', to_ea) as target FROM xrefs WHERE from_ea >= 0x401000 AND from_ea < 0x401100; +``` + +#### blocks +Basic blocks within functions. **Use `func_ea` constraint for performance.** + +| Column | Type | Description | +|--------|------|-------------| +| `func_ea` | INT | Containing function | +| `start_ea` | INT | Block start | +| `end_ea` | INT | Block end | +| `size` | INT | Block size | + +```sql +-- Blocks in a specific function (FAST - uses constraint pushdown) +SELECT * FROM blocks WHERE func_ea = 0x401000; + +-- Functions with most basic blocks +SELECT func_at(func_ea) as name, COUNT(*) as blocks +FROM blocks GROUP BY func_ea ORDER BY blocks DESC LIMIT 10; +``` + +### Convenience Views + +Pre-built views for common xref analysis patterns. These simplify caller/callee queries. + +#### callers +Who calls each function. Use this instead of manual xref JOINs. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Target function address | +| `caller_addr` | INT | Xref source address | +| `caller_name` | TEXT | Calling function name | +| `caller_func_addr` | INT | Calling function start | + +```sql +-- Who calls function at 0x401000? +SELECT caller_name, printf('0x%X', caller_addr) as from_addr +FROM callers WHERE func_addr = 0x401000; + +-- Most called functions +SELECT printf('0x%X', func_addr) as addr, COUNT(*) as callers +FROM callers GROUP BY func_addr ORDER BY callers DESC LIMIT 10; +``` + +#### callees +What each function calls. Inverse of callers view. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Calling function address | +| `func_name` | TEXT | Calling function name | +| `callee_addr` | INT | Called address | +| `callee_name` | TEXT | Called function/symbol name | + +```sql +-- What does main call? +SELECT callee_name, printf('0x%X', callee_addr) as addr +FROM callees WHERE func_name LIKE '%main%'; + +-- Functions making most calls +SELECT func_name, COUNT(*) as call_count +FROM callees GROUP BY func_addr ORDER BY call_count DESC LIMIT 10; +``` + +#### string_refs +Which functions reference which strings. Great for finding functions by string content. + +| Column | Type | Description | +|--------|------|-------------| +| `string_addr` | INT | String address | +| `string_value` | TEXT | String content | +| `string_length` | INT | String length | +| `ref_addr` | INT | Reference address | +| `func_addr` | INT | Referencing function | +| `func_name` | TEXT | Function name | + +```sql +-- Find functions using error strings +SELECT func_name, string_value +FROM string_refs +WHERE string_value LIKE '%error%' OR string_value LIKE '%fail%'; + +-- Functions with most string references +SELECT func_name, COUNT(*) as string_count +FROM string_refs WHERE func_name IS NOT NULL +GROUP BY func_addr ORDER BY string_count DESC LIMIT 10; +``` + +### Instruction Tables + +#### instructions +Decoded instructions. Supports DELETE (converts instruction to unexplored bytes). **Always filter by `func_addr` for performance.** + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Instruction address | +| `func_addr` | INT | Containing function | +| `itype` | INT | Instruction type (architecture-specific) | +| `mnemonic` | TEXT | Instruction mnemonic | +| `size` | INT | Instruction size | +| `operand0` | TEXT | First operand | +| `operand1` | TEXT | Second operand | +| `disasm` | TEXT | Full disassembly line | + +```sql +-- Instruction profile of a function (FAST) +SELECT mnemonic, COUNT(*) as count +FROM instructions WHERE func_addr = 0x401330 +GROUP BY mnemonic ORDER BY count DESC; + +-- Find all call instructions in a function +SELECT address, disasm FROM instructions +WHERE func_addr = 0x401000 AND mnemonic = 'call'; + +-- Delete an instruction (convert to unexplored bytes) +DELETE FROM instructions WHERE address = 0x401000; +``` + +**Performance:** `WHERE func_addr = X` uses O(function_size) iteration. Without this constraint, it scans the entire database - SLOW. + +#### disasm_calls +All call instructions with resolved targets. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Function containing the call | +| `ea` | INT | Call instruction address | +| `callee_addr` | INT | Target address (0 if unknown) | +| `callee_name` | TEXT | Target name | + +```sql +-- Functions that call malloc +SELECT DISTINCT func_at(func_addr) as caller +FROM disasm_calls WHERE callee_name LIKE '%malloc%'; +``` + +### Database Modification + +The following tables support modification: + +| Table | INSERT | UPDATE columns | DELETE | +|-------|--------|---------------|--------| +| `breakpoints` | Yes | `enabled`, `type`, `size`, `flags`, `pass_count`, `condition`, `group` | Yes | +| `funcs` | Yes | `name`, `flags` | Yes | +| `names` | Yes | `name` | Yes | +| `comments` | Yes | `comment`, `rep_comment` | Yes | +| `bookmarks` | Yes | `description` | Yes | +| `segments` | — | `name`, `class`, `perm` | Yes | +| `instructions` | — | — | Yes | +| `types` | Yes | Yes | Yes | +| `types_members` | Yes | Yes | Yes | +| `types_enum_values` | Yes | Yes | Yes | +| `ctree_lvars` | — | `name`, `type` | — | + +**INSERT examples:** +```sql +-- Create a function (IDA auto-detects boundaries) +INSERT INTO funcs (address) VALUES (0x401000); + +-- Create a function with name and explicit end +INSERT INTO funcs (address, name, end_ea) VALUES (0x401000, 'my_func', 0x401050); + +-- Set a name at an address +INSERT INTO names (address, name) VALUES (0x401000, 'main'); + +-- Add a comment +INSERT INTO comments (address, comment) VALUES (0x401050, 'Check return value'); + +-- Add a repeatable comment +INSERT INTO comments (address, rpt_comment) VALUES (0x404000, 'Global config'); + +-- Add a bookmark (auto-assigned slot) +INSERT INTO bookmarks (address, description) VALUES (0x401000, 'interesting'); + +-- Add a bookmark at specific slot +INSERT INTO bookmarks (slot, address, description) VALUES (5, 0x401000, 'slot 5'); +``` + +**UPDATE examples:** +```sql +-- Rename a function +UPDATE funcs SET name = 'my_main' WHERE address = 0x401000; + +-- Rename any named address +UPDATE names SET name = 'my_global' WHERE address = 0x404000; + +-- Add/update comment +UPDATE comments SET comment = 'Check return value' WHERE address = 0x401050; + +-- Add repeatable comment +UPDATE comments SET rep_comment = 'Global config' WHERE address = 0x404000; + +-- Delete a name +DELETE FROM names WHERE address = 0x401000; +``` + +**Segments:** +```sql +-- Rename a segment +UPDATE segments SET name = '.mytext' WHERE start_ea = 0x401000; + +-- Change segment class +UPDATE segments SET class = 'DATA' WHERE name = '.rdata'; + +-- Change permissions (R=4, W=2, X=1) +UPDATE segments SET perm = 5 WHERE name = '.text'; + +-- Delete a segment +DELETE FROM segments WHERE name = '.rdata'; +``` + +**Instructions:** +```sql +-- Delete an instruction (convert to unexplored bytes) +DELETE FROM instructions WHERE address = 0x401000; +``` + +**Types:** +```sql +-- Create a new struct +INSERT INTO types (name, kind) VALUES ('my_struct', 'struct'); + +-- Create an enum +INSERT INTO types (name, kind) VALUES ('my_flags', 'enum'); + +-- Create a union +INSERT INTO types (name, kind) VALUES ('my_union', 'union'); + +-- Add a struct member with type +INSERT INTO types_members (type_ordinal, member_name, member_type) VALUES (42, 'field1', 'int'); + +-- Add a struct member (name only, default type) +INSERT INTO types_members (type_ordinal, member_name) VALUES (42, 'field2'); + +-- Add an enum value +INSERT INTO types_enum_values (type_ordinal, value_name, value) VALUES (15, 'FLAG_ACTIVE', 1); + +-- Add an enum value with comment +INSERT INTO types_enum_values (type_ordinal, value_name, value, comment) +VALUES (15, 'FLAG_HIDDEN', 2, 'not visible in UI'); +``` + +**Decompiler local variables (requires Hex-Rays):** +```sql +-- Rename a local variable +UPDATE ctree_lvars SET name = 'buffer_size' +WHERE func_addr = 0x401000 AND name = 'v1'; + +-- Change variable type +UPDATE ctree_lvars SET type = 'char *' +WHERE func_addr = 0x401000 AND idx = 2; +``` + +### Persisting Changes + +Changes to the database (UPDATE, set_name, etc.) are held in memory by default. + +**To persist changes:** +```sql +-- Explicit save (recommended for scripts) +SELECT save_database(); -- Returns 1 on success, 0 on failure +``` + +**CLI flag for auto-save:** +```bash +# Auto-save on exit (use with caution) +idasql -s db.i64 -q "UPDATE funcs SET name='main' WHERE address=0x401000" -w +``` + +**Best practice for batch operations:** +```sql +-- Make multiple changes +UPDATE funcs SET name = 'init_config' WHERE address = 0x401000; +UPDATE names SET name = 'g_settings' WHERE address = 0x402000; +-- Persist once at the end +SELECT save_database(); +``` + +> Without `save_database()` or `-w`, changes are lost when the session ends. + +### Decompiler Tables (Hex-Rays Required) + +**CRITICAL:** Always filter by `func_addr`. Without constraint, these tables will decompile EVERY function - extremely slow! + +#### pseudocode +Structured line-by-line pseudocode with writable comments. **Use `decompile(addr)` to view pseudocode; use this table only for surgical edits (comments) or structured queries.** + +| Column | Type | Writable | Description | +|--------|------|----------|-------------| +| `func_addr` | INT | No | Function address | +| `line_num` | INT | No | Line number | +| `line` | TEXT | No | Pseudocode text | +| `ea` | INT | No | Corresponding assembly address (from COLOR_ADDR anchor) | +| `comment` | TEXT | **Yes** | Decompiler comment at this ea | +| `comment_placement` | TEXT | **Yes** | Comment placement: `semi` (inline, default), `block1` (above line) | + +**Comment placements:** `semi` (after `;`), `block1` (own line above), `block2`, `curly1`, `curly2`, `colon`, `case`, `else`, `do` + +```sql +-- VIEWING: Use decompile() function, NOT the pseudocode table +SELECT decompile(0x401000); + +-- COMMENTING: Use pseudocode table to add/edit/delete comments +-- Add inline comment (appears after semicolon) +UPDATE pseudocode SET comment = 'buffer overflow here' +WHERE func_addr = 0x401000 AND ea = 0x401020; + +-- Add block comment (appears on own line above the statement) +UPDATE pseudocode SET comment_placement = 'block1', comment = 'vulnerable call' +WHERE func_addr = 0x401000 AND ea = 0x401020; + +-- Delete a comment +UPDATE pseudocode SET comment = NULL +WHERE func_addr = 0x401000 AND ea = 0x401020; + +-- STRUCTURED QUERY: Get specific lines with ea and comment info +SELECT ea, line, comment FROM pseudocode WHERE func_addr = 0x401000; +``` + +#### ctree +Full Abstract Syntax Tree of decompiled code. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Function address | +| `item_id` | INT | Unique node ID | +| `is_expr` | INT | 1=expression, 0=statement | +| `op_name` | TEXT | Node type (`cot_call`, `cit_if`, etc.) | +| `ea` | INT | Address in binary | +| `parent_id` | INT | Parent node ID | +| `depth` | INT | Tree depth | +| `x_id`, `y_id`, `z_id` | INT | Child node IDs | +| `var_idx` | INT | Local variable index | +| `var_name` | TEXT | Variable name | +| `obj_ea` | INT | Target address | +| `obj_name` | TEXT | Symbol name | +| `num_value` | INT | Numeric literal | +| `str_value` | TEXT | String literal | + +#### ctree_lvars +Local variables from decompilation. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Function address | +| `idx` | INT | Variable index | +| `name` | TEXT | Variable name | +| `type` | TEXT | Type string | +| `size` | INT | Size in bytes | +| `is_arg` | INT | 1=function argument | +| `is_stk_var` | INT | 1=stack variable | +| `stkoff` | INT | Stack offset | + +#### ctree_call_args +Flattened call arguments for easy querying. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Function address | +| `call_item_id` | INT | Call node ID | +| `arg_idx` | INT | Argument index (0-based) | +| `arg_op` | TEXT | Argument type | +| `arg_var_name` | TEXT | Variable name if applicable | +| `arg_var_is_stk` | INT | 1=stack variable | +| `arg_num_value` | INT | Numeric value | +| `arg_str_value` | TEXT | String value | + +### Decompiler Views + +Pre-built views for common patterns: + +| View | Purpose | +|------|---------| +| `ctree_v_calls` | Function calls with callee info | +| `ctree_v_loops` | for/while/do loops | +| `ctree_v_ifs` | if statements | +| `ctree_v_comparisons` | Comparisons with operands | +| `ctree_v_assignments` | Assignments with operands | +| `ctree_v_derefs` | Pointer dereferences | +| `ctree_v_returns` | Return statements with value details | +| `ctree_v_calls_in_loops` | Calls inside loops (recursive) | +| `ctree_v_calls_in_ifs` | Calls inside if branches (recursive) | +| `ctree_v_leaf_funcs` | Functions with no outgoing calls | +| `ctree_v_call_chains` | Call chain paths up to depth 10 | + +#### ctree_v_returns + +Return statements with details about what's being returned. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Function address | +| `item_id` | INT | Return statement item_id | +| `ea` | INT | Address of return | +| `return_op` | TEXT | Return value opcode (`cot_num`, `cot_var`, `cot_call`, etc.) | +| `return_num` | INT | Numeric value (if `cot_num`) | +| `return_str` | TEXT | String value (if `cot_str`) | +| `return_var` | TEXT | Variable name (if `cot_var`) | +| `returns_arg` | INT | 1 if returning a function argument | +| `returns_call_result` | INT | 1 if returning result of another call | + +```sql +-- Functions that return 0 +SELECT DISTINCT func_at(func_addr) as name FROM ctree_v_returns +WHERE return_op = 'cot_num' AND return_num = 0; + +-- Functions that return -1 (error sentinel) +SELECT DISTINCT func_at(func_addr) as name FROM ctree_v_returns +WHERE return_op = 'cot_num' AND return_num = -1; + +-- Functions that return their argument (pass-through) +SELECT DISTINCT func_at(func_addr) as name FROM ctree_v_returns +WHERE returns_arg = 1; +``` + +### Type Tables + +#### types +All local type definitions. Supports INSERT (create struct/union/enum), UPDATE, and DELETE. + +| Column | Type | Description | +|--------|------|-------------| +| `ordinal` | INT | Type ordinal | +| `name` | TEXT | Type name | +| `size` | INT | Size in bytes | +| `kind` | TEXT | struct/union/enum/typedef/func | +| `is_struct` | INT | 1=struct | +| `is_union` | INT | 1=union | +| `is_enum` | INT | 1=enum | + +#### types_members +Structure and union members. Supports INSERT (add member to struct/union), UPDATE, and DELETE. + +| Column | Type | Description | +|--------|------|-------------| +| `type_ordinal` | INT | Parent type ordinal | +| `type_name` | TEXT | Parent type name | +| `member_name` | TEXT | Member name | +| `offset` | INT | Byte offset | +| `size` | INT | Member size | +| `member_type` | TEXT | Type string | +| `mt_is_ptr` | INT | 1=pointer | +| `mt_is_array` | INT | 1=array | +| `mt_is_struct` | INT | 1=embedded struct | + +#### types_enum_values +Enum constant values. Supports INSERT (add value to enum), UPDATE, and DELETE. + +| Column | Type | Description | +|--------|------|-------------| +| `type_ordinal` | INT | Enum type ordinal | +| `type_name` | TEXT | Enum name | +| `value_name` | TEXT | Constant name | +| `value` | INT | Constant value | + +#### types_func_args +Function prototype arguments with type classification. + +| Column | Type | Description | +|--------|------|-------------| +| `type_ordinal` | INT | Function type ordinal | +| `type_name` | TEXT | Function type name | +| `arg_index` | INT | Argument index (-1 = return type, 0+ = args) | +| `arg_name` | TEXT | Argument name | +| `arg_type` | TEXT | Argument type string | +| `calling_conv` | TEXT | Calling convention (on return row only) | + +**Surface-level type classification** (literal type as written): + +| Column | Type | Description | +|--------|------|-------------| +| `is_ptr` | INT | 1 if pointer type | +| `is_int` | INT | 1 if exactly int type | +| `is_integral` | INT | 1 if int-like (int, long, short, char, bool) | +| `is_float` | INT | 1 if float/double | +| `is_void` | INT | 1 if void | +| `is_struct` | INT | 1 if struct/union | +| `is_array` | INT | 1 if array | +| `ptr_depth` | INT | Pointer depth (int** = 2) | +| `base_type` | TEXT | Type with pointers stripped | + +**Resolved type classification** (after typedef resolution): + +| Column | Type | Description | +|--------|------|-------------| +| `is_ptr_resolved` | INT | 1 if resolved type is pointer | +| `is_int_resolved` | INT | 1 if resolved type is exactly int | +| `is_integral_resolved` | INT | 1 if resolved type is int-like | +| `is_float_resolved` | INT | 1 if resolved type is float/double | +| `is_void_resolved` | INT | 1 if resolved type is void | +| `ptr_depth_resolved` | INT | Pointer depth after resolution | +| `base_type_resolved` | TEXT | Resolved type with pointers stripped | + +```sql +-- Functions returning integers (strict: exactly int) +SELECT type_name FROM types_func_args +WHERE arg_index = -1 AND is_int = 1; + +-- Functions returning integers (loose: includes BOOL, DWORD, LONG) +SELECT type_name FROM types_func_args +WHERE arg_index = -1 AND is_integral_resolved = 1; + +-- Functions taking 4 pointer arguments +SELECT type_name, COUNT(*) as ptr_args FROM types_func_args +WHERE arg_index >= 0 AND is_ptr = 1 +GROUP BY type_ordinal HAVING ptr_args = 4; + +-- Typedefs that hide pointers (HANDLE, etc.) +SELECT type_name, arg_type FROM types_func_args +WHERE is_ptr = 0 AND is_ptr_resolved = 1; +``` + +### Type Views + +Convenience views for filtering types: + +| View | Description | +|------|-------------| +| `types_v_structs` | `SELECT * FROM types WHERE is_struct = 1` | +| `types_v_unions` | `SELECT * FROM types WHERE is_union = 1` | +| `types_v_enums` | `SELECT * FROM types WHERE is_enum = 1` | +| `types_v_typedefs` | `SELECT * FROM types WHERE is_typedef = 1` | +| `types_v_funcs` | `SELECT * FROM types WHERE is_func = 1` | +| `local_types` | Legacy compatibility view | + +### Extended Tables + +#### bookmarks +User-defined bookmarks/marked positions. + +| Column | Type | Description | +|--------|------|-------------| +| `index` | INT | Bookmark index | +| `address` | INT | Bookmarked address | +| `description` | TEXT | Bookmark description | + +```sql +-- List all bookmarks +SELECT printf('0x%X', address) as addr, description FROM bookmarks; +``` + +#### heads +All defined items (code/data heads) in the database. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Head address | +| `size` | INT | Item size | +| `flags` | INT | IDA flags | + +**Performance:** This table can be very large. Always use address range filters. + +#### fixups +Relocation and fixup information. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Fixup address | +| `type` | INT | Fixup type | +| `target` | INT | Target address | + +#### hidden_ranges +Collapsed/hidden code regions in IDA. + +| Column | Type | Description | +|--------|------|-------------| +| `start_ea` | INT | Range start | +| `end_ea` | INT | Range end | +| `description` | TEXT | Description | +| `visible` | INT | Visibility state | + +#### problems +IDA analysis problems and warnings. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Problem address | +| `type` | INT | Problem type code | +| `description` | TEXT | Problem description | + +```sql +-- Find all analysis problems +SELECT printf('0x%X', address) as addr, description FROM problems; +``` + +#### fchunks +Function chunks (for functions with non-contiguous code, like exception handlers). + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Parent function | +| `start_ea` | INT | Chunk start | +| `end_ea` | INT | Chunk end | +| `size` | INT | Chunk size | + +```sql +-- Functions with multiple chunks (complex control flow) +SELECT func_at(func_addr) as name, COUNT(*) as chunks +FROM fchunks GROUP BY func_addr HAVING chunks > 1; +``` + +#### signatures +FLIRT signature matches. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Matched address | +| `name` | TEXT | Signature name | +| `library` | TEXT | Library name | + +#### mappings +Memory mappings for debugging. + +| Column | Type | Description | +|--------|------|-------------| +| `from_ea` | INT | Mapped from | +| `to_ea` | INT | Mapped to | +| `size` | INT | Mapping size | + +### Metadata Tables + +#### db_info +Database-level metadata. + +| Column | Type | Description | +|--------|------|-------------| +| `key` | TEXT | Metadata key | +| `value` | TEXT | Metadata value | + +```sql +-- Get database info +SELECT * FROM db_info; +``` + +#### ida_info +IDA processor and analysis info. + +| Column | Type | Description | +|--------|------|-------------| +| `key` | TEXT | Info key | +| `value` | TEXT | Info value | + +```sql +-- Get processor type +SELECT value FROM ida_info WHERE key = 'procname'; +``` + +### Disassembly Tables + +#### disasm_loops +Detected loops in disassembly. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Function address | +| `loop_start` | INT | Loop header address | +| `loop_end` | INT | Loop end address | + +### Disassembly Views + +Views for disassembly-level analysis (no Hex-Rays required): + +| View | Description | +|------|-------------| +| `disasm_v_leaf_funcs` | Functions with no outgoing calls | +| `disasm_v_call_chains` | Call chain paths (recursive CTE) | +| `disasm_v_calls_in_loops` | Calls inside loop bodies | +| `disasm_v_funcs_with_loops` | Functions containing loops | + +```sql +-- Find functions that don't call anything +SELECT * FROM disasm_v_leaf_funcs LIMIT 10; + +-- Find hotspot calls (inside loops) +SELECT func_at(func_addr) as func, callee_name +FROM disasm_v_calls_in_loops; +``` + +--- + +## SQL Functions + +### Disassembly +| Function | Description | +|----------|-------------| +| `disasm(addr)` | Disassembly line at address | +| `disasm(addr, n)` | Multiple lines from address | +| `bytes(addr, n)` | Bytes as hex string | +| `bytes_raw(addr, n)` | Raw bytes as BLOB | +| `mnemonic(addr)` | Instruction mnemonic only | +| `operand(addr, n)` | Operand text (n=0-5) | + +### Binary Search +| Function | Description | +|----------|-------------| +| `search_bytes(pattern)` | Find all matches, returns JSON array | +| `search_bytes(pattern, start, end)` | Search within address range | +| `search_first(pattern)` | First match address (or NULL) | +| `search_first(pattern, start, end)` | First match in range | + +**Pattern syntax (IDA native):** +- `"48 8B 05"` - Exact bytes (hex, space-separated) +- `"48 ? 05"` or `"48 ?? 05"` - `?` = any byte wildcard (whole byte only) +- `"(01 02 03)"` - Alternatives (match any of these bytes) + +**Note:** Unlike Binary Ninja, IDA does NOT support nibble wildcards or regex. + +**Example:** +```sql +-- Find all matches for a pattern +SELECT search_bytes('48 8B ? 00'); + +-- Parse JSON results +SELECT json_extract(value, '$.address') as addr +FROM json_each(search_bytes('48 89 ?')) +LIMIT 10; + +-- First match only +SELECT printf('0x%llX', search_first('CC CC CC')); + +-- Search with alternatives +SELECT search_bytes('E8 (01 02 03 04)'); +``` + +**Optimization Pattern: Find functions using specific instruction** + +To answer "How many functions use RDTSC instruction?" efficiently: +```sql +-- Count unique functions containing RDTSC (opcode: 0F 31) +SELECT COUNT(DISTINCT func_start(json_extract(value, '$.address'))) as count +FROM json_each(search_bytes('0F 31')) +WHERE func_start(json_extract(value, '$.address')) IS NOT NULL; + +-- List those functions with names +SELECT DISTINCT + func_start(json_extract(value, '$.address')) as func_ea, + name_at(func_start(json_extract(value, '$.address'))) as func_name +FROM json_each(search_bytes('0F 31')) +WHERE func_start(json_extract(value, '$.address')) IS NOT NULL; +``` + +This is **much faster** than scanning all disassembly lines because: +- `search_bytes()` uses native binary search +- `func_start()` is O(1) lookup in IDA's function index + +### Names & Functions +| Function | Description | +|----------|-------------| +| `name_at(addr)` | Name at address | +| `func_at(addr)` | Function name containing address | +| `func_start(addr)` | Start of containing function | +| `func_end(addr)` | End of containing function | +| `func_qty()` | Total function count | +| `func_at_index(n)` | Function address at index (O(1)) | + +### Cross-References +| Function | Description | +|----------|-------------| +| `xrefs_to(addr)` | JSON array of xrefs TO address | +| `xrefs_from(addr)` | JSON array of xrefs FROM address | + +### Navigation +| Function | Description | +|----------|-------------| +| `next_head(addr)` | Next defined item | +| `prev_head(addr)` | Previous defined item | +| `segment_at(addr)` | Segment name at address | +| `hex(val)` | Format as hex string | + +### Comments +| Function | Description | +|----------|-------------| +| `comment_at(addr)` | Get comment at address | +| `set_comment(addr, text)` | Set regular comment | +| `set_comment(addr, text, 1)` | Set repeatable comment | + +### Modification +| Function | Description | +|----------|-------------| +| `set_name(addr, name)` | Set name at address | + +### Item Analysis +| Function | Description | +|----------|-------------| +| `item_type(addr)` | Item type flags at address | +| `item_size(addr)` | Item size at address | +| `is_code(addr)` | Returns 1 if address is code | +| `is_data(addr)` | Returns 1 if address is data | +| `flags_at(addr)` | Raw IDA flags at address | + +### Instruction Details +| Function | Description | +|----------|-------------| +| `itype(addr)` | Instruction type code (processor-specific) | +| `decode_insn(addr)` | Full instruction info as JSON | +| `operand_type(addr, n)` | Operand type code (o_void, o_reg, etc.) | +| `operand_value(addr, n)` | Operand value (register num, immediate, etc.) | + +```sql +-- Get instruction type for filtering +SELECT address, itype(address) as itype, mnemonic(address) +FROM heads WHERE is_code(address) = 1 LIMIT 10; + +-- Decode full instruction +SELECT decode_insn(0x401000); +``` + +### Decompilation + +**When to use `decompile()` vs `pseudocode` table:** +- **To view/show pseudocode** → always use `SELECT decompile(addr)`. Returns the full function as a single text block with `/* ea */` address prefixes. This is fast, efficient, and what you should use when the user asks to "decompile", "show the code", or "show the pseudocode". +- **To read specific lines or columns** → query the `pseudocode` table. If you already have the full output from `decompile()`, refer to it directly. Only query the table when you need structured access (e.g. filtering by ea, reading comment values). +- **To add/edit/delete comments** → `UPDATE pseudocode SET comment = '...' WHERE func_addr = X AND ea = Y`. The pseudocode table is the write interface for decompiler comments. + +| Function | Description | +|----------|-------------| +| `decompile(addr)` | **PREFERRED** — Full pseudocode with `/* ea */` prefixes (requires Hex-Rays) | +| `decompile(addr, 1)` | Same but forces re-decompilation (use after writing comments or renaming variables) | +| `list_lvars(addr)` | List local variables as JSON | +| `rename_lvar(addr, old, new)` | Rename a local variable (shortcut for `UPDATE ctree_lvars`) | + +```sql +-- Decompile a function (PREFERRED way to view pseudocode) +SELECT decompile(0x401000); + +-- After modifying comments or variables, re-decompile to see changes +SELECT decompile(0x401000, 1); + +-- Get all local variables in a function +SELECT list_lvars(0x401000); + +-- Rename a variable (function shortcut) +SELECT rename_lvar(0x401000, 'v1', 'buffer_size'); + +-- Equivalent using UPDATE (canonical approach) +UPDATE ctree_lvars SET name = 'buffer_size' WHERE func_addr = 0x401000 AND name = 'v1'; +``` + +### File Generation +| Function | Description | +|----------|-------------| +| `gen_asm_file(start, end, path)` | Generate ASM file | +| `gen_lst_file(start, end, path)` | Generate listing file | +| `gen_map_file(path)` | Generate MAP file | +| `gen_idc_file(start, end, path)` | Generate IDC script | +| `gen_html_file(start, end, path)` | Generate HTML file | + +```sql +-- Export function as ASM +SELECT gen_asm_file(0x401000, 0x401100, '/tmp/func.asm'); + +-- Generate MAP file +SELECT gen_map_file('/tmp/binary.map'); +``` + +### Graph Generation +| Function | Description | +|----------|-------------| +| `gen_cfg_dot(addr)` | Generate CFG as DOT graph string | +| `gen_cfg_dot_file(addr, path)` | Write CFG DOT to file | +| `gen_schema_dot()` | Generate database schema as DOT | + +```sql +-- Get CFG for a function as DOT format +SELECT gen_cfg_dot(0x401000); + +-- Export schema visualization +SELECT gen_schema_dot(); +``` + +### Entity Search ("Jump to Anything") +| Function | Description | +|----------|-------------| +| `jump_search(pattern, mode, limit, offset)` | Search entities, returns JSON array | +| `jump_query(pattern, mode, limit, offset)` | Returns the generated SQL string | + +```sql +-- Search for functions/types/labels starting with 'sub' +SELECT jump_search('sub', 'prefix', 10, 0); + +-- Search for anything containing 'main' +SELECT jump_search('main', 'contains', 10, 0); +``` + +### String List Functions + +IDA maintains a cached list of strings. Use `rebuild_strings()` to detect and cache strings. + +| Function | Description | +|----------|-------------| +| `rebuild_strings()` | Rebuild with ASCII + UTF-16, minlen 5 (default) | +| `rebuild_strings(minlen)` | Rebuild with custom minimum length | +| `rebuild_strings(minlen, types)` | Rebuild with custom length and type mask | +| `string_count()` | Get current string count (no rebuild) | + +**Type mask values:** +- `1` = ASCII only (STRTYPE_C) +- `2` = UTF-16 only (STRTYPE_C_16) +- `4` = UTF-32 only (STRTYPE_C_32) +- `3` = ASCII + UTF-16 (default) +- `7` = All types + +```sql +-- Check current string count +SELECT string_count(); + +-- Rebuild with defaults (ASCII + UTF-16, minlen 5) +SELECT rebuild_strings(); + +-- Rebuild with shorter minimum length +SELECT rebuild_strings(4); + +-- Rebuild with specific types +SELECT rebuild_strings(5, 1); -- ASCII only +SELECT rebuild_strings(5, 7); -- All types (ASCII + UTF-16 + UTF-32) + +-- Typical workflow: rebuild then query +SELECT rebuild_strings(); +SELECT * FROM strings WHERE content LIKE '%error%'; +``` + +**IMPORTANT - Agent Behavior for String Queries:** +When the user asks about strings (e.g., "show me the strings", "what strings are in this binary"): +1. First run `SELECT rebuild_strings()` to ensure strings are detected +2. Then query the `strings` table + +The `rebuild_strings()` function configures IDA's string detection with sensible defaults (ASCII + UTF-16, minimum length 5) and rebuilds the string list. This ensures the user gets results even if the database had no prior string analysis. + +--- + +## Entity Search Table (jump_entities) + +A table-valued function for unified entity search with full SQL composability. + +### Usage + +```sql +-- Basic search (function-call syntax) +SELECT * FROM jump_entities('sub', 'prefix') LIMIT 10; + +-- Filter by kind +SELECT * FROM jump_entities('EH', 'prefix') WHERE kind = 'struct'; + +-- JOIN with other tables +SELECT j.name, f.size +FROM jump_entities('sub', 'prefix') j +LEFT JOIN funcs f ON j.address = f.address +WHERE j.kind = 'function'; +``` + +### Parameters + +| Parameter | Description | +|-----------|-------------| +| `pattern` | Search pattern (required) | +| `mode` | `'prefix'` or `'contains'` | + +### Columns + +| Column | Type | Description | +|--------|------|-------------| +| `name` | TEXT | Entity name | +| `kind` | TEXT | function/label/segment/struct/union/enum/member/enum_member | +| `address` | INT | Address (for functions, labels, segments) | +| `ordinal` | INT | Type ordinal (for types, members) | +| `parent_name` | TEXT | Parent type (for members) | +| `full_name` | TEXT | Fully qualified name | + +**Use Case:** Implement "Jump to Anything" with virtual scrolling - lazy cursor respects LIMIT. + +--- + +## Performance Rules + +### CRITICAL: Constraint Pushdown + +Some tables have **optimized filters** that use efficient IDA SDK APIs: + +| Table | Optimized Filter | Without Filter | +|-------|------------------|----------------| +| `instructions` | `func_addr = X` | O(all instructions) - SLOW | +| `blocks` | `func_ea = X` | O(all blocks) | +| `xrefs` | `to_ea = X` or `from_ea = X` | O(all xrefs) | +| `pseudocode` | `func_addr = X` | **Decompiles ALL functions** | +| `ctree*` | `func_addr = X` | **Decompiles ALL functions** | + +**Always filter decompiler tables by `func_addr`!** + +### Use Integer Comparisons + +```sql +-- SLOW: String comparison +WHERE mnemonic = 'call' + +-- FAST: Integer comparison +WHERE itype IN (16, 18) -- x86 call opcodes +``` + +### O(1) Random Access + +```sql +-- SLOW: O(n) - sorts all rows +SELECT address FROM funcs ORDER BY RANDOM() LIMIT 1; + +-- FAST: O(1) - direct index access +SELECT func_at_index(ABS(RANDOM()) % func_qty()); +``` + +--- + +## Common Query Patterns + +### Find Most Called Functions + +```sql +SELECT f.name, COUNT(*) as callers +FROM funcs f +JOIN xrefs x ON f.address = x.to_ea +WHERE x.is_code = 1 +GROUP BY f.address +ORDER BY callers DESC +LIMIT 10; +``` + +### Find Functions Calling a Specific API + +```sql +SELECT DISTINCT func_at(from_ea) as caller +FROM xrefs +WHERE to_ea = (SELECT address FROM imports WHERE name = 'CreateFileW'); +``` + +### String Cross-Reference Analysis + +```sql +SELECT s.content, func_at(x.from_ea) as used_by +FROM strings s +JOIN xrefs x ON s.address = x.to_ea +WHERE s.content LIKE '%password%'; +``` + +### Function Complexity (by Block Count) + +```sql +SELECT func_at(func_ea) as name, COUNT(*) as block_count +FROM blocks +GROUP BY func_ea +ORDER BY block_count DESC +LIMIT 10; +``` + +### Find Leaf Functions (No Outgoing Calls) + +```sql +SELECT f.name, f.size +FROM funcs f +LEFT JOIN disasm_calls c ON c.func_addr = f.address +GROUP BY f.address +HAVING COUNT(c.ea) = 0 +ORDER BY f.size DESC; +``` + +### Functions with Deep Call Chains + +```sql +SELECT f.name, MAX(cc.depth) as max_depth +FROM disasm_v_call_chains cc +JOIN funcs f ON f.address = cc.root_func +GROUP BY cc.root_func +ORDER BY max_depth DESC +LIMIT 10; +``` + +### Security: Dangerous Function Calls with Stack Buffers + +```sql +SELECT f.name, c.callee_name, printf('0x%X', c.ea) as address +FROM funcs f +JOIN ctree_v_calls c ON c.func_addr = f.address +JOIN ctree_call_args a ON a.func_addr = c.func_addr AND a.call_item_id = c.item_id +WHERE c.callee_name IN ('strcpy', 'strcat', 'sprintf', 'gets', 'memcpy') + AND a.arg_idx = 0 AND a.arg_var_is_stk = 1 +ORDER BY f.name; +``` + +### Find Zero Comparisons (Potential Error Checks) + +```sql +SELECT func_at(func_addr) as func, printf('0x%X', ea) as addr +FROM ctree_v_comparisons +WHERE op_name = 'cot_eq' AND rhs_op = 'cot_num' AND rhs_num = 0; +``` + +### Calls Inside Loops (Performance Hotspots) + +```sql +SELECT f.name, l.callee_name, l.loop_op +FROM ctree_v_calls_in_loops l +JOIN funcs f ON f.address = l.func_addr +ORDER BY f.name; +``` + +### malloc with Constant Size + +```sql +SELECT func_at(c.func_addr) as func, a.arg_num_value as size +FROM ctree_v_calls c +JOIN ctree_call_args a ON a.func_addr = c.func_addr AND a.call_item_id = c.item_id +WHERE c.callee_name LIKE '%malloc%' + AND a.arg_idx = 0 AND a.arg_op = 'cot_num' +ORDER BY a.arg_num_value DESC; +``` + +### Largest Structures + +```sql +SELECT name, size, alignment +FROM types +WHERE is_struct = 1 AND size > 0 +ORDER BY size DESC +LIMIT 10; +``` + +### Instruction Profile for a Function + +```sql +SELECT mnemonic, COUNT(*) as count +FROM instructions +WHERE func_addr = 0x401330 +GROUP BY mnemonic +ORDER BY count DESC; +``` + +### Import Dependency Map + +```sql +-- Which modules does each function depend on? +SELECT f.name as func_name, i.module, COUNT(*) as api_count +FROM funcs f +JOIN disasm_calls dc ON dc.func_addr = f.address +JOIN imports i ON dc.callee_addr = i.address +GROUP BY f.address, i.module +ORDER BY f.name, api_count DESC; +``` + +### Find Indirect Calls (Potential Virtual Functions/Callbacks) + +```sql +-- Functions with indirect calls (call through register/memory) +SELECT f.name, COUNT(*) as indirect_calls +FROM funcs f +JOIN disasm_calls dc ON dc.func_addr = f.address +WHERE dc.callee_addr = 0 -- Unresolved target = indirect +GROUP BY f.address +ORDER BY indirect_calls DESC +LIMIT 20; +``` + +### String Format Audit (printf-style Vulnerabilities) + +```sql +-- Format string usage with variable formats (potential vuln) +SELECT f.name, c.callee_name, printf('0x%X', c.ea) as addr +FROM funcs f +JOIN ctree_v_calls c ON c.func_addr = f.address +JOIN ctree_call_args a ON a.func_addr = c.func_addr AND a.call_item_id = c.item_id +WHERE c.callee_name LIKE '%printf%' + AND a.arg_idx = 0 -- First arg is format string + AND a.arg_op = 'cot_var'; -- Variable, not constant string +``` + +### Memory Allocation Patterns + +```sql +-- Find functions that allocate but may not free +WITH allocators AS ( + SELECT func_addr, COUNT(*) as alloc_count + FROM disasm_calls + WHERE callee_name LIKE '%alloc%' OR callee_name LIKE '%malloc%' + GROUP BY func_addr +), +freers AS ( + SELECT func_addr, COUNT(*) as free_count + FROM disasm_calls + WHERE callee_name LIKE '%free%' + GROUP BY func_addr +) +SELECT f.name, + COALESCE(a.alloc_count, 0) as allocations, + COALESCE(r.free_count, 0) as frees +FROM funcs f +LEFT JOIN allocators a ON f.address = a.func_addr +LEFT JOIN freers r ON f.address = r.func_addr +WHERE a.alloc_count > 0 AND COALESCE(r.free_count, 0) = 0 +ORDER BY allocations DESC; +``` + +### Control Flow Anomalies + +```sql +-- Functions with many basic blocks but few instructions (possibly obfuscated) +SELECT + f.name, + f.size, + COUNT(DISTINCT b.start_ea) as blocks, + f.size / COUNT(DISTINCT b.start_ea) as avg_block_size +FROM funcs f +JOIN blocks b ON b.func_ea = f.address +WHERE f.size > 100 +GROUP BY f.address +HAVING COUNT(DISTINCT b.start_ea) > 10 + AND f.size / COUNT(DISTINCT b.start_ea) < 10 -- Very small blocks +ORDER BY blocks DESC; +``` + +### Return Value Analysis + +```sql +-- Functions with multiple return statements (complex control flow) +SELECT f.name, COUNT(*) as return_count +FROM funcs f +JOIN ctree ct ON ct.func_addr = f.address +WHERE ct.op_name = 'cit_return' +GROUP BY f.address +HAVING COUNT(*) > 3 +ORDER BY return_count DESC; + +-- Functions that return 0 (common success pattern) +SELECT DISTINCT func_at(func_addr) as name FROM ctree_v_returns +WHERE return_op = 'cot_num' AND return_num = 0; + +-- Functions that return -1 (error sentinel) +SELECT DISTINCT func_at(func_addr) as name FROM ctree_v_returns +WHERE return_op = 'cot_num' AND return_num = -1; + +-- Functions that return a specific constant +SELECT DISTINCT func_at(func_addr) as name FROM ctree_v_returns +WHERE return_op = 'cot_num' AND return_num = 1; +``` + +### Function Signature Queries + +```sql +-- Functions returning integers (includes BOOL, DWORD via resolved) +SELECT type_name FROM types_func_args +WHERE arg_index = -1 AND is_integral_resolved = 1; + +-- Functions taking exactly 4 pointer arguments +SELECT type_name, COUNT(*) as ptr_args FROM types_func_args +WHERE arg_index >= 0 AND is_ptr = 1 +GROUP BY type_ordinal HAVING ptr_args = 4; + +-- Functions with string parameters (char*/wchar_t*) +SELECT DISTINCT type_name FROM types_func_args +WHERE arg_index >= 0 AND is_ptr = 1 + AND base_type_resolved IN ('char', 'wchar_t', 'CHAR', 'WCHAR'); + +-- Typedefs hiding pointers (HANDLE, HMODULE, etc.) +SELECT DISTINCT type_name, arg_type FROM types_func_args +WHERE is_ptr = 0 AND is_ptr_resolved = 1; + +-- Functions returning void pointers +SELECT type_name FROM types_func_args +WHERE arg_index = -1 AND is_ptr_resolved = 1 AND is_void_resolved = 1; +``` + +### Loops with System Calls (Performance/Security Hotspots) + +```sql +-- System API calls inside loops +SELECT + f.name as function, + l.callee_name as api_called, + l.loop_op as loop_type +FROM ctree_v_calls_in_loops l +JOIN funcs f ON f.address = l.func_addr +JOIN imports i ON l.callee_name = i.name +ORDER BY f.name; +``` + +### Type Usage Statistics + +```sql +-- Most referenced types (by struct member usage in decompiled code) +SELECT tm.type_name, COUNT(DISTINCT ct.func_addr) as func_count +FROM types_members tm +JOIN ctree ct ON ct.var_name = tm.member_name +GROUP BY tm.type_name +ORDER BY func_count DESC +LIMIT 20; +``` + +### Data Section Analysis + +```sql +-- Find functions referencing data sections +SELECT + f.name, + s.name as segment, + COUNT(*) as data_refs +FROM funcs f +JOIN xrefs x ON x.from_ea BETWEEN f.address AND f.end_ea +JOIN segments s ON x.to_ea BETWEEN s.start_ea AND s.end_ea +WHERE s.class = 'DATA' AND x.is_code = 0 +GROUP BY f.address, s.name +ORDER BY data_refs DESC +LIMIT 20; +``` + +### Exception Handler Detection + +```sql +-- Functions with multiple chunks (often due to exception handlers) +SELECT + f.name, + COUNT(*) as chunk_count, + SUM(fc.size) as total_size +FROM funcs f +JOIN fchunks fc ON fc.func_addr = f.address +GROUP BY f.address +HAVING COUNT(*) > 1 +ORDER BY chunk_count DESC; +``` + +--- + +## Advanced SQL Patterns + +### Common Table Expressions (CTEs) + +CTEs make complex queries readable and allow recursive traversal. + +#### Basic CTE for Filtering + +```sql +-- Find functions that both call malloc AND check return value +WITH malloc_callers AS ( + SELECT DISTINCT func_addr + FROM disasm_calls + WHERE callee_name LIKE '%malloc%' +), +null_checkers AS ( + SELECT DISTINCT func_addr + FROM ctree_v_comparisons + WHERE rhs_num = 0 AND op_name = 'cot_eq' +) +SELECT f.name +FROM funcs f +JOIN malloc_callers m ON f.address = m.func_addr +JOIN null_checkers n ON f.address = n.func_addr; +``` + +#### CTE with Aggregation + +```sql +-- Functions ranked by complexity (calls * blocks) +WITH call_counts AS ( + SELECT func_addr, COUNT(*) as call_cnt + FROM disasm_calls + GROUP BY func_addr +), +block_counts AS ( + SELECT func_ea as func_addr, COUNT(*) as block_cnt + FROM blocks + GROUP BY func_ea +) +SELECT f.name, + COALESCE(c.call_cnt, 0) as calls, + COALESCE(b.block_cnt, 0) as blocks, + COALESCE(c.call_cnt, 0) * COALESCE(b.block_cnt, 0) as complexity +FROM funcs f +LEFT JOIN call_counts c ON f.address = c.func_addr +LEFT JOIN block_counts b ON f.address = b.func_addr +ORDER BY complexity DESC +LIMIT 10; +``` + +### Recursive CTEs (Call Graph Traversal) + +```sql +-- Find all functions reachable from main (up to depth 5) +WITH RECURSIVE call_graph AS ( + -- Base case: start from main + SELECT address as func_addr, name, 0 as depth + FROM funcs WHERE name = 'main' + + UNION ALL + + -- Recursive case: follow calls + SELECT f.address, f.name, cg.depth + 1 + FROM call_graph cg + JOIN disasm_calls dc ON dc.func_addr = cg.func_addr + JOIN funcs f ON f.address = dc.callee_addr + WHERE cg.depth < 5 + AND dc.callee_addr != 0 -- Skip indirect calls +) +SELECT DISTINCT func_addr, name, MIN(depth) as min_depth +FROM call_graph +GROUP BY func_addr +ORDER BY min_depth, name; +``` + +```sql +-- Reverse call graph: who calls this function (transitive) +WITH RECURSIVE callers AS ( + -- Base: direct callers of target + SELECT DISTINCT dc.func_addr, 1 as depth + FROM disasm_calls dc + WHERE dc.callee_addr = 0x401000 + + UNION ALL + + -- Recursive: who calls the callers + SELECT DISTINCT dc.func_addr, c.depth + 1 + FROM callers c + JOIN disasm_calls dc ON dc.callee_addr = c.func_addr + WHERE c.depth < 5 +) +SELECT func_at(func_addr) as caller, MIN(depth) as distance +FROM callers +GROUP BY func_addr +ORDER BY distance, caller; +``` + +### Window Functions + +```sql +-- Rank functions by size within each segment +SELECT + segment_at(f.address) as seg, + f.name, + f.size, + ROW_NUMBER() OVER (PARTITION BY segment_at(f.address) ORDER BY f.size DESC) as rank +FROM funcs f +WHERE f.size > 0; +``` + +```sql +-- Running total of function sizes +SELECT + name, + size, + SUM(size) OVER (ORDER BY address) as cumulative_size +FROM funcs +ORDER BY address; +``` + +```sql +-- Find consecutive functions with similar sizes (possible duplicates) +SELECT + name, + size, + LAG(name) OVER (ORDER BY size) as prev_name, + LAG(size) OVER (ORDER BY size) as prev_size +FROM funcs +WHERE size > 100; +``` + +### Complex JOINs + +#### Multi-Table Join (Functions with Context) + +```sql +-- Function overview with all relationships +SELECT + f.name, + f.size, + segment_at(f.address) as segment, + (SELECT COUNT(*) FROM blocks WHERE func_ea = f.address) as block_count, + (SELECT COUNT(*) FROM disasm_calls WHERE func_addr = f.address) as outgoing_calls, + (SELECT COUNT(*) FROM xrefs WHERE to_ea = f.address AND is_code = 1) as incoming_calls, + (SELECT COUNT(*) FROM ctree_lvars WHERE func_addr = f.address) as local_vars +FROM funcs f +ORDER BY f.size DESC +LIMIT 20; +``` + +#### Self-Join (Compare Functions) + +```sql +-- Find functions with identical sizes (potential clones) +SELECT + f1.name as func1, + f2.name as func2, + f1.size +FROM funcs f1 +JOIN funcs f2 ON f1.size = f2.size AND f1.address < f2.address +WHERE f1.size > 50 -- Ignore tiny functions +ORDER BY f1.size DESC; +``` + +### Subqueries + +```sql +-- Functions that call more APIs than average +SELECT f.name, call_count +FROM ( + SELECT func_addr, COUNT(*) as call_count + FROM disasm_calls dc + JOIN imports i ON dc.callee_addr = i.address + GROUP BY func_addr +) sub +JOIN funcs f ON f.address = sub.func_addr +WHERE call_count > ( + SELECT AVG(cnt) FROM ( + SELECT COUNT(*) as cnt + FROM disasm_calls dc + JOIN imports i ON dc.callee_addr = i.address + GROUP BY func_addr + ) +) +ORDER BY call_count DESC; +``` + +### CASE Expressions + +```sql +-- Categorize functions by complexity +SELECT + name, + size, + CASE + WHEN size < 50 THEN 'tiny' + WHEN size < 200 THEN 'small' + WHEN size < 1000 THEN 'medium' + WHEN size < 5000 THEN 'large' + ELSE 'huge' + END as category +FROM funcs +ORDER BY size DESC; +``` + +```sql +-- Classify strings by content +SELECT + content, + CASE + WHEN content LIKE '%error%' OR content LIKE '%fail%' THEN 'error' + WHEN content LIKE '%password%' OR content LIKE '%key%' THEN 'sensitive' + WHEN content LIKE '%http%' OR content LIKE '%://% ' THEN 'url' + WHEN content LIKE '%.dll%' OR content LIKE '%.exe%' THEN 'file' + ELSE 'other' + END as category +FROM strings +WHERE length > 5; +``` + +### Batch Analysis with UNION ALL + +```sql +-- Comprehensive security audit in one query +SELECT 'dangerous_func' as check_type, func_at(func_addr) as location, callee_name as detail +FROM disasm_calls +WHERE callee_name IN ('strcpy', 'strcat', 'sprintf', 'gets', 'scanf') + +UNION ALL + +SELECT 'crypto_usage', func_at(func_addr), callee_name +FROM disasm_calls +WHERE callee_name LIKE '%Crypt%' OR callee_name LIKE '%AES%' OR callee_name LIKE '%RSA%' + +UNION ALL + +SELECT 'network_call', func_at(func_addr), callee_name +FROM disasm_calls +WHERE callee_name IN ('socket', 'connect', 'send', 'recv', 'WSAStartup') + +UNION ALL + +SELECT 'registry_access', func_at(func_addr), callee_name +FROM disasm_calls +WHERE callee_name LIKE 'Reg%' + +ORDER BY check_type, location; +``` + +### Efficient Pagination + +```sql +-- Page through large result sets efficiently +SELECT * FROM ( + SELECT + f.name, + f.size, + ROW_NUMBER() OVER (ORDER BY f.size DESC) as row_num + FROM funcs f +) +WHERE row_num BETWEEN 101 AND 200; -- Page 2 (100 per page) +``` + +### EXISTS for Efficient Filtering + +```sql +-- Functions that have at least one string reference (more efficient than JOIN + DISTINCT) +SELECT f.name +FROM funcs f +WHERE EXISTS ( + SELECT 1 FROM xrefs x + JOIN strings s ON x.to_ea = s.address + WHERE x.from_ea BETWEEN f.address AND f.end_ea +); +``` + +```sql +-- Functions without any calls (leaf functions, EXISTS version) +SELECT f.name, f.size +FROM funcs f +WHERE NOT EXISTS ( + SELECT 1 FROM disasm_calls dc + WHERE dc.func_addr = f.address +) +ORDER BY f.size DESC; +``` + +--- + +## Hex Address Formatting + +IDA uses integer addresses. For display, use `printf()`: + +```sql +-- 32-bit format +SELECT printf('0x%08X', address) as addr FROM funcs; + +-- 64-bit format +SELECT printf('0x%016llX', address) as addr FROM funcs; + +-- Auto-width +SELECT printf('0x%X', address) as addr FROM funcs; +``` + +--- + +## Common x86 Instruction Types + +When filtering by `itype` (faster than string comparison): + +| itype | Mnemonic | Description | +|-------|----------|-------------| +| 16 | call (near) | Direct call | +| 18 | call (indirect) | Indirect call | +| 122 | mov | Move data | +| 143 | push | Push to stack | +| 134 | pop | Pop from stack | +| 159 | retn | Return | +| 85 | jz | Jump if zero | +| 79 | jnz | Jump if not zero | +| 27 | cmp | Compare | +| 103 | nop | No operation | + +--- + +## ctree Operation Names + +Common Hex-Rays AST node types: + +**Expressions (cot_*):** +- `cot_call` - Function call +- `cot_var` - Local variable +- `cot_obj` - Global object/function +- `cot_num` - Numeric constant +- `cot_str` - String literal +- `cot_ptr` - Pointer dereference +- `cot_ref` - Address-of +- `cot_asg` - Assignment +- `cot_add`, `cot_sub`, `cot_mul`, `cot_sdiv`, `cot_udiv` - Arithmetic +- `cot_eq`, `cot_ne`, `cot_lt`, `cot_gt` - Comparisons +- `cot_land`, `cot_lor`, `cot_lnot` - Logical +- `cot_band`, `cot_bor`, `cot_xor` - Bitwise + +**Statements (cit_*):** +- `cit_if` - If statement +- `cit_for` - For loop +- `cit_while` - While loop +- `cit_do` - Do-while loop +- `cit_return` - Return statement +- `cit_block` - Code block + +--- + +## Error Handling + +- **No Hex-Rays license:** Decompiler tables (`pseudocode`, `ctree*`, `ctree_lvars`) will be empty or unavailable +- **No constraint on decompiler tables:** Query will be extremely slow (decompiles all functions) +- **Invalid address:** Functions like `func_at(addr)` return NULL +- **Missing function:** JOINs may return fewer rows than expected + +--- + +## Quick Start Examples + +### "What does this binary do?" + +```sql +-- Entry points +SELECT * FROM entries; + +-- Imported APIs (hints at functionality) +SELECT module, name FROM imports ORDER BY module, name; + +-- Interesting strings +SELECT content FROM strings WHERE length > 10 ORDER BY length DESC LIMIT 20; +``` + +### "Find security-relevant code" + +```sql +-- Dangerous string functions +SELECT DISTINCT func_at(func_addr) FROM disasm_calls +WHERE callee_name IN ('strcpy', 'strcat', 'sprintf', 'gets'); + +-- Crypto-related +SELECT * FROM imports WHERE name LIKE '%Crypt%' OR name LIKE '%Hash%'; + +-- Network-related +SELECT * FROM imports WHERE name LIKE '%socket%' OR name LIKE '%connect%' OR name LIKE '%send%'; +``` + +### "Understand a specific function" + +```sql +-- Basic info +SELECT * FROM funcs WHERE address = 0x401000; + +-- Decompile (if Hex-Rays available) +SELECT decompile(0x401000); + +-- Local variables +SELECT name, type, size FROM ctree_lvars WHERE func_addr = 0x401000; + +-- What it calls +SELECT callee_name FROM disasm_calls WHERE func_addr = 0x401000; + +-- What calls it +SELECT func_at(from_ea) FROM xrefs WHERE to_ea = 0x401000 AND is_code = 1; +``` + +### "Find all uses of a string" + +```sql +SELECT s.content, func_at(x.from_ea) as function, printf('0x%X', x.from_ea) as location +FROM strings s +JOIN xrefs x ON s.address = x.to_ea +WHERE s.content LIKE '%config%'; +``` + +--- + +## Natural Language Query Examples + +These examples show how to translate common user questions into SQL. + +### Function Signature Queries + +**"Show me functions that return integers"** +```sql +-- Using funcs table (recommended - direct and fast) +SELECT name, return_type, arg_count FROM funcs +WHERE return_is_integral = 1 +LIMIT 20; + +-- Or via types_func_args (for typedef-aware queries) +SELECT DISTINCT type_name FROM types_func_args +WHERE arg_index = -1 AND is_integral_resolved = 1; +``` + +**"Show me functions that take 4 string arguments"** +```sql +-- String = char* or wchar_t* +SELECT type_name, COUNT(*) as string_args +FROM types_func_args +WHERE arg_index >= 0 + AND is_ptr_resolved = 1 + AND base_type_resolved IN ('char', 'wchar_t', 'CHAR', 'WCHAR') +GROUP BY type_ordinal +HAVING string_args = 4; +``` + +**"Which functions return pointers?"** +```sql +SELECT name, return_type FROM funcs +WHERE return_is_ptr = 1 +ORDER BY name LIMIT 20; +``` + +**"Find void functions with many arguments"** +```sql +SELECT name, arg_count FROM funcs +WHERE return_is_void = 1 AND arg_count >= 4 +ORDER BY arg_count DESC; +``` + +**"What calling conventions are used?"** +```sql +SELECT calling_conv, COUNT(*) as count +FROM funcs +WHERE calling_conv IS NOT NULL AND calling_conv != '' +GROUP BY calling_conv ORDER BY count DESC; +``` + +### Return Value Analysis + +**"Which functions return 0?"** +```sql +SELECT DISTINCT f.name FROM funcs f +JOIN ctree_v_returns r ON r.func_addr = f.address +WHERE r.return_num = 0; +``` + +**"Find functions that return -1 (error pattern)"** +```sql +SELECT DISTINCT f.name FROM funcs f +JOIN ctree_v_returns r ON r.func_addr = f.address +WHERE r.return_num = -1; +``` + +**"Functions that return their input argument"** +```sql +SELECT DISTINCT f.name FROM funcs f +JOIN ctree_v_returns r ON r.func_addr = f.address +WHERE r.returns_arg = 1; +``` + +**"Functions that return the result of another call (wrappers)"** +```sql +SELECT DISTINCT f.name FROM funcs f +JOIN ctree_v_returns r ON r.func_addr = f.address +WHERE r.returns_call_result = 1; +``` + +**"Functions with multiple return statements"** +```sql +SELECT f.name, COUNT(*) as return_count +FROM funcs f +JOIN ctree_v_returns r ON r.func_addr = f.address +GROUP BY f.address +HAVING return_count > 1 +ORDER BY return_count DESC LIMIT 20; +``` + +### Type Analysis + +**"Find typedefs that hide pointers (like HANDLE)"** +```sql +SELECT DISTINCT type_name, arg_type, base_type_resolved +FROM types_func_args +WHERE is_ptr = 0 AND is_ptr_resolved = 1; +``` + +**"Functions with struct parameters"** +```sql +SELECT type_name, arg_name, arg_type FROM types_func_args +WHERE arg_index >= 0 AND is_struct = 1; +``` + +### Combined Queries + +**"Integer-returning functions with 3+ args that return specific values"** +```sql +SELECT f.name, f.return_type, f.arg_count, r.return_num +FROM funcs f +JOIN ctree_v_returns r ON r.func_addr = f.address +WHERE f.return_is_integral = 1 + AND f.arg_count >= 3 + AND r.return_num IS NOT NULL +ORDER BY r.return_num; +``` + +**"Fastcall functions that return pointers"** +```sql +SELECT name, return_type, arg_count FROM funcs +WHERE calling_conv = 'fastcall' AND return_is_ptr = 1; +``` + +--- + +## Summary: When to Use What + +| Goal | Table/Function | +|------|----------------| +| List all functions | `funcs` | +| Functions by return type | `funcs WHERE return_is_integral = 1` | +| Functions by arg count | `funcs WHERE arg_count >= N` | +| Void functions | `funcs WHERE return_is_void = 1` | +| Pointer-returning functions | `funcs WHERE return_is_ptr = 1` | +| Functions by calling convention | `funcs WHERE calling_conv = 'fastcall'` | +| Find who calls what | `xrefs` with `is_code = 1` | +| Find data references | `xrefs` with `is_code = 0` | +| Analyze imports | `imports` | +| Find strings | `strings` | +| Configure string types | `rebuild_strings(types, minlen)` | +| Instruction analysis | `instructions WHERE func_addr = X` | +| View decompiled code | `decompile(addr)` | +| Edit decompiler comments | `UPDATE pseudocode SET comment = '...' WHERE func_addr = X AND ea = Y` | +| AST pattern matching | `ctree WHERE func_addr = X` | +| Call patterns | `ctree_v_calls`, `disasm_calls` | +| Control flow | `ctree_v_loops`, `ctree_v_ifs` | +| Return value analysis | `ctree_v_returns` | +| Functions returning specific values | `ctree_v_returns WHERE return_num = 0` | +| Pass-through functions | `ctree_v_returns WHERE returns_arg = 1` | +| Wrapper functions | `ctree_v_returns WHERE returns_call_result = 1` | +| Variable analysis | `ctree_lvars WHERE func_addr = X` | +| Type information | `types`, `types_members` | +| Function signatures | `types_func_args` (with type classification) | +| Functions by return type | `types_func_args WHERE arg_index = -1` | +| Typedef-aware type queries | `types_func_args` (surface vs resolved) | +| Hidden pointer types | `types_func_args WHERE is_ptr = 0 AND is_ptr_resolved = 1` | +| Manage breakpoints | `breakpoints` (full CRUD) | +| Modify segments | `segments` (UPDATE name/class/perm, DELETE) | +| Delete instructions | `instructions` (DELETE converts to unexplored bytes) | +| Create types | `types` (INSERT struct/union/enum) | +| Add struct members | `types_members` (INSERT) | +| Add enum values | `types_enum_values` (INSERT) | +| Modify database | `funcs`, `names`, `comments`, `bookmarks` (INSERT/UPDATE/DELETE) | +| Jump to Anything | `jump_entities('pattern', 'mode')` | +| Entity search (JSON) | `jump_search('pattern', 'mode', limit, offset)` | + +**Remember:** Always use `func_addr = X` constraints on instruction and decompiler tables for acceptable performance. + +--- + +## Server Modes + +IDASQL supports two server protocols for remote queries: **HTTP REST** (recommended) and raw TCP. + +--- + +### HTTP REST Server (Recommended) + +Standard REST API that works with curl, any HTTP client, or LLM tools. + +**Starting the server:** +```bash +# Default port 8081 +idasql -s database.i64 --http + +# Custom port and bind address +idasql -s database.i64 --http 9000 --bind 0.0.0.0 + +# With authentication +idasql -s database.i64 --http 8081 --token mysecret +``` + +**HTTP Endpoints:** + +| Endpoint | Method | Auth | Description | +|----------|--------|------|-------------| +| `/` | GET | No | Welcome message | +| `/help` | GET | No | API documentation (for LLM discovery) | +| `/query` | POST | Yes* | Execute SQL (body = raw SQL) | +| `/status` | GET | Yes* | Health check | +| `/shutdown` | POST | Yes* | Stop server | + +*Auth required only if `--token` was specified. + +**Example with curl:** +```bash +# Get API documentation +curl http://localhost:8081/help + +# Execute SQL query +curl -X POST http://localhost:8081/query -d "SELECT name, size FROM funcs LIMIT 5" + +# With authentication +curl -X POST http://localhost:8081/query \ + -H "Authorization: Bearer mysecret" \ + -d "SELECT * FROM funcs" + +# Check status +curl http://localhost:8081/status +``` + +**Response Format (JSON):** +```json +{"success": true, "columns": ["name", "size"], "rows": [["main", "500"]], "row_count": 1} +``` + +```json +{"success": false, "error": "no such table: bad_table"} +``` + +--- + +### Raw TCP Server (Legacy) + +Binary protocol with length-prefixed JSON. Use only when HTTP is not available. + +**Starting the server:** +```bash +idasql -s database.i64 --server 13337 +idasql -s database.i64 --server 13337 --token mysecret +``` + +**Connecting as client:** +```bash +idasql --remote localhost:13337 -q "SELECT name FROM funcs LIMIT 5" +idasql --remote localhost:13337 -i +``` + +``` + +`scripts/embed_prompt.py`: + +```py +#!/usr/bin/env python3 +""" +Converts idasql-agent.md to a C++ header with embedded raw string literal. +Only regenerates if the hpp is older than the md or doesn't exist. + +Run: python scripts/embed_prompt.py prompts/idasql_agent.md src/common/idasql_agent_prompt.hpp +""" + +import sys +import os +from datetime import datetime + + +def needs_regeneration(input_path: str, output_path: str) -> bool: + """Check if output needs regeneration based on file timestamps.""" + if not os.path.exists(output_path): + return True + input_mtime = os.path.getmtime(input_path) + output_mtime = os.path.getmtime(output_path) + return input_mtime > output_mtime + + +def split_content(content: str, max_chunk: int = 15000) -> list: + """Split content into chunks that MSVC can handle. + + MSVC has a ~16KB limit per string literal segment. + We split at line boundaries to keep it readable. + """ + chunks = [] + lines = content.split('\n') + current_chunk = [] + current_size = 0 + + for line in lines: + line_size = len(line) + 1 # +1 for newline + if current_size + line_size > max_chunk and current_chunk: + chunks.append('\n'.join(current_chunk)) + current_chunk = [line] + current_size = line_size + else: + current_chunk.append(line) + current_size += line_size + + if current_chunk: + chunks.append('\n'.join(current_chunk)) + + return chunks + + +def embed_prompt(input_path: str, output_path: str, force: bool = False) -> bool: + if not force and not needs_regeneration(input_path, output_path): + print(f"Skipping {output_path} (up-to-date)") + return False + + with open(input_path, 'r', encoding='utf-8') as f: + content = f.read() + + # Use short delimiter (MSVC max is 16 chars) + delimiter = "PROMPT" + + # Split content for MSVC compatibility + chunks = split_content(content) + + # Build concatenated string literals + if len(chunks) == 1: + string_literal = f'R"{delimiter}({chunks[0]}){delimiter}"' + else: + parts = [] + for i, chunk in enumerate(chunks): + parts.append(f'R"{delimiter}({chunk}){delimiter}"') + string_literal = '\n '.join(parts) + + header = f'''// Auto-generated from {os.path.basename(input_path)} +// Generated: {datetime.now().isoformat()} +// DO NOT EDIT - regenerate with: python scripts/embed_prompt.py + +#pragma once + +namespace idasql {{ + +inline constexpr const char* SYSTEM_PROMPT = + {string_literal}; + +}} // namespace idasql +''' + + os.makedirs(os.path.dirname(output_path), exist_ok=True) + with open(output_path, 'w', encoding='utf-8') as f: + f.write(header) + + print(f"Generated {output_path} ({len(content)} bytes, {len(chunks)} chunks)") + return True + + +if __name__ == "__main__": + force = "--force" in sys.argv + args = [a for a in sys.argv[1:] if a != "--force"] + + if len(args) != 2: + print(f"Usage: {sys.argv[0]} [--force] ") + sys.exit(1) + + embed_prompt(args[0], args[1], force) + +``` + +`src/CMakeLists.txt`: + +```txt +# src/CMakeLists.txt - idasql source directory +# SQLite is now provided by xsql::xsql from libxsql + +add_subdirectory(lib) + +# CLI tool (standalone, uses idalib) +option(IDASQL_BUILD_CLI "Build idasql CLI tool" ON) +if(IDASQL_BUILD_CLI) + add_subdirectory(cli) +endif() + +# IDA Plugin (requires IDASDK) +option(IDASQL_BUILD_PLUGIN "Build idasql IDA plugin" ON) +if(IDASQL_BUILD_PLUGIN) + add_subdirectory(plugin) +endif() + +``` + +`src/cli/CMakeLists.txt`: + +```txt +# idasql CLI - Command-line SQL interface to IDA databases +# Built as part of the main idasql project + +# idasql CLI executable +ida_add_idalib(idasql_cli + TYPE EXECUTABLE + SOURCES + main.cpp +) +target_link_libraries(idasql_cli PRIVATE xsql::xsql) +target_include_directories(idasql_cli PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../lib/include) +target_compile_definitions(idasql_cli PRIVATE USE_IDA_SDK) + +# Output name: idasql (not idasql_cli), placed next to ida.exe for idalib +set_target_properties(idasql_cli PROPERTIES + OUTPUT_NAME "idasql" + RUNTIME_OUTPUT_DIRECTORY "${IDABIN}" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${IDABIN}" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${IDABIN}" +) + +# Add Windows manifest for UTF-8 support (Windows 10 1903+) +if(WIN32) + target_sources(idasql_cli PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/idasql.manifest + ) +endif() + +# Platform-specific settings +if(MSVC) + file(TO_NATIVE_PATH "${IDABIN}" IDABIN_NATIVE) + set_target_properties(idasql_cli PROPERTIES + VS_DEBUGGER_ENVIRONMENT "PATH=${IDABIN_NATIVE};%PATH%" + ) + target_link_libraries(idasql_cli PRIVATE ws2_32) +elseif(APPLE) + set_target_properties(idasql_cli PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "@executable_path;$ENV{IDASDK}/bin" + ) +elseif(UNIX) + set_target_properties(idasql_cli PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "$ORIGIN;$ENV{IDASDK}/bin" + ) +endif() + +# Hex-Rays decompiler support +option(USE_HEXRAYS "Enable Hex-Rays decompiler support" ON) +if(USE_HEXRAYS) + target_compile_definitions(idasql_cli PRIVATE USE_HEXRAYS) +endif() + +# AI Agent support (from parent project) +if(IDASQL_WITH_AI_AGENT) + target_sources(idasql_cli PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../common/ai_agent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../common/mcp_server.cpp + ) + target_include_directories(idasql_cli PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../common + ${CMAKE_CURRENT_SOURCE_DIR}/../../external/libagents/external/fastmcpp/include + ) + target_link_libraries(idasql_cli PRIVATE libagents fastmcpp_core) + target_compile_definitions(idasql_cli PRIVATE IDASQL_HAS_AI_AGENT) + + if(TARGET generate_prompt) + add_dependencies(idasql_cli generate_prompt) + endif() + + message(STATUS "idasql_cli: AI agent support enabled (with MCP server)") +endif() + +# HTTP server support +target_sources(idasql_cli PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../common/http_server.cpp +) +target_compile_definitions(idasql_cli PRIVATE XSQL_HAS_THINCLIENT) + +# cpp-httplib: provided transitively by xsql::xsql when XSQL_WITH_THINCLIENT=ON. +# Only fetch standalone if the target doesn't already exist (e.g. standalone build). +if(NOT TARGET httplib) + include(FetchContent) + FetchContent_Declare( + cpp_httplib + GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git + GIT_TAG v0.15.3 + ) + FetchContent_MakeAvailable(cpp_httplib) + target_include_directories(idasql_cli PRIVATE ${cpp_httplib_SOURCE_DIR}) +endif() + +# idasql.exe is placed next to ida.exe via RUNTIME_OUTPUT_DIRECTORY above. + +``` + +`src/cli/idasql.manifest`: + +```manifest + + + + + + UTF-8 + + + + +``` + +`src/cli/main.cpp`: + +```cpp +/** + * idasql CLI - Command-line SQL interface to IDA databases + * + * Usage: + * idasql -s database.i64 -q "SELECT * FROM funcs" # Single query (local) + * idasql -s database.i64 -c "SELECT * FROM funcs" # Same as -q (Python-style) + * idasql -s database.i64 -f script.sql # Execute SQL file + * idasql -s database.i64 -i # Interactive mode + * idasql -s database.i64 --export out.sql # Export all tables to SQL + * idasql -s database.i64 --export out.sql --export-tables=funcs,segments + * idasql --remote localhost:13337 -q "SELECT * FROM funcs" # Remote mode + * + * Switches: + * -s IDA database file (.idb/.i64) for local mode + * --remote Connect to IDASQL plugin server + * -q Execute single SQL query + * -c Execute single SQL query (alias for -q) + * -f Execute SQL from file + * -i Interactive REPL mode + * --export Export tables to SQL file (local only) + * --export-tables=... Tables to export (* for all, or table1,table2,...) + * -h Show help + * + * Architecture Note: + * Remote mode (--remote) is a thin client that only uses sockets - no IDA + * functions are called. However, ida.dll must still be in PATH because the + * executable links against it (delayed loading is not possible due to + * data symbol imports like callui). + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Windows UTF-8 console support +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif + +// Socket client for remote mode (shared library, no IDA dependency) +#include +#include +#include "../common/http_server.hpp" + +#include +#include "../common/idasql_version.hpp" + +// AI Agent integration (optional, enabled via IDASQL_WITH_AI_AGENT) +#ifdef IDASQL_HAS_AI_AGENT +#include "../common/ai_agent.hpp" +#include "../common/idasql_commands.hpp" +#include "../common/mcp_server.hpp" + +// Global signal handler state +namespace { + std::atomic g_quit_requested{false}; + idasql::AIAgent* g_agent = nullptr; + std::unique_ptr g_mcp_server; + std::unique_ptr g_mcp_agent; + std::unique_ptr g_repl_http_server; +} + +extern "C" void signal_handler(int sig) { + (void)sig; + g_quit_requested.store(true); + if (g_agent) { + g_agent->request_quit(); + } +} +#endif + +// ============================================================================ +// Table Printing (shared between remote and local modes) +// ============================================================================ + +static int print_callback(void*, int argc, char** argv, char** colNames) { + for (int i = 0; i < argc; i++) { + std::cout << colNames[i] << " = " << (argv[i] ? argv[i] : "NULL"); + if (i < argc - 1) std::cout << " | "; + } + std::cout << std::endl; + return 0; +} + +// Table-style output +struct TablePrinter { + std::vector columns; + std::vector> rows; + std::vector widths; + bool first_row = true; + + void add_row(int argc, char** argv, char** colNames) { + if (first_row) { + columns.reserve(argc); + widths.resize(argc, 0); + for (int i = 0; i < argc; i++) { + columns.push_back(colNames[i] ? colNames[i] : ""); + widths[i] = (std::max)(widths[i], columns[i].length()); + } + first_row = false; + } + + std::vector row; + row.reserve(argc); + for (int i = 0; i < argc; i++) { + std::string val = argv[i] ? argv[i] : "NULL"; + row.push_back(val); + widths[i] = (std::max)(widths[i], val.length()); + } + rows.push_back(std::move(row)); + } + + void add_row(const std::vector& cols, + const std::vector& values) { + if (first_row) { + columns = cols; + widths.assign(columns.size(), 0); + for (size_t i = 0; i < columns.size(); i++) { + widths[i] = (std::max)(widths[i], columns[i].length()); + } + first_row = false; + } + + std::vector row = values; + if (row.size() < columns.size()) { + row.resize(columns.size()); + } + for (size_t i = 0; i < row.size(); i++) { + widths[i] = (std::max)(widths[i], row[i].length()); + } + rows.push_back(std::move(row)); + } + + void print() { + if (columns.empty()) return; + + // Header separator + std::string sep = "+"; + for (size_t w : widths) { + sep += std::string(w + 2, '-') + "+"; + } + + // Header + std::cout << sep << "\n| "; + for (size_t i = 0; i < columns.size(); i++) { + std::cout << std::left; + std::cout.width(widths[i]); + std::cout << columns[i] << " | "; + } + std::cout << "\n" << sep << "\n"; + + // Rows + for (const auto& row : rows) { + std::cout << "| "; + for (size_t i = 0; i < row.size(); i++) { + std::cout << std::left; + std::cout.width(widths[i]); + std::cout << row[i] << " | "; + } + std::cout << "\n"; + } + std::cout << sep << "\n"; + std::cout << rows.size() << " row(s)\n"; + } +}; + +static TablePrinter* g_printer = nullptr; + +static int table_callback(void*, int argc, char** argv, char** colNames) { + if (g_printer) { + g_printer->add_row(argc, argv, colNames); + } + return 0; +} + +static bool parse_port(const std::string& s, int& port) { + try { + size_t idx = 0; + int v = std::stoi(s, &idx, 10); + if (idx != s.size()) return false; + if (v < 1 || v > 65535) return false; + port = v; + return true; + } catch (...) { + return false; + } +} + +// ============================================================================ +// Validation Helpers +// ============================================================================ + +static bool is_safe_table_name(const std::string& name) { + if (name.empty() || name.size() > 128) return false; + return std::all_of(name.begin(), name.end(), [](unsigned char c) { + return std::isalnum(c) || c == '_'; + }); +} + +// ============================================================================ +// Remote Mode - Pure socket client (NO IDA DEPENDENCIES) +// ============================================================================ +// This entire section uses only standard C++ and sockets. +// On Windows with delayed loading, ida.dll/idalib.dll are never loaded +// when running in remote mode. + +static void print_remote_result(const xsql::socket::RemoteResult& qr) { + if (qr.rows.empty() && qr.columns.empty()) { + std::cout << "OK\n"; + return; + } + TablePrinter printer; + for (size_t r = 0; r < qr.rows.size(); r++) { + std::vector argv_ptrs(qr.columns.size()); + std::vector cols_ptrs(qr.columns.size()); + for (size_t c = 0; c < qr.columns.size(); c++) { + argv_ptrs[c] = const_cast(qr.rows[r][c].c_str()); + cols_ptrs[c] = const_cast(qr.columns[c].c_str()); + } + printer.add_row(static_cast(qr.columns.size()), + argv_ptrs.data(), cols_ptrs.data()); + } + printer.print(); +} + +static int run_remote_mode(const std::string& host, int port, + const std::string& query, + const std::string& sql_file, + const std::string& auth_token, + bool interactive, + const std::string& nl_prompt = "", + bool verbose_mode = false, + const std::string& provider_override = "") { + std::cerr << "Connecting to " << host << ":" << port << "..." << std::endl; + xsql::socket::Client remote; + if (!auth_token.empty()) { + remote.set_auth_token(auth_token); + } + if (!remote.connect(host, port)) { + std::cerr << "Error: " << remote.error() << std::endl; + return 1; + } + std::cerr << "Connected." << std::endl; + + int result = 0; + +#ifdef IDASQL_HAS_AI_AGENT + if (!nl_prompt.empty()) { + // Natural language query via remote + auto executor = [&remote](const std::string& sql) -> std::string { + auto qr = remote.query(sql); + if (!qr.success) { + return "Error: " + qr.error; + } + // Format result as string + std::ostringstream oss; + if (!qr.columns.empty()) { + for (size_t i = 0; i < qr.columns.size(); i++) { + if (i > 0) oss << " | "; + oss << qr.columns[i]; + } + oss << "\n"; + for (const auto& row : qr.rows) { + for (size_t i = 0; i < row.size(); i++) { + if (i > 0) oss << " | "; + oss << row[i]; + } + oss << "\n"; + } + } + return oss.str(); + }; + + idasql::AgentSettings settings = idasql::LoadAgentSettings(); + if (!provider_override.empty()) { + try { + settings.default_provider = idasql::ParseProviderType(provider_override); + } catch (...) {} + } + + idasql::AIAgent agent(executor, settings, verbose_mode); + g_agent = &agent; + std::signal(SIGINT, signal_handler); + + agent.start(); + std::string response = agent.query(nl_prompt); + agent.stop(); + + g_agent = nullptr; + std::signal(SIGINT, SIG_DFL); + + std::cout << response << "\n"; + return 0; + } +#else + (void)nl_prompt; + (void)verbose_mode; + (void)provider_override; +#endif + + if (!query.empty()) { + // Single query + auto qr = remote.query(query); + if (qr.success) { + print_remote_result(qr); + } else { + std::cerr << "Error: " << qr.error << "\n"; + result = 1; + } + } else if (!sql_file.empty()) { + // File execution (remote) + std::ifstream file(sql_file); + if (!file.is_open()) { + std::cerr << "Cannot open file: " << sql_file << "\n"; + return 1; + } + std::stringstream buffer; + buffer << file.rdbuf(); + std::string content = buffer.str(); + + std::vector statements; + std::string parse_error; + if (!xsql::collect_statements(nullptr, content, statements, parse_error)) { + std::cerr << "Error parsing SQL file: " << parse_error << "\n"; + return 1; + } + + for (const auto& stmt : statements) { + auto qr = remote.query(stmt); + if (qr.success) { + print_remote_result(qr); + std::cout << "\n"; + } else { + std::cerr << "Error: " << qr.error << "\n"; + std::cerr << "Query: " << stmt << "\n"; + result = 1; + break; + } + } + } else if (interactive) { + // Interactive REPL (remote) + std::string line; + std::string stmt; + std::cout << "IDASQL Remote Interactive Mode (" << host << ":" << port << ")\n" + << "Type .quit to exit\n\n"; + + while (true) { + std::cout << (stmt.empty() ? "idasql> " : " ...> "); + std::cout.flush(); + if (!std::getline(std::cin, line)) break; + if (line.empty()) continue; + + if (stmt.empty() && line[0] == '.') { + if (line == ".quit" || line == ".exit") break; + if (line == ".tables") { + auto qr = remote.query("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;"); + if (qr.success) { + std::cout << "Tables:\n"; + for (const auto& row : qr.rows) { + std::cout << " " << row[0] << "\n"; + } + } + continue; + } + if (line == ".help") { + std::cout << R"( +Commands: + .tables List all tables + .clear Clear session + .quit / .exit Exit interactive mode + .help Show this help + +SQL queries end with semicolon (;) +)" << std::endl; + continue; + } + if (line == ".clear") { + std::cout << "Session cleared\n"; + continue; + } + std::cerr << "Unknown command: " << line << "\n"; + continue; + } + + stmt += line + " "; + size_t last = line.length() - 1; + while (last > 0 && (line[last] == ' ' || line[last] == '\t')) last--; + if (line[last] == ';') { + auto qr = remote.query(stmt); + if (qr.success) { + print_remote_result(qr); + } else { + std::cerr << "Error: " << qr.error << "\n"; + } + stmt.clear(); + } + } + } + + return result; +} + +// ============================================================================ +// Local Mode - Uses IDA SDK (delay-loaded on Windows) +// ============================================================================ +// From here on, code may call IDA functions. On Windows with /DELAYLOAD, +// ida.dll and idalib.dll are loaded on first use. +// +// Platform-specific include order: +// - Windows: json before IDA (IDA poisons stdlib functions) +// - macOS/Linux: IDA before json +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif + +// ============================================================================ +// REPL - Interactive Mode (Local) +// ============================================================================ + +static void show_help() { + std::cout << R"( +Commands: + .tables List all tables + .schema [table] Show table schema + .info Show database info + .clear Clear session (reset conversation) + .quit / .exit Exit interactive mode + .help Show this help + +SQL queries end with semicolon (;) +Multi-line queries are supported. +)" << std::endl; +} + +static void show_tables(idasql::Database& db) { + std::cout << "Tables:\n"; + db.exec( + "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;", + [](void*, int, char** argv, char**) -> int { + std::cout << " " << (argv[0] ? argv[0] : "") << "\n"; + return 0; + }, + nullptr + ); +} + +static void show_schema(idasql::Database& db, const std::string& table) { + if (!is_safe_table_name(table)) { + std::cerr << "Invalid table name\n"; + return; + } + + std::string sql = "SELECT sql FROM sqlite_master WHERE type='table' AND name='" + table + "';"; + db.exec(sql.c_str(), + [](void*, int, char** argv, char**) -> int { + std::cout << (argv[0] ? argv[0] : "Not found") << "\n"; + return 0; + }, + nullptr + ); +} + +// Helper to execute SQL and format results as string (for AI agent) +static std::string execute_sql_to_string(idasql::Database& db, const std::string& sql) { + std::stringstream ss; + TablePrinter printer; + g_printer = &printer; + int rc = db.exec(sql.c_str(), table_callback, nullptr); + g_printer = nullptr; + + if (rc == SQLITE_OK) { + // Capture to string instead of stdout + std::streambuf* old_cout = std::cout.rdbuf(ss.rdbuf()); + printer.print(); + std::cout.rdbuf(old_cout); + return ss.str(); + } else { + return "Error: " + std::string(db.error()); + } +} + +// Forward declaration (defined in HTTP section below) +static std::string query_result_to_json(idasql::Database& db, const std::string& sql); + +#ifdef IDASQL_HAS_AI_AGENT +static void run_repl(idasql::Database& db, bool agent_mode, bool verbose, + const std::string& provider_override = "") { +#else +static void run_repl(idasql::Database& db) { + [[maybe_unused]] bool agent_mode = false; +#endif + std::string line; + std::string query; + +#ifdef IDASQL_HAS_AI_AGENT + std::unique_ptr agent; + if (agent_mode) { + auto executor = [&db](const std::string& sql) -> std::string { + return execute_sql_to_string(db, sql); + }; + + // Load settings (includes BYOK, provider, timeout) + idasql::AgentSettings settings = idasql::LoadAgentSettings(); + + // Apply provider override from CLI if specified + if (!provider_override.empty()) { + try { + settings.default_provider = idasql::ParseProviderType(provider_override); + } catch (...) { + // Already validated in argument parsing + } + } + + agent = std::make_unique(executor, settings, verbose); + + // Register signal handler for clean Ctrl-C handling + g_agent = agent.get(); + std::signal(SIGINT, signal_handler); +#ifdef _WIN32 + // Windows also needs SIGBREAK for Ctrl-Break + std::signal(SIGBREAK, signal_handler); +#endif + + agent->start(); // Initialize agent + + std::cout << "IDASQL AI Agent Mode\n" + << "Ask questions in natural language or use SQL directly.\n" + << "Type .help for commands, .clear to reset, .quit to exit\n\n"; + } else { +#endif + std::cout << "IDASQL Interactive Mode\n" + << "Type .help for commands, .clear to reset, .quit to exit\n\n"; +#ifdef IDASQL_HAS_AI_AGENT + } +#endif + + while (true) { +#ifdef IDASQL_HAS_AI_AGENT + // Check for quit request from signal handler + if (g_quit_requested.load()) { + std::cout << "\nInterrupted.\n"; + break; + } +#endif + + // Prompt + std::cout << (query.empty() ? "idasql> " : " ...> "); + std::cout.flush(); + + if (!std::getline(std::cin, line)) break; + if (line.empty()) continue; + + // Handle dot commands + if (query.empty() && !line.empty() && line[0] == '.') { +#ifdef IDASQL_HAS_AI_AGENT + // Use unified command handler for agent mode + idasql::CommandCallbacks callbacks; + callbacks.get_tables = [&db]() -> std::string { + std::stringstream ss; + auto result = db.query("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"); + for (const auto& row : result.rows) { + if (row.size() > 0) ss << row[0] << "\n"; + } + return ss.str(); + }; + callbacks.get_schema = [&db](const std::string& table) -> std::string { + auto result = db.query("SELECT sql FROM sqlite_master WHERE name='" + table + "'"); + if (!result.empty() && result.rows[0].size() > 0) { + return std::string(result.rows[0][0]); + } + return "Table not found: " + table; + }; + callbacks.get_info = [&db]() -> std::string { + return db.info(); + }; + callbacks.clear_session = [&agent]() -> std::string { + if (agent) { + agent->reset_session(); + return "Session cleared (conversation history reset)"; + } + return "Session cleared"; + }; + + // MCP server callbacks + callbacks.mcp_status = []() -> std::string { + if (g_mcp_server && g_mcp_server->is_running()) { + return idasql::format_mcp_status(g_mcp_server->port(), true); + } else { + return "MCP server not running\nUse '.mcp start' to start\n"; + } + }; + + callbacks.mcp_start = [&db, &agent](int req_port, const std::string& bind_addr) -> std::string { + if (g_mcp_server && g_mcp_server->is_running()) { + return idasql::format_mcp_status(g_mcp_server->port(), true); + } + + // Create MCP server if needed + if (!g_mcp_server) { + g_mcp_server = std::make_unique(); + } + + // SQL executor - will be called on main thread via wait() + idasql::QueryCallback sql_cb = [&db](const std::string& sql) -> std::string { + auto result = db.query(sql); + if (result.success) { + return result.to_string(); + } + return "Error: " + result.error; + }; + + // Create MCP agent for natural language queries + g_mcp_agent = std::make_unique(sql_cb); + g_mcp_agent->start(); + + idasql::AskCallback ask_cb = [](const std::string& question) -> std::string { + if (!g_mcp_agent) return "Error: AI agent not available"; + return g_mcp_agent->query(question); + }; + + // Start with use_queue=true for CLI mode (main thread execution) + int port = g_mcp_server->start(req_port, sql_cb, ask_cb, bind_addr, true); + if (port <= 0) { + g_mcp_agent.reset(); + return "Error: Failed to start MCP server\n"; + } + + // Print info + std::cout << idasql::format_mcp_info(port, true); + std::cout << "Press Ctrl+C to stop MCP server and return to REPL...\n\n"; + std::cout.flush(); + + // Install signal handler so Ctrl+C sets g_quit_requested + g_quit_requested.store(false); + auto old_handler = std::signal(SIGINT, signal_handler); +#ifdef _WIN32 + auto old_break_handler = std::signal(SIGBREAK, signal_handler); +#endif + + // Set interrupt check to stop on Ctrl+C + g_mcp_server->set_interrupt_check([]() { + return g_quit_requested.load(); + }); + + // Enter wait loop - processes MCP commands on main thread + // This blocks until Ctrl+C or .mcp stop via another client + g_mcp_server->run_until_stopped(); + + // Restore previous signal handler + std::signal(SIGINT, old_handler); +#ifdef _WIN32 + std::signal(SIGBREAK, old_break_handler); +#endif + g_mcp_agent.reset(); + g_quit_requested.store(false); // Reset for continued REPL use + + return "MCP server stopped. Returning to REPL.\n"; + }; + + callbacks.mcp_stop = []() -> std::string { + if (g_mcp_server && g_mcp_server->is_running()) { + g_mcp_server->stop(); + g_mcp_agent.reset(); + return "MCP server stopped\n"; + } + return "MCP server not running\n"; + }; + + // HTTP server callbacks + callbacks.http_status = []() -> std::string { + if (g_repl_http_server && g_repl_http_server->is_running()) { + return idasql::format_http_status(g_repl_http_server->port(), true); + } + return "HTTP server not running\nUse '.http start' to start\n"; + }; + + callbacks.http_start = [&db](int req_port, const std::string& bind_addr) -> std::string { + if (g_repl_http_server && g_repl_http_server->is_running()) { + return idasql::format_http_status(g_repl_http_server->port(), true); + } + + // Create HTTP server if needed + if (!g_repl_http_server) { + g_repl_http_server = std::make_unique(); + } + + // SQL executor - called on main thread via run_until_stopped() + idasql::HTTPQueryCallback sql_cb = [&db](const std::string& sql) -> std::string { + return query_result_to_json(db, sql); + }; + + // Start with use_queue=true (CLI mode) + int port = g_repl_http_server->start(req_port, sql_cb, bind_addr, true); + if (port <= 0) { + return "Error: Failed to start HTTP server\n"; + } + + // Print info + std::cout << idasql::format_http_info(port); + std::cout.flush(); + + // Install signal handler so Ctrl+C sets g_quit_requested + g_quit_requested.store(false); + auto old_handler = std::signal(SIGINT, signal_handler); +#ifdef _WIN32 + auto old_break_handler = std::signal(SIGBREAK, signal_handler); +#endif + + // Set interrupt check to stop on Ctrl+C + g_repl_http_server->set_interrupt_check([]() { + return g_quit_requested.load(); + }); + + // Enter wait loop - processes HTTP commands on main thread + // This blocks until Ctrl+C or /shutdown + g_repl_http_server->run_until_stopped(); + + // Restore previous signal handler + std::signal(SIGINT, old_handler); +#ifdef _WIN32 + std::signal(SIGBREAK, old_break_handler); +#endif + g_quit_requested.store(false); // Reset for continued REPL use + + return "HTTP server stopped. Returning to REPL.\n"; + }; + + callbacks.http_stop = []() -> std::string { + if (g_repl_http_server && g_repl_http_server->is_running()) { + g_repl_http_server->stop(); + return "HTTP server stopped\n"; + } + return "HTTP server not running\n"; + }; + + std::string output; + auto result = idasql::handle_command(line, callbacks, output); + + switch (result) { + case idasql::CommandResult::QUIT: + goto exit_repl; // Exit the while loop + case idasql::CommandResult::HANDLED: + if (!output.empty()) { + std::cout << output; + if (output.back() != '\n') std::cout << "\n"; + } + continue; + case idasql::CommandResult::NOT_HANDLED: + // Fall through to standard handling + break; + } +#else + // Non-agent mode: basic command handling + if (line == ".quit" || line == ".exit") break; + if (line == ".tables") { show_tables(db); continue; } + if (line == ".info") { std::cout << db.info(); continue; } + if (line == ".help") { show_help(); continue; } + if (line == ".clear") { + std::cout << "Session cleared\n"; + continue; + } + if (line.substr(0, 7) == ".schema") { + std::string table = line.length() > 8 ? line.substr(8) : ""; + while (!table.empty() && table[0] == ' ') table = table.substr(1); + if (table.empty()) { + std::cerr << "Usage: .schema \n"; + } else { + show_schema(db, table); + } + continue; + } + std::cerr << "Unknown command: " << line << "\n"; + continue; +#endif + } + +#ifdef IDASQL_HAS_AI_AGENT + // In agent mode, use query for main-thread safety + if (agent_mode && agent) { + std::string result = agent->query(line); + if (!result.empty()) { + std::cout << result << "\n"; + } + + // Check if we were interrupted + if (agent->quit_requested()) { + std::cout << "Interrupted.\n"; + break; + } + continue; + } +#endif + + // Standard SQL mode: accumulate query + query += line + " "; + + // Execute if complete (ends with ;) + size_t last = line.length() - 1; + while (last > 0 && (line[last] == ' ' || line[last] == '\t')) last--; + if (line[last] == ';') { + TablePrinter printer; + g_printer = &printer; + int rc = db.exec(query.c_str(), table_callback, nullptr); + g_printer = nullptr; + + if (rc == SQLITE_OK) { + printer.print(); + } else { + std::cerr << "Error: " << db.error() << "\n"; + } + query.clear(); + } + } + +#ifdef IDASQL_HAS_AI_AGENT +exit_repl: + if (agent) { + agent->stop(); + g_agent = nullptr; + } + // Restore default signal handler + std::signal(SIGINT, SIG_DFL); +#endif +} + +// ============================================================================ +// Export to SQL +// ============================================================================ + +// Parse table list from string (comma or semicolon separated) +static std::vector parse_table_list(const std::string& spec) { + std::vector tables; + std::string current; + for (char c : spec) { + if (c == ',' || c == ';') { + if (!current.empty()) { + tables.push_back(current); + current.clear(); + } + } else if (c != ' ' && c != '\t') { + current += c; + } + } + if (!current.empty()) { + tables.push_back(current); + } + return tables; +} + +// Export tables to SQL file +static bool export_to_sql(idasql::Database& db, const char* path, + const std::string& table_spec) { + std::vector tables; + if (!(table_spec.empty() || table_spec == "*")) { + tables = parse_table_list(table_spec); + } + + std::string error; + if (!xsql::export_tables(db.handle(), tables, path, error)) { + std::cerr << "Error: " << error << "\n"; + return false; + } + + std::cerr << "Export complete: " << path << "\n"; + return true; +} + +// ============================================================================ +// File Execution +// ============================================================================ + +static bool execute_file(idasql::Database& db, const char* path) { + std::ifstream file(path); + if (!file.is_open()) { + std::cerr << "Cannot open file: " << path << "\n"; + return false; + } + + std::stringstream buffer; + buffer << file.rdbuf(); + std::string content = buffer.str(); + + std::vector results; + std::string error; + if (!xsql::execute_script(db.handle(), content, results, error)) { + std::cerr << "Error: " << error << "\n"; + return false; + } + + for (const auto& res : results) { + if (res.columns.empty()) { + continue; + } + TablePrinter printer; + for (const auto& row : res.rows) { + printer.add_row(res.columns, row); + } + printer.print(); + std::cout << "\n"; + } + + return true; +} + +// ============================================================================ +// HTTP Server Mode +// ============================================================================ + +static xsql::thinclient::server* g_http_server = nullptr; +static std::atomic g_http_stop_requested{false}; + +static void http_signal_handler(int) { + g_http_stop_requested.store(true); + if (g_http_server) g_http_server->stop(); +} + +// Command queue for main-thread execution (needed for Hex-Rays decompiler) +struct HttpPendingCommand { + std::string sql; + std::string result; + bool completed = false; + std::mutex* done_mutex = nullptr; + std::condition_variable* done_cv = nullptr; +}; + +static std::mutex g_http_queue_mutex; +static std::condition_variable g_http_queue_cv; +static std::queue g_http_pending_commands; +static std::atomic g_http_running{false}; + +// Queue a command and wait for main thread to execute it +static std::string http_queue_and_wait(const std::string& sql) { + if (!g_http_running.load()) { + return xsql::json{{"success", false}, {"error", "Server not running"}}.dump(); + } + + HttpPendingCommand cmd; + cmd.sql = sql; + cmd.completed = false; + + std::mutex done_mutex; + std::condition_variable done_cv; + cmd.done_mutex = &done_mutex; + cmd.done_cv = &done_cv; + + { + std::lock_guard lock(g_http_queue_mutex); + g_http_pending_commands.push(&cmd); + } + g_http_queue_cv.notify_one(); + + // Wait for completion - cleanup code will signal if server stops + // Timeout after 60s as safety net against shutdown race conditions + { + std::unique_lock lock(done_mutex); + int wait_count = 0; + while (!cmd.completed && wait_count < 600) { // 60 seconds max + done_cv.wait_for(lock, std::chrono::milliseconds(100)); + wait_count++; + } + if (!cmd.completed) { + // Timed out - likely shutdown race, remove from queue if still there + std::lock_guard qlock(g_http_queue_mutex); + // Can't easily remove from std::queue, but server is stopping anyway + return xsql::json{{"success", false}, {"error", "Request timed out"}}.dump(); + } + } + + return cmd.result; +} + +static std::string query_result_to_json(idasql::Database& db, const std::string& sql) { + auto result = db.query(sql); + xsql::json j = {{"success", result.success}}; + + if (result.success) { + j["columns"] = result.columns; + + xsql::json rows = xsql::json::array(); + for (const auto& row : result.rows) { + rows.push_back(row.values); // Row::values is std::vector + } + j["rows"] = rows; + j["row_count"] = result.rows.size(); + } else { + j["error"] = result.error; + } + + return j.dump(); +} + +static const char* IDASQL_HELP_TEXT = R"(IDASQL HTTP REST API +==================== + +SQL interface for IDA Pro databases via HTTP. + +Endpoints: + GET / - Welcome message + GET /help - This documentation (for LLM discovery) + POST /query - Execute SQL (body = raw SQL, response = JSON) + GET /status - Server health + POST /shutdown - Stop server + +Tables: + funcs - Functions with address, size, flags + segments - Segment/section information + imports - Imported functions + exports - Exported functions + names - Named locations + strings - String references + comments - User comments + xrefs - Cross references + structs - Structure definitions + struct_members - Structure members + enums - Enumeration definitions + enum_members - Enumeration values + localvars - Local variables (requires Hex-Rays) + pseudocode - Decompiled pseudocode (requires Hex-Rays) + +Example Queries: + SELECT name, start_ea, size FROM funcs ORDER BY size DESC LIMIT 10; + SELECT * FROM imports WHERE name LIKE '%malloc%'; + SELECT s.name, COUNT(*) FROM structs s JOIN struct_members m ON s.id = m.struct_id GROUP BY s.id; + +Response Format: + Success: {"success": true, "columns": [...], "rows": [[...]], "row_count": N} + Error: {"success": false, "error": "message"} + +Authentication (if enabled): + Header: Authorization: Bearer + Or: X-XSQL-Token: + +Example: + curl http://localhost:8081/help + curl -X POST http://localhost:8081/query -d "SELECT name FROM funcs LIMIT 5" +)"; + +static int run_http_mode(idasql::Database& db, int port, const std::string& bind_addr, const std::string& auth_token) { + xsql::thinclient::server_config cfg; + cfg.port = port; + cfg.bind_address = bind_addr.empty() ? "127.0.0.1" : bind_addr; + if (!auth_token.empty()) cfg.auth_token = auth_token; + // Allow non-loopback binds if explicitly requested (with warning) + if (!bind_addr.empty() && bind_addr != "127.0.0.1" && bind_addr != "localhost") { + cfg.allow_insecure_no_auth = auth_token.empty(); + std::cerr << "WARNING: Binding to non-loopback address " << bind_addr << "\n"; + if (auth_token.empty()) { + std::cerr << "WARNING: No authentication token set. Server is accessible without authentication.\n"; + std::cerr << " Consider using --token for remote access.\n"; + } + } + + cfg.setup_routes = [&auth_token, port](httplib::Server& svr) { + svr.Get("/", [port](const httplib::Request&, httplib::Response& res) { + std::string welcome = "IDASQL HTTP Server\n\nEndpoints:\n" + " GET /help - API documentation\n" + " POST /query - Execute SQL query\n" + " GET /status - Health check\n" + " POST /shutdown - Stop server\n\n" + "Example: curl -X POST http://localhost:" + std::to_string(port) + "/query -d \"SELECT name FROM funcs LIMIT 5\"\n"; + res.set_content(welcome, "text/plain"); + }); + + svr.Get("/help", [](const httplib::Request&, httplib::Response& res) { + res.set_content(IDASQL_HELP_TEXT, "text/plain"); + }); + + // POST /query - Queue command for main thread execution + // This is necessary because IDA's Hex-Rays decompiler has thread affinity + svr.Post("/query", [&auth_token](const httplib::Request& req, httplib::Response& res) { + if (!auth_token.empty()) { + std::string token; + if (req.has_header("X-XSQL-Token")) token = req.get_header_value("X-XSQL-Token"); + else if (req.has_header("Authorization")) { + auto auth = req.get_header_value("Authorization"); + if (auth.rfind("Bearer ", 0) == 0) token = auth.substr(7); + } + if (token != auth_token) { + res.status = 401; + res.set_content(xsql::json{{"success", false}, {"error", "Unauthorized"}}.dump(), "application/json"); + return; + } + } + if (req.body.empty()) { + res.status = 400; + res.set_content(xsql::json{{"success", false}, {"error", "Empty query"}}.dump(), "application/json"); + return; + } + // Queue command for main thread execution + res.set_content(http_queue_and_wait(req.body), "application/json"); + }); + + // GET /status - Also needs main thread for db.query() + svr.Get("/status", [&auth_token](const httplib::Request& req, httplib::Response& res) { + if (!auth_token.empty()) { + std::string token; + if (req.has_header("X-XSQL-Token")) token = req.get_header_value("X-XSQL-Token"); + else if (req.has_header("Authorization")) { + auto auth = req.get_header_value("Authorization"); + if (auth.rfind("Bearer ", 0) == 0) token = auth.substr(7); + } + if (token != auth_token) { + res.status = 401; + res.set_content(xsql::json{{"success", false}, {"error", "Unauthorized"}}.dump(), "application/json"); + return; + } + } + // Queue for main thread + std::string result = http_queue_and_wait("SELECT COUNT(*) FROM funcs"); + // Parse result to extract count + try { + auto j = xsql::json::parse(result); + if (j.value("success", false) && j.contains("rows") && !j["rows"].empty()) { + int count = std::stoi(j["rows"][0][0].get()); + res.set_content(xsql::json{{"success", true}, {"status", "ok"}, {"tool", "idasql"}, {"functions", count}}.dump(), "application/json"); + return; + } + } catch (...) {} + res.set_content(xsql::json{{"success", true}, {"status", "ok"}, {"tool", "idasql"}, {"functions", "?"}}.dump(), "application/json"); + }); + + svr.Post("/shutdown", [&svr, &auth_token](const httplib::Request& req, httplib::Response& res) { + if (!auth_token.empty()) { + std::string token; + if (req.has_header("X-XSQL-Token")) token = req.get_header_value("X-XSQL-Token"); + else if (req.has_header("Authorization")) { + auto auth = req.get_header_value("Authorization"); + if (auth.rfind("Bearer ", 0) == 0) token = auth.substr(7); + } + if (token != auth_token) { + res.status = 401; + res.set_content(xsql::json{{"success", false}, {"error", "Unauthorized"}}.dump(), "application/json"); + return; + } + } + res.set_content(xsql::json{{"success", true}, {"message", "Shutting down"}}.dump(), "application/json"); + g_http_stop_requested.store(true); + g_http_queue_cv.notify_all(); + std::thread([&svr] { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + svr.stop(); + }).detach(); + }); + }; + + xsql::thinclient::server http_server(cfg); + g_http_server = &http_server; + g_http_running.store(true); + g_http_stop_requested.store(false); + + auto old_handler = std::signal(SIGINT, http_signal_handler); +#ifdef _WIN32 + auto old_break_handler = std::signal(SIGBREAK, http_signal_handler); +#else + auto old_term_handler = std::signal(SIGTERM, http_signal_handler); +#endif + + // Start HTTP server on a background thread (resolves random port) + http_server.run_async(); + int actual_port = http_server.port(); + + std::cout << "IDASQL HTTP server listening on http://" << cfg.bind_address << ":" << actual_port << "\n"; + std::cout << "Database: " << db.info() << "\n"; + std::cout << "Endpoints: /help, /query, /status, /shutdown\n"; + std::cout << "Example: curl http://localhost:" << actual_port << "/help\n"; + std::cout << "Press Ctrl+C to stop.\n\n"; + std::cout.flush(); + + // Main thread processes the command queue (required for Hex-Rays thread affinity) + while (g_http_running.load() && !g_http_stop_requested.load()) { + HttpPendingCommand* cmd = nullptr; + + { + std::unique_lock lock(g_http_queue_mutex); + if (g_http_queue_cv.wait_for(lock, std::chrono::milliseconds(100), + []() { return !g_http_pending_commands.empty() || + g_http_stop_requested.load(); })) { + if (!g_http_pending_commands.empty()) { + cmd = g_http_pending_commands.front(); + g_http_pending_commands.pop(); + } + } + } + + if (cmd) { + // Execute query on main thread - safe for Hex-Rays decompiler + cmd->result = query_result_to_json(db, cmd->sql); + if (cmd->done_mutex && cmd->done_cv) { + { + std::lock_guard lock(*cmd->done_mutex); + cmd->completed = true; + } + cmd->done_cv->notify_one(); + } + } + } + + // Cleanup + g_http_running.store(false); + g_http_queue_cv.notify_all(); + + // Complete any pending commands with error + { + std::lock_guard lock(g_http_queue_mutex); + while (!g_http_pending_commands.empty()) { + HttpPendingCommand* cmd = g_http_pending_commands.front(); + g_http_pending_commands.pop(); + if (!cmd || !cmd->done_mutex || !cmd->done_cv) continue; + cmd->result = xsql::json{{"success", false}, {"error", "Server stopped"}}.dump(); + { + std::lock_guard dlock(*cmd->done_mutex); + cmd->completed = true; + } + cmd->done_cv->notify_one(); + } + } + + // Stop HTTP server (run_async thread joined internally) + http_server.stop(); + + std::signal(SIGINT, old_handler); +#ifdef _WIN32 + std::signal(SIGBREAK, old_break_handler); +#else + std::signal(SIGTERM, old_term_handler); +#endif + g_http_server = nullptr; + std::cout << "\nHTTP server stopped.\n"; + return 0; +} + +// ============================================================================ +// Main +// ============================================================================ + +static void print_usage() { + std::cerr << "idasql v" IDASQL_VERSION_STRING " - SQL interface to IDA databases\n\n" + << "Usage: idasql -s [-q|-c ] [-f ] [-i] [--export ]\n" + << " idasql --remote [-q|-c ] [-f ] [-i]\n\n" + << "Options:\n" + << " -s IDA database file (.idb/.i64) for local mode\n" + << " --remote Connect to IDASQL plugin server (e.g., localhost:13337)\n" + << " --token Auth token for remote mode (if server requires it)\n" + << " -q Execute single SQL query\n" + << " -c Execute single SQL query (alias for -q)\n" + << " -f Execute SQL from file\n" + << " -i Interactive REPL mode\n" + << " -w, --write Save database on exit (persist changes)\n" + << " --export Export tables to SQL file (local mode only)\n" + << " --export-tables=X Tables to export: * (all, default) or table1,table2,...\n" + << " --http [port] Start HTTP REST server (default: 8080, local mode only)\n" + << " --bind Bind address for HTTP/MCP server (default: 127.0.0.1)\n" +#ifdef IDASQL_HAS_AI_AGENT + << " --mcp [port] Start MCP server (default: random port, use in -i mode)\n" + << " Or use .mcp start in interactive mode\n" +#endif +#ifdef IDASQL_HAS_AI_AGENT + << " --prompt Natural language query (uses AI agent)\n" + << " --agent Enable AI agent mode in interactive REPL\n" + << " --provider Override AI provider (claude, copilot)\n" + << " --config [path] [val] View/set agent configuration\n" + << " -v, --verbose Show agent debug logs\n" + << "\n" + << "Agent settings stored in: ~/.idasql/agent_settings.json\n" + << "Configure via: .agent provider, .agent byok, .agent timeout\n" +#endif + << " -h, --help Show this help\n" + << " --version Show version\n\n" + << "Examples:\n" + << " idasql -s test.i64 -q \"SELECT name, size FROM funcs LIMIT 10\"\n" + << " idasql -s test.i64 -f queries.sql\n" + << " idasql -s test.i64 -i\n" + << " idasql -s test.i64 --export dump.sql\n" + << " idasql --remote localhost:13337 -q \"SELECT * FROM funcs LIMIT 5\"\n" +#ifdef IDASQL_HAS_AI_AGENT + << " idasql -s test.i64 --prompt \"Find the largest functions\"\n" + << " idasql -s test.i64 -i --agent\n" + << " idasql -s test.i64 --provider copilot --prompt \"How many functions?\"\n" +#endif + << " idasql --remote localhost:13337 -i\n"; +} + +int main(int argc, char* argv[]) { +#ifdef _WIN32 + // Enable UTF-8 output on Windows console for proper Unicode display + SetConsoleOutputCP(CP_UTF8); +#endif + + // Check for help/version first - before any IDA initialization + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + print_usage(); + return 0; + } + if (strcmp(argv[i], "--version") == 0) { + std::cout << "idasql v" IDASQL_VERSION_STRING "\n"; + return 0; + } + } + + std::string db_path; + std::string query; + std::string sql_file; + std::string export_file; + std::string export_tables = "*"; // Default: all tables + std::string remote_spec; // host:port for remote mode + std::string auth_token; // --token for remote mode + std::string bind_addr; // --bind for HTTP/MCP mode + bool interactive = false; + bool write_mode = false; // -w/--write to save on exit + bool http_mode = false; + int http_port = 8080; + bool mcp_mode = false; + int mcp_port = 0; // 0 = random port +#ifdef IDASQL_HAS_AI_AGENT + std::string nl_prompt; // --prompt for natural language + bool agent_mode = false; // --agent for interactive mode + bool verbose_mode = false; // -v for verbose agent output + std::string provider_override; // --provider overrides stored setting +#endif + + // Parse arguments + for (int i = 1; i < argc; i++) { + if ((strcmp(argv[i], "-s") == 0) && i + 1 < argc) { + db_path = argv[++i]; + } else if (strcmp(argv[i], "--remote") == 0 && i + 1 < argc) { + remote_spec = argv[++i]; + } else if (strcmp(argv[i], "--token") == 0 && i + 1 < argc) { + auth_token = argv[++i]; + } else if ((strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "-c") == 0) && i + 1 < argc) { + query = argv[++i]; + } else if ((strcmp(argv[i], "-f") == 0) && i + 1 < argc) { + sql_file = argv[++i]; + } else if (strcmp(argv[i], "-i") == 0) { + interactive = true; + } else if (strcmp(argv[i], "-w") == 0 || strcmp(argv[i], "--write") == 0) { + write_mode = true; + } else if (strcmp(argv[i], "--export") == 0 && i + 1 < argc) { + export_file = argv[++i]; + } else if (strncmp(argv[i], "--export-tables=", 16) == 0) { + export_tables = argv[i] + 16; +#ifdef IDASQL_HAS_AI_AGENT + } else if (strcmp(argv[i], "--prompt") == 0 && i + 1 < argc) { + nl_prompt = argv[++i]; + } else if (strcmp(argv[i], "--agent") == 0) { + agent_mode = true; + } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) { + verbose_mode = true; + } else if (strcmp(argv[i], "--provider") == 0 && i + 1 < argc) { + provider_override = argv[++i]; + // Validate provider name + if (provider_override != "copilot" && provider_override != "Copilot" && + provider_override != "claude" && provider_override != "Claude") { + std::cerr << "Unknown provider: " << provider_override << "\n"; + std::cerr << "Available providers: claude, copilot\n"; + return 1; + } + } else if (strcmp(argv[i], "--config") == 0) { + // Handle --config [path] [value] and exit immediately + std::string config_path = (i + 1 < argc && argv[i + 1][0] != '-') ? argv[++i] : ""; + std::string config_value = (i + 1 < argc && argv[i + 1][0] != '-') ? argv[++i] : ""; + auto [ok, output, code] = idasql::handle_config_command(config_path, config_value); + std::cout << output; + return code; +#endif + } else if (strcmp(argv[i], "--http") == 0) { + http_mode = true; + if (i + 1 < argc && argv[i + 1][0] != '-') { + http_port = std::stoi(argv[++i]); + } + } else if (strcmp(argv[i], "--mcp") == 0) { + mcp_mode = true; + if (i + 1 < argc && argv[i + 1][0] != '-') { + mcp_port = std::stoi(argv[++i]); + } + } else if (strcmp(argv[i], "--bind") == 0 && i + 1 < argc) { + bind_addr = argv[++i]; + } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + // Already handled above, but skip here to avoid "unknown option" + continue; + } else { + std::cerr << "Unknown option: " << argv[i] << "\n"; + print_usage(); + return 1; + } + } + + // Validate arguments + bool remote_mode = !remote_spec.empty(); + + if (!remote_mode && db_path.empty()) { + std::cerr << "Error: Database path required (-s) or use --remote\n\n"; + print_usage(); + return 1; + } + + if (remote_mode && !db_path.empty()) { + std::cerr << "Error: Cannot use both -s and --remote\n\n"; + print_usage(); + return 1; + } + + bool has_action = !query.empty() || !sql_file.empty() || interactive || !export_file.empty() || http_mode || mcp_mode; +#ifdef IDASQL_HAS_AI_AGENT + has_action = has_action || !nl_prompt.empty(); +#endif + if (!has_action) { + std::cerr << "Error: Specify -q, -c, -f, -i, --export, --http, --mcp" +#ifdef IDASQL_HAS_AI_AGENT + << ", or --prompt" +#endif + << "\n\n"; + print_usage(); + return 1; + } + + if (remote_mode && !export_file.empty()) { + std::cerr << "Error: --export not supported in remote mode\n\n"; + print_usage(); + return 1; + } + + if (remote_mode && http_mode) { + std::cerr << "Error: Cannot use both --remote and --http\n\n"; + print_usage(); + return 1; + } + + //========================================================================= + // Remote mode - thin client, no IDA kernel loaded + //========================================================================= + // IMPORTANT: This path never calls any IDA functions. + // On Windows with delayed loading, ida.dll/idalib.dll stay unloaded. + if (remote_mode) { + // Parse host:port + std::string host = "127.0.0.1"; + int port = 13337; + auto colon = remote_spec.find(':'); + if (colon != std::string::npos) { + host = remote_spec.substr(0, colon); + std::string port_str = remote_spec.substr(colon + 1); + if (!parse_port(port_str, port)) { + std::cerr << "Error: Invalid port in --remote: " << port_str << "\n"; + return 1; + } + } else { + host = remote_spec; + } +#ifdef IDASQL_HAS_AI_AGENT + return run_remote_mode(host, port, query, sql_file, auth_token, interactive, + nl_prompt, verbose_mode, provider_override); +#else + return run_remote_mode(host, port, query, sql_file, auth_token, interactive); +#endif + } + + //========================================================================= + // Local mode - requires IDA SDK + //========================================================================= + std::cerr << "Opening: " << db_path << "..." << std::endl; + idasql::Database db; + if (!db.open(db_path.c_str())) { + std::cerr << "Error: " << db.error() << std::endl; + return 1; + } + std::cerr << "Database opened successfully." << std::endl; + + // HTTP server mode + if (http_mode) { + int http_result = run_http_mode(db, http_port, bind_addr, auth_token); + db.close(); + return http_result; + } + + // MCP server mode (standalone, not interactive REPL) +#ifdef IDASQL_HAS_AI_AGENT + if (mcp_mode) { + // SQL executor - will be called on main thread via wait() + idasql::QueryCallback sql_cb = [&db](const std::string& sql) -> std::string { + auto result = db.query(sql); + if (result.success) { + return result.to_string(); + } + return "Error: " + result.error; + }; + + // Create MCP agent for natural language queries + auto mcp_agent = std::make_unique(sql_cb); + mcp_agent->start(); + + idasql::AskCallback ask_cb = [&mcp_agent](const std::string& question) -> std::string { + if (!mcp_agent) return "Error: AI agent not available"; + return mcp_agent->query(question); + }; + + // Create and start MCP server with use_queue=true + idasql::IDAMCPServer mcp_server; + int port = mcp_server.start(mcp_port, sql_cb, ask_cb, + bind_addr.empty() ? "127.0.0.1" : bind_addr, true); + if (port <= 0) { + std::cerr << "Error: Failed to start MCP server\n"; + db.close(); + return 1; + } + + std::cout << idasql::format_mcp_info(port, true); + std::cout << "Press Ctrl+C to stop...\n\n"; + std::cout.flush(); + + // Set up signal handler + g_quit_requested.store(false); + std::signal(SIGINT, signal_handler); +#ifdef _WIN32 + std::signal(SIGBREAK, signal_handler); +#endif + + // Set interrupt check + mcp_server.set_interrupt_check([]() { + return g_quit_requested.load(); + }); + + // Enter wait loop - processes MCP commands on main thread + mcp_server.run_until_stopped(); + + std::signal(SIGINT, SIG_DFL); + mcp_agent->stop(); + std::cout << "\nMCP server stopped.\n"; + db.close(); + return 0; + } +#else + if (mcp_mode) { + std::cerr << "Error: MCP mode not available. Rebuild with -DIDASQL_WITH_AI_AGENT=ON\n"; + db.close(); + return 1; + } +#endif + + int result = 0; + + // Execute based on mode + if (!export_file.empty()) { + // Export mode + if (!export_to_sql(db, export_file.c_str(), export_tables)) { + result = 1; + } +#ifdef IDASQL_HAS_AI_AGENT + } else if (!nl_prompt.empty()) { + // Natural language query mode (one-shot) + auto executor = [&db](const std::string& sql) -> std::string { + return execute_sql_to_string(db, sql); + }; + + // Load settings (includes BYOK, provider, timeout) + idasql::AgentSettings settings = idasql::LoadAgentSettings(); + + // Apply provider override from CLI if specified + if (!provider_override.empty()) { + try { + settings.default_provider = idasql::ParseProviderType(provider_override); + } catch (...) { + // Already validated in argument parsing + } + } + + idasql::AIAgent agent(executor, settings, verbose_mode); + + // Register signal handler + g_agent = &agent; + std::signal(SIGINT, signal_handler); + + agent.start(); + std::string response = agent.query(nl_prompt); + agent.stop(); + + g_agent = nullptr; + std::signal(SIGINT, SIG_DFL); + + std::cout << response << "\n"; +#endif + } else if (!query.empty()) { + // Single query mode + TablePrinter printer; + g_printer = &printer; + int rc = db.exec(query.c_str(), table_callback, nullptr); + g_printer = nullptr; + + if (rc == SQLITE_OK) { + printer.print(); + } else { + std::cerr << "Error: " << db.error() << "\n"; + result = 1; + } + } else if (!sql_file.empty()) { + // File execution mode + if (!execute_file(db, sql_file.c_str())) { + result = 1; + } + } else if (interactive) { + // Interactive REPL +#ifdef IDASQL_HAS_AI_AGENT + run_repl(db, agent_mode, verbose_mode, provider_override); +#else + run_repl(db); +#endif + } + + // Save database if -w/--write was specified + if (write_mode) { + if (save_database()) { + std::cerr << "Database saved.\n"; + } else { + std::cerr << "Warning: Failed to save database.\n"; + } + } + + db.close(); + return result; +} + +``` + +`src/common/agent_settings.hpp`: + +```hpp +#pragma once + +#ifdef IDASQL_HAS_AI_AGENT + +#include +#include +#include + +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#endif + +namespace idasql { + +// BYOK (Bring Your Own Key) settings for a single provider +struct BYOKSettings { + bool enabled = false; + std::string api_key; + std::string base_url; + std::string model; + std::string provider_type; // "openai", "anthropic", "azure" + int timeout_ms = 0; + + // Convert to libagents BYOKConfig + libagents::BYOKConfig to_config() const { + libagents::BYOKConfig config; + config.api_key = api_key; + config.base_url = base_url; + config.model = model; + config.provider_type = provider_type; + config.timeout_ms = timeout_ms; + return config; + } + + // Check if BYOK is usable (enabled and has API key) + bool is_usable() const { return enabled && !api_key.empty(); } +}; + +// Agent settings stored in ~/.idasql/agent_settings.json +struct AgentSettings { + // Default provider (claude, copilot) + libagents::ProviderType default_provider = libagents::ProviderType::Claude; + + // User's custom prompt (additive to system prompt) + std::string custom_prompt; + + // Response timeout in milliseconds (0 = use default) + int response_timeout_ms = 120000; // 2 minutes default + + // BYOK configuration per provider + // Key: provider name ("copilot", "claude") + std::unordered_map byok; + + // Get BYOK settings for the current provider + const BYOKSettings* get_byok() const { + std::string provider_name = libagents::provider_type_name(default_provider); + auto it = byok.find(provider_name); + if (it != byok.end()) + return &it->second; + return nullptr; + } + + // Get or create BYOK settings for the current provider + BYOKSettings& get_or_create_byok() { + std::string provider_name = libagents::provider_type_name(default_provider); + return byok[provider_name]; + } +}; + +// Get the settings directory path (~/.idasql or %APPDATA%\idasql) +inline std::string GetSettingsDir() { +#ifdef _WIN32 + char path[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPathA(nullptr, CSIDL_APPDATA, nullptr, 0, path))) { + return std::string(path) + "\\idasql"; + } + // Fallback to USERPROFILE + const char* userprofile = std::getenv("USERPROFILE"); + if (userprofile) { + return std::string(userprofile) + "\\.idasql"; + } + return ".idasql"; +#else + const char* home = std::getenv("HOME"); + if (!home) { + struct passwd* pw = getpwuid(getuid()); + if (pw) home = pw->pw_dir; + } + if (home) { + return std::string(home) + "/.idasql"; + } + return ".idasql"; +#endif +} + +// Get the settings file path +inline std::string GetSettingsPath() { + return GetSettingsDir() + +#ifdef _WIN32 + "\\agent_settings.json"; +#else + "/agent_settings.json"; +#endif +} + +// Ensure directory exists +inline bool EnsureDir(const std::string& path) { +#ifdef _WIN32 + return CreateDirectoryA(path.c_str(), nullptr) || GetLastError() == ERROR_ALREADY_EXISTS; +#else + return mkdir(path.c_str(), 0755) == 0 || errno == EEXIST; +#endif +} + +// JSON serialization +inline void to_json(nlohmann::json& j, const BYOKSettings& s) { + j = nlohmann::json{ + {"enabled", s.enabled}, + {"api_key", s.api_key}, + {"base_url", s.base_url}, + {"model", s.model}, + {"provider_type", s.provider_type}, + {"timeout_ms", s.timeout_ms} + }; +} + +inline void from_json(const nlohmann::json& j, BYOKSettings& s) { + s.enabled = j.value("enabled", false); + s.api_key = j.value("api_key", ""); + s.base_url = j.value("base_url", ""); + s.model = j.value("model", ""); + s.provider_type = j.value("provider_type", ""); + s.timeout_ms = j.value("timeout_ms", 0); +} + +inline void to_json(nlohmann::json& j, const AgentSettings& s) { + j = nlohmann::json{ + {"default_provider", libagents::provider_type_name(s.default_provider)}, + {"custom_prompt", s.custom_prompt}, + {"response_timeout_ms", s.response_timeout_ms}, + {"byok", s.byok} + }; +} + +inline void from_json(const nlohmann::json& j, AgentSettings& s) { + std::string provider_str = j.value("default_provider", "claude"); + if (provider_str == "copilot") { + s.default_provider = libagents::ProviderType::Copilot; + } else { + s.default_provider = libagents::ProviderType::Claude; + } + s.custom_prompt = j.value("custom_prompt", ""); + s.response_timeout_ms = j.value("response_timeout_ms", 120000); + if (j.contains("byok") && j["byok"].is_object()) { + s.byok = j["byok"].get>(); + } +} + +// Load settings from disk (creates default if not exists) +inline AgentSettings LoadAgentSettings() { + AgentSettings settings; + std::string path = GetSettingsPath(); + + std::ifstream f(path); + if (f.is_open()) { + try { + nlohmann::json j = nlohmann::json::parse(f); + settings = j.get(); + } catch (...) { + // Ignore parse errors, use defaults + } + } + return settings; +} + +// Save settings to disk +inline bool SaveAgentSettings(const AgentSettings& settings) { + std::string dir = GetSettingsDir(); + if (!EnsureDir(dir)) { + return false; + } + + std::string path = GetSettingsPath(); + std::ofstream f(path); + if (!f.is_open()) { + return false; + } + + try { + nlohmann::json j = settings; + f << j.dump(2); + return true; + } catch (...) { + return false; + } +} + +// Parse provider type from string +inline libagents::ProviderType ParseProviderType(const std::string& name) { + if (name == "copilot" || name == "Copilot" || name == "COPILOT") { + return libagents::ProviderType::Copilot; + } + if (name == "claude" || name == "Claude" || name == "CLAUDE") { + return libagents::ProviderType::Claude; + } + throw std::runtime_error("Unknown provider: " + name + " (use 'claude' or 'copilot')"); +} + +} // namespace idasql + +#endif // IDASQL_HAS_AI_AGENT + +``` + +`src/common/ai_agent.cpp`: + +```cpp +#include "ai_agent.hpp" + +#ifdef IDASQL_HAS_AI_AGENT + +// Embedded documentation from prompts/idasql_agent.md +#include "idasql_agent_prompt.hpp" + +#include +#include +#include +#include +#include + +namespace idasql { + +// ============================================================================ +// Construction / Destruction +// ============================================================================ + +AIAgent::AIAgent(SqlExecutor executor, const AgentSettings& settings, bool verbose) + : executor_(std::move(executor)), verbose_(verbose), + provider_type_(settings.default_provider), + response_timeout_ms_(settings.response_timeout_ms) +{ + // Create agent with provider from settings + agent_ = libagents::create_agent(provider_type_); + + if (verbose_) { + std::cerr << "[AGENT] Created " << libagents::provider_type_name(provider_type_) + << " provider" << std::endl; + } + + // Apply BYOK from settings if configured + const BYOKSettings* byok = settings.get_byok(); + if (byok && byok->is_usable()) { + set_byok(byok->to_config()); + if (verbose_) { + std::cerr << "[AGENT] Loaded BYOK from settings" << std::endl; + } + } else { + // Fall back to environment variables + load_byok_from_env(); + } +} + +AIAgent::AIAgent(SqlExecutor executor, bool verbose) + : AIAgent(std::move(executor), LoadAgentSettings(), verbose) +{ + // Delegates to settings-based constructor +} + +void AIAgent::set_byok(const libagents::BYOKConfig& config) { + byok_config_ = config; + byok_configured_ = config.is_configured(); + if (verbose_ && byok_configured_) { + std::cerr << "[AGENT] BYOK configured"; + if (!config.model.empty()) std::cerr << " (model: " << config.model << ")"; + if (!config.base_url.empty()) std::cerr << " (endpoint: " << config.base_url << ")"; + std::cerr << std::endl; + } +} + +bool AIAgent::load_byok_from_env() { + libagents::BYOKConfig config; + + // Load from COPILOT_SDK_BYOK_* environment variables + if (const char* key = std::getenv("COPILOT_SDK_BYOK_API_KEY")) + config.api_key = key; + if (const char* url = std::getenv("COPILOT_SDK_BYOK_BASE_URL")) + config.base_url = url; + if (const char* model = std::getenv("COPILOT_SDK_BYOK_MODEL")) + config.model = model; + if (const char* type = std::getenv("COPILOT_SDK_BYOK_PROVIDER_TYPE")) + config.provider_type = type; + + if (config.is_configured()) { + if (verbose_) { + std::cerr << "[AGENT] Loaded BYOK from environment:" << std::endl; + std::cerr << "[AGENT] Model: " << (config.model.empty() ? "(default)" : config.model) << std::endl; + std::cerr << "[AGENT] Endpoint: " << (config.base_url.empty() ? "(default)" : config.base_url) << std::endl; + std::cerr << "[AGENT] Type: " << (config.provider_type.empty() ? "(default)" : config.provider_type) << std::endl; + } + set_byok(config); + return true; + } + return false; +} + +AIAgent::~AIAgent() { + stop(); +} + +// ============================================================================ +// Lifecycle +// ============================================================================ + +void AIAgent::start() { + if (!agent_) return; + + setup_tools(); + + // Apply BYOK configuration before initialize() - required for Copilot provider + if (byok_configured_) { + agent_->set_byok(byok_config_); + if (verbose_) { + std::cerr << "[AGENT] Applied BYOK configuration" << std::endl; + } + } + + // Apply response timeout if configured + if (response_timeout_ms_ > 0) { + agent_->set_response_timeout(std::chrono::milliseconds(response_timeout_ms_)); + if (verbose_) { + std::cerr << "[AGENT] Response timeout: " << response_timeout_ms_ << " ms" << std::endl; + } + } + + // Note: We don't use set_system_prompt() because it can break MCP tool + // visibility with some providers. Instead, we embed the prompt in the + // first message (priming). + + if (!agent_->initialize()) { + if (verbose_) { + std::cerr << "[AGENT] Failed to initialize agent" << std::endl; + } + return; + } + + if (verbose_) { + std::cerr << "[AGENT] Agent initialized (" << agent_->provider_name() << ")" << std::endl; + } +} + +void AIAgent::stop() { + if (agent_ && agent_->is_initialized()) { + agent_->shutdown(); + if (verbose_) { + std::cerr << "[AGENT] Agent shutdown" << std::endl; + } + } +} + +void AIAgent::reset_session() { + if (verbose_) { + std::cerr << "[AGENT] Resetting session..." << std::endl; + } + + docs_primed_ = false; + quit_requested_.store(false); + + if (agent_) { + agent_->clear_session(); + } + + if (verbose_) { + std::cerr << "[AGENT] Session reset complete" << std::endl; + } +} + +void AIAgent::request_quit() { + quit_requested_.store(true); + if (agent_) { + agent_->abort(); + } +} + +// ============================================================================ +// Tool Registration +// ============================================================================ + +void AIAgent::setup_tools() { + libagents::Tool idasql_tool; + idasql_tool.name = "idasql"; + idasql_tool.description = + "Execute a SQL query against an IDA Pro database. " + "Available tables: funcs, strings, imports, segments, names, xrefs, instructions, " + "blocks, comments, types, entries, heads, fchunks, bookmarks, pseudocode, ctree, " + "ctree_lvars, ctree_call_args. " + "Example: SELECT name, size FROM funcs WHERE name LIKE 'sub_%' ORDER BY size DESC LIMIT 10"; + + idasql_tool.parameters_schema = R"({ + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "SQL query to execute against the IDA database" + } + }, + "required": ["query"] + })"; + + // Tool handler - executes on caller thread via query_hosted() + idasql_tool.handler = [this](const std::string& args) -> std::string { + try { + auto j = libagents::json::parse(args); + std::string sql = j.value("query", ""); + + if (verbose_) { + std::cerr << "[TOOL] Executing SQL: " << sql.substr(0, 80) + << (sql.size() > 80 ? "..." : "") << std::endl; + } + + // This runs on the main thread (query_hosted guarantees this) + std::string result = executor_(sql); + + if (verbose_) { + std::cerr << "[TOOL] Result: " << result.size() << " bytes" << std::endl; + } + + return result; + + } catch (const std::exception& e) { + return std::string("Error: ") + e.what(); + } + }; + + agent_->register_tool(idasql_tool); + + if (verbose_) { + std::cerr << "[AGENT] Registered idasql tool" << std::endl; + } +} + +// ============================================================================ +// Query Interface +// ============================================================================ + +std::string AIAgent::query(const std::string& prompt) { + // SQL passthrough - execute directly + if (looks_like_sql(prompt)) { + return executor_(prompt); + } + + if (!agent_ || !agent_->is_initialized()) { + return "Error: Agent not initialized"; + } + + // Build message (prime with docs if first message) + std::string message = docs_primed_ ? prompt : build_primed_message(prompt); + docs_primed_ = true; + + // Use query_hosted for main-thread tool dispatch + libagents::HostContext host; + host.should_abort = [this]() { return quit_requested_.load(); }; + + try { + return agent_->query_hosted(message, host); + } catch (const std::exception& e) { + return std::string("Error: ") + e.what(); + } +} + +std::string AIAgent::query_streaming(const std::string& prompt, ContentCallback on_content) { + // SQL passthrough + if (looks_like_sql(prompt)) { + std::string result = executor_(prompt); + if (on_content) on_content(result); + return result; + } + + if (!agent_ || !agent_->is_initialized()) { + std::string err = "Error: Agent not initialized"; + if (on_content) on_content(err); + return err; + } + + // Build message + std::string message = docs_primed_ ? prompt : build_primed_message(prompt); + docs_primed_ = true; + + // Use query_hosted with streaming callback + libagents::HostContext host; + host.should_abort = [this]() { return quit_requested_.load(); }; + host.on_event = [on_content](const libagents::Event& event) { + if (on_content && event.type == libagents::EventType::ContentDelta) { + on_content(event.content); + } + }; + + try { + return agent_->query_hosted(message, host); + } catch (const std::exception& e) { + return std::string("Error: ") + e.what(); + } +} + +// ============================================================================ +// Helpers +// ============================================================================ + +std::string AIAgent::build_primed_message(const std::string& user_message) { + return std::string(SYSTEM_PROMPT) + + "\n\n---\n\n" + "# User Request\n\n" + "Use the `idasql` tool to execute SQL queries. " + "Do not use Bash, Grep, or other tools - only use the idasql tool.\n\n" + + user_message; +} + +bool AIAgent::looks_like_sql(const std::string& input) { + if (input.empty()) return false; + + // Find first non-whitespace character + size_t start = 0; + while (start < input.size() && std::isspace(static_cast(input[start]))) { + ++start; + } + if (start >= input.size()) return false; + + // Convert first ~20 chars to uppercase for comparison + std::string prefix; + for (size_t i = start; i < input.size() && i < start + 20; ++i) { + prefix += static_cast(std::toupper(static_cast(input[i]))); + } + + // Check for SQL keywords + return prefix.rfind("SELECT ", 0) == 0 || + prefix.rfind("INSERT ", 0) == 0 || + prefix.rfind("UPDATE ", 0) == 0 || + prefix.rfind("DELETE ", 0) == 0 || + prefix.rfind("CREATE ", 0) == 0 || + prefix.rfind("DROP ", 0) == 0 || + prefix.rfind("PRAGMA ", 0) == 0 || + prefix.rfind("WITH ", 0) == 0 || + prefix.rfind("EXPLAIN ", 0) == 0 || + prefix.rfind(".TABLES", 0) == 0 || + prefix.rfind(".SCHEMA", 0) == 0 || + prefix.rfind(".HELP", 0) == 0 || + prefix.rfind(".QUIT", 0) == 0 || + prefix.rfind(".EXIT", 0) == 0; +} + +bool AIAgent::is_available() { + try { + // Just check if we can create an agent - don't initialize + // (initialization is expensive and would be redundant if we're about to + // create another agent anyway) + auto agent = libagents::create_agent(libagents::ProviderType::Claude); + return agent != nullptr; + } catch (...) { + return false; + } +} + +} // namespace idasql + +#endif // IDASQL_HAS_AI_AGENT + +``` + +`src/common/ai_agent.hpp`: + +```hpp +#pragma once + +#ifdef IDASQL_HAS_AI_AGENT + +#include +#include +#include "agent_settings.hpp" +#include +#include +#include +#include + +namespace idasql { + +/** + * AIAgent - Natural language interface for IDASQL using libagents + * + * This is a simplified wrapper around libagents that provides: + * - Tool registration for SQL execution + * - Main-thread tool dispatch via query_hosted() (required for IDA safety) + * - SQL passthrough detection + * - Signal handling for Ctrl-C + * - BYOK (Bring Your Own Key) support for Copilot provider + * + * Architecture: + * - libagents handles all threading internally + * - query_hosted() ensures tool handlers run on the caller thread + * - No custom queues or thread management needed + */ +class AIAgent { +public: + /// Callback to execute SQL and return formatted results + using SqlExecutor = std::function; + + /// Callback for streaming content + using ContentCallback = std::function; + + /** + * Construct agent with SQL executor and settings + * @param executor Function that executes SQL and returns formatted results + * @param settings Agent settings (provider, BYOK, timeout, etc.) + * @param verbose If true, show debug output + */ + explicit AIAgent(SqlExecutor executor, const AgentSettings& settings, bool verbose = false); + + /** + * Construct agent with SQL executor (uses stored settings) + * @param executor Function that executes SQL and returns formatted results + * @param verbose If true, show debug output + */ + explicit AIAgent(SqlExecutor executor, bool verbose = false); + + /** + * Configure BYOK (Bring Your Own Key) - call before start() + * Required for Copilot provider, optional for Claude + * @param config BYOK configuration (api_key, base_url, model, provider_type) + */ + void set_byok(const libagents::BYOKConfig& config); + + /** + * Load BYOK config from environment variables (fallback) + * Looks for COPILOT_SDK_BYOK_API_KEY, COPILOT_SDK_BYOK_BASE_URL, etc. + * @return true if BYOK was configured from environment + */ + bool load_byok_from_env(); + + /** + * Get the current provider type + */ + libagents::ProviderType provider_type() const { return provider_type_; } + + ~AIAgent(); + + // Non-copyable, non-movable + AIAgent(const AIAgent&) = delete; + AIAgent& operator=(const AIAgent&) = delete; + AIAgent(AIAgent&&) = delete; + AIAgent& operator=(AIAgent&&) = delete; + + /** + * Start the agent and connect to provider + */ + void start(); + + /** + * Stop the agent and disconnect + */ + void stop(); + + /** + * Reset the session - clears conversation history + */ + void reset_session(); + + /** + * Request to quit (e.g., from Ctrl-C handler) + * Thread-safe, can be called from signal handler + */ + void request_quit(); + + /** + * Check if quit was requested + */ + bool quit_requested() const { return quit_requested_.load(); } + + /** + * Send a query and get response (blocking) + * SQL is passed through directly, natural language goes to AI. + * Tool handlers execute on the caller thread (main thread safe). + * + * @param prompt User input (natural language or SQL) + * @return Response text + */ + std::string query(const std::string& prompt); + + /** + * Send a query with streaming output + * @param prompt User input + * @param on_content Callback for content deltas + * @return Final response text + */ + std::string query_streaming(const std::string& prompt, ContentCallback on_content); + + /** + * Check if input looks like SQL (for passthrough) + * @param input User input string + * @return true if input appears to be SQL + */ + static bool looks_like_sql(const std::string& input); + + /** + * Check if AI agent is available + * @return true if the default provider is available + */ + static bool is_available(); + +private: + SqlExecutor executor_; + bool verbose_ = false; + bool docs_primed_ = false; + std::atomic quit_requested_{false}; + std::unique_ptr agent_; + libagents::ProviderType provider_type_ = libagents::ProviderType::Claude; + libagents::BYOKConfig byok_config_; + bool byok_configured_ = false; + int response_timeout_ms_ = 0; + + /// Register the idasql tool with libagents + void setup_tools(); + + /// Build primed message with documentation prepended + std::string build_primed_message(const std::string& user_message); +}; + +} // namespace idasql + +#endif // IDASQL_HAS_AI_AGENT + +``` + +`src/common/http_server.cpp`: + +```cpp +#include "http_server.hpp" + +#include + +namespace idasql { + +// Help text served at /help endpoint +static const char* HTTP_HELP_TEXT = R"(IDASQL HTTP REST API +==================== + +SQL interface for IDA Pro databases via HTTP. + +Endpoints: + GET / - Welcome message + GET /help - This documentation + POST /query - Execute SQL (body = raw SQL, response = JSON) + GET /status - Server health check + POST /shutdown - Stop server + +Response Format: + Success: {"success": true, "columns": [...], "rows": [[...]], "row_count": N} + Error: {"success": false, "error": "message"} + +Example: + curl http://localhost:/help + curl -X POST http://localhost:/query -d "SELECT name FROM funcs LIMIT 5" +)"; + +int IDAHTTPServer::start(int port, HTTPQueryCallback query_cb, + const std::string& bind_addr, bool use_queue) { + if (impl_ && impl_->is_running()) { + return impl_->port(); + } + + xsql::thinclient::http_query_server_config config; + config.tool_name = "idasql"; + config.help_text = HTTP_HELP_TEXT; + config.port = port; + config.bind_address = bind_addr; + config.query_fn = std::move(query_cb); + config.use_queue = use_queue; + config.status_fn = []() { + return xsql::json{{"mode", "repl"}}; + }; + + impl_ = std::make_unique(config); + return impl_->start(); +} + +void IDAHTTPServer::run_until_stopped() { + if (impl_) impl_->run_until_stopped(); +} + +void IDAHTTPServer::stop() { + if (impl_) { + impl_->stop(); + impl_.reset(); + } +} + +bool IDAHTTPServer::is_running() const { + return impl_ && impl_->is_running(); +} + +int IDAHTTPServer::port() const { + return impl_ ? impl_->port() : 0; +} + +std::string IDAHTTPServer::url() const { + return impl_ ? impl_->url() : ""; +} + +void IDAHTTPServer::set_interrupt_check(std::function check) { + if (impl_) impl_->set_interrupt_check(std::move(check)); +} + +std::string format_http_info(int port, const std::string& stop_hint) { + return xsql::thinclient::format_http_info("idasql", port, stop_hint); +} + +std::string format_http_status(int port, bool running) { + return xsql::thinclient::format_http_status(port, running); +} + +} // namespace idasql + +``` + +`src/common/http_server.hpp`: + +```hpp +#pragma once + +/** + * IDAHTTPServer - HTTP REST server for IDASQL REPL + * + * Thin wrapper over xsql::thinclient::http_query_server. + * Preserves the existing API for backward compatibility. + * + * Usage modes: + * 1. CLI (idalib): Call run_until_stopped() to process commands on main thread + * 2. Plugin: Use execute_sync() wrapper in callbacks (no run_until_stopped() needed) + */ + +#include + +#include +#include +#include + +namespace idasql { + +// Callback for handling SQL queries +using HTTPQueryCallback = std::function; + +class IDAHTTPServer { +public: + IDAHTTPServer() = default; + ~IDAHTTPServer() { stop(); } + + // Non-copyable + IDAHTTPServer(const IDAHTTPServer&) = delete; + IDAHTTPServer& operator=(const IDAHTTPServer&) = delete; + + /** + * Start HTTP server on given port with callbacks + * + * @param port Port to listen on (0 = random port 8100-8199) + * @param query_cb SQL query callback (returns JSON string) + * @param bind_addr Address to bind to (default: localhost only) + * @param use_queue If true, callbacks are queued for main thread (CLI mode) + * If false, callbacks called directly (plugin mode with execute_sync) + * @return Actual port used, or -1 on failure + */ + int start(int port, HTTPQueryCallback query_cb, + const std::string& bind_addr = "127.0.0.1", + bool use_queue = false); + + /** + * Block until server stops, processing commands on the calling thread. + * Only needed when use_queue=true (CLI mode). + */ + void run_until_stopped(); + + /** Stop the server */ + void stop(); + + /** Check if server is running */ + bool is_running() const; + + /** Get the port the server is listening on */ + int port() const; + + /** Get the server URL */ + std::string url() const; + + /** Set interrupt check function (called during wait loop) */ + void set_interrupt_check(std::function check); + +private: + std::unique_ptr impl_; +}; + +/** + * Format HTTP server info for display + */ +std::string format_http_info(int port, const std::string& stop_hint = "Press Ctrl+C to stop and return to REPL."); + +/** + * Format HTTP server status + */ +std::string format_http_status(int port, bool running); + +} // namespace idasql + +``` + +`src/common/idasql_agent_prompt.hpp`: + +```hpp +// Auto-generated from idasql_agent.md +// Generated: 2026-02-12T18:29:38.360124 +// DO NOT EDIT - regenerate with: python scripts/embed_prompt.py + +#pragma once + +namespace idasql { + +inline constexpr const char* SYSTEM_PROMPT = + R"PROMPT(# IDASQL Agent Guide + +A comprehensive reference for AI agents to effectively use IDASQL - an SQL interface for reverse engineering binary analysis with IDA Pro. + +--- + +## What is IDA and Why SQL? + +**IDA Pro** is the industry-standard disassembler and reverse engineering tool. It analyzes compiled binaries (executables, DLLs, firmware) and produces: +- **Disassembly** - Human-readable assembly code +- **Functions** - Detected code boundaries with names +- **Cross-references** - Who calls what, who references what data +- **Types** - Structures, enums, function prototypes +- **Decompilation** - C-like pseudocode (with Hex-Rays plugin) + +**IDASQL** exposes all this analysis data through SQL virtual tables, enabling: +- Complex queries across multiple data types (JOINs) +- Aggregations and statistics (COUNT, GROUP BY) +- Pattern detection across the entire binary +- Scriptable analysis without writing IDA plugins or IDAPython scripts + +--- + +## Core Concepts for Binary Analysis + +### Addresses (ea_t) +Everything in a binary has an **address** - a memory location where code or data lives. IDA uses `ea_t` (effective address) as unsigned 64-bit integers. SQL shows these as integers; use `printf('0x%X', address)` for hex display. + +### Functions +IDA groups code into **functions** with: +- `address` / `start_ea` - Where the function begins +- `end_ea` - Where it ends +- `name` - Assigned or auto-generated name (e.g., `main`, `sub_401000`) +- `size` - Total bytes in the function + +### Cross-References (xrefs) +Binary analysis is about understanding **relationships**: +- **Code xrefs** - Function calls, jumps between code +- **Data xrefs** - Code reading/writing data locations +- `from_ea` → `to_ea` represents "address X references address Y" + +### Segments +Memory is divided into **segments** with different purposes: +- `.text` - Executable code (typically) +- `.data` - Initialized global data +- `.rdata` - Read-only data (strings, constants) +- `.bss` - Uninitialized data + +Of course, segment names and types can vary. You may query the `segments` table to understand memory layout. + +### Basic Blocks +Within a function, **basic blocks** are straight-line code sequences: +- No branches in the middle +- Single entry, single exit +- Useful for control flow analysis + +### Decompilation (Hex-Rays) +The **Hex-Rays decompiler** converts assembly to C-like **pseudocode**: +- **ctree** - The Abstract Syntax Tree of decompiled code +- **lvars** - Local variables detected by the decompiler +- Much easier to analyze than raw assembly + +--- + +## Command-Line Interface + +IDASQL provides SQL access to IDA databases via command line or as a server. + +### Invocation Modes + +**1. Single Query (Local)** +```bash +idasql -s database.i64 -q "SELECT * FROM funcs LIMIT 10" +idasql -s database.i64 -c "SELECT COUNT(*) FROM funcs" # -c is alias for -q +``` + +**2. SQL File Execution** +```bash +idasql -s database.i64 -f analysis.sql +``` + +**3. Interactive REPL** +```bash +idasql -s database.i64 -i +``` + +**4. Remote Mode** (connect to running server) +```bash +idasql --remote localhost:8080 -q "SELECT * FROM funcs" +idasql --remote localhost:8080 -i # Remote interactive +``` + +**5. HTTP Server Mode** +```bash +idasql -s database.i64 --http 8080 +# Then query via: curl -X POST http://localhost:8080/query -d "SELECT * FROM funcs" +``` + +**6. Export Mode** +```bash +idasql -s database.i64 --export dump.sql +idasql -s database.i64 --export dump.sql --export-tables=funcs,segments +``` + +### CLI Options + +| Option | Description | +|--------|-------------| +| `-s ` | IDA database file (.idb/.i64) | +| `--remote ` | Connect to IDASQL server | +| `--token ` | Auth token for remote/server mode | +| `-q ` | Execute single SQL query | +| `-c ` | Alias for -q (Python-style) | +| `-f ` | Execute SQL from file | +| `-i` | Interactive REPL mode | +| `-w, --write` | Save database changes on exit | +| `--export ` | Export tables to SQL file | +| `--export-tables=X` | Tables to export: `*` (all) or `table1,table2,...` | +| `--http [port]` | Start HTTP REST server (default: 8080) | +| `--bind ` | Bind address for server (default: 127.0.0.1) | +| `-h, --help` | Show help | + +### REPL Commands + +| Command | Description | +|---------|-------------| +| `.tables` | List all virtual tables | +| `.schema [table]` | Show table schema | +| `.info` | Show database metadata | +| `.clear` | Clear session | +| `.quit` / `.exit` | Exit REPL | +| `.help` | Show available commands | +| `.http start` | Start HTTP server on random port | +| `.http stop` | Stop HTTP server | +| `.http status` | Show HTTP server status | +| `.agent` | Start AI agent mode | + +### Performance Strategy + +**Single queries:** Use `-q` directly. +```bash +idasql -s database.i64 -q "SELECT COUNT(*) FROM funcs" +``` + +**Multiple queries / exploration:** Start a server once, then query as a client. + +Opening an IDA database has startup overhead (idalib initialization, auto-analysis). If you plan to run many queries—exploring the database, experimenting with different queries, or iterating on analysis—avoid re-opening the database each time. + +**Recommended workflow for iterative analysis:** +```bash +# Terminal 1: Start server (opens database once) +idasql -s database.i64 --http 8080 + +# Terminal 2: Query repeatedly via remote client (instant responses) +idasql --remote localhost:8080 -q "SELECT * FROM funcs LIMIT 5" +idasql --remote localhost:8080 -q "SELECT * FROM strings WHERE content LIKE '%error%'" +idasql --remote localhost:8080 -q "SELECT name, size FROM funcs ORDER BY size DESC" +# ... as many queries as needed, no startup cost +``` + +Or use interactive mode on the remote connection: +```bash +idasql --remote localhost:8080 -i +idasql> SELECT COUNT(*) FROM funcs; +idasql> SELECT * FROM xrefs WHERE to_ea = 0x401000; +idasql> .quit +``` + +This approach is significantly faster for iterative analysis since the database remains open and queries go directly through the already-initialized session. + +--- + +## Tables Reference + +### Debugger Tables (Full CRUD) + +#### breakpoints +Debugger breakpoints. Supports full CRUD (SELECT, INSERT, UPDATE, DELETE). Breakpoints persist in the IDB even without an active debugger session. + +| Column | Type | RW | Description | +|--------|------|----|-------------| +| `address` | INT | R | Breakpoint address | +| `enabled` | INT | RW | 1=enabled, 0=disabled | +| `type` | INT | RW | Breakpoint type (0=software, 1=hw_write, 2=hw_read, 3=hw_rdwr, 4=hw_exec) | +| `type_name` | TEXT | R | Type name (software, hardware_write, etc.) | +| `size` | INT | RW | Breakpoint size (for hardware breakpoints) | +| `flags` | INT | RW | Breakpoint flags | +| `pass_count` | INT | RW | Pass count before trigger | +| `condition` | TEXT | RW | Condition expression | +| `loc_type` | INT | R | Location type code | +| `loc_type_name` | TEXT | R | Location type (absolute, relative, symbolic, source) | +| `module` | TEXT | R | Module path (relative breakpoints) | +| `symbol` | TEXT | R | Symbol name (symbolic breakpoints) | +| `offset` | INT | R | Offset (relative/symbolic) | +| `source_file` | TEXT | R | Source file (source breakpoints) | +| `source_line` | INT | R | Source line number | +| `is_hardware` | INT | R | 1=hardware breakpoint | +| `is_active` | INT | R | 1=currently active | +| `group` | TEXT | RW | Breakpoint group name | +| `bptid` | INT | R | Breakpoint ID | + +```sql +-- List all breakpoints +SELECT printf('0x%08X', address) as addr, type_name, enabled, condition +FROM breakpoints; + +-- Add software breakpoint +INSERT INTO breakpoints (address) VALUES (0x401000); + +-- Add hardware write watchpoint +INSERT INTO breakpoints (address, type, size) VALUES (0x402000, 1, 4); + +-- Add conditional breakpoint +INSERT INTO breakpoints (address, condition) VALUES (0x401000, 'eax == 0'); + +-- Disable a breakpoint +UPDATE breakpoints SET enabled = 0 WHERE address = 0x401000; + +-- Delete a breakpoint +DELETE FROM breakpoints WHERE address = 0x401000; + +-- Find which functions have breakpoints +SELECT b.address, f.name, b.type_name, b.enabled +FROM breakpoints b +JOIN funcs f ON b.address >= f.address AND b.address < f.end_ea; +``` + +### Entity Tables + +#### funcs +All detected functions in the binary with prototype information. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Function start address | +| `name` | TEXT | Function name | +| `size` | INT | Function size in bytes | +| `end_ea` | INT | Function end address | +| `flags` | INT | Function flags | + +**Prototype columns** (populated when type info available): + +| Column | Type | Description | +|--------|------|-------------| +| `return_type` | TEXT | Return type string (e.g., "int", "void *") | +| `return_is_ptr` | INT | 1 if return type is pointer | +| `return_is_int` | INT | 1 if return type is exactly int | +| `return_is_integral` | INT | 1 if return type is int-like (int, long, DWORD, BOOL) | +| `return_is_void` | INT | 1 if return type is void | +| `arg_count` | INT | Number of function arguments | +| `calling_conv` | TEXT | Calling convention (cdecl, stdcall, fastcall, etc.) | + +```sql +-- 10 largest functions +SELECT name, size FROM funcs ORDER BY size DESC LIMIT 10; + +-- Functions starting with "sub_" (auto-named, not analyzed) +SELECT name, printf('0x%X', address) as addr FROM funcs WHERE name LIKE 'sub_%'; + +-- Functions returning integers with 3+ arguments +SELECT name, return_type, arg_count FROM funcs +WHERE return_is_integral = 1 AND arg_count >= 3; + +-- Void functions (side effects, callbacks) +SELECT name, arg_count FROM funcs WHERE return_is_void = 1; + +-- Pointer-returning functions (factories, allocators) +SELECT name, return_type FROM funcs WHERE return_is_ptr = 1; + +-- Simple getter functions (no args, returns value) +SELECT name, return_type FROM funcs +WHERE arg_count = 0 AND return_is_void = 0; + +-- Functions by calling convention +SELECT calling_conv, COUNT(*) as count FROM funcs +WHERE calling_conv IS NOT NULL AND calling_conv != '' +GROUP BY calling_conv ORDER BY count DESC; +``` + +#### segments +Memory segments. Supports UPDATE (`name`, `class`, `perm`) and DELETE. + +| Column | Type | RW | Description | +|--------|------|----|-------------| +| `start_ea` | INT | R | Segment start | +| `end_ea` | INT | R | Segment end | +| `name` | TEXT | RW | Segment name (.text, .data, etc.) | +| `class` | TEXT | RW | Segment class (CODE, DATA) | +| `perm` | INT | RW | Permissions (R=4, W=2, X=1) | + +```sql +-- Find executable segments +SELECT name, printf('0x%X', start_ea) as start FROM segments WHERE perm & 1 = 1; + +-- Rename a segment +UPDATE segments SET name = '.mytext' WHERE start_ea = 0x401000; + +-- Change segment permissions to read+exec +UPDATE segments SET perm = 5 WHERE name = '.text'; + +-- Delete a segment +DELETE FROM segments WHERE name = '.rdata'; +``` + +#### names +All named locations (functions, labels, data). + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Address | +| `name` | TEXT | Name | + +#### entries +Entry points (exports, program entry). + +| Column | Type | Description | +|--------|------|-------------| +| `ordinal` | INT | Export ordinal | +| `address` | INT | Entry address | +| `name` | TEXT | Entry name | + +#### imports +Imported functions from external libraries. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Import address (IAT entry) | +| `name` | TEXT | Import name | +| `module` | TEXT | Module/DLL name | +| `ordinal` | INT | Import ordinal | + +```sql +-- Imports from kernel32.dll +SELECT name FROM imports WHERE module LIKE '%kernel32%'; +``` + +#### strings +String literals found in the binary. IDA maintains a cached string list that can be configured. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | String address | +| `length` | INT | String length | +| `type` | INT | String type (raw encoding bits) | +| `type_name` | TEXT | Type name: ascii, utf16, utf32 | +| `width` | INT | Char width (0=1-byte, 1=2-byte, 2=4-byte) | +| `width_name` | TEXT | Width name: 1-byte, 2-byte, 4-byte | +| `layout` | INT | String layout (0=null-terminated, 1-3=pascal) | +| `layout_name` | TEXT | Layout name: termchr, pascal1, pascal2, pascal4 | +| `encoding` | INT | Encoding index (0=default) | +| `content` | TEXT | String content | + +**String Type Encoding:** +IDA stores string type as a 32-bit value: +- Bits 0-1: Width (0=1B/ASCII, 1=2B/UTF-16, 2=4B/UTF-32) +- Bits 2-7: Layout (0=TERMCHR, 1=PASCAL1, 2=PASCAL2, 3=PASCAL4) +- Bits 8-15: term1 (first termination character) +- Bits 16-23: term2 (second termination character) +- Bits 24-31: encoding index + +```sql +-- Find error messages +SELECT content, printf('0x%X', address) as addr FROM strings WHERE content LIKE '%error%'; + +-- ASCII strings only +SELECT * FROM strings WHERE type_name = 'ascii'; + +-- UTF-16 strings (common in Windows) +SELECT * FROM strings WHERE type_name = 'utf16'; + +-- Count strings by type +SELECT type_name, layout_name, COUNT(*) as count +FROM strings GROUP BY type_name, layout_name ORDER BY count DESC; +``` + +**Important:** For new analysis (exe/dll), strings are auto-built. For existing databases (i64/idb), strings are already saved. If you see 0 strings unexpectedly, run `SELECT rebuild_strings()` once to rebuild the list. See String List Functions section below. + +#### xrefs +Cross-references - the most important table for understanding code relationships. + +| Column | Type | Description | +|--------|------|-------------| +| `from_ea` | INT | Source address (who references) | +| `to_ea` | INT | Target address (what is referenced) | +| `type` | INT | Xref type code | +| `is_code` | INT | 1=code xref (call/jump), 0=data xref | + +```sql +-- Who calls function at 0x401000? +SELECT printf('0x%X', from_ea) as caller FROM xrefs WHERE to_ea = 0x401000 AND is_code = 1; + +-- What does function at 0x401000 reference? +SELECT printf('0x%X', to_ea) as target FROM xrefs WHERE from_ea >= 0x401000 AND from_ea < 0x401100; +``` + +#### blocks +Basic blocks within functions. **Use `func_ea` constraint for performance.** + +| Column | Type | Description | +|--------|------|-------------| +| `func_ea` | INT | Containing function | +| `start_ea` | INT | Block start | +| `end_ea` | INT | Block end | +| `size` | INT | Block size | + +```sql +-- Blocks in a specific function (FAST - uses constraint pushdown) +SELECT * FROM blocks WHERE func_ea = 0x401000; + +-- Functions with most basic blocks +SELECT func_at(func_ea) as name, COUNT(*) as blocks +FROM blocks GROUP BY func_ea ORDER BY blocks DESC LIMIT 10; +``` + +### Convenience Views + +Pre-built views for common xref analysis patterns. These simplify caller/callee queries. + +#### callers +Who calls each function. Use this instead of manual xref JOINs. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Target function address | +| `caller_addr` | INT | Xref source address | +| `caller_name` | TEXT | Calling function name | +| `caller_func_addr` | INT | Calling function start | + +```sql +-- Who calls function at 0x401000? +SELECT caller_name, printf('0x%X', caller_addr) as from_addr)PROMPT" + R"PROMPT(FROM callers WHERE func_addr = 0x401000; + +-- Most called functions +SELECT printf('0x%X', func_addr) as addr, COUNT(*) as callers +FROM callers GROUP BY func_addr ORDER BY callers DESC LIMIT 10; +``` + +#### callees +What each function calls. Inverse of callers view. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Calling function address | +| `func_name` | TEXT | Calling function name | +| `callee_addr` | INT | Called address | +| `callee_name` | TEXT | Called function/symbol name | + +```sql +-- What does main call? +SELECT callee_name, printf('0x%X', callee_addr) as addr +FROM callees WHERE func_name LIKE '%main%'; + +-- Functions making most calls +SELECT func_name, COUNT(*) as call_count +FROM callees GROUP BY func_addr ORDER BY call_count DESC LIMIT 10; +``` + +#### string_refs +Which functions reference which strings. Great for finding functions by string content. + +| Column | Type | Description | +|--------|------|-------------| +| `string_addr` | INT | String address | +| `string_value` | TEXT | String content | +| `string_length` | INT | String length | +| `ref_addr` | INT | Reference address | +| `func_addr` | INT | Referencing function | +| `func_name` | TEXT | Function name | + +```sql +-- Find functions using error strings +SELECT func_name, string_value +FROM string_refs +WHERE string_value LIKE '%error%' OR string_value LIKE '%fail%'; + +-- Functions with most string references +SELECT func_name, COUNT(*) as string_count +FROM string_refs WHERE func_name IS NOT NULL +GROUP BY func_addr ORDER BY string_count DESC LIMIT 10; +``` + +### Instruction Tables + +#### instructions +Decoded instructions. Supports DELETE (converts instruction to unexplored bytes). **Always filter by `func_addr` for performance.** + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Instruction address | +| `func_addr` | INT | Containing function | +| `itype` | INT | Instruction type (architecture-specific) | +| `mnemonic` | TEXT | Instruction mnemonic | +| `size` | INT | Instruction size | +| `operand0` | TEXT | First operand | +| `operand1` | TEXT | Second operand | +| `disasm` | TEXT | Full disassembly line | + +```sql +-- Instruction profile of a function (FAST) +SELECT mnemonic, COUNT(*) as count +FROM instructions WHERE func_addr = 0x401330 +GROUP BY mnemonic ORDER BY count DESC; + +-- Find all call instructions in a function +SELECT address, disasm FROM instructions +WHERE func_addr = 0x401000 AND mnemonic = 'call'; + +-- Delete an instruction (convert to unexplored bytes) +DELETE FROM instructions WHERE address = 0x401000; +``` + +**Performance:** `WHERE func_addr = X` uses O(function_size) iteration. Without this constraint, it scans the entire database - SLOW. + +#### disasm_calls +All call instructions with resolved targets. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Function containing the call | +| `ea` | INT | Call instruction address | +| `callee_addr` | INT | Target address (0 if unknown) | +| `callee_name` | TEXT | Target name | + +```sql +-- Functions that call malloc +SELECT DISTINCT func_at(func_addr) as caller +FROM disasm_calls WHERE callee_name LIKE '%malloc%'; +``` + +### Database Modification + +The following tables support modification: + +| Table | INSERT | UPDATE columns | DELETE | +|-------|--------|---------------|--------| +| `breakpoints` | Yes | `enabled`, `type`, `size`, `flags`, `pass_count`, `condition`, `group` | Yes | +| `funcs` | Yes | `name`, `flags` | Yes | +| `names` | Yes | `name` | Yes | +| `comments` | Yes | `comment`, `rep_comment` | Yes | +| `bookmarks` | Yes | `description` | Yes | +| `segments` | — | `name`, `class`, `perm` | Yes | +| `instructions` | — | — | Yes | +| `types` | Yes | Yes | Yes | +| `types_members` | Yes | Yes | Yes | +| `types_enum_values` | Yes | Yes | Yes | +| `ctree_lvars` | — | `name`, `type` | — | + +**INSERT examples:** +```sql +-- Create a function (IDA auto-detects boundaries) +INSERT INTO funcs (address) VALUES (0x401000); + +-- Create a function with name and explicit end +INSERT INTO funcs (address, name, end_ea) VALUES (0x401000, 'my_func', 0x401050); + +-- Set a name at an address +INSERT INTO names (address, name) VALUES (0x401000, 'main'); + +-- Add a comment +INSERT INTO comments (address, comment) VALUES (0x401050, 'Check return value'); + +-- Add a repeatable comment +INSERT INTO comments (address, rpt_comment) VALUES (0x404000, 'Global config'); + +-- Add a bookmark (auto-assigned slot) +INSERT INTO bookmarks (address, description) VALUES (0x401000, 'interesting'); + +-- Add a bookmark at specific slot +INSERT INTO bookmarks (slot, address, description) VALUES (5, 0x401000, 'slot 5'); +``` + +**UPDATE examples:** +```sql +-- Rename a function +UPDATE funcs SET name = 'my_main' WHERE address = 0x401000; + +-- Rename any named address +UPDATE names SET name = 'my_global' WHERE address = 0x404000; + +-- Add/update comment +UPDATE comments SET comment = 'Check return value' WHERE address = 0x401050; + +-- Add repeatable comment +UPDATE comments SET rep_comment = 'Global config' WHERE address = 0x404000; + +-- Delete a name +DELETE FROM names WHERE address = 0x401000; +``` + +**Segments:** +```sql +-- Rename a segment +UPDATE segments SET name = '.mytext' WHERE start_ea = 0x401000; + +-- Change segment class +UPDATE segments SET class = 'DATA' WHERE name = '.rdata'; + +-- Change permissions (R=4, W=2, X=1) +UPDATE segments SET perm = 5 WHERE name = '.text'; + +-- Delete a segment +DELETE FROM segments WHERE name = '.rdata'; +``` + +**Instructions:** +```sql +-- Delete an instruction (convert to unexplored bytes) +DELETE FROM instructions WHERE address = 0x401000; +``` + +**Types:** +```sql +-- Create a new struct +INSERT INTO types (name, kind) VALUES ('my_struct', 'struct'); + +-- Create an enum +INSERT INTO types (name, kind) VALUES ('my_flags', 'enum'); + +-- Create a union +INSERT INTO types (name, kind) VALUES ('my_union', 'union'); + +-- Add a struct member with type +INSERT INTO types_members (type_ordinal, member_name, member_type) VALUES (42, 'field1', 'int'); + +-- Add a struct member (name only, default type) +INSERT INTO types_members (type_ordinal, member_name) VALUES (42, 'field2'); + +-- Add an enum value +INSERT INTO types_enum_values (type_ordinal, value_name, value) VALUES (15, 'FLAG_ACTIVE', 1); + +-- Add an enum value with comment +INSERT INTO types_enum_values (type_ordinal, value_name, value, comment) +VALUES (15, 'FLAG_HIDDEN', 2, 'not visible in UI'); +``` + +**Decompiler local variables (requires Hex-Rays):** +```sql +-- Rename a local variable +UPDATE ctree_lvars SET name = 'buffer_size' +WHERE func_addr = 0x401000 AND name = 'v1'; + +-- Change variable type +UPDATE ctree_lvars SET type = 'char *' +WHERE func_addr = 0x401000 AND idx = 2; +``` + +### Persisting Changes + +Changes to the database (UPDATE, set_name, etc.) are held in memory by default. + +**To persist changes:** +```sql +-- Explicit save (recommended for scripts) +SELECT save_database(); -- Returns 1 on success, 0 on failure +``` + +**CLI flag for auto-save:** +```bash +# Auto-save on exit (use with caution) +idasql -s db.i64 -q "UPDATE funcs SET name='main' WHERE address=0x401000" -w +``` + +**Best practice for batch operations:** +```sql +-- Make multiple changes +UPDATE funcs SET name = 'init_config' WHERE address = 0x401000; +UPDATE names SET name = 'g_settings' WHERE address = 0x402000; +-- Persist once at the end +SELECT save_database(); +``` + +> Without `save_database()` or `-w`, changes are lost when the session ends. + +### Decompiler Tables (Hex-Rays Required) + +**CRITICAL:** Always filter by `func_addr`. Without constraint, these tables will decompile EVERY function - extremely slow! + +#### pseudocode +Structured line-by-line pseudocode with writable comments. **Use `decompile(addr)` to view pseudocode; use this table only for surgical edits (comments) or structured queries.** + +| Column | Type | Writable | Description | +|--------|------|----------|-------------| +| `func_addr` | INT | No | Function address | +| `line_num` | INT | No | Line number | +| `line` | TEXT | No | Pseudocode text | +| `ea` | INT | No | Corresponding assembly address (from COLOR_ADDR anchor) | +| `comment` | TEXT | **Yes** | Decompiler comment at this ea | +| `comment_placement` | TEXT | **Yes** | Comment placement: `semi` (inline, default), `block1` (above line) | + +**Comment placements:** `semi` (after `;`), `block1` (own line above), `block2`, `curly1`, `curly2`, `colon`, `case`, `else`, `do` + +```sql +-- VIEWING: Use decompile() function, NOT the pseudocode table +SELECT decompile(0x401000); + +-- COMMENTING: Use pseudocode table to add/edit/delete comments +-- Add inline comment (appears after semicolon) +UPDATE pseudocode SET comment = 'buffer overflow here' +WHERE func_addr = 0x401000 AND ea = 0x401020; + +-- Add block comment (appears on own line above the statement) +UPDATE pseudocode SET comment_placement = 'block1', comment = 'vulnerable call' +WHERE func_addr = 0x401000 AND ea = 0x401020; + +-- Delete a comment +UPDATE pseudocode SET comment = NULL +WHERE func_addr = 0x401000 AND ea = 0x401020; + +-- STRUCTURED QUERY: Get specific lines with ea and comment info +SELECT ea, line, comment FROM pseudocode WHERE func_addr = 0x401000; +``` + +#### ctree +Full Abstract Syntax Tree of decompiled code. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Function address | +| `item_id` | INT | Unique node ID | +| `is_expr` | INT | 1=expression, 0=statement | +| `op_name` | TEXT | Node type (`cot_call`, `cit_if`, etc.) | +| `ea` | INT | Address in binary | +| `parent_id` | INT | Parent node ID | +| `depth` | INT | Tree depth | +| `x_id`, `y_id`, `z_id` | INT | Child node IDs | +| `var_idx` | INT | Local variable index | +| `var_name` | TEXT | Variable name | +| `obj_ea` | INT | Target address | +| `obj_name` | TEXT | Symbol name | +| `num_value` | INT | Numeric literal | +| `str_value` | TEXT | String literal | + +#### ctree_lvars +Local variables from decompilation. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Function address | +| `idx` | INT | Variable index | +| `name` | TEXT | Variable name | +| `type` | TEXT | Type string | +| `size` | INT | Size in bytes | +| `is_arg` | INT | 1=function argument | +| `is_stk_var` | INT | 1=stack variable | +| `stkoff` | INT | Stack offset | + +#### ctree_call_args +Flattened call arguments for easy querying. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Function address | +| `call_item_id` | INT | Call node ID | +| `arg_idx` | INT | Argument index (0-based) | +| `arg_op` | TEXT | Argument type | +| `arg_var_name` | TEXT | Variable name if applicable | +| `arg_var_is_stk` | INT | 1=stack variable | +| `arg_num_value` | INT | Numeric value | +| `arg_str_value` | TEXT | String value | + +### Decompiler Views + +Pre-built views for common patterns: + +| View | Purpose | +|------|---------| +| `ctree_v_calls` | Function calls with callee info | +| `ctree_v_loops` | for/while/do loops | +| `ctree_v_ifs` | if statements | +| `ctree_v_comparisons` | Comparisons with operands | +| `ctree_v_assignments` | Assignments with operands | +| `ctree_v_derefs` | Pointer dereferences | +| `ctree_v_returns` | Return statements with value details | +| `ctree_v_calls_in_loops` | Calls inside loops (recursive) | +| `ctree_v_calls_in_ifs` | Calls inside if branches (recursive) | +| `ctree_v_leaf_funcs` | Functions with no outgoing calls | +| `ctree_v_call_chains` | Call chain paths up to depth 10 | + +#### ctree_v_returns + +Return statements with details about what's being returned. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Function address | +| `item_id` | INT | Return statement item_id | +| `ea` | INT | Address of return | +| `return_op` | TEXT | Return value opcode (`cot_num`, `cot_var`, `cot_call`, etc.) | +| `return_num` | INT | Numeric value (if `cot_num`) | +| `return_str` | TEXT | String value (if `cot_str`) | +| `return_var` | TEXT | Variable name (if `cot_var`) | +| `returns_arg` | INT | 1 if returning a function argument | +| `returns_call_result` | INT | 1 if returning result of another call | + +```sql +-- Functions that return 0 +SELECT DISTINCT func_at(func_addr) as name FROM ctree_v_returns +WHERE return_op = 'cot_num' AND return_num = 0; + +-- Functions that return -1 (error sentinel) +SELECT DISTINCT func_at(func_addr) as name FROM ctree_v_returns +WHERE return_op = 'cot_num' AND return_num = -1; + +-- Functions that return their argument (pass-through) +SELECT DISTINCT func_at(func_addr) as name FROM ctree_v_returns +WHERE returns_arg = 1; +``` + +### Type Tables + +#### types +All local type definitions. Supports INSERT (create struct/union/enum), UPDATE, and DELETE. + +| Column | Type | Description | +|--------|------|-------------| +| `ordinal` | INT | Type ordinal | +| `name` | TEXT | Type name | +| `size` | INT | Size in bytes | +| `kind` | TEXT | struct/union/enum/typedef/func | +| `is_struct` | INT | 1=struct | +| `is_union` | INT | 1=union | +| `is_enum` | INT | 1=enum | + +#### types_members +Structure and union members. Supports INSERT (add member to struct/union), UPDATE, and DELETE. + +| Column | Type | Description | +|--------|------|-------------| +| `type_ordinal` | INT | Parent type ordinal | +| `type_name` | TEXT | Parent type name | +| `member_name` | TEXT | Member name | +| `offset` | INT | Byte offset | +| `size` | INT | Member size | +| `member_type` | TEXT | Type string | +| `mt_is_ptr` | INT | 1=pointer | +| `mt_is_array` | INT | 1=array | +| `mt_is_struct` | INT | 1=embedded struct | + +#### types_enum_values +Enum constant values. Supports INSERT (add value to enum), UPDATE, and DELETE. + +| Column | Type | Description | +|--------|------|-------------| +| `type_ordinal` | INT | Enum type ordinal | +| `type_name` | TEXT | Enum name | +| `value_name` | TEXT | Constant name | +| `value` | INT | Constant value | + +#### types_func_args +Function prototype arguments with type classification. + +| Column | Type | Description | +|--------|------|-------------| +| `type_ordinal` | INT | Function type ordinal | +| `type_name` | TEXT | Function type name | +| `arg_index` | INT | Argument index (-1 = return type, 0+ = args) | +| `arg_name` | TEXT | Argument name | +| `arg_type` | TEXT | Argument type string | +| `calling_conv` | TEXT | Calling convention (on return row only) | + +**Surface-level type classification** (literal type as written): + +| Column | Type | Description | +|--------|------|-------------| +| `is_ptr` | INT | 1 if pointer type | +| `is_int` | INT | 1 if exactly int type | +| `is_integral` | INT | 1 if int-like (int, long, short, char, bool) | +| `is_float` | INT | 1 if float/double | +| `is_void` | INT | 1 if void | +| `is_struct` | INT | 1 if struct/union | +| `is_array` | INT | 1 if array | +| `ptr_depth` | INT | Pointer depth (int** = 2) | +| `base_type` | TEXT | Type with pointers stripped | + +**Resolved type classification** (after typedef resolution): + +| Column | Type | Description | +|--------|------|-------------| +| `is_ptr_resolved` | INT | 1 if resolved type is pointer |)PROMPT" + R"PROMPT(| `is_int_resolved` | INT | 1 if resolved type is exactly int | +| `is_integral_resolved` | INT | 1 if resolved type is int-like | +| `is_float_resolved` | INT | 1 if resolved type is float/double | +| `is_void_resolved` | INT | 1 if resolved type is void | +| `ptr_depth_resolved` | INT | Pointer depth after resolution | +| `base_type_resolved` | TEXT | Resolved type with pointers stripped | + +```sql +-- Functions returning integers (strict: exactly int) +SELECT type_name FROM types_func_args +WHERE arg_index = -1 AND is_int = 1; + +-- Functions returning integers (loose: includes BOOL, DWORD, LONG) +SELECT type_name FROM types_func_args +WHERE arg_index = -1 AND is_integral_resolved = 1; + +-- Functions taking 4 pointer arguments +SELECT type_name, COUNT(*) as ptr_args FROM types_func_args +WHERE arg_index >= 0 AND is_ptr = 1 +GROUP BY type_ordinal HAVING ptr_args = 4; + +-- Typedefs that hide pointers (HANDLE, etc.) +SELECT type_name, arg_type FROM types_func_args +WHERE is_ptr = 0 AND is_ptr_resolved = 1; +``` + +### Type Views + +Convenience views for filtering types: + +| View | Description | +|------|-------------| +| `types_v_structs` | `SELECT * FROM types WHERE is_struct = 1` | +| `types_v_unions` | `SELECT * FROM types WHERE is_union = 1` | +| `types_v_enums` | `SELECT * FROM types WHERE is_enum = 1` | +| `types_v_typedefs` | `SELECT * FROM types WHERE is_typedef = 1` | +| `types_v_funcs` | `SELECT * FROM types WHERE is_func = 1` | +| `local_types` | Legacy compatibility view | + +### Extended Tables + +#### bookmarks +User-defined bookmarks/marked positions. + +| Column | Type | Description | +|--------|------|-------------| +| `index` | INT | Bookmark index | +| `address` | INT | Bookmarked address | +| `description` | TEXT | Bookmark description | + +```sql +-- List all bookmarks +SELECT printf('0x%X', address) as addr, description FROM bookmarks; +``` + +#### heads +All defined items (code/data heads) in the database. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Head address | +| `size` | INT | Item size | +| `flags` | INT | IDA flags | + +**Performance:** This table can be very large. Always use address range filters. + +#### fixups +Relocation and fixup information. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Fixup address | +| `type` | INT | Fixup type | +| `target` | INT | Target address | + +#### hidden_ranges +Collapsed/hidden code regions in IDA. + +| Column | Type | Description | +|--------|------|-------------| +| `start_ea` | INT | Range start | +| `end_ea` | INT | Range end | +| `description` | TEXT | Description | +| `visible` | INT | Visibility state | + +#### problems +IDA analysis problems and warnings. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Problem address | +| `type` | INT | Problem type code | +| `description` | TEXT | Problem description | + +```sql +-- Find all analysis problems +SELECT printf('0x%X', address) as addr, description FROM problems; +``` + +#### fchunks +Function chunks (for functions with non-contiguous code, like exception handlers). + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Parent function | +| `start_ea` | INT | Chunk start | +| `end_ea` | INT | Chunk end | +| `size` | INT | Chunk size | + +```sql +-- Functions with multiple chunks (complex control flow) +SELECT func_at(func_addr) as name, COUNT(*) as chunks +FROM fchunks GROUP BY func_addr HAVING chunks > 1; +``` + +#### signatures +FLIRT signature matches. + +| Column | Type | Description | +|--------|------|-------------| +| `address` | INT | Matched address | +| `name` | TEXT | Signature name | +| `library` | TEXT | Library name | + +#### mappings +Memory mappings for debugging. + +| Column | Type | Description | +|--------|------|-------------| +| `from_ea` | INT | Mapped from | +| `to_ea` | INT | Mapped to | +| `size` | INT | Mapping size | + +### Metadata Tables + +#### db_info +Database-level metadata. + +| Column | Type | Description | +|--------|------|-------------| +| `key` | TEXT | Metadata key | +| `value` | TEXT | Metadata value | + +```sql +-- Get database info +SELECT * FROM db_info; +``` + +#### ida_info +IDA processor and analysis info. + +| Column | Type | Description | +|--------|------|-------------| +| `key` | TEXT | Info key | +| `value` | TEXT | Info value | + +```sql +-- Get processor type +SELECT value FROM ida_info WHERE key = 'procname'; +``` + +### Disassembly Tables + +#### disasm_loops +Detected loops in disassembly. + +| Column | Type | Description | +|--------|------|-------------| +| `func_addr` | INT | Function address | +| `loop_start` | INT | Loop header address | +| `loop_end` | INT | Loop end address | + +### Disassembly Views + +Views for disassembly-level analysis (no Hex-Rays required): + +| View | Description | +|------|-------------| +| `disasm_v_leaf_funcs` | Functions with no outgoing calls | +| `disasm_v_call_chains` | Call chain paths (recursive CTE) | +| `disasm_v_calls_in_loops` | Calls inside loop bodies | +| `disasm_v_funcs_with_loops` | Functions containing loops | + +```sql +-- Find functions that don't call anything +SELECT * FROM disasm_v_leaf_funcs LIMIT 10; + +-- Find hotspot calls (inside loops) +SELECT func_at(func_addr) as func, callee_name +FROM disasm_v_calls_in_loops; +``` + +--- + +## SQL Functions + +### Disassembly +| Function | Description | +|----------|-------------| +| `disasm(addr)` | Disassembly line at address | +| `disasm(addr, n)` | Multiple lines from address | +| `bytes(addr, n)` | Bytes as hex string | +| `bytes_raw(addr, n)` | Raw bytes as BLOB | +| `mnemonic(addr)` | Instruction mnemonic only | +| `operand(addr, n)` | Operand text (n=0-5) | + +### Binary Search +| Function | Description | +|----------|-------------| +| `search_bytes(pattern)` | Find all matches, returns JSON array | +| `search_bytes(pattern, start, end)` | Search within address range | +| `search_first(pattern)` | First match address (or NULL) | +| `search_first(pattern, start, end)` | First match in range | + +**Pattern syntax (IDA native):** +- `"48 8B 05"` - Exact bytes (hex, space-separated) +- `"48 ? 05"` or `"48 ?? 05"` - `?` = any byte wildcard (whole byte only) +- `"(01 02 03)"` - Alternatives (match any of these bytes) + +**Note:** Unlike Binary Ninja, IDA does NOT support nibble wildcards or regex. + +**Example:** +```sql +-- Find all matches for a pattern +SELECT search_bytes('48 8B ? 00'); + +-- Parse JSON results +SELECT json_extract(value, '$.address') as addr +FROM json_each(search_bytes('48 89 ?')) +LIMIT 10; + +-- First match only +SELECT printf('0x%llX', search_first('CC CC CC')); + +-- Search with alternatives +SELECT search_bytes('E8 (01 02 03 04)'); +``` + +**Optimization Pattern: Find functions using specific instruction** + +To answer "How many functions use RDTSC instruction?" efficiently: +```sql +-- Count unique functions containing RDTSC (opcode: 0F 31) +SELECT COUNT(DISTINCT func_start(json_extract(value, '$.address'))) as count +FROM json_each(search_bytes('0F 31')) +WHERE func_start(json_extract(value, '$.address')) IS NOT NULL; + +-- List those functions with names +SELECT DISTINCT + func_start(json_extract(value, '$.address')) as func_ea, + name_at(func_start(json_extract(value, '$.address'))) as func_name +FROM json_each(search_bytes('0F 31')) +WHERE func_start(json_extract(value, '$.address')) IS NOT NULL; +``` + +This is **much faster** than scanning all disassembly lines because: +- `search_bytes()` uses native binary search +- `func_start()` is O(1) lookup in IDA's function index + +### Names & Functions +| Function | Description | +|----------|-------------| +| `name_at(addr)` | Name at address | +| `func_at(addr)` | Function name containing address | +| `func_start(addr)` | Start of containing function | +| `func_end(addr)` | End of containing function | +| `func_qty()` | Total function count | +| `func_at_index(n)` | Function address at index (O(1)) | + +### Cross-References +| Function | Description | +|----------|-------------| +| `xrefs_to(addr)` | JSON array of xrefs TO address | +| `xrefs_from(addr)` | JSON array of xrefs FROM address | + +### Navigation +| Function | Description | +|----------|-------------| +| `next_head(addr)` | Next defined item | +| `prev_head(addr)` | Previous defined item | +| `segment_at(addr)` | Segment name at address | +| `hex(val)` | Format as hex string | + +### Comments +| Function | Description | +|----------|-------------| +| `comment_at(addr)` | Get comment at address | +| `set_comment(addr, text)` | Set regular comment | +| `set_comment(addr, text, 1)` | Set repeatable comment | + +### Modification +| Function | Description | +|----------|-------------| +| `set_name(addr, name)` | Set name at address | + +### Item Analysis +| Function | Description | +|----------|-------------| +| `item_type(addr)` | Item type flags at address | +| `item_size(addr)` | Item size at address | +| `is_code(addr)` | Returns 1 if address is code | +| `is_data(addr)` | Returns 1 if address is data | +| `flags_at(addr)` | Raw IDA flags at address | + +### Instruction Details +| Function | Description | +|----------|-------------| +| `itype(addr)` | Instruction type code (processor-specific) | +| `decode_insn(addr)` | Full instruction info as JSON | +| `operand_type(addr, n)` | Operand type code (o_void, o_reg, etc.) | +| `operand_value(addr, n)` | Operand value (register num, immediate, etc.) | + +```sql +-- Get instruction type for filtering +SELECT address, itype(address) as itype, mnemonic(address) +FROM heads WHERE is_code(address) = 1 LIMIT 10; + +-- Decode full instruction +SELECT decode_insn(0x401000); +``` + +### Decompilation + +**When to use `decompile()` vs `pseudocode` table:** +- **To view/show pseudocode** → always use `SELECT decompile(addr)`. Returns the full function as a single text block with `/* ea */` address prefixes. This is fast, efficient, and what you should use when the user asks to "decompile", "show the code", or "show the pseudocode". +- **To read specific lines or columns** → query the `pseudocode` table. If you already have the full output from `decompile()`, refer to it directly. Only query the table when you need structured access (e.g. filtering by ea, reading comment values). +- **To add/edit/delete comments** → `UPDATE pseudocode SET comment = '...' WHERE func_addr = X AND ea = Y`. The pseudocode table is the write interface for decompiler comments. + +| Function | Description | +|----------|-------------| +| `decompile(addr)` | **PREFERRED** — Full pseudocode with `/* ea */` prefixes (requires Hex-Rays) | +| `decompile(addr, 1)` | Same but forces re-decompilation (use after writing comments or renaming variables) | +| `list_lvars(addr)` | List local variables as JSON | +| `rename_lvar(addr, old, new)` | Rename a local variable (shortcut for `UPDATE ctree_lvars`) | + +```sql +-- Decompile a function (PREFERRED way to view pseudocode) +SELECT decompile(0x401000); + +-- After modifying comments or variables, re-decompile to see changes +SELECT decompile(0x401000, 1); + +-- Get all local variables in a function +SELECT list_lvars(0x401000); + +-- Rename a variable (function shortcut) +SELECT rename_lvar(0x401000, 'v1', 'buffer_size'); + +-- Equivalent using UPDATE (canonical approach) +UPDATE ctree_lvars SET name = 'buffer_size' WHERE func_addr = 0x401000 AND name = 'v1'; +``` + +### File Generation +| Function | Description | +|----------|-------------| +| `gen_asm_file(start, end, path)` | Generate ASM file | +| `gen_lst_file(start, end, path)` | Generate listing file | +| `gen_map_file(path)` | Generate MAP file | +| `gen_idc_file(start, end, path)` | Generate IDC script | +| `gen_html_file(start, end, path)` | Generate HTML file | + +```sql +-- Export function as ASM +SELECT gen_asm_file(0x401000, 0x401100, '/tmp/func.asm'); + +-- Generate MAP file +SELECT gen_map_file('/tmp/binary.map'); +``` + +### Graph Generation +| Function | Description | +|----------|-------------| +| `gen_cfg_dot(addr)` | Generate CFG as DOT graph string | +| `gen_cfg_dot_file(addr, path)` | Write CFG DOT to file | +| `gen_schema_dot()` | Generate database schema as DOT | + +```sql +-- Get CFG for a function as DOT format +SELECT gen_cfg_dot(0x401000); + +-- Export schema visualization +SELECT gen_schema_dot(); +``` + +### Entity Search ("Jump to Anything") +| Function | Description | +|----------|-------------| +| `jump_search(pattern, mode, limit, offset)` | Search entities, returns JSON array | +| `jump_query(pattern, mode, limit, offset)` | Returns the generated SQL string | + +```sql +-- Search for functions/types/labels starting with 'sub' +SELECT jump_search('sub', 'prefix', 10, 0); + +-- Search for anything containing 'main' +SELECT jump_search('main', 'contains', 10, 0); +``` + +### String List Functions + +IDA maintains a cached list of strings. Use `rebuild_strings()` to detect and cache strings. + +| Function | Description | +|----------|-------------| +| `rebuild_strings()` | Rebuild with ASCII + UTF-16, minlen 5 (default) | +| `rebuild_strings(minlen)` | Rebuild with custom minimum length | +| `rebuild_strings(minlen, types)` | Rebuild with custom length and type mask | +| `string_count()` | Get current string count (no rebuild) | + +**Type mask values:** +- `1` = ASCII only (STRTYPE_C) +- `2` = UTF-16 only (STRTYPE_C_16) +- `4` = UTF-32 only (STRTYPE_C_32) +- `3` = ASCII + UTF-16 (default) +- `7` = All types + +```sql +-- Check current string count +SELECT string_count(); + +-- Rebuild with defaults (ASCII + UTF-16, minlen 5) +SELECT rebuild_strings(); + +-- Rebuild with shorter minimum length +SELECT rebuild_strings(4); + +-- Rebuild with specific types +SELECT rebuild_strings(5, 1); -- ASCII only +SELECT rebuild_strings(5, 7); -- All types (ASCII + UTF-16 + UTF-32) + +-- Typical workflow: rebuild then query +SELECT rebuild_strings(); +SELECT * FROM strings WHERE content LIKE '%error%'; +``` + +**IMPORTANT - Agent Behavior for String Queries:** +When the user asks about strings (e.g., "show me the strings", "what strings are in this binary"): +1. First run `SELECT rebuild_strings()` to ensure strings are detected +2. Then query the `strings` table + +The `rebuild_strings()` function configures IDA's string detection with sensible defaults (ASCII + UTF-16, minimum length 5) and rebuilds the string list. This ensures the user gets results even if the database had no prior string analysis. + +--- + +## Entity Search Table (jump_entities) + +A table-valued function for unified entity search with full SQL composability. + +### Usage + +```sql +-- Basic search (function-call syntax) +SELECT * FROM jump_entities('sub', 'prefix') LIMIT 10; + +-- Filter by kind +SELECT * FROM jump_entities('EH', 'prefix') WHERE kind = 'struct'; + +-- JOIN with other tables +SELECT j.name, f.size +FROM jump_entities('sub', 'prefix') j +LEFT JOIN funcs f ON j.address = f.address +WHERE j.kind = 'function'; +``` + +### Parameters + +| Parameter | Description | +|-----------|-------------| +| `pattern` | Search pattern (required) | +| `mode` | `'prefix'` or `'contains'` | + +### Columns + +| Column | Type | Description | +|--------|------|-------------| +| `name` | TEXT | Entity name | +| `kind` | TEXT | function/label/segment/struct/union/enum/member/enum_member |)PROMPT" + R"PROMPT(| `address` | INT | Address (for functions, labels, segments) | +| `ordinal` | INT | Type ordinal (for types, members) | +| `parent_name` | TEXT | Parent type (for members) | +| `full_name` | TEXT | Fully qualified name | + +**Use Case:** Implement "Jump to Anything" with virtual scrolling - lazy cursor respects LIMIT. + +--- + +## Performance Rules + +### CRITICAL: Constraint Pushdown + +Some tables have **optimized filters** that use efficient IDA SDK APIs: + +| Table | Optimized Filter | Without Filter | +|-------|------------------|----------------| +| `instructions` | `func_addr = X` | O(all instructions) - SLOW | +| `blocks` | `func_ea = X` | O(all blocks) | +| `xrefs` | `to_ea = X` or `from_ea = X` | O(all xrefs) | +| `pseudocode` | `func_addr = X` | **Decompiles ALL functions** | +| `ctree*` | `func_addr = X` | **Decompiles ALL functions** | + +**Always filter decompiler tables by `func_addr`!** + +### Use Integer Comparisons + +```sql +-- SLOW: String comparison +WHERE mnemonic = 'call' + +-- FAST: Integer comparison +WHERE itype IN (16, 18) -- x86 call opcodes +``` + +### O(1) Random Access + +```sql +-- SLOW: O(n) - sorts all rows +SELECT address FROM funcs ORDER BY RANDOM() LIMIT 1; + +-- FAST: O(1) - direct index access +SELECT func_at_index(ABS(RANDOM()) % func_qty()); +``` + +--- + +## Common Query Patterns + +### Find Most Called Functions + +```sql +SELECT f.name, COUNT(*) as callers +FROM funcs f +JOIN xrefs x ON f.address = x.to_ea +WHERE x.is_code = 1 +GROUP BY f.address +ORDER BY callers DESC +LIMIT 10; +``` + +### Find Functions Calling a Specific API + +```sql +SELECT DISTINCT func_at(from_ea) as caller +FROM xrefs +WHERE to_ea = (SELECT address FROM imports WHERE name = 'CreateFileW'); +``` + +### String Cross-Reference Analysis + +```sql +SELECT s.content, func_at(x.from_ea) as used_by +FROM strings s +JOIN xrefs x ON s.address = x.to_ea +WHERE s.content LIKE '%password%'; +``` + +### Function Complexity (by Block Count) + +```sql +SELECT func_at(func_ea) as name, COUNT(*) as block_count +FROM blocks +GROUP BY func_ea +ORDER BY block_count DESC +LIMIT 10; +``` + +### Find Leaf Functions (No Outgoing Calls) + +```sql +SELECT f.name, f.size +FROM funcs f +LEFT JOIN disasm_calls c ON c.func_addr = f.address +GROUP BY f.address +HAVING COUNT(c.ea) = 0 +ORDER BY f.size DESC; +``` + +### Functions with Deep Call Chains + +```sql +SELECT f.name, MAX(cc.depth) as max_depth +FROM disasm_v_call_chains cc +JOIN funcs f ON f.address = cc.root_func +GROUP BY cc.root_func +ORDER BY max_depth DESC +LIMIT 10; +``` + +### Security: Dangerous Function Calls with Stack Buffers + +```sql +SELECT f.name, c.callee_name, printf('0x%X', c.ea) as address +FROM funcs f +JOIN ctree_v_calls c ON c.func_addr = f.address +JOIN ctree_call_args a ON a.func_addr = c.func_addr AND a.call_item_id = c.item_id +WHERE c.callee_name IN ('strcpy', 'strcat', 'sprintf', 'gets', 'memcpy') + AND a.arg_idx = 0 AND a.arg_var_is_stk = 1 +ORDER BY f.name; +``` + +### Find Zero Comparisons (Potential Error Checks) + +```sql +SELECT func_at(func_addr) as func, printf('0x%X', ea) as addr +FROM ctree_v_comparisons +WHERE op_name = 'cot_eq' AND rhs_op = 'cot_num' AND rhs_num = 0; +``` + +### Calls Inside Loops (Performance Hotspots) + +```sql +SELECT f.name, l.callee_name, l.loop_op +FROM ctree_v_calls_in_loops l +JOIN funcs f ON f.address = l.func_addr +ORDER BY f.name; +``` + +### malloc with Constant Size + +```sql +SELECT func_at(c.func_addr) as func, a.arg_num_value as size +FROM ctree_v_calls c +JOIN ctree_call_args a ON a.func_addr = c.func_addr AND a.call_item_id = c.item_id +WHERE c.callee_name LIKE '%malloc%' + AND a.arg_idx = 0 AND a.arg_op = 'cot_num' +ORDER BY a.arg_num_value DESC; +``` + +### Largest Structures + +```sql +SELECT name, size, alignment +FROM types +WHERE is_struct = 1 AND size > 0 +ORDER BY size DESC +LIMIT 10; +``` + +### Instruction Profile for a Function + +```sql +SELECT mnemonic, COUNT(*) as count +FROM instructions +WHERE func_addr = 0x401330 +GROUP BY mnemonic +ORDER BY count DESC; +``` + +### Import Dependency Map + +```sql +-- Which modules does each function depend on? +SELECT f.name as func_name, i.module, COUNT(*) as api_count +FROM funcs f +JOIN disasm_calls dc ON dc.func_addr = f.address +JOIN imports i ON dc.callee_addr = i.address +GROUP BY f.address, i.module +ORDER BY f.name, api_count DESC; +``` + +### Find Indirect Calls (Potential Virtual Functions/Callbacks) + +```sql +-- Functions with indirect calls (call through register/memory) +SELECT f.name, COUNT(*) as indirect_calls +FROM funcs f +JOIN disasm_calls dc ON dc.func_addr = f.address +WHERE dc.callee_addr = 0 -- Unresolved target = indirect +GROUP BY f.address +ORDER BY indirect_calls DESC +LIMIT 20; +``` + +### String Format Audit (printf-style Vulnerabilities) + +```sql +-- Format string usage with variable formats (potential vuln) +SELECT f.name, c.callee_name, printf('0x%X', c.ea) as addr +FROM funcs f +JOIN ctree_v_calls c ON c.func_addr = f.address +JOIN ctree_call_args a ON a.func_addr = c.func_addr AND a.call_item_id = c.item_id +WHERE c.callee_name LIKE '%printf%' + AND a.arg_idx = 0 -- First arg is format string + AND a.arg_op = 'cot_var'; -- Variable, not constant string +``` + +### Memory Allocation Patterns + +```sql +-- Find functions that allocate but may not free +WITH allocators AS ( + SELECT func_addr, COUNT(*) as alloc_count + FROM disasm_calls + WHERE callee_name LIKE '%alloc%' OR callee_name LIKE '%malloc%' + GROUP BY func_addr +), +freers AS ( + SELECT func_addr, COUNT(*) as free_count + FROM disasm_calls + WHERE callee_name LIKE '%free%' + GROUP BY func_addr +) +SELECT f.name, + COALESCE(a.alloc_count, 0) as allocations, + COALESCE(r.free_count, 0) as frees +FROM funcs f +LEFT JOIN allocators a ON f.address = a.func_addr +LEFT JOIN freers r ON f.address = r.func_addr +WHERE a.alloc_count > 0 AND COALESCE(r.free_count, 0) = 0 +ORDER BY allocations DESC; +``` + +### Control Flow Anomalies + +```sql +-- Functions with many basic blocks but few instructions (possibly obfuscated) +SELECT + f.name, + f.size, + COUNT(DISTINCT b.start_ea) as blocks, + f.size / COUNT(DISTINCT b.start_ea) as avg_block_size +FROM funcs f +JOIN blocks b ON b.func_ea = f.address +WHERE f.size > 100 +GROUP BY f.address +HAVING COUNT(DISTINCT b.start_ea) > 10 + AND f.size / COUNT(DISTINCT b.start_ea) < 10 -- Very small blocks +ORDER BY blocks DESC; +``` + +### Return Value Analysis + +```sql +-- Functions with multiple return statements (complex control flow) +SELECT f.name, COUNT(*) as return_count +FROM funcs f +JOIN ctree ct ON ct.func_addr = f.address +WHERE ct.op_name = 'cit_return' +GROUP BY f.address +HAVING COUNT(*) > 3 +ORDER BY return_count DESC; + +-- Functions that return 0 (common success pattern) +SELECT DISTINCT func_at(func_addr) as name FROM ctree_v_returns +WHERE return_op = 'cot_num' AND return_num = 0; + +-- Functions that return -1 (error sentinel) +SELECT DISTINCT func_at(func_addr) as name FROM ctree_v_returns +WHERE return_op = 'cot_num' AND return_num = -1; + +-- Functions that return a specific constant +SELECT DISTINCT func_at(func_addr) as name FROM ctree_v_returns +WHERE return_op = 'cot_num' AND return_num = 1; +``` + +### Function Signature Queries + +```sql +-- Functions returning integers (includes BOOL, DWORD via resolved) +SELECT type_name FROM types_func_args +WHERE arg_index = -1 AND is_integral_resolved = 1; + +-- Functions taking exactly 4 pointer arguments +SELECT type_name, COUNT(*) as ptr_args FROM types_func_args +WHERE arg_index >= 0 AND is_ptr = 1 +GROUP BY type_ordinal HAVING ptr_args = 4; + +-- Functions with string parameters (char*/wchar_t*) +SELECT DISTINCT type_name FROM types_func_args +WHERE arg_index >= 0 AND is_ptr = 1 + AND base_type_resolved IN ('char', 'wchar_t', 'CHAR', 'WCHAR'); + +-- Typedefs hiding pointers (HANDLE, HMODULE, etc.) +SELECT DISTINCT type_name, arg_type FROM types_func_args +WHERE is_ptr = 0 AND is_ptr_resolved = 1; + +-- Functions returning void pointers +SELECT type_name FROM types_func_args +WHERE arg_index = -1 AND is_ptr_resolved = 1 AND is_void_resolved = 1; +``` + +### Loops with System Calls (Performance/Security Hotspots) + +```sql +-- System API calls inside loops +SELECT + f.name as function, + l.callee_name as api_called, + l.loop_op as loop_type +FROM ctree_v_calls_in_loops l +JOIN funcs f ON f.address = l.func_addr +JOIN imports i ON l.callee_name = i.name +ORDER BY f.name; +``` + +### Type Usage Statistics + +```sql +-- Most referenced types (by struct member usage in decompiled code) +SELECT tm.type_name, COUNT(DISTINCT ct.func_addr) as func_count +FROM types_members tm +JOIN ctree ct ON ct.var_name = tm.member_name +GROUP BY tm.type_name +ORDER BY func_count DESC +LIMIT 20; +``` + +### Data Section Analysis + +```sql +-- Find functions referencing data sections +SELECT + f.name, + s.name as segment, + COUNT(*) as data_refs +FROM funcs f +JOIN xrefs x ON x.from_ea BETWEEN f.address AND f.end_ea +JOIN segments s ON x.to_ea BETWEEN s.start_ea AND s.end_ea +WHERE s.class = 'DATA' AND x.is_code = 0 +GROUP BY f.address, s.name +ORDER BY data_refs DESC +LIMIT 20; +``` + +### Exception Handler Detection + +```sql +-- Functions with multiple chunks (often due to exception handlers) +SELECT + f.name, + COUNT(*) as chunk_count, + SUM(fc.size) as total_size +FROM funcs f +JOIN fchunks fc ON fc.func_addr = f.address +GROUP BY f.address +HAVING COUNT(*) > 1 +ORDER BY chunk_count DESC; +``` + +--- + +## Advanced SQL Patterns + +### Common Table Expressions (CTEs) + +CTEs make complex queries readable and allow recursive traversal. + +#### Basic CTE for Filtering + +```sql +-- Find functions that both call malloc AND check return value +WITH malloc_callers AS ( + SELECT DISTINCT func_addr + FROM disasm_calls + WHERE callee_name LIKE '%malloc%' +), +null_checkers AS ( + SELECT DISTINCT func_addr + FROM ctree_v_comparisons + WHERE rhs_num = 0 AND op_name = 'cot_eq' +) +SELECT f.name +FROM funcs f +JOIN malloc_callers m ON f.address = m.func_addr +JOIN null_checkers n ON f.address = n.func_addr; +``` + +#### CTE with Aggregation + +```sql +-- Functions ranked by complexity (calls * blocks) +WITH call_counts AS ( + SELECT func_addr, COUNT(*) as call_cnt + FROM disasm_calls + GROUP BY func_addr +), +block_counts AS ( + SELECT func_ea as func_addr, COUNT(*) as block_cnt + FROM blocks + GROUP BY func_ea +) +SELECT f.name, + COALESCE(c.call_cnt, 0) as calls, + COALESCE(b.block_cnt, 0) as blocks, + COALESCE(c.call_cnt, 0) * COALESCE(b.block_cnt, 0) as complexity +FROM funcs f +LEFT JOIN call_counts c ON f.address = c.func_addr +LEFT JOIN block_counts b ON f.address = b.func_addr +ORDER BY complexity DESC +LIMIT 10; +``` + +### Recursive CTEs (Call Graph Traversal) + +```sql +-- Find all functions reachable from main (up to depth 5) +WITH RECURSIVE call_graph AS ( + -- Base case: start from main + SELECT address as func_addr, name, 0 as depth + FROM funcs WHERE name = 'main' + + UNION ALL + + -- Recursive case: follow calls + SELECT f.address, f.name, cg.depth + 1 + FROM call_graph cg + JOIN disasm_calls dc ON dc.func_addr = cg.func_addr + JOIN funcs f ON f.address = dc.callee_addr + WHERE cg.depth < 5 + AND dc.callee_addr != 0 -- Skip indirect calls +) +SELECT DISTINCT func_addr, name, MIN(depth) as min_depth +FROM call_graph +GROUP BY func_addr +ORDER BY min_depth, name; +``` + +```sql +-- Reverse call graph: who calls this function (transitive) +WITH RECURSIVE callers AS ( + -- Base: direct callers of target + SELECT DISTINCT dc.func_addr, 1 as depth + FROM disasm_calls dc + WHERE dc.callee_addr = 0x401000 + + UNION ALL + + -- Recursive: who calls the callers + SELECT DISTINCT dc.func_addr, c.depth + 1 + FROM callers c + JOIN disasm_calls dc ON dc.callee_addr = c.func_addr + WHERE c.depth < 5 +) +SELECT func_at(func_addr) as caller, MIN(depth) as distance +FROM callers +GROUP BY func_addr +ORDER BY distance, caller; +``` + +### Window Functions + +```sql +-- Rank functions by size within each segment +SELECT + segment_at(f.address) as seg, + f.name, + f.size, + ROW_NUMBER() OVER (PARTITION BY segment_at(f.address) ORDER BY f.size DESC) as rank +FROM funcs f +WHERE f.size > 0; +``` + +```sql +-- Running total of function sizes +SELECT + name, + size, + SUM(size) OVER (ORDER BY address) as cumulative_size +FROM funcs +ORDER BY address; +``` + +```sql +-- Find consecutive functions with similar sizes (possible duplicates) +SELECT + name, + size, + LAG(name) OVER (ORDER BY size) as prev_name, + LAG(size) OVER (ORDER BY size) as prev_size +FROM funcs +WHERE size > 100; +``` + +### Complex JOINs + +#### Multi-Table Join (Functions with Context) + +```sql +-- Function overview with all relationships +SELECT + f.name, + f.size, + segment_at(f.address) as segment, + (SELECT COUNT(*) FROM blocks WHERE func_ea = f.address) as block_count, + (SELECT COUNT(*) FROM disasm_calls WHERE func_addr = f.address) as outgoing_calls, + (SELECT COUNT(*) FROM xrefs WHERE to_ea = f.address AND is_code = 1) as incoming_calls, + (SELECT COUNT(*) FROM ctree_lvars WHERE func_addr = f.address) as local_vars +FROM funcs f +ORDER BY f.size DESC +LIMIT 20; +``` + +#### Self-Join (Compare Functions) + +```sql +-- Find functions with identical sizes (potential clones) +SELECT + f1.name as func1, + f2.name as func2, + f1.size +FROM funcs f1 +JOIN funcs f2 ON f1.size = f2.size AND f1.address < f2.address +WHERE f1.size > 50 -- Ignore tiny functions +ORDER BY f1.size DESC; +``` + +### Subqueries + +```sql +-- Functions that call more APIs than average +SELECT f.name, call_count +FROM ( + SELECT func_addr, COUNT(*) as call_count + FROM disasm_calls dc + JOIN imports i ON dc.callee_addr = i.address + GROUP BY func_addr +) sub +JOIN funcs f ON f.address = sub.func_addr +WHERE call_count > ( + SELECT AVG(cnt) FROM ( + SELECT COUNT(*) as cnt + FROM disasm_calls dc + JOIN imports i ON dc.callee_addr = i.address + GROUP BY func_addr + ) +) +ORDER BY call_count DESC; +``` + +### CASE Expressions + +```sql +-- Categorize functions by complexity +SELECT + name, + size, + CASE + WHEN size < 50 THEN 'tiny' + WHEN size < 200 THEN 'small' + WHEN size < 1000 THEN 'medium' + WHEN size < 5000 THEN 'large' + ELSE 'huge' + END as category +FROM funcs +ORDER BY size DESC; +``` + +```sql +-- Classify strings by content +SELECT + content, + CASE + WHEN content LIKE '%error%' OR content LIKE '%fail%' THEN 'error' + WHEN content LIKE '%password%' OR content LIKE '%key%' THEN 'sensitive' + WHEN content LIKE '%http%' OR content LIKE '%://% ' THEN 'url' + WHEN content LIKE '%.dll%' OR content LIKE '%.exe%' THEN 'file' + ELSE 'other' + END as category +FROM strings +WHERE length > 5; +``` + +### Batch Analysis with UNION ALL + +```sql +-- Comprehensive security audit in one query +SELECT 'dangerous_func' as check_type, func_at(func_addr) as location, callee_name as detail +FROM disasm_calls +WHERE callee_name IN ('strcpy', 'strcat', 'sprintf', 'gets', 'scanf') + +UNION ALL + +SELECT 'crypto_usage', func_at(func_addr), callee_name +FROM disasm_calls)PROMPT" + R"PROMPT(WHERE callee_name LIKE '%Crypt%' OR callee_name LIKE '%AES%' OR callee_name LIKE '%RSA%' + +UNION ALL + +SELECT 'network_call', func_at(func_addr), callee_name +FROM disasm_calls +WHERE callee_name IN ('socket', 'connect', 'send', 'recv', 'WSAStartup') + +UNION ALL + +SELECT 'registry_access', func_at(func_addr), callee_name +FROM disasm_calls +WHERE callee_name LIKE 'Reg%' + +ORDER BY check_type, location; +``` + +### Efficient Pagination + +```sql +-- Page through large result sets efficiently +SELECT * FROM ( + SELECT + f.name, + f.size, + ROW_NUMBER() OVER (ORDER BY f.size DESC) as row_num + FROM funcs f +) +WHERE row_num BETWEEN 101 AND 200; -- Page 2 (100 per page) +``` + +### EXISTS for Efficient Filtering + +```sql +-- Functions that have at least one string reference (more efficient than JOIN + DISTINCT) +SELECT f.name +FROM funcs f +WHERE EXISTS ( + SELECT 1 FROM xrefs x + JOIN strings s ON x.to_ea = s.address + WHERE x.from_ea BETWEEN f.address AND f.end_ea +); +``` + +```sql +-- Functions without any calls (leaf functions, EXISTS version) +SELECT f.name, f.size +FROM funcs f +WHERE NOT EXISTS ( + SELECT 1 FROM disasm_calls dc + WHERE dc.func_addr = f.address +) +ORDER BY f.size DESC; +``` + +--- + +## Hex Address Formatting + +IDA uses integer addresses. For display, use `printf()`: + +```sql +-- 32-bit format +SELECT printf('0x%08X', address) as addr FROM funcs; + +-- 64-bit format +SELECT printf('0x%016llX', address) as addr FROM funcs; + +-- Auto-width +SELECT printf('0x%X', address) as addr FROM funcs; +``` + +--- + +## Common x86 Instruction Types + +When filtering by `itype` (faster than string comparison): + +| itype | Mnemonic | Description | +|-------|----------|-------------| +| 16 | call (near) | Direct call | +| 18 | call (indirect) | Indirect call | +| 122 | mov | Move data | +| 143 | push | Push to stack | +| 134 | pop | Pop from stack | +| 159 | retn | Return | +| 85 | jz | Jump if zero | +| 79 | jnz | Jump if not zero | +| 27 | cmp | Compare | +| 103 | nop | No operation | + +--- + +## ctree Operation Names + +Common Hex-Rays AST node types: + +**Expressions (cot_*):** +- `cot_call` - Function call +- `cot_var` - Local variable +- `cot_obj` - Global object/function +- `cot_num` - Numeric constant +- `cot_str` - String literal +- `cot_ptr` - Pointer dereference +- `cot_ref` - Address-of +- `cot_asg` - Assignment +- `cot_add`, `cot_sub`, `cot_mul`, `cot_sdiv`, `cot_udiv` - Arithmetic +- `cot_eq`, `cot_ne`, `cot_lt`, `cot_gt` - Comparisons +- `cot_land`, `cot_lor`, `cot_lnot` - Logical +- `cot_band`, `cot_bor`, `cot_xor` - Bitwise + +**Statements (cit_*):** +- `cit_if` - If statement +- `cit_for` - For loop +- `cit_while` - While loop +- `cit_do` - Do-while loop +- `cit_return` - Return statement +- `cit_block` - Code block + +--- + +## Error Handling + +- **No Hex-Rays license:** Decompiler tables (`pseudocode`, `ctree*`, `ctree_lvars`) will be empty or unavailable +- **No constraint on decompiler tables:** Query will be extremely slow (decompiles all functions) +- **Invalid address:** Functions like `func_at(addr)` return NULL +- **Missing function:** JOINs may return fewer rows than expected + +--- + +## Quick Start Examples + +### "What does this binary do?" + +```sql +-- Entry points +SELECT * FROM entries; + +-- Imported APIs (hints at functionality) +SELECT module, name FROM imports ORDER BY module, name; + +-- Interesting strings +SELECT content FROM strings WHERE length > 10 ORDER BY length DESC LIMIT 20; +``` + +### "Find security-relevant code" + +```sql +-- Dangerous string functions +SELECT DISTINCT func_at(func_addr) FROM disasm_calls +WHERE callee_name IN ('strcpy', 'strcat', 'sprintf', 'gets'); + +-- Crypto-related +SELECT * FROM imports WHERE name LIKE '%Crypt%' OR name LIKE '%Hash%'; + +-- Network-related +SELECT * FROM imports WHERE name LIKE '%socket%' OR name LIKE '%connect%' OR name LIKE '%send%'; +``` + +### "Understand a specific function" + +```sql +-- Basic info +SELECT * FROM funcs WHERE address = 0x401000; + +-- Decompile (if Hex-Rays available) +SELECT decompile(0x401000); + +-- Local variables +SELECT name, type, size FROM ctree_lvars WHERE func_addr = 0x401000; + +-- What it calls +SELECT callee_name FROM disasm_calls WHERE func_addr = 0x401000; + +-- What calls it +SELECT func_at(from_ea) FROM xrefs WHERE to_ea = 0x401000 AND is_code = 1; +``` + +### "Find all uses of a string" + +```sql +SELECT s.content, func_at(x.from_ea) as function, printf('0x%X', x.from_ea) as location +FROM strings s +JOIN xrefs x ON s.address = x.to_ea +WHERE s.content LIKE '%config%'; +``` + +--- + +## Natural Language Query Examples + +These examples show how to translate common user questions into SQL. + +### Function Signature Queries + +**"Show me functions that return integers"** +```sql +-- Using funcs table (recommended - direct and fast) +SELECT name, return_type, arg_count FROM funcs +WHERE return_is_integral = 1 +LIMIT 20; + +-- Or via types_func_args (for typedef-aware queries) +SELECT DISTINCT type_name FROM types_func_args +WHERE arg_index = -1 AND is_integral_resolved = 1; +``` + +**"Show me functions that take 4 string arguments"** +```sql +-- String = char* or wchar_t* +SELECT type_name, COUNT(*) as string_args +FROM types_func_args +WHERE arg_index >= 0 + AND is_ptr_resolved = 1 + AND base_type_resolved IN ('char', 'wchar_t', 'CHAR', 'WCHAR') +GROUP BY type_ordinal +HAVING string_args = 4; +``` + +**"Which functions return pointers?"** +```sql +SELECT name, return_type FROM funcs +WHERE return_is_ptr = 1 +ORDER BY name LIMIT 20; +``` + +**"Find void functions with many arguments"** +```sql +SELECT name, arg_count FROM funcs +WHERE return_is_void = 1 AND arg_count >= 4 +ORDER BY arg_count DESC; +``` + +**"What calling conventions are used?"** +```sql +SELECT calling_conv, COUNT(*) as count +FROM funcs +WHERE calling_conv IS NOT NULL AND calling_conv != '' +GROUP BY calling_conv ORDER BY count DESC; +``` + +### Return Value Analysis + +**"Which functions return 0?"** +```sql +SELECT DISTINCT f.name FROM funcs f +JOIN ctree_v_returns r ON r.func_addr = f.address +WHERE r.return_num = 0; +``` + +**"Find functions that return -1 (error pattern)"** +```sql +SELECT DISTINCT f.name FROM funcs f +JOIN ctree_v_returns r ON r.func_addr = f.address +WHERE r.return_num = -1; +``` + +**"Functions that return their input argument"** +```sql +SELECT DISTINCT f.name FROM funcs f +JOIN ctree_v_returns r ON r.func_addr = f.address +WHERE r.returns_arg = 1; +``` + +**"Functions that return the result of another call (wrappers)"** +```sql +SELECT DISTINCT f.name FROM funcs f +JOIN ctree_v_returns r ON r.func_addr = f.address +WHERE r.returns_call_result = 1; +``` + +**"Functions with multiple return statements"** +```sql +SELECT f.name, COUNT(*) as return_count +FROM funcs f +JOIN ctree_v_returns r ON r.func_addr = f.address +GROUP BY f.address +HAVING return_count > 1 +ORDER BY return_count DESC LIMIT 20; +``` + +### Type Analysis + +**"Find typedefs that hide pointers (like HANDLE)"** +```sql +SELECT DISTINCT type_name, arg_type, base_type_resolved +FROM types_func_args +WHERE is_ptr = 0 AND is_ptr_resolved = 1; +``` + +**"Functions with struct parameters"** +```sql +SELECT type_name, arg_name, arg_type FROM types_func_args +WHERE arg_index >= 0 AND is_struct = 1; +``` + +### Combined Queries + +**"Integer-returning functions with 3+ args that return specific values"** +```sql +SELECT f.name, f.return_type, f.arg_count, r.return_num +FROM funcs f +JOIN ctree_v_returns r ON r.func_addr = f.address +WHERE f.return_is_integral = 1 + AND f.arg_count >= 3 + AND r.return_num IS NOT NULL +ORDER BY r.return_num; +``` + +**"Fastcall functions that return pointers"** +```sql +SELECT name, return_type, arg_count FROM funcs +WHERE calling_conv = 'fastcall' AND return_is_ptr = 1; +``` + +--- + +## Summary: When to Use What + +| Goal | Table/Function | +|------|----------------| +| List all functions | `funcs` | +| Functions by return type | `funcs WHERE return_is_integral = 1` | +| Functions by arg count | `funcs WHERE arg_count >= N` | +| Void functions | `funcs WHERE return_is_void = 1` | +| Pointer-returning functions | `funcs WHERE return_is_ptr = 1` | +| Functions by calling convention | `funcs WHERE calling_conv = 'fastcall'` | +| Find who calls what | `xrefs` with `is_code = 1` | +| Find data references | `xrefs` with `is_code = 0` | +| Analyze imports | `imports` | +| Find strings | `strings` | +| Configure string types | `rebuild_strings(types, minlen)` | +| Instruction analysis | `instructions WHERE func_addr = X` | +| View decompiled code | `decompile(addr)` | +| Edit decompiler comments | `UPDATE pseudocode SET comment = '...' WHERE func_addr = X AND ea = Y` | +| AST pattern matching | `ctree WHERE func_addr = X` | +| Call patterns | `ctree_v_calls`, `disasm_calls` | +| Control flow | `ctree_v_loops`, `ctree_v_ifs` | +| Return value analysis | `ctree_v_returns` | +| Functions returning specific values | `ctree_v_returns WHERE return_num = 0` | +| Pass-through functions | `ctree_v_returns WHERE returns_arg = 1` | +| Wrapper functions | `ctree_v_returns WHERE returns_call_result = 1` | +| Variable analysis | `ctree_lvars WHERE func_addr = X` | +| Type information | `types`, `types_members` | +| Function signatures | `types_func_args` (with type classification) | +| Functions by return type | `types_func_args WHERE arg_index = -1` | +| Typedef-aware type queries | `types_func_args` (surface vs resolved) | +| Hidden pointer types | `types_func_args WHERE is_ptr = 0 AND is_ptr_resolved = 1` | +| Manage breakpoints | `breakpoints` (full CRUD) | +| Modify segments | `segments` (UPDATE name/class/perm, DELETE) | +| Delete instructions | `instructions` (DELETE converts to unexplored bytes) | +| Create types | `types` (INSERT struct/union/enum) | +| Add struct members | `types_members` (INSERT) | +| Add enum values | `types_enum_values` (INSERT) | +| Modify database | `funcs`, `names`, `comments`, `bookmarks` (INSERT/UPDATE/DELETE) | +| Jump to Anything | `jump_entities('pattern', 'mode')` | +| Entity search (JSON) | `jump_search('pattern', 'mode', limit, offset)` | + +**Remember:** Always use `func_addr = X` constraints on instruction and decompiler tables for acceptable performance. + +--- + +## Server Modes + +IDASQL supports two server protocols for remote queries: **HTTP REST** (recommended) and raw TCP. + +--- + +### HTTP REST Server (Recommended) + +Standard REST API that works with curl, any HTTP client, or LLM tools. + +**Starting the server:** +```bash +# Default port 8081 +idasql -s database.i64 --http + +# Custom port and bind address +idasql -s database.i64 --http 9000 --bind 0.0.0.0 + +# With authentication +idasql -s database.i64 --http 8081 --token mysecret +``` + +**HTTP Endpoints:** + +| Endpoint | Method | Auth | Description | +|----------|--------|------|-------------| +| `/` | GET | No | Welcome message | +| `/help` | GET | No | API documentation (for LLM discovery) | +| `/query` | POST | Yes* | Execute SQL (body = raw SQL) | +| `/status` | GET | Yes* | Health check | +| `/shutdown` | POST | Yes* | Stop server | + +*Auth required only if `--token` was specified. + +**Example with curl:** +```bash +# Get API documentation +curl http://localhost:8081/help + +# Execute SQL query +curl -X POST http://localhost:8081/query -d "SELECT name, size FROM funcs LIMIT 5" + +# With authentication +curl -X POST http://localhost:8081/query \ + -H "Authorization: Bearer mysecret" \ + -d "SELECT * FROM funcs" + +# Check status +curl http://localhost:8081/status +``` + +**Response Format (JSON):** +```json +{"success": true, "columns": ["name", "size"], "rows": [["main", "500"]], "row_count": 1} +``` + +```json +{"success": false, "error": "no such table: bad_table"} +``` + +--- + +### Raw TCP Server (Legacy) + +Binary protocol with length-prefixed JSON. Use only when HTTP is not available. + +**Starting the server:** +```bash +idasql -s database.i64 --server 13337 +idasql -s database.i64 --server 13337 --token mysecret +``` + +**Connecting as client:** +```bash +idasql --remote localhost:13337 -q "SELECT name FROM funcs LIMIT 5" +idasql --remote localhost:13337 -i +``` +)PROMPT"; + +} // namespace idasql + +``` + +`src/common/idasql_cli.hpp`: + +```hpp +#pragma once + +/** + * IdasqlCLI - IDA plugin command line interface + * + * Wraps SessionHandler and provides cli_t integration for IDA. + * The actual query processing logic is in SessionHandler (testable without IDA). + * + * Usage: + * IdasqlCLI cli(executor); + * cli.install(); // Register with IDA + * // ... plugin lifetime ... + * cli.uninstall(); // Unregister on shutdown + */ + +#include +#include +#include +#include + +#include "session_handler.hpp" + +namespace idasql { + +class IdasqlCLI +{ +public: + using SqlExecutor = std::function; + + explicit IdasqlCLI(SqlExecutor executor, bool enable_agent = true) + : session_(std::move(executor), enable_agent) + { + // Override clear_session callback to add IDA-specific behavior + session_.callbacks().clear_session = [this]() { + return clear_session(); + }; + } + + ~IdasqlCLI() + { + uninstall(); + } + + // Non-copyable + IdasqlCLI(const IdasqlCLI&) = delete; + IdasqlCLI& operator=(const IdasqlCLI&) = delete; + + /** + * Install the CLI with IDA + */ + bool install() + { + if (installed_) return true; + + // Store pointer for static callback + s_instance_ = this; + + // Setup cli_t structure + cli_.size = sizeof(cli_t); + cli_.flags = 0; + cli_.sname = "idasql"; + cli_.lname = session_.is_agent_enabled() + ? "idasql - SQL queries with AI agent support" + : "idasql - SQL interface to IDA database"; + cli_.hint = "Enter SQL query, .command, or natural language"; + cli_.execute_line = &IdasqlCLI::execute_line_cb; + cli_.keydown = nullptr; + cli_.find_completions = nullptr; + + install_command_interpreter(&cli_); + installed_ = true; + msg("IDASQL CLI: Installed (AI agent: %s)\n", + session_.is_agent_enabled() ? "enabled" : "disabled"); + return true; + } + + /** + * Uninstall the CLI from IDA + */ + void uninstall() + { + if (!installed_) return; + + session_.end_session(); + remove_command_interpreter(&cli_); + installed_ = false; + s_instance_ = nullptr; + + msg("IDASQL CLI: Uninstalled\n"); + } + + bool is_installed() const { return installed_; } + bool is_agent_enabled() const { return session_.is_agent_enabled(); } + + /** + * Clear session - clears IDA message window and resets AI agent + */ + std::string clear_session() + { + // Clear IDA's message window + msg_clear(); + + // Clear the AI agent session + std::string result = session_.clear_session(); + + // Print status to fresh message window + msg("IDASQL: %s\n", result.c_str()); + + return result; + } + + /** + * Process a line of input (delegates to SessionHandler) + */ + std::string process_line(const std::string& line) + { + return session_.process_line(line); + } + + /** + * Get the underlying session handler (for testing) + */ + SessionHandler& session() { return session_; } + const SessionHandler& session() const { return session_; } + +private: + SessionHandler session_; + cli_t cli_{}; + bool installed_ = false; + + // Static instance for callback + static IdasqlCLI* s_instance_; + + // Static callback for cli_t + static bool idaapi execute_line_cb(const char* line) + { + if (!s_instance_ || !line) return true; + + std::string result = s_instance_->process_line(line); + if (!result.empty()) { + msg("%s\n", result.c_str()); + } + + return true; // Line was executed + } +}; + +// Static member definition +inline IdasqlCLI* IdasqlCLI::s_instance_ = nullptr; + +} // namespace idasql + +``` + +`src/common/idasql_commands.hpp`: + +```hpp +#pragma once + +#include +#include +#include + +#ifdef IDASQL_HAS_AI_AGENT +#include "agent_settings.hpp" +#endif + +namespace idasql { + +/** + * Command handler result + */ +enum class CommandResult { + NOT_HANDLED, // Not a command, process as query + HANDLED, // Command executed successfully + QUIT // User requested quit +}; + +/** + * Command handler callbacks + * + * These callbacks allow different environments (CLI, plugin) to extend + * command behavior. For example, .clear might: + * - Core: Reset the AI agent session + * - Plugin: Also call msg_clear() to clear IDA's message window + */ +struct CommandCallbacks { + std::function get_tables; // Return table list + std::function get_schema; // Return schema for table + std::function get_info; // Return database info + std::function clear_session; // Clear/reset session (agent, UI, etc.) + + // MCP server callbacks (optional - plugin only) + std::function mcp_status; // Get MCP status + std::function mcp_start; // Start MCP server (port, bind_addr) + std::function mcp_stop; // Stop MCP server + + // HTTP server callbacks (optional) + std::function http_status; // Get HTTP server status + std::function http_start; // Start HTTP server (port, bind_addr) + std::function http_stop; // Stop HTTP server +}; + +/** + * Handle dot commands (.tables, .schema, .help, .quit, etc.) + * + * @param input User input line + * @param callbacks Callbacks to execute commands + * @param output Output string (filled if command produces output) + * @return CommandResult indicating how to proceed + */ +inline CommandResult handle_command( + const std::string& input, + const CommandCallbacks& callbacks, + std::string& output) +{ + if (input.empty() || input[0] != '.') { + return CommandResult::NOT_HANDLED; + } + + if (input == ".quit" || input == ".exit") { + return CommandResult::QUIT; + } + + if (input == ".tables") { + if (callbacks.get_tables) { + output = callbacks.get_tables(); + } + return CommandResult::HANDLED; + } + + if (input == ".info") { + if (callbacks.get_info) { + output = callbacks.get_info(); + } + return CommandResult::HANDLED; + } + + if (input == ".clear") { + if (callbacks.clear_session) { + output = callbacks.clear_session(); + } else { + output = "Session cleared"; + } + return CommandResult::HANDLED; + } + + if (input == ".help") { + output = "IDASQL Commands:\n" + " .tables List all tables\n" + " .schema Show table schema\n" + " .info Show database info\n" + " .clear Clear/reset session\n" + " .quit / .exit Exit\n" + " .help Show this help\n" +#ifdef IDASQL_HAS_AI_AGENT + "\n" + "MCP Server:\n" + " .mcp Show status or start if not running\n" + " .mcp start [bind] [port] Start MCP server\n" + " .mcp stop Stop MCP server\n" + " .mcp help Show MCP help\n" +#endif + "\n" + "HTTP Server:\n" + " .http Show status or start if not running\n" + " .http start [bind] [port] Start HTTP server\n" + " .http stop Stop HTTP server\n" + " .http help Show HTTP help\n" + "\n" + "SQL:\n" + " SELECT * FROM funcs LIMIT 10;\n" + " SELECT name, size FROM funcs ORDER BY size DESC;\n" +#ifdef IDASQL_HAS_AI_AGENT + "\n" + "AI Agent:\n" + " .agent help Show agent commands\n" + " .agent provider Show/set AI provider\n" + " .agent clear Clear conversation\n" + "\n" + "Natural Language:\n" + " Find the largest functions\n" + " Show functions that call malloc\n" + " What imports does this binary use?\n" +#endif + ; + return CommandResult::HANDLED; + } + + // .mcp commands (MCP server control - plugin only) + if (input.rfind(".mcp", 0) == 0) { +#ifdef IDASQL_HAS_AI_AGENT + std::string subargs = input.length() > 4 ? input.substr(4) : ""; + // Trim leading whitespace + size_t start = subargs.find_first_not_of(" \t"); + if (start != std::string::npos) + subargs = subargs.substr(start); + + if (subargs.empty()) { + // .mcp - show status, start if not running + if (callbacks.mcp_status) { + output = callbacks.mcp_status(); + } else { + output = "MCP server not available (plugin mode only)"; + } + } + else if (subargs.rfind("start", 0) == 0) { + int port = 0; + std::string bind_addr = "127.0.0.1"; + // Parse optional: "start [bind] [port]" + std::string rest = subargs.length() > 5 ? subargs.substr(5) : ""; + size_t rs = rest.find_first_not_of(" \t"); + if (rs != std::string::npos) { + rest = rest.substr(rs); + // Split into tokens + std::string tok1, tok2; + size_t sp = rest.find_first_of(" \t"); + if (sp != std::string::npos) { + tok1 = rest.substr(0, sp); + size_t t2s = rest.find_first_not_of(" \t", sp); + if (t2s != std::string::npos) tok2 = rest.substr(t2s); + } else { + tok1 = rest; + } + // Heuristic: if tok1 is all digits, treat as port; otherwise bind_addr + bool tok1_numeric = !tok1.empty() && tok1.find_first_not_of("0123456789") == std::string::npos; + if (tok1_numeric) { + port = std::stoi(tok1); + } else { + bind_addr = tok1; + if (!tok2.empty()) port = std::stoi(tok2); + } + } + if (callbacks.mcp_start) { + output = callbacks.mcp_start(port, bind_addr); + } else { + output = "MCP server not available (plugin mode only)"; + } + } + else if (subargs == "stop") { + if (callbacks.mcp_stop) { + output = callbacks.mcp_stop(); + } else { + output = "MCP server not available (plugin mode only)"; + } + } + else if (subargs == "help") { + output = "MCP Server Commands:\n" + " .mcp Show status, start if not running\n" + " .mcp start [bind] [port] Start MCP server (default: 127.0.0.1, random port)\n" + " .mcp stop Stop MCP server\n" + " .mcp help Show this help\n" + "\n" + "The MCP server exposes two tools:\n" + " idasql_query - Execute SQL query directly\n" + " idasql_agent - Ask natural language question (AI-powered)\n" + "\n" + "Connect with Claude Desktop by adding to config:\n" + " {\"mcpServers\": {\"idasql\": {\"url\": \"http://127.0.0.1:/sse\"}}}\n"; + } + else { + output = "Unknown MCP command: " + subargs + "\nUse '.mcp help' for available commands."; + } +#else + output = "MCP server requires AI agent support. Rebuild with -DIDASQL_WITH_AI_AGENT=ON"; +#endif + return CommandResult::HANDLED; + } + + // .http commands (HTTP server control) + if (input.rfind(".http", 0) == 0) { + std::string subargs = input.length() > 5 ? input.substr(5) : ""; + // Trim leading whitespace + size_t start = subargs.find_first_not_of(" \t"); + if (start != std::string::npos) + subargs = subargs.substr(start); + + if (subargs.empty()) { + // .http - show status, start if not running + if (callbacks.http_status) { + output = callbacks.http_status(); + } else { + output = "HTTP server not available"; + } + } + else if (subargs.rfind("start", 0) == 0) { + int port = 0; + std::string bind_addr = "127.0.0.1"; + // Parse optional: "start [bind] [port]" + std::string rest = subargs.length() > 5 ? subargs.substr(5) : ""; + size_t rs = rest.find_first_not_of(" \t"); + if (rs != std::string::npos) { + rest = rest.substr(rs); + // Split into tokens + std::string tok1, tok2; + size_t sp = rest.find_first_of(" \t"); + if (sp != std::string::npos) { + tok1 = rest.substr(0, sp); + size_t t2s = rest.find_first_not_of(" \t", sp); + if (t2s != std::string::npos) tok2 = rest.substr(t2s); + } else { + tok1 = rest; + } + // Heuristic: if tok1 is all digits, treat as port; otherwise bind_addr + bool tok1_numeric = !tok1.empty() && tok1.find_first_not_of("0123456789") == std::string::npos; + if (tok1_numeric) { + port = std::stoi(tok1); + } else { + bind_addr = tok1; + if (!tok2.empty()) port = std::stoi(tok2); + } + } + if (callbacks.http_start) { + output = callbacks.http_start(port, bind_addr); + } else { + output = "HTTP server not available"; + } + } + else if (subargs == "stop") { + if (callbacks.http_stop) { + output = callbacks.http_stop(); + } else { + output = "HTTP server not available"; + } + } + else if (subargs == "help") { + output = "HTTP Server Commands:\n" + " .http Show status, start if not running\n" + " .http start [bind] [port] Start HTTP server (default: 127.0.0.1, random port)\n" + " .http stop Stop HTTP server\n" + " .http help Show this help\n" + "\n" + "Endpoints:\n" + " GET /help API documentation\n" + " POST /query Execute SQL (body = raw SQL)\n" + " GET /status Health check\n" + " POST /shutdown Stop server\n" + "\n" + "Example:\n" + " curl -X POST http://127.0.0.1:/query -d \"SELECT name FROM funcs LIMIT 5\"\n"; + } + else { + output = "Unknown HTTP command: " + subargs + "\nUse '.http help' for available commands."; + } + return CommandResult::HANDLED; + } + + // .agent commands + if (input.rfind(".agent", 0) == 0) { +#ifdef IDASQL_HAS_AI_AGENT + std::string subargs = input.length() > 6 ? input.substr(6) : ""; + // Trim leading whitespace + size_t start = subargs.find_first_not_of(" \t"); + if (start != std::string::npos) + subargs = subargs.substr(start); + + // Parse subcmd and value + std::string subcmd, value; + size_t space = subargs.find(' '); + if (space != std::string::npos) { + subcmd = subargs.substr(0, space); + value = subargs.substr(space + 1); + size_t val_start = value.find_first_not_of(" \t"); + if (val_start != std::string::npos) + value = value.substr(val_start); + } else { + subcmd = subargs; + } + + auto settings = LoadAgentSettings(); + std::string provider_name = libagents::provider_type_name(settings.default_provider); + + if (subcmd.empty() || subcmd == "help") { + output = "Agent Commands:\n" + " .agent help Show this help\n" + " .agent provider Show current provider\n" + " .agent provider NAME Switch provider (claude, copilot)\n" + " .agent clear Clear conversation\n" + " .agent timeout Show response timeout\n" + " .agent timeout MS Set response timeout in milliseconds\n" + " .agent byok Show BYOK status\n" + " .agent byok enable Enable BYOK\n" + " .agent byok disable Disable BYOK\n" + " .agent byok key VALUE Set API key\n" + " .agent byok endpoint URL Set API endpoint\n" + " .agent byok model NAME Set model name\n" + " .agent byok type TYPE Set provider type (openai, anthropic, azure)\n" + "\nCurrent provider: " + provider_name + "\n"; + } + else if (subcmd == "provider") { + if (value.empty()) { + output = "Current provider: " + provider_name + "\n" + "\nAvailable providers:\n" + " claude - Claude Code (Anthropic)\n" + " copilot - GitHub Copilot\n"; + } else { + try { + auto type = ParseProviderType(value); + settings.default_provider = type; + SaveAgentSettings(settings); + output = "Provider set to: " + std::string(libagents::provider_type_name(type)) + + " (saved to settings)\n" + "Note: Restart agent session for changes to take effect.\n"; + } catch (const std::exception& e) { + output = std::string("Error: ") + e.what() + "\n" + "Available providers: claude, copilot\n"; + } + } + } + else if (subcmd == "clear") { + if (callbacks.clear_session) { + output = callbacks.clear_session(); + } else { + output = "Session cleared"; + } + } + else if (subcmd == "timeout") { + if (value.empty()) { + output = "Response timeout: " + std::to_string(settings.response_timeout_ms) + " ms (" + + std::to_string(settings.response_timeout_ms / 1000) + " seconds)\n"; + } else { + try { + int ms = std::stoi(value); + if (ms < 1000) { + output = "Error: Timeout must be at least 1000 ms (1 second).\n"; + } else { + settings.response_timeout_ms = ms; + SaveAgentSettings(settings); + output = "Timeout set to " + std::to_string(ms) + " ms (" + + std::to_string(ms / 1000) + " seconds).\n"; + } + } catch (...) { + output = "Error: Invalid timeout value. Use milliseconds.\n"; + } + } + } + else if (subcmd == "byok") { + // Parse BYOK subcommand + std::string byok_subcmd, byok_value; + size_t byok_space = value.find(' '); + if (byok_space != std::string::npos) { + byok_subcmd = value.substr(0, byok_space); + byok_value = value.substr(byok_space + 1); + size_t bv_start = byok_value.find_first_not_of(" \t"); + if (bv_start != std::string::npos) + byok_value = byok_value.substr(bv_start); + } else { + byok_subcmd = value; + } + + const BYOKSettings* byok = settings.get_byok(); + + if (byok_subcmd.empty()) { + std::stringstream ss; + ss << "BYOK status for provider '" << provider_name << "':\n"; + if (byok) { + ss << " Enabled: " << (byok->enabled ? "yes" : "no") << "\n" + << " API Key: " << (byok->api_key.empty() ? "(not set)" : "********") << "\n" + << " Endpoint: " << (byok->base_url.empty() ? "(default)" : byok->base_url) << "\n" + << " Model: " << (byok->model.empty() ? "(default)" : byok->model) << "\n" + << " Type: " << (byok->provider_type.empty() ? "(default)" : byok->provider_type) << "\n" + << " Usable: " << (byok->is_usable() ? "yes" : "no") << "\n"; + } else { + ss << " (not configured)\n"; + } + output = ss.str(); + } + else if (byok_subcmd == "enable") { + auto& b = settings.get_or_create_byok(); + b.enabled = true; + SaveAgentSettings(settings); + output = "BYOK enabled for provider '" + provider_name + "'.\n"; + if (b.api_key.empty()) { + output += "Warning: API key not set. Use '.agent byok key ' to set it.\n"; + } + } + else if (byok_subcmd == "disable") { + auto& b = settings.get_or_create_byok(); + b.enabled = false; + SaveAgentSettings(settings); + output = "BYOK disabled for provider '" + provider_name + "'.\n"; + } + else if (byok_subcmd == "key") { + if (byok_value.empty()) { + output = "Error: API key value required.\n" + "Usage: .agent byok key \n"; + } else { + auto& b = settings.get_or_create_byok(); + b.api_key = byok_value; + SaveAgentSettings(settings); + output = "BYOK API key set for provider '" + provider_name + "'.\n"; + } + } + else if (byok_subcmd == "endpoint") { + auto& b = settings.get_or_create_byok(); + b.base_url = byok_value; + SaveAgentSettings(settings); + output = byok_value.empty() ? + "BYOK endpoint cleared (using default).\n" : + "BYOK endpoint set to: " + byok_value + "\n"; + } + else if (byok_subcmd == "model") { + auto& b = settings.get_or_create_byok(); + b.model = byok_value; + SaveAgentSettings(settings); + output = byok_value.empty() ? + "BYOK model cleared (using default).\n" : + "BYOK model set to: " + byok_value + "\n"; + } + else if (byok_subcmd == "type") { + auto& b = settings.get_or_create_byok(); + b.provider_type = byok_value; + SaveAgentSettings(settings); + output = byok_value.empty() ? + "BYOK type cleared (using default).\n" : + "BYOK type set to: " + byok_value + "\n"; + } + else { + output = "Unknown byok subcommand: " + byok_subcmd + "\n" + "Use '.agent byok' to see available commands.\n"; + } + } + else { + output = "Unknown agent subcommand: " + subcmd + "\n" + "Use '.agent help' for available commands.\n"; + } +#else + output = "AI agent support not compiled in. Rebuild with -DIDASQL_WITH_AI_AGENT=ON\n"; +#endif + return CommandResult::HANDLED; + } + + if (input.rfind(".schema", 0) == 0) { + std::string table = input.length() > 8 ? input.substr(8) : ""; + // Trim leading whitespace + size_t start = table.find_first_not_of(" \t"); + if (start != std::string::npos) { + table = table.substr(start); + // Trim trailing whitespace + size_t end = table.find_last_not_of(" \t"); + if (end != std::string::npos) { + table = table.substr(0, end + 1); + } + } else { + table.clear(); + } + + if (table.empty()) { + output = "Usage: .schema "; + } else if (callbacks.get_schema) { + output = callbacks.get_schema(table); + } + return CommandResult::HANDLED; + } + + output = "Unknown command: " + input; + return CommandResult::HANDLED; +} + +/** + * Handle --config CLI commands + * + * @param path Config path like "agent.provider" or "agent.byok.key" + * @param value Value to set (empty = get current value) + * @return tuple + */ +inline std::tuple handle_config_command( + const std::string& path, + const std::string& value) +{ +#ifdef IDASQL_HAS_AI_AGENT + auto settings = LoadAgentSettings(); + std::string provider_name = libagents::provider_type_name(settings.default_provider); + std::stringstream ss; + + // Show all config + if (path.empty()) { + ss << "Settings: " << GetSettingsPath() << "\n\n"; + ss << "agent.provider: " << provider_name << "\n"; + ss << "agent.timeout: " << settings.response_timeout_ms << " ms\n"; + ss << "agent.prompt: " << (settings.custom_prompt.empty() ? "(not set)" : "\"" + settings.custom_prompt + "\"") << "\n"; + ss << "\n"; + + const BYOKSettings* byok = settings.get_byok(); + ss << "agent.byok (" << provider_name << "):\n"; + if (byok) { + ss << " enabled: " << (byok->enabled ? "true" : "false") << "\n"; + ss << " key: " << (byok->api_key.empty() ? "(not set)" : "********") << "\n"; + ss << " endpoint: " << (byok->base_url.empty() ? "(default)" : byok->base_url) << "\n"; + ss << " model: " << (byok->model.empty() ? "(default)" : byok->model) << "\n"; + ss << " type: " << (byok->provider_type.empty() ? "(default)" : byok->provider_type) << "\n"; + } else { + ss << " (not configured)\n"; + } + return {true, ss.str(), 0}; + } + + // Parse path + std::vector parts; + std::string part; + std::istringstream iss(path); + while (std::getline(iss, part, '.')) { + if (!part.empty()) parts.push_back(part); + } + + if (parts.empty() || parts[0] != "agent") { + return {false, "Error: Unknown config path: " + path + "\nUse --config to see available options.\n", 1}; + } + + // agent.* + if (parts.size() == 1) { + // Just "agent" - show agent settings + ss << "agent.provider: " << provider_name << "\n"; + ss << "agent.timeout: " << settings.response_timeout_ms << " ms\n"; + ss << "agent.prompt: " << (settings.custom_prompt.empty() ? "(not set)" : "\"" + settings.custom_prompt + "\"") << "\n"; + return {true, ss.str(), 0}; + } + + std::string key = parts[1]; + + // agent.provider + if (key == "provider") { + if (value.empty()) { + ss << "agent.provider = " << provider_name << "\n"; + } else { + try { + auto type = ParseProviderType(value); + settings.default_provider = type; + SaveAgentSettings(settings); + ss << "agent.provider = " << libagents::provider_type_name(type) << " (saved)\n"; + } catch (const std::exception& e) { + return {false, std::string("Error: ") + e.what() + "\n", 1}; + } + } + return {true, ss.str(), 0}; + } + + // agent.timeout + if (key == "timeout") { + if (value.empty()) { + ss << "agent.timeout = " << settings.response_timeout_ms << " ms\n"; + } else { + try { + int ms = std::stoi(value); + if (ms < 1000) { + return {false, "Error: Timeout must be at least 1000 ms.\n", 1}; + } + settings.response_timeout_ms = ms; + SaveAgentSettings(settings); + ss << "agent.timeout = " << ms << " ms (saved)\n"; + } catch (...) { + return {false, "Error: Invalid timeout value.\n", 1}; + } + } + return {true, ss.str(), 0}; + } + + // agent.prompt + if (key == "prompt") { + if (value.empty()) { + ss << "agent.prompt = " << (settings.custom_prompt.empty() ? "(not set)" : "\"" + settings.custom_prompt + "\"") << "\n"; + } else { + settings.custom_prompt = value; + SaveAgentSettings(settings); + ss << "agent.prompt = \"" << value << "\" (saved)\n"; + } + return {true, ss.str(), 0}; + } + + // agent.byok.* + if (key == "byok") { + // Helper lambda to show BYOK status for a provider + auto show_byok = [&ss](const std::string& pname, const BYOKSettings* byok) { + ss << "agent.byok." << pname << ":\n"; + if (byok) { + ss << " enabled: " << (byok->enabled ? "true" : "false") << "\n"; + ss << " key: " << (byok->api_key.empty() ? "(not set)" : "********") << "\n"; + ss << " endpoint: " << (byok->base_url.empty() ? "(default)" : byok->base_url) << "\n"; + ss << " model: " << (byok->model.empty() ? "(default)" : byok->model) << "\n"; + ss << " type: " << (byok->provider_type.empty() ? "(default)" : byok->provider_type) << "\n"; + } else { + ss << " (not configured)\n"; + } + }; + + // Helper lambda to get/set a BYOK field + auto handle_byok_field = [&](BYOKSettings& byok, const std::string& field, + const std::string& prefix) -> std::tuple { + std::stringstream out; + if (field == "enabled") { + if (value.empty()) { + out << prefix << ".enabled = " << (byok.enabled ? "true" : "false") << "\n"; + } else { + byok.enabled = (value == "true" || value == "1" || value == "yes"); + SaveAgentSettings(settings); + out << prefix << ".enabled = " << (byok.enabled ? "true" : "false") << " (saved)\n"; + } + return {true, out.str(), 0}; + } + if (field == "key") { + if (value.empty()) { + out << prefix << ".key = " << (byok.api_key.empty() ? "(not set)" : "********") << "\n"; + } else { + byok.api_key = value; + SaveAgentSettings(settings); + out << prefix << ".key = ******** (saved)\n"; + } + return {true, out.str(), 0}; + } + if (field == "endpoint") { + if (value.empty()) { + out << prefix << ".endpoint = " << (byok.base_url.empty() ? "(default)" : byok.base_url) << "\n"; + } else { + byok.base_url = value; + SaveAgentSettings(settings); + out << prefix << ".endpoint = " << value << " (saved)\n"; + } + return {true, out.str(), 0}; + } + if (field == "model") { + if (value.empty()) { + out << prefix << ".model = " << (byok.model.empty() ? "(default)" : byok.model) << "\n"; + } else { + byok.model = value; + SaveAgentSettings(settings); + out << prefix << ".model = " << value << " (saved)\n"; + } + return {true, out.str(), 0}; + } + if (field == "type") { + if (value.empty()) { + out << prefix << ".type = " << (byok.provider_type.empty() ? "(default)" : byok.provider_type) << "\n"; + } else { + byok.provider_type = value; + SaveAgentSettings(settings); + out << prefix << ".type = " << value << " (saved)\n"; + } + return {true, out.str(), 0}; + } + return {false, "Error: Unknown BYOK field: " + field + "\n", 1}; + }; + + if (parts.size() == 2) { + // "agent.byok" - show all providers' BYOK status + auto it_claude = settings.byok.find("claude"); + auto it_copilot = settings.byok.find("copilot"); + show_byok("claude", it_claude != settings.byok.end() ? &it_claude->second : nullptr); + ss << "\n"; + show_byok("copilot", it_copilot != settings.byok.end() ? &it_copilot->second : nullptr); + return {true, ss.str(), 0}; + } + + std::string part2 = parts[2]; + + // Check if part2 is a provider name (claude/copilot) or a field name + if (part2 == "claude" || part2 == "copilot") { + // agent.byok. or agent.byok.. + std::string target_provider = part2; + + if (parts.size() == 3) { + // "agent.byok.copilot" - show this provider's BYOK + auto it = settings.byok.find(target_provider); + show_byok(target_provider, it != settings.byok.end() ? &it->second : nullptr); + return {true, ss.str(), 0}; + } + + if (parts.size() == 4) { + // "agent.byok.copilot." - get/set field + std::string field = parts[3]; + auto& byok = settings.byok[target_provider]; + return handle_byok_field(byok, field, "agent.byok." + target_provider); + } + } else { + // agent.byok. - uses current provider + auto& byok = settings.get_or_create_byok(); + return handle_byok_field(byok, part2, "agent.byok"); + } + + return {false, "Error: Unknown config path: " + path + "\n", 1}; + } + + return {false, "Error: Unknown config path: " + path + "\nUse --config to see available options.\n", 1}; + +#else + return {false, "Error: AI agent not compiled in. Rebuild with -DIDASQL_WITH_AI_AGENT=ON\n", 1}; +#endif +} + +} // namespace idasql + +``` + +`src/common/idasql_version.hpp`: + +```hpp +#pragma once + +#define IDASQL_VERSION_MAJOR 0 +#define IDASQL_VERSION_MINOR 0 +#define IDASQL_VERSION_PATCH 8 +#define IDASQL_VERSION_STRING "0.0.8" + +``` + +`src/common/json_utils.hpp`: + +```hpp +#pragma once + +#include +#include +#include + +namespace idasql { + +inline std::string escape_json(const std::string& input) { + std::ostringstream oss; + for (unsigned char c : input) { + switch (c) { + case '\"': oss << "\\\""; break; + case '\\': oss << "\\\\"; break; + case '\b': oss << "\\b"; break; + case '\f': oss << "\\f"; break; + case '\n': oss << "\\n"; break; + case '\r': oss << "\\r"; break; + case '\t': oss << "\\t"; break; + default: + if (c < 0x20) { + oss << "\\u" + << std::hex << std::uppercase << std::setfill('0') + << std::setw(4) << static_cast(c); + // reset flags for subsequent writes + oss << std::dec; + } else { + oss << static_cast(c); + } + break; + } + } + return oss.str(); +} + +} // namespace idasql + +``` + +`src/common/mcp_server.cpp`: + +```cpp +#include "mcp_server.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace idasql { + +using Json = nlohmann::json; + +class IDAMCPServer::Impl { +public: + fastmcpp::tools::ToolManager tool_manager; + std::unique_ptr server; +}; + +IDAMCPServer::IDAMCPServer() = default; + +IDAMCPServer::~IDAMCPServer() { + stop(); +} + +MCPQueueResult IDAMCPServer::queue_and_wait(MCPPendingCommand::Type type, const std::string& input) { + if (!running_.load()) { + return {false, "Error: MCP server is not running"}; + } + + MCPPendingCommand cmd; + cmd.type = type; + cmd.input = input; + cmd.completed = false; + + std::mutex done_mutex; + std::condition_variable done_cv; + cmd.done_mutex = &done_mutex; + cmd.done_cv = &done_cv; + + { + std::lock_guard lock(queue_mutex_); + pending_commands_.push(&cmd); + } + queue_cv_.notify_one(); + + { + std::unique_lock lock(done_mutex); + done_cv.wait(lock, [&]() { return cmd.completed || !running_.load(); }); + } + + if (!cmd.completed) { + return {false, "Error: MCP server stopped"}; + } + + return {true, cmd.result}; +} + +int IDAMCPServer::start(int port, QueryCallback query_cb, AskCallback ask_cb, + const std::string& bind_addr, bool use_queue) { + if (running_.load()) { + return port_; + } + + query_cb_ = query_cb; + ask_cb_ = ask_cb; + bind_addr_ = bind_addr; + use_queue_.store(use_queue); + + // If port is 0, pick a random port in the 9000-9999 range + if (port == 0) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(9000, 9999); + port = dis(gen); + } + + impl_ = std::make_unique(); + + // Register idasql_query tool - direct SQL execution + Json query_input_schema = { + {"type", "object"}, + {"properties", { + {"query", { + {"type", "string"}, + {"description", "SQL query to execute against the IDA database"} + }} + }}, + {"required", Json::array({"query"})} + }; + + Json query_output_schema = { + {"type", "object"}, + {"properties", { + {"result", {{"type", "string"}}}, + {"success", {{"type", "boolean"}}} + }} + }; + + fastmcpp::tools::Tool sql_query_tool{ + "idasql_query", + query_input_schema, + query_output_schema, + [this](const Json& args) -> Json { + std::string query = args.value("query", ""); + if (query.empty()) { + return Json{ + {"content", Json::array({ + Json{{"type", "text"}, {"text", "Error: missing query"}} + })}, + {"isError", true} + }; + } + + std::string result; + bool success = true; + + if (use_queue_.load()) { + // Queue mode (CLI): queue command for main thread execution + auto qr = queue_and_wait(MCPPendingCommand::Type::Query, query); + result = qr.payload; + success = qr.success; + } else { + // Direct mode (plugin): callback uses execute_sync internally + if (!query_cb_) { + return Json{ + {"content", Json::array({ + Json{{"type", "text"}, {"text", "Error: query callback not set"}} + })}, + {"isError", true} + }; + } + result = query_cb_(query); + } + + // MCP tools/call expects content array format + return Json{ + {"content", Json::array({ + Json{{"type", "text"}, {"text", result}} + })}, + {"isError", !success} + }; + } + }; + sql_query_tool.set_description("Execute a SQL query against the IDA database and return results"); + impl_->tool_manager.register_tool(sql_query_tool); + + // Register idasql_agent tool - natural language query (if ask_cb provided) + if (ask_cb_) { + Json ask_input_schema = { + {"type", "object"}, + {"properties", { + {"question", { + {"type", "string"}, + {"description", "Natural language question about the binary (e.g., 'What functions call malloc?')"} + }} + }}, + {"required", Json::array({"question"})} + }; + + Json ask_output_schema = { + {"type", "object"}, + {"properties", { + {"response", {{"type", "string"}}}, + {"success", {{"type", "boolean"}}} + }} + }; + + fastmcpp::tools::Tool agent_ask_tool{ + "idasql_agent", + ask_input_schema, + ask_output_schema, + [this](const Json& args) -> Json { + std::string question = args.value("question", ""); + if (question.empty()) { + return Json{ + {"content", Json::array({ + Json{{"type", "text"}, {"text", "Error: missing question"}} + })}, + {"isError", true} + }; + } + + std::string result; + bool success = true; + + if (use_queue_.load()) { + // Queue mode (CLI): queue command for main thread execution + auto qr = queue_and_wait(MCPPendingCommand::Type::Ask, question); + result = qr.payload; + success = qr.success; + } else { + // Direct mode (plugin): callback handles thread safety + result = ask_cb_(question); + } + + return Json{ + {"content", Json::array({ + Json{{"type", "text"}, {"text", result}} + })}, + {"isError", !success} + }; + } + }; + agent_ask_tool.set_description("Ask a natural language question about the binary - AI translates to SQL and returns results"); + impl_->tool_manager.register_tool(agent_ask_tool); + } + + // Create MCP handler + std::unordered_map descriptions = { + {"idasql_query", "Execute a SQL query against the IDA database and return results"} + }; + if (ask_cb_) { + descriptions["idasql_agent"] = "Ask a natural language question about the binary - AI translates to SQL and returns results"; + } + + auto handler = fastmcpp::mcp::make_mcp_handler( + "idasql", + "1.0.0", + impl_->tool_manager, + descriptions + ); + + // Create and start SSE server + impl_->server = std::make_unique( + handler, + bind_addr_, + port, + "/sse", + "/messages" + ); + + if (!impl_->server->start()) { + impl_.reset(); + return -1; + } + + port_ = impl_->server->port(); + running_.store(true); + + return port_; +} + +void IDAMCPServer::set_interrupt_check(std::function check) { + interrupt_check_ = check; +} + +void IDAMCPServer::run_until_stopped() { + while (running_.load()) { + if (interrupt_check_ && interrupt_check_()) { + stop(); + break; + } + + MCPPendingCommand* cmd = nullptr; + + { + std::unique_lock lock(queue_mutex_); + if (queue_cv_.wait_for(lock, std::chrono::milliseconds(100), + [this]() { return !pending_commands_.empty() || !running_.load(); })) { + if (!pending_commands_.empty()) { + cmd = pending_commands_.front(); + pending_commands_.pop(); + } + } + } + + if (cmd) { + try { + if (cmd->type == MCPPendingCommand::Type::Query && query_cb_) { + cmd->result = query_cb_(cmd->input); + } else if (cmd->type == MCPPendingCommand::Type::Ask && ask_cb_) { + cmd->result = ask_cb_(cmd->input); + } else { + cmd->result = "Error: No handler for command type"; + } + } catch (const std::exception& e) { + cmd->result = std::string("Error: ") + e.what(); + } + + if (cmd->done_mutex && cmd->done_cv) { + { + std::lock_guard lock(*cmd->done_mutex); + cmd->completed = true; + } + cmd->done_cv->notify_one(); + } + } + } +} + +void IDAMCPServer::stop() { + running_.store(false); + queue_cv_.notify_all(); + complete_pending_commands("Error: MCP server stopped"); + + if (impl_ && impl_->server) { + impl_->server->stop(); + } + + impl_.reset(); +} + +void IDAMCPServer::complete_pending_commands(const std::string& result) { + std::queue pending; + { + std::lock_guard lock(queue_mutex_); + std::swap(pending, pending_commands_); + } + + while (!pending.empty()) { + MCPPendingCommand* cmd = pending.front(); + pending.pop(); + if (!cmd || !cmd->done_mutex || !cmd->done_cv) { + continue; + } + + { + std::lock_guard lock(*cmd->done_mutex); + if (!cmd->completed) { + cmd->result = result; + cmd->completed = true; + } + } + cmd->done_cv->notify_one(); + } +} + +std::string IDAMCPServer::url() const { + std::ostringstream ss; + ss << "http://" << bind_addr_ << ":" << port_; + return ss.str(); +} + +std::string format_mcp_info(int port, bool has_agent) { + std::ostringstream ss; + ss << "MCP server started on port " << port << "\n"; + ss << "SSE endpoint: http://127.0.0.1:" << port << "/sse\n\n"; + + ss << "Available tools:\n"; + ss << " idasql_query - Execute SQL query directly\n"; + if (has_agent) { + ss << " idasql_agent - Ask natural language question (AI-powered)\n"; + } + ss << "\n"; + + ss << "Add to Claude Desktop config:\n"; + ss << "{\n"; + ss << " \"mcpServers\": {\n"; + ss << " \"idasql\": {\n"; + ss << " \"url\": \"http://127.0.0.1:" << port << "/sse\"\n"; + ss << " }\n"; + ss << " }\n"; + ss << "}\n"; + + return ss.str(); +} + +std::string format_mcp_status(int port, bool running) { + std::ostringstream ss; + if (running) { + ss << "MCP server running on port " << port << "\n"; + ss << "SSE endpoint: http://127.0.0.1:" << port << "/sse\n"; + } else { + ss << "MCP server not running\n"; + ss << "Use '.mcp start' to start\n"; + } + return ss.str(); +} + +} // namespace idasql + +``` + +`src/common/mcp_server.hpp`: + +```hpp +#pragma once + +/** + * IDAMCPServer - MCP server for IDASQL + * + * Thread-safe MCP server using command queue pattern. + * Tool handlers queue commands for execution on the main thread. + * + * Usage modes: + * 1. CLI (idalib): Call wait() to process commands on main thread + * 2. Plugin: Use execute_sync() wrapper in callbacks (no wait() needed) + * + * For CLI, start() returns immediately. Call wait() to block and process + * commands. For plugin, the callback itself uses execute_sync() to marshal + * to IDA's main thread, so no wait() is needed. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace idasql { + +// Callbacks for handling requests +// QueryCallback: Direct SQL execution +// AskCallback: Natural language query (requires AI agent) +using QueryCallback = std::function; +using AskCallback = std::function; + +// Internal command structure for cross-thread execution +struct MCPPendingCommand { + enum class Type { Query, Ask }; + Type type; + std::string input; + std::string result; + bool completed = false; + std::mutex* done_mutex = nullptr; + std::condition_variable* done_cv = nullptr; +}; + +struct MCPQueueResult { + bool success; + std::string payload; +}; + +class IDAMCPServer { +public: + IDAMCPServer(); + ~IDAMCPServer(); + + // Non-copyable + IDAMCPServer(const IDAMCPServer&) = delete; + IDAMCPServer& operator=(const IDAMCPServer&) = delete; + + /** + * Start MCP server on given port with callbacks + * + * @param port Port to listen on (0 = random port 9000-9999) + * @param query_cb SQL query callback + * @param ask_cb Natural language callback (optional) + * @param bind_addr Address to bind to (default: localhost only) + * @param use_queue If true, callbacks are queued for main thread (CLI mode) + * If false, callbacks called directly (plugin mode with execute_sync) + * @return Actual port used, or -1 on failure + */ + int start(int port, QueryCallback query_cb, AskCallback ask_cb = nullptr, + const std::string& bind_addr = "127.0.0.1", bool use_queue = false); + + /** + * Block until server stops, processing commands on the calling thread + * Only needed when use_queue=true (CLI mode) + * This is where query_cb and ask_cb get called + */ + void run_until_stopped(); + + /** + * Stop the server + */ + void stop(); + + /** + * Check if server is running + */ + bool is_running() const { return running_.load(); } + + /** + * Get the port the server is listening on + */ + int port() const { return port_; } + + /** + * Get the SSE endpoint URL + */ + std::string url() const; + + /** + * Set interrupt check function (called during wait loop) + */ + void set_interrupt_check(std::function check); + + /** + * Queue a command for execution on the main thread + * Called by MCP tool handlers when use_queue=true + */ + MCPQueueResult queue_and_wait(MCPPendingCommand::Type type, const std::string& input); + +private: + std::function interrupt_check_; + std::atomic running_{false}; + std::atomic use_queue_{false}; + std::string bind_addr_{"127.0.0.1"}; + int port_{0}; + + // Command queue for cross-thread execution (CLI mode) + std::mutex queue_mutex_; + std::condition_variable queue_cv_; + std::queue pending_commands_; + + // Callbacks stored for execution + QueryCallback query_cb_; + AskCallback ask_cb_; + + // Forward declaration - impl hides fastmcpp + class Impl; + std::unique_ptr impl_; + + void complete_pending_commands(const std::string& result); +}; + +/** + * Format MCP server info for display + */ +std::string format_mcp_info(int port, bool has_agent); + +/** + * Format MCP server status + */ +std::string format_mcp_status(int port, bool running); + +} // namespace idasql + +``` + +`src/common/plugin_control.hpp`: + +```hpp +#pragma once +// IDASQL Plugin Control Codes + +namespace idasql { + +constexpr size_t PLUGIN_ARG_TOGGLE_CLI = 23; // Toggle CLI + +} // namespace idasql + +``` + +`src/common/session_handler.hpp`: + +```hpp +#pragma once + +/** + * SessionHandler - Core query processing logic for IDASQL + * + * This class handles: + * - SQL query execution + * - Meta commands (.tables, .schema, .help) + * - Natural language queries via AI agent (when enabled) + * - Multi-turn conversation state + * + * NO IDA DEPENDENCIES - can be tested standalone. + * + * Used by: + * - CLI main.cpp (directly) + * - IdasqlCLI (wraps this for cli_t) + */ + +#include +#include +#include +#include +#include + +#include "idasql_commands.hpp" + +#ifdef IDASQL_HAS_AI_AGENT +#include "ai_agent.hpp" +#endif + +namespace idasql { + +class SessionHandler +{ +public: + using SqlExecutor = std::function; + + // Simple allowlist for table identifiers (alnum + underscore) + static bool is_safe_table_name(const std::string& name) + { + if (name.empty() || name.size() > 128) return false; + return std::all_of(name.begin(), name.end(), [](unsigned char c) { + return std::isalnum(c) || c == '_'; + }); + } + + /** + * Create a session handler + * @param executor Function to execute SQL and return formatted results + * @param enable_agent Whether to enable AI agent (if available) + */ + explicit SessionHandler(SqlExecutor executor, bool enable_agent = false) + : executor_(std::move(executor)) + { + // Setup command callbacks + callbacks_.get_tables = [this]() { + return executor_("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"); + }; + callbacks_.get_schema = [this](const std::string& table) { + if (!is_safe_table_name(table)) { + return std::string("Invalid table name"); + } + std::string sql = "SELECT sql FROM sqlite_master WHERE name='" + table + "'"; + return executor_(sql); + }; + callbacks_.get_info = [this]() { + return executor_("PRAGMA database_list"); + }; + callbacks_.clear_session = [this]() { + return clear_session(); + }; + +#ifdef IDASQL_HAS_AI_AGENT + if (enable_agent && AIAgent::is_available()) { + agent_ = std::make_unique(executor_); + agent_->start(); + agent_enabled_ = true; + } +#else + (void)enable_agent; // Suppress unused warning +#endif + } + + ~SessionHandler() + { + end_session(); + } + + // Non-copyable, movable + SessionHandler(const SessionHandler&) = delete; + SessionHandler& operator=(const SessionHandler&) = delete; + SessionHandler(SessionHandler&&) = default; + SessionHandler& operator=(SessionHandler&&) = default; + + /** + * Process a line of input + * @param line User input (SQL, meta command, or natural language) + * @return Result string, or empty if no output + */ + std::string process_line(const std::string& line) + { + if (line.empty()) { + return ""; + } + + // Check for meta commands first + std::string output; + auto cmd_result = handle_command(line, callbacks_, output); + + switch (cmd_result) { + case CommandResult::QUIT: + quit_requested_ = true; + return ""; + + case CommandResult::HANDLED: + return output; + + case CommandResult::NOT_HANDLED: + // Continue to process as query + break; + } + +#ifdef IDASQL_HAS_AI_AGENT + // If AI agent is enabled and input doesn't look like SQL, use agent + if (agent_enabled_ && agent_ && !AIAgent::looks_like_sql(line)) { + return agent_->query(line); + } +#endif + + // Execute as raw SQL + return executor_(line); + } + + /** + * One-shot query (no session, no conversation history) + */ + std::string query(const std::string& prompt) + { +#ifdef IDASQL_HAS_AI_AGENT + if (agent_enabled_ && agent_) { + return agent_->query(prompt); + } +#endif + // Fallback: treat as SQL + return executor_(prompt); + } + + /** + * End the session (cleanup agent) + */ + void end_session() + { +#ifdef IDASQL_HAS_AI_AGENT + if (agent_) { + agent_->stop(); + agent_.reset(); + } +#endif + agent_enabled_ = false; + } + + bool is_agent_enabled() const { return agent_enabled_; } + bool is_quit_requested() const { return quit_requested_; } + + /** + * Clear/reset the session + * Resets AI agent conversation history if enabled. + * Override the callback to add UI-specific behavior (e.g., msg_clear). + * + * @return Status message + */ + virtual std::string clear_session() + { +#ifdef IDASQL_HAS_AI_AGENT + if (agent_) { + agent_->reset_session(); + return "Session cleared (conversation history reset)"; + } +#endif + return "Session cleared"; + } + + /** + * Get command callbacks (for overriding in derived classes) + */ + CommandCallbacks& callbacks() { return callbacks_; } + const CommandCallbacks& callbacks() const { return callbacks_; } + + /** + * Check if AI agent is available on this system + */ + static bool is_agent_available() + { +#ifdef IDASQL_HAS_AI_AGENT + return AIAgent::is_available(); +#else + return false; +#endif + } + +private: + SqlExecutor executor_; + CommandCallbacks callbacks_; + bool agent_enabled_ = false; + bool quit_requested_ = false; + +#ifdef IDASQL_HAS_AI_AGENT + std::unique_ptr agent_; +#endif +}; + +} // namespace idasql + +``` + +`src/lib/CMakeLists.txt`: + +```txt +# libidasql - SQL interface to IDA databases +# +# This is a header-only library that extends xsql with IDA-specific tables. +# Users link against this target to get the include paths. + +add_library(idasql INTERFACE) + +target_include_directories(idasql INTERFACE + $ + $ +) + +# Link against xsql::xsql for SQLite virtual table framework +target_link_libraries(idasql INTERFACE xsql::xsql) + +# Export the library target +# install(TARGETS idasql EXPORT idasqlTargets) +# install(DIRECTORY include/idasql DESTINATION include) + +``` + +`src/lib/include/idasql/database.hpp`: + +```hpp +/** + * database.hpp - IDASQL API + * + * Two-tier API design reflecting IDA's singleton nature: + * + * TIER 1: QueryEngine (recommended for most use) + * - Use when IDA is already initialized (plugins, scripts, idalib) + * - Just creates SQLite + virtual tables + * - No IDA lifecycle management + * + * TIER 2: Session (for standalone CLI tools) + * - Full IDA lifecycle: init_library + open_database + close + * - Use for tools like idasql.exe that manage everything + * + * TIER 3: Free functions (quick one-liners) + * - idasql::query(), idasql::exec() + * - Use global engine, lazily initialized + * + * IMPORTANT: IDA SDK is a singleton. Only ONE database can be open. + * The Session class doesn't create "a database" - it manages THE database. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include + +// IDA SDK +#include +#include +#include +#include +#include + +// IDASQL components +#include +#include +#include +#include +#include +#include +#include +#include + +// Optional: Decompiler (may not be available) +#ifdef USE_HEXRAYS +#include +#endif + +namespace idasql { + +// ============================================================================ +// Result Types +// ============================================================================ + +/** + * Single row from a query result + */ +struct Row { + std::vector values; + + const std::string& operator[](size_t i) const { return values[i]; } + size_t size() const { return values.size(); } +}; + +/** + * Query result set + */ +struct QueryResult { + std::vector columns; + std::vector rows; + std::string error; + bool success = false; + + // Convenience accessors + size_t row_count() const { return rows.size(); } + size_t column_count() const { return columns.size(); } + bool empty() const { return rows.empty(); } + + // Get first cell as scalar (for single-value queries) + std::string scalar() const { + return (!empty() && rows[0].size() > 0) ? rows[0][0] : ""; + } + + // Iterator support + auto begin() { return rows.begin(); } + auto end() { return rows.end(); } + auto begin() const { return rows.begin(); } + auto end() const { return rows.end(); } + + // Format as string for display + std::string to_string() const { + if (!success) return error; + if (empty()) return "(0 rows)"; + + std::string result; + // Header + for (size_t i = 0; i < columns.size(); ++i) { + if (i > 0) result += " | "; + result += columns[i]; + } + result += "\n"; + // Separator + for (size_t i = 0; i < columns.size(); ++i) { + if (i > 0) result += "-+-"; + result += std::string(columns[i].size(), '-'); + } + result += "\n"; + // Rows + for (const auto& row : rows) { + for (size_t i = 0; i < row.size(); ++i) { + if (i > 0) result += " | "; + result += row[i]; + } + result += "\n"; + } + result += "(" + std::to_string(row_count()) + " rows)"; + return result; + } +}; + +// ============================================================================ +// TIER 1: QueryEngine - SQL interface (no IDA lifecycle) +// ============================================================================ + +/** + * QueryEngine - SQLite query interface to the current IDA database + * + * Use this when IDA is already initialized. Does NOT manage IDA lifecycle. + * You can have multiple QueryEngine instances - they all query the same + * IDA database (because IDA is singleton). + * + * Example: + * idasql::QueryEngine qe; + * auto result = qe.query("SELECT name, size FROM funcs LIMIT 10"); + * for (const auto& row : result) { + * msg("%s: %s\n", row[0].c_str(), row[1].c_str()); + * } + */ +class QueryEngine { +public: + QueryEngine() { + init(); + } + + ~QueryEngine() = default; + + // Moveable but not copyable + QueryEngine(QueryEngine&&) noexcept = default; + QueryEngine& operator=(QueryEngine&&) noexcept = default; + + QueryEngine(const QueryEngine&) = delete; + QueryEngine& operator=(const QueryEngine&) = delete; + + /** + * Execute SQL and return results + */ + QueryResult query(const std::string& sql) { + return query(sql.c_str()); + } + + QueryResult query(const char* sql) { + QueryResult result; + + if (!db_.is_open()) { + result.error = "QueryEngine not initialized"; + return result; + } + + struct QueryData { + QueryResult* result; + bool first_row; + } qd = { &result, true }; + + auto callback = [](void* data, int argc, char** argv, char** cols) -> int { + auto* qd = static_cast(data); + + if (qd->first_row) { + for (int i = 0; i < argc; i++) { + qd->result->columns.push_back(cols[i] ? cols[i] : ""); + } + qd->first_row = false; + } + + Row row; + row.values.reserve(argc); + for (int i = 0; i < argc; i++) { + row.values.push_back(argv[i] ? argv[i] : "NULL"); + } + qd->result->rows.push_back(std::move(row)); + + return 0; + }; + + int rc = exec(sql, callback, &qd); + result.success = (rc == SQLITE_OK); + if (!result.success && result.error.empty()) { + result.error = sqlite3_errmsg(db_.handle()); + } + + return result; + } + + /** + * Execute SQL with callback (for streaming large results) + */ + int exec(const char* sql, sqlite3_callback callback, void* data) { + if (!db_.is_open()) { + error_ = "QueryEngine not initialized"; + return SQLITE_ERROR; + } + + char* err_msg = nullptr; + int rc = sqlite3_exec(db_.handle(), sql, callback, data, &err_msg); + if (err_msg) { + error_ = err_msg; + sqlite3_free(err_msg); + } + return rc; + } + + /** + * Execute SQL, ignore results (for INSERT/UPDATE/DELETE) + */ + bool execute(const char* sql) { + return exec(sql, nullptr, nullptr) == SQLITE_OK; + } + + /** + * Get single value (first column of first row) + */ + std::string scalar(const std::string& sql) { + return scalar(sql.c_str()); + } + + std::string scalar(const char* sql) { + auto result = query(sql); + if (result.success && !result.empty()) { + return result.rows[0].values[0]; + } + return ""; + } + + /** + * Get last error message + */ + const std::string& error() const { return error_; } + + /** + * Check if initialized + */ + bool is_valid() const { return db_.is_open(); } + + /** + * Get raw SQLite handle (for advanced use) + */ + sqlite3* handle() { return db_.handle(); } + +private: + xsql::Database db_; + std::string error_; + + // Table registries (prevent dangling virtual table pointers) + std::unique_ptr entities_; + std::unique_ptr metadata_; + std::unique_ptr extended_; + std::unique_ptr disassembly_; + std::unique_ptr types_; + std::unique_ptr debugger_; + std::unique_ptr decompiler_; // Runtime detection + + void init() { + // db_ auto-opens :memory: via xsql::Database constructor + + // Register all virtual tables + entities_ = std::make_unique(); + entities_->register_all(db_); + + metadata_ = std::make_unique(); + metadata_->register_all(db_); + + extended_ = std::make_unique(); + extended_->register_all(db_); + + disassembly_ = std::make_unique(); + disassembly_->register_all(db_); + + types_ = std::make_unique(); + types_->register_all(db_); + + debugger_ = std::make_unique(); + debugger_->register_all(db_); + + // Decompiler registry - register_all() handles runtime Hex-Rays detection + // Must be registered before SQL functions so hexrays_available() is set + decompiler_ = std::make_unique(); + decompiler_->register_all(db_); + + functions::register_sql_functions(db_); + search::register_search_bytes(db_); + } +}; + +// ============================================================================ +// TIER 2: Session - Full IDA lifecycle management +// ============================================================================ + +/** + * Session - Manages THE IDA database session + * + * Use this for standalone tools that need to open/close IDA databases. + * Remember: IDA is singleton, so there's only ever ONE session. + * + * Example (CLI tool): + * idasql::Session session; + * if (!session.open("binary.i64")) { + * std::cerr << session.error() << std::endl; + * return 1; + * } + * auto result = session.query("SELECT * FROM funcs"); + * session.close(); + */ +class Session { +public: + Session() = default; + ~Session() { close(); } + + // Non-copyable, non-moveable (singleton semantics) + Session(const Session&) = delete; + Session& operator=(const Session&) = delete; + Session(Session&&) = delete; + Session& operator=(Session&&) = delete; + + /** + * Open an IDA database + * @param idb_path Path to .idb/.i64 file + * @return true on success + */ + bool open(const char* idb_path) { + if (engine_) close(); + + // Initialize IDA library + int rc = init_library(); + if (rc != 0) { + error_ = "Failed to initialize IDA library: " + std::to_string(rc); + return false; + } + + // Open the database + rc = open_database(idb_path, true, nullptr); + if (rc != 0) { + error_ = "Failed to open database: " + std::string(idb_path); + return false; + } + ida_opened_ = true; + + // Wait for auto-analysis + auto_wait(); + + // For new analysis (exe/dll/etc), build strings after auto-analysis completes + // For existing databases (i64/idb), strings are already saved + std::string path_lower = idb_path; + std::transform(path_lower.begin(), path_lower.end(), path_lower.begin(), ::tolower); + auto ends_with = [](const std::string& s, const std::string& suffix) { + return s.size() >= suffix.size() && + s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0; + }; + bool is_new_analysis = !( + ends_with(path_lower, ".i64") || + ends_with(path_lower, ".idb") + ); + if (is_new_analysis) { + // Configure and build string list with sensible defaults + strwinsetup_t* opts = const_cast(get_strlist_options()); + opts->strtypes.clear(); + opts->strtypes.push_back(STRTYPE_C); // ASCII + opts->strtypes.push_back(STRTYPE_C_16); // UTF-16 + opts->minlen = 5; + opts->only_7bit = 0; + clear_strlist(); // Clear before building (like rebuild_strings) + build_strlist(); + } + + // Create query engine + engine_ = std::make_unique(); + if (!engine_->is_valid()) { + error_ = engine_->error(); + close(); + return false; + } + + return true; + } + + /** + * Close the session + */ + void close() { + engine_.reset(); + if (ida_opened_) { + close_database(false); + ida_opened_ = false; + } + } + + /** + * Check if session is open + */ + bool is_open() const { return engine_ && engine_->is_valid() && ida_opened_; } + + /** + * Get last error + */ + const std::string& error() const { + return engine_ ? engine_->error() : error_; + } + + // Delegate query methods to engine (with string overloads) + QueryResult query(const std::string& sql) { return query(sql.c_str()); } + QueryResult query(const char* sql) { + if (!engine_) { + QueryResult r; + r.error = "Session not open"; + return r; + } + return engine_->query(sql); + } + + int exec(const char* sql, sqlite3_callback cb, void* data) { + return engine_ ? engine_->exec(sql, cb, data) : SQLITE_ERROR; + } + + bool execute(const std::string& sql) { return execute(sql.c_str()); } + bool execute(const char* sql) { + return engine_ ? engine_->execute(sql) : false; + } + + std::string scalar(const std::string& sql) { return scalar(sql.c_str()); } + std::string scalar(const char* sql) { + return engine_ ? engine_->scalar(sql) : ""; + } + + /** + * Get raw SQLite handle + */ + sqlite3* handle() { return engine_ ? engine_->handle() : nullptr; } + + /** + * Get query engine (for advanced use) + */ + QueryEngine* engine() { return engine_.get(); } + + /** + * Get database info + */ + std::string info() const { + if (!ida_opened_) return "Not opened"; + + std::string s; + s += "Processor: " + std::string(inf_get_procname().c_str()) + "\n"; + s += "Functions: " + std::to_string(get_func_qty()) + "\n"; + s += "Segments: " + std::to_string(get_segm_qty()) + "\n"; + s += "Names: " + std::to_string(get_nlist_size()) + "\n"; + return s; + } + +private: + std::unique_ptr engine_; + bool ida_opened_ = false; + std::string error_; +}; + +// ============================================================================ +// TIER 3: Free Functions - Quick one-liners +// ============================================================================ + +namespace detail { + inline QueryEngine& global_engine() { + static QueryEngine engine; + return engine; + } +} + +/** + * Quick query - uses global engine + * + * Example: + * auto funcs = idasql::query("SELECT name FROM funcs LIMIT 5"); + * for (const auto& row : funcs) { + * msg("%s\n", row[0].c_str()); + * } + */ +inline QueryResult query(const char* sql) { + return detail::global_engine().query(sql); +} + +/** + * Quick exec with callback + */ +inline int exec(const char* sql, sqlite3_callback cb, void* data) { + return detail::global_engine().exec(sql, cb, data); +} + +/** + * Quick execute (no results) + */ +inline bool execute(const char* sql) { + return detail::global_engine().execute(sql); +} + +/** + * Quick scalar query + */ +inline std::string scalar(const char* sql) { + return detail::global_engine().scalar(sql); +} + +// ============================================================================ +// Backwards Compatibility Alias +// ============================================================================ + +// For existing code using idasql::Database +using Database = Session; + +} // namespace idasql + +``` + +`src/lib/include/idasql/decompiler.hpp`: + +```hpp +/** + * ida_decompiler.hpp - Hex-Rays Decompiler Virtual Tables + * + * Provides SQLite virtual tables for accessing decompiled function data: + * pseudocode - Decompiled function pseudocode lines + * ctree_lvars - Local variables from decompiled functions + * ctree - Full AST (expressions and statements) + * ctree_call_args - Flattened call arguments + * + * All tables support constraint pushdown on func_addr via filter_eq framework: + * SELECT * FROM pseudocode WHERE func_addr = 0x401000; + * SELECT * FROM ctree_lvars WHERE func_addr = 0x401000; + * + * Requires Hex-Rays decompiler license. + */ + +#pragma once + +#include + +#include +#include + +#include +#include +#include + +#include + +// IDA SDK headers +#include +#include +#include +#include + +// Hex-Rays decompiler headers +#include +#include + +namespace idasql { +namespace decompiler { + +// ============================================================================ +// Decompiler Initialization +// ============================================================================ + +// Global flag tracking if Hex-Rays is available +// Set once during DecompilerRegistry::register_all() +inline bool& hexrays_available() { + static bool available = false; + return available; +} + +// Initialize Hex-Rays decompiler - call ONCE at startup +// Returns true if decompiler is available +inline bool init_hexrays() { + static bool initialized = false; + + if (!initialized) { + initialized = true; + hexrays_available() = init_hexrays_plugin(); + if (hexrays_available()) { + // Hex-Rays initialization may trigger additional auto-analysis work. + // Ensure analysis is complete before running decompiler-backed queries. + auto_wait(); + } + } + return hexrays_available(); +} + +// Invalidate decompiler cache for the function containing ea. +// Safe to call even if Hex-Rays is unavailable or ea is not in a function. +inline void invalidate_decompiler_cache(ea_t ea) { + if (!hexrays_available()) return; + func_t* f = get_func(ea); + if (f) { + mark_cfunc_dirty(f->start_ea, false); + } +} + +// ============================================================================ +// Data Structures +// ============================================================================ + +// ITP name ↔ enum helpers +inline const char* itp_to_name(item_preciser_t itp) { + switch (itp) { + case ITP_SEMI: return "semi"; + case ITP_BLOCK1: return "block1"; + case ITP_BLOCK2: return "block2"; + case ITP_CURLY1: return "curly1"; + case ITP_CURLY2: return "curly2"; + case ITP_BRACE1: return "brace1"; + case ITP_BRACE2: return "brace2"; + case ITP_COLON: return "colon"; + case ITP_CASE: return "case"; + case ITP_ELSE: return "else"; + case ITP_DO: return "do"; + case ITP_ASM: return "asm"; + default: return "semi"; + } +} + +inline item_preciser_t name_to_itp(const char* name) { + if (!name || !name[0]) return ITP_SEMI; + if (stricmp(name, "block1") == 0) return ITP_BLOCK1; + if (stricmp(name, "block2") == 0) return ITP_BLOCK2; + if (stricmp(name, "curly1") == 0) return ITP_CURLY1; + if (stricmp(name, "curly2") == 0) return ITP_CURLY2; + if (stricmp(name, "brace1") == 0) return ITP_BRACE1; + if (stricmp(name, "brace2") == 0) return ITP_BRACE2; + if (stricmp(name, "colon") == 0) return ITP_COLON; + if (stricmp(name, "case") == 0) return ITP_CASE; + if (stricmp(name, "else") == 0) return ITP_ELSE; + if (stricmp(name, "do") == 0) return ITP_DO; + if (stricmp(name, "asm") == 0) return ITP_ASM; + return ITP_SEMI; // default +} + +// Pseudocode line data +struct PseudocodeLine { + ea_t func_addr; + int line_num; + std::string text; + ea_t ea; // Associated address (from COLOR_ADDR anchor) + std::string comment; // User comment at this ea (from restore_user_cmts) + item_preciser_t comment_placement = ITP_SEMI; // Comment placement type +}; + +// Local variable data +struct LvarInfo { + ea_t func_addr; + int idx; + std::string name; + std::string type; + int size; + bool is_arg; + bool is_result; + bool is_stk_var; + bool is_reg_var; + sval_t stkoff; + mreg_t mreg; +}; + +// Ctree item data +struct CtreeItem { + ea_t func_addr; + int item_id; + bool is_expr; + int op; + std::string op_name; + ea_t ea; + int parent_id; + int depth; + int x_id, y_id, z_id; + int cond_id, then_id, else_id; + int body_id, init_id, step_id; + int var_idx; + ea_t obj_ea; + int64_t num_value; + std::string str_value; + std::string helper_name; + int member_offset; + std::string var_name; + bool var_is_stk, var_is_reg, var_is_arg; + std::string obj_name; + + CtreeItem() : func_addr(0), item_id(-1), is_expr(false), op(0), ea(BADADDR), + parent_id(-1), depth(0), + x_id(-1), y_id(-1), z_id(-1), + cond_id(-1), then_id(-1), else_id(-1), + body_id(-1), init_id(-1), step_id(-1), + var_idx(-1), obj_ea(BADADDR), num_value(0), member_offset(0), + var_is_stk(false), var_is_reg(false), var_is_arg(false) {} +}; + +// Call argument data +struct CallArgInfo { + ea_t func_addr; + int call_item_id; + int arg_idx; + int arg_item_id; + std::string arg_op; + int arg_var_idx; + std::string arg_var_name; + bool arg_var_is_stk; + bool arg_var_is_arg; + ea_t arg_obj_ea; + std::string arg_obj_name; + int64_t arg_num_value; + std::string arg_str_value; + + CallArgInfo() : func_addr(0), call_item_id(-1), arg_idx(-1), arg_item_id(-1), + arg_var_idx(-1), arg_var_is_stk(false), arg_var_is_arg(false), + arg_obj_ea(BADADDR), arg_num_value(0) {} +}; + +// ============================================================================ +// Helper Functions +// ============================================================================ + +// Get full ctype name with cot_/cit_ prefix +inline std::string get_full_ctype_name(ctype_t op) { + const char* name = get_ctype_name(op); + if (!name || !name[0]) return ""; + if (op < cit_empty) { + return std::string("cot_") + name; + } else { + return std::string("cit_") + name; + } +} + +// Extract the first COLOR_ADDR anchor ea from a raw pseudocode line. +// Returns BADADDR if no anchor found. +inline ea_t extract_line_ea(cfunc_t* cfunc, const qstring& raw_line) { + const char* p = raw_line.c_str(); + while (*p) { + if (*p == COLOR_ON && *(p + 1) == COLOR_ADDR) { + p += 2; // skip COLOR_ON + COLOR_ADDR + // Read 16 hex chars + char hex[17] = {}; + for (int i = 0; i < 16; i++) { + if (!p[i]) return BADADDR; + hex[i] = p[i]; + } + uint64_t val = strtoull(hex, nullptr, 16); + uint32_t anchor = static_cast(val); + // ANCHOR_CITEM = type 0 (bits 31-30) + uint32_t anchor_type = (anchor >> 30) & 0x3; + if (anchor_type != 0) return BADADDR; + uint32_t idx = anchor & 0x3FFFFFFF; + if (idx >= cfunc->treeitems.size()) return BADADDR; + citem_t* item = cfunc->treeitems[idx]; + return item ? item->ea : BADADDR; + } + p++; + } + return BADADDR; +} + +// Collect pseudocode for a single function +inline bool collect_pseudocode(std::vector& lines, ea_t func_addr) { + lines.clear(); + + if (!hexrays_available()) return false; + + func_t* f = get_func(func_addr); + if (!f) return false; + + hexrays_failure_t hf; + cfuncptr_t cfunc = decompile(f, &hf); + if (!cfunc) return false; + + const strvec_t& sv = cfunc->get_pseudocode(); + + for (int i = 0; i < sv.size(); i++) { + PseudocodeLine pl; + pl.func_addr = func_addr; + pl.line_num = i; + + // Extract ea from COLOR_ADDR anchor BEFORE stripping tags + pl.ea = extract_line_ea(&*cfunc, sv[i].line); + + qstring clean; + tag_remove(&clean, sv[i].line); + pl.text = clean.c_str(); + + lines.push_back(pl); + } + + // Read stored comments and match to lines by ea + user_cmts_t* cmts = restore_user_cmts(func_addr); + if (cmts) { + for (auto it = user_cmts_begin(cmts); it != user_cmts_end(cmts); it = user_cmts_next(it)) { + const treeloc_t& loc = user_cmts_first(it); + const citem_cmt_t& cmt = user_cmts_second(it); + // Match comment to first line with this ea + for (auto& pl : lines) { + if (pl.ea == loc.ea && pl.comment.empty()) { + pl.comment = cmt.c_str(); + pl.comment_placement = loc.itp; + break; + } + } + } + user_cmts_free(cmts); + } + + return true; +} + +// Collect pseudocode for all functions +inline void collect_all_pseudocode(std::vector& lines) { + lines.clear(); + + if (!hexrays_available()) return; + + size_t func_qty = get_func_qty(); + for (size_t i = 0; i < func_qty; i++) { + func_t* f = getn_func(i); + if (!f) continue; + + std::vector func_lines; + if (collect_pseudocode(func_lines, f->start_ea)) { + lines.insert(lines.end(), func_lines.begin(), func_lines.end()); + } + } +} + +// Collect lvars for a single function +inline bool collect_lvars(std::vector& vars, ea_t func_addr) { + vars.clear(); + + if (!hexrays_available()) return false; + + func_t* f = get_func(func_addr); + if (!f) return false; + + hexrays_failure_t hf; + cfuncptr_t cfunc = decompile(f, &hf); + if (!cfunc) return false; + + lvars_t* lvars = cfunc->get_lvars(); + if (!lvars) return false; + + for (int i = 0; i < lvars->size(); i++) { + const lvar_t& lv = (*lvars)[i]; + + LvarInfo vi; + vi.func_addr = func_addr; + vi.idx = i; + vi.name = lv.name.c_str(); + + qstring type_str; + lv.type().print(&type_str); + vi.type = type_str.c_str(); + + vi.size = lv.width; + vi.is_arg = lv.is_arg_var(); + vi.is_result = lv.is_result_var(); + vi.is_stk_var = lv.is_stk_var(); + vi.is_reg_var = lv.is_reg_var(); + vi.stkoff = vi.is_stk_var ? lv.get_stkoff() : 0; + vi.mreg = vi.is_reg_var ? lv.location.reg1() : mr_none; + + vars.push_back(vi); + } + + return true; +} + +// Collect lvars for all functions +inline void collect_all_lvars(std::vector& vars) { + vars.clear(); + + if (!hexrays_available()) return; + + size_t func_qty = get_func_qty(); + for (size_t i = 0; i < func_qty; i++) { + func_t* f = getn_func(i); + if (!f) continue; + + std::vector func_vars; + if (collect_lvars(func_vars, f->start_ea)) { + vars.insert(vars.end(), func_vars.begin(), func_vars.end()); + } + } +} + +// Ctree collector visitor +struct ctree_collector_t : public ctree_parentee_t { + std::vector& items; + std::map item_ids; + cfunc_t* cfunc; + ea_t func_addr; + int next_id; + + ctree_collector_t(std::vector& items_, cfunc_t* cfunc_, ea_t func_addr_) + : ctree_parentee_t(false), items(items_), cfunc(cfunc_), func_addr(func_addr_), next_id(0) {} + + int idaapi visit_insn(cinsn_t* insn) override { + int my_id = next_id++; + item_ids[insn] = my_id; + + CtreeItem ci; + ci.func_addr = func_addr; + ci.item_id = my_id; + ci.is_expr = false; + ci.op = insn->op; + ci.op_name = get_full_ctype_name(insn->op); + ci.ea = insn->ea; + ci.depth = parents.size(); + + citem_t* p = parent_item(); + if (p) { + auto it = item_ids.find(p); + if (it != item_ids.end()) ci.parent_id = it->second; + } + + items.push_back(ci); + return 0; + } + + int idaapi visit_expr(cexpr_t* expr) override { + int my_id = next_id++; + item_ids[expr] = my_id; + + CtreeItem ci; + ci.func_addr = func_addr; + ci.item_id = my_id; + ci.is_expr = true; + ci.op = expr->op; + ci.op_name = get_full_ctype_name(expr->op); + ci.ea = expr->ea; + ci.depth = parents.size(); + + citem_t* p = parent_item(); + if (p) { + auto it = item_ids.find(p); + if (it != item_ids.end()) ci.parent_id = it->second; + } + + switch (expr->op) { + case cot_var: + ci.var_idx = expr->v.idx; + if (cfunc && ci.var_idx >= 0 && ci.var_idx < cfunc->get_lvars()->size()) { + const lvar_t& lv = (*cfunc->get_lvars())[ci.var_idx]; + ci.var_name = lv.name.c_str(); + ci.var_is_stk = lv.is_stk_var(); + ci.var_is_reg = lv.is_reg_var(); + ci.var_is_arg = lv.is_arg_var(); + } + break; + case cot_obj: + ci.obj_ea = expr->obj_ea; + { + qstring name; + if (get_name(&name, expr->obj_ea) > 0) { + ci.obj_name = name.c_str(); + } + } + break; + case cot_num: + ci.num_value = expr->numval(); + break; + case cot_str: + if (expr->string) ci.str_value = expr->string; + break; + case cot_helper: + if (expr->helper) ci.helper_name = expr->helper; + break; + case cot_memref: + case cot_memptr: + ci.member_offset = expr->m; + break; + default: + break; + } + + items.push_back(ci); + return 0; + } + + void resolve_child_ids() { + for (auto& ci : items) { + if (ci.item_id < 0) continue; + + citem_t* item = nullptr; + for (auto& kv : item_ids) { + if (kv.second == ci.item_id) { + item = kv.first; + break; + } + } + if (!item) continue; + + if (ci.is_expr) { + cexpr_t* expr = static_cast(item); + + if (expr->x) { + auto it = item_ids.find(expr->x); + if (it != item_ids.end()) ci.x_id = it->second; + } + if (expr->y && expr->op != cot_call) { + auto it = item_ids.find(expr->y); + if (it != item_ids.end()) ci.y_id = it->second; + } + if (expr->z) { + auto it = item_ids.find(expr->z); + if (it != item_ids.end()) ci.z_id = it->second; + } + } else { + cinsn_t* insn = static_cast(item); + + switch (insn->op) { + case cit_if: + if (insn->cif) { + auto cond_it = item_ids.find(&insn->cif->expr); + if (cond_it != item_ids.end()) ci.cond_id = cond_it->second; + if (insn->cif->ithen) { + auto it = item_ids.find(insn->cif->ithen); + if (it != item_ids.end()) ci.then_id = it->second; + } + if (insn->cif->ielse) { + auto it = item_ids.find(insn->cif->ielse); + if (it != item_ids.end()) ci.else_id = it->second; + } + } + break; + case cit_for: + if (insn->cfor) { + auto cond_it = item_ids.find(&insn->cfor->expr); + if (cond_it != item_ids.end()) ci.cond_id = cond_it->second; + auto init_it = item_ids.find(&insn->cfor->init); + if (init_it != item_ids.end()) ci.init_id = init_it->second; + auto step_it = item_ids.find(&insn->cfor->step); + if (step_it != item_ids.end()) ci.step_id = step_it->second; + if (insn->cfor->body) { + auto it = item_ids.find(insn->cfor->body); + if (it != item_ids.end()) ci.body_id = it->second; + } + } + break; + case cit_while: + if (insn->cwhile) { + auto cond_it = item_ids.find(&insn->cwhile->expr); + if (cond_it != item_ids.end()) ci.cond_id = cond_it->second; + if (insn->cwhile->body) { + auto it = item_ids.find(insn->cwhile->body); + if (it != item_ids.end()) ci.body_id = it->second; + } + } + break; + case cit_do: + if (insn->cdo) { + auto cond_it = item_ids.find(&insn->cdo->expr); + if (cond_it != item_ids.end()) ci.cond_id = cond_it->second; + if (insn->cdo->body) { + auto it = item_ids.find(insn->cdo->body); + if (it != item_ids.end()) ci.body_id = it->second; + } + } + break; + case cit_return: + if (insn->creturn) { + auto it = item_ids.find(&insn->creturn->expr); + if (it != item_ids.end()) ci.x_id = it->second; + } + break; + case cit_expr: + if (insn->cexpr) { + auto it = item_ids.find(insn->cexpr); + if (it != item_ids.end()) ci.x_id = it->second; + } + break; + default: + break; + } + } + } + } +}; + +// Collect ctree items for a single function +inline bool collect_ctree(std::vector& items, ea_t func_addr) { + items.clear(); + + if (!hexrays_available()) return false; + + func_t* f = get_func(func_addr); + if (!f) return false; + + hexrays_failure_t hf; + cfuncptr_t cfunc = decompile(f, &hf); + if (!cfunc) return false; + + ctree_collector_t collector(items, &*cfunc, func_addr); + collector.apply_to(&cfunc->body, nullptr); + collector.resolve_child_ids(); + + return true; +} + +// Collect ctree for all functions +inline void collect_all_ctree(std::vector& items) { + items.clear(); + + if (!hexrays_available()) return; + + size_t func_qty = get_func_qty(); + for (size_t i = 0; i < func_qty; i++) { + func_t* f = getn_func(i); + if (!f) continue; + + std::vector func_items; + if (collect_ctree(func_items, f->start_ea)) { + items.insert(items.end(), func_items.begin(), func_items.end()); + } + } +} + +// Call args collector visitor +struct call_args_collector_t : public ctree_parentee_t { + std::vector& args; + std::map item_ids; + cfunc_t* cfunc; + ea_t func_addr; + int next_id; + + call_args_collector_t(std::vector& args_, cfunc_t* cfunc_, ea_t func_addr_) + : ctree_parentee_t(false), args(args_), cfunc(cfunc_), func_addr(func_addr_), next_id(0) {} + + int idaapi visit_insn(cinsn_t* insn) override { + item_ids[insn] = next_id++; + return 0; + } + + int idaapi visit_expr(cexpr_t* expr) override { + int my_id = next_id++; + item_ids[expr] = my_id; + + if (expr->op == cot_call && expr->a) { + carglist_t& arglist = *expr->a; + for (size_t i = 0; i < arglist.size(); i++) { + const carg_t& arg = arglist[i]; + + CallArgInfo ai; + ai.func_addr = func_addr; + ai.call_item_id = my_id; + ai.arg_idx = i; + ai.arg_op = get_full_ctype_name(arg.op); + + auto it = item_ids.find((citem_t*)&arg); + if (it != item_ids.end()) { + ai.arg_item_id = it->second; + } else { + ai.arg_item_id = next_id++; + item_ids[(citem_t*)&arg] = ai.arg_item_id; + } + + switch (arg.op) { + case cot_var: + ai.arg_var_idx = arg.v.idx; + if (cfunc && ai.arg_var_idx >= 0 && ai.arg_var_idx < cfunc->get_lvars()->size()) { + const lvar_t& lv = (*cfunc->get_lvars())[ai.arg_var_idx]; + ai.arg_var_name = lv.name.c_str(); + ai.arg_var_is_stk = lv.is_stk_var(); + ai.arg_var_is_arg = lv.is_arg_var(); + } + break; + case cot_obj: + ai.arg_obj_ea = arg.obj_ea; + { + qstring name; + if (get_name(&name, arg.obj_ea) > 0) { + ai.arg_obj_name = name.c_str(); + } + } + break; + case cot_num: + ai.arg_num_value = arg.numval(); + break; + case cot_str: + if (arg.string) ai.arg_str_value = arg.string; + break; + default: + break; + } + + args.push_back(ai); + } + } + + return 0; + } +}; + +// Collect call args for a single function +inline bool collect_call_args(std::vector& args, ea_t func_addr) { + args.clear(); + + if (!hexrays_available()) return false; + + func_t* f = get_func(func_addr); + if (!f) return false; + + hexrays_failure_t hf; + cfuncptr_t cfunc = decompile(f, &hf); + if (!cfunc) return false; + + call_args_collector_t collector(args, &*cfunc, func_addr); + collector.apply_to(&cfunc->body, nullptr); + + return true; +} + +// Collect call args for all functions +inline void collect_all_call_args(std::vector& args) { + args.clear(); + + if (!hexrays_available()) return; + + size_t func_qty = get_func_qty(); + for (size_t i = 0; i < func_qty; i++) { + func_t* f = getn_func(i); + if (!f) continue; + + std::vector func_args; + if (collect_call_args(func_args, f->start_ea)) { + args.insert(args.end(), func_args.begin(), func_args.end()); + } + } +} + +// ============================================================================ +// Caches for full scans +// ============================================================================ + +struct LvarsCache { + static std::vector& get() { + static std::vector cache; + return cache; + } + static void rebuild() { collect_all_lvars(get()); } +}; + +// Note: ctree and ctree_call_args use streaming generator tables (GeneratorTableDef) +// No static caches needed - iteration is lazy and owned by the query cursor + +// ============================================================================ +// Iterators for constraint pushdown +// ============================================================================ + +// Pseudocode iterator for single function +class PseudocodeInFuncIterator : public xsql::RowIterator { + std::vector lines_; + size_t idx_ = 0; + bool started_ = false; + +public: + explicit PseudocodeInFuncIterator(ea_t func_addr) { + collect_pseudocode(lines_, func_addr); + } + + bool next() override { + if (!started_) { + started_ = true; + if (lines_.empty()) return false; + idx_ = 0; + return true; + } + if (idx_ + 1 < lines_.size()) { ++idx_; return true; } + idx_ = lines_.size(); + return false; + } + + bool eof() const override { return started_ && idx_ >= lines_.size(); } + + void column(sqlite3_context* ctx, int col) override { + if (idx_ >= lines_.size()) { sqlite3_result_null(ctx); return; } + const auto& line = lines_[idx_]; + switch (col) { + case 0: sqlite3_result_int64(ctx, line.func_addr); break; + case 1: sqlite3_result_int(ctx, line.line_num); break; + case 2: sqlite3_result_text(ctx, line.text.c_str(), -1, SQLITE_TRANSIENT); break; + case 3: + sqlite3_result_int64(ctx, line.ea != BADADDR ? line.ea : 0); + break; + case 4: + if (!line.comment.empty()) + sqlite3_result_text(ctx, line.comment.c_str(), -1, SQLITE_TRANSIENT); + else + sqlite3_result_null(ctx); + break; + case 5: + sqlite3_result_text(ctx, itp_to_name(line.comment_placement), -1, SQLITE_STATIC); + break; + } + } + + int64_t rowid() const override { return static_cast(idx_); } +}; + +// Lvars iterator for single function +class LvarsInFuncIterator : public xsql::RowIterator { + std::vector vars_; + size_t idx_ = 0; + bool started_ = false; + +public: + explicit LvarsInFuncIterator(ea_t func_addr) { + collect_lvars(vars_, func_addr); + } + + bool next() override { + if (!started_) { + started_ = true; + if (vars_.empty()) return false; + idx_ = 0; + return true; + } + if (idx_ + 1 < vars_.size()) { ++idx_; return true; } + idx_ = vars_.size(); + return false; + } + + bool eof() const override { return started_ && idx_ >= vars_.size(); } + + void column(sqlite3_context* ctx, int col) override { + if (idx_ >= vars_.size()) { sqlite3_result_null(ctx); return; } + const auto& v = vars_[idx_]; + switch (col) { + case 0: sqlite3_result_int64(ctx, v.func_addr); break; + case 1: sqlite3_result_int(ctx, v.idx); break; + case 2: sqlite3_result_text(ctx, v.name.c_str(), -1, SQLITE_TRANSIENT); break; + case 3: sqlite3_result_text(ctx, v.type.c_str(), -1, SQLITE_TRANSIENT); break; + case 4: sqlite3_result_int(ctx, v.size); break; + case 5: sqlite3_result_int(ctx, v.is_arg ? 1 : 0); break; + case 6: sqlite3_result_int(ctx, v.is_result ? 1 : 0); break; + case 7: sqlite3_result_int(ctx, v.is_stk_var ? 1 : 0); break; + case 8: sqlite3_result_int(ctx, v.is_reg_var ? 1 : 0); break; + case 9: v.is_stk_var ? sqlite3_result_int64(ctx, v.stkoff) : sqlite3_result_null(ctx); break; + case 10: v.is_reg_var ? sqlite3_result_int(ctx, v.mreg) : sqlite3_result_null(ctx); break; + } + } + + int64_t rowid() const override { return static_cast(idx_); } +}; + +// Ctree iterator for single function +class CtreeInFuncIterator : public xsql::RowIterator { + std::vector items_; + size_t idx_ = 0; + bool started_ = false; + +public: + explicit CtreeInFuncIterator(ea_t func_addr) { + collect_ctree(items_, func_addr); + } + + bool next() override { + if (!started_) { + started_ = true; + if (items_.empty()) return false; + idx_ = 0; + return true; + } + if (idx_ + 1 < items_.size()) { ++idx_; return true; } + idx_ = items_.size(); + return false; + } + + bool eof() const override { return started_ && idx_ >= items_.size(); } + + void column(sqlite3_context* ctx, int col) override { + if (idx_ >= items_.size()) { sqlite3_result_null(ctx); return; } + const auto& item = items_[idx_]; + switch (col) { + case 0: sqlite3_result_int64(ctx, item.func_addr); break; + case 1: sqlite3_result_int(ctx, item.item_id); break; + case 2: sqlite3_result_int(ctx, item.is_expr ? 1 : 0); break; + case 3: sqlite3_result_int(ctx, item.op); break; + case 4: sqlite3_result_text(ctx, item.op_name.c_str(), -1, SQLITE_TRANSIENT); break; + case 5: item.ea != BADADDR ? sqlite3_result_int64(ctx, item.ea) : sqlite3_result_null(ctx); break; + case 6: item.parent_id >= 0 ? sqlite3_result_int(ctx, item.parent_id) : sqlite3_result_null(ctx); break; + case 7: sqlite3_result_int(ctx, item.depth); break; + case 8: item.x_id >= 0 ? sqlite3_result_int(ctx, item.x_id) : sqlite3_result_null(ctx); break; + case 9: item.y_id >= 0 ? sqlite3_result_int(ctx, item.y_id) : sqlite3_result_null(ctx); break; + case 10: item.z_id >= 0 ? sqlite3_result_int(ctx, item.z_id) : sqlite3_result_null(ctx); break; + case 11: item.cond_id >= 0 ? sqlite3_result_int(ctx, item.cond_id) : sqlite3_result_null(ctx); break; + case 12: item.then_id >= 0 ? sqlite3_result_int(ctx, item.then_id) : sqlite3_result_null(ctx); break; + case 13: item.else_id >= 0 ? sqlite3_result_int(ctx, item.else_id) : sqlite3_result_null(ctx); break; + case 14: item.body_id >= 0 ? sqlite3_result_int(ctx, item.body_id) : sqlite3_result_null(ctx); break; + case 15: item.init_id >= 0 ? sqlite3_result_int(ctx, item.init_id) : sqlite3_result_null(ctx); break; + case 16: item.step_id >= 0 ? sqlite3_result_int(ctx, item.step_id) : sqlite3_result_null(ctx); break; + case 17: item.var_idx >= 0 ? sqlite3_result_int(ctx, item.var_idx) : sqlite3_result_null(ctx); break; + case 18: item.obj_ea != BADADDR ? sqlite3_result_int64(ctx, item.obj_ea) : sqlite3_result_null(ctx); break; + case 19: item.op == cot_num ? sqlite3_result_int64(ctx, item.num_value) : sqlite3_result_null(ctx); break; + case 20: !item.str_value.empty() ? sqlite3_result_text(ctx, item.str_value.c_str(), -1, SQLITE_TRANSIENT) : sqlite3_result_null(ctx); break; + case 21: !item.helper_name.empty() ? sqlite3_result_text(ctx, item.helper_name.c_str(), -1, SQLITE_TRANSIENT) : sqlite3_result_null(ctx); break; + case 22: (item.op == cot_memref || item.op == cot_memptr) ? sqlite3_result_int(ctx, item.member_offset) : sqlite3_result_null(ctx); break; + case 23: !item.var_name.empty() ? sqlite3_result_text(ctx, item.var_name.c_str(), -1, SQLITE_TRANSIENT) : sqlite3_result_null(ctx); break; + case 24: item.op == cot_var ? sqlite3_result_int(ctx, item.var_is_stk ? 1 : 0) : sqlite3_result_null(ctx); break; + case 25: item.op == cot_var ? sqlite3_result_int(ctx, item.var_is_reg ? 1 : 0) : sqlite3_result_null(ctx); break; + case 26: item.op == cot_var ? sqlite3_result_int(ctx, item.var_is_arg ? 1 : 0) : sqlite3_result_null(ctx); break; + case 27: !item.obj_name.empty() ? sqlite3_result_text(ctx, item.obj_name.c_str(), -1, SQLITE_TRANSIENT) : sqlite3_result_null(ctx); break; + } + } + + int64_t rowid() const override { return static_cast(idx_); } +}; + +// Call args iterator for single function +class CallArgsInFuncIterator : public xsql::RowIterator { + std::vector args_; + size_t idx_ = 0; + bool started_ = false; + +public: + explicit CallArgsInFuncIterator(ea_t func_addr) { + collect_call_args(args_, func_addr); + } + + bool next() override { + if (!started_) { + started_ = true; + if (args_.empty()) return false; + idx_ = 0; + return true; + } + if (idx_ + 1 < args_.size()) { ++idx_; return true; } + idx_ = args_.size(); + return false; + } + + bool eof() const override { return started_ && idx_ >= args_.size(); } + + void column(sqlite3_context* ctx, int col) override { + if (idx_ >= args_.size()) { sqlite3_result_null(ctx); return; } + const auto& ai = args_[idx_]; + switch (col) { + case 0: sqlite3_result_int64(ctx, ai.func_addr); break; + case 1: sqlite3_result_int(ctx, ai.call_item_id); break; + case 2: sqlite3_result_int(ctx, ai.arg_idx); break; + case 3: ai.arg_item_id >= 0 ? sqlite3_result_int(ctx, ai.arg_item_id) : sqlite3_result_null(ctx); break; + case 4: sqlite3_result_text(ctx, ai.arg_op.c_str(), -1, SQLITE_TRANSIENT); break; + case 5: ai.arg_var_idx >= 0 ? sqlite3_result_int(ctx, ai.arg_var_idx) : sqlite3_result_null(ctx); break; + case 6: !ai.arg_var_name.empty() ? sqlite3_result_text(ctx, ai.arg_var_name.c_str(), -1, SQLITE_TRANSIENT) : sqlite3_result_null(ctx); break; + case 7: ai.arg_var_idx >= 0 ? sqlite3_result_int(ctx, ai.arg_var_is_stk ? 1 : 0) : sqlite3_result_null(ctx); break; + case 8: ai.arg_var_idx >= 0 ? sqlite3_result_int(ctx, ai.arg_var_is_arg ? 1 : 0) : sqlite3_result_null(ctx); break; + case 9: ai.arg_obj_ea != BADADDR ? sqlite3_result_int64(ctx, ai.arg_obj_ea) : sqlite3_result_null(ctx); break; + case 10: !ai.arg_obj_name.empty() ? sqlite3_result_text(ctx, ai.arg_obj_name.c_str(), -1, SQLITE_TRANSIENT) : sqlite3_result_null(ctx); break; + case 11: ai.arg_op == "cot_num" ? sqlite3_result_int64(ctx, ai.arg_num_value) : sqlite3_result_null(ctx); break; + case 12: !ai.arg_str_value.empty() ? sqlite3_result_text(ctx, ai.arg_str_value.c_str(), -1, SQLITE_TRANSIENT) : sqlite3_result_null(ctx); break; + } + } + + int64_t rowid() const override { return static_cast(idx_); } +}; + +// ============================================================================ +// Generators for full scans (lazy, one function at a time) +// ============================================================================ + +class CtreeGenerator : public xsql::Generator { + size_t func_idx_ = 0; + std::vector items_; + size_t idx_ = 0; + sqlite3_int64 rowid_ = -1; + bool started_ = false; + + bool load_next_func() { + if (!hexrays_available()) return false; + + size_t func_qty = get_func_qty(); + while (func_idx_ < func_qty) { + func_t* f = getn_func(func_idx_++); + if (!f) continue; + + if (collect_ctree(items_, f->start_ea) && !items_.empty()) { + idx_ = 0; + return true; + } + } + return false; + } + +public: + bool next() override { + if (!started_) { + started_ = true; + if (!load_next_func()) return false; + rowid_ = 0; + return true; + } + + if (idx_ + 1 < items_.size()) { + ++idx_; + ++rowid_; + return true; + } + + if (!load_next_func()) return false; + ++rowid_; + return true; + } + + const CtreeItem& current() const override { return items_[idx_]; } + + sqlite3_int64 rowid() const override { return rowid_; } +}; + +class CallArgsGenerator : public xsql::Generator { + size_t func_idx_ = 0; + std::vector args_; + size_t idx_ = 0; + sqlite3_int64 rowid_ = -1; + bool started_ = false; + + bool load_next_func() { + if (!hexrays_available()) return false; + + size_t func_qty = get_func_qty(); + while (func_idx_ < func_qty) { + func_t* f = getn_func(func_idx_++); + if (!f) continue; + + if (collect_call_args(args_, f->start_ea) && !args_.empty()) { + idx_ = 0; + return true; + } + } + return false; + } + +public: + bool next() override { + if (!started_) { + started_ = true; + if (!load_next_func()) return false; + rowid_ = 0; + return true; + } + + if (idx_ + 1 < args_.size()) { + ++idx_; + ++rowid_; + return true; + } + + if (!load_next_func()) return false; + ++rowid_; + return true; + } + + const CallArgInfo& current() const override { return args_[idx_]; } + + sqlite3_int64 rowid() const override { return rowid_; } +}; + +// ============================================================================ +// Table Definitions +// ============================================================================ + +// Helper: Set or delete a decompiler comment at an ea within a function +inline bool set_decompiler_comment(ea_t func_addr, ea_t target_ea, const char* comment, item_preciser_t itp = ITP_SEMI) { + if (!hexrays_available()) return false; + if (target_ea == BADADDR || target_ea == 0) return false; + + func_t* f = get_func(func_addr); + if (!f) return false; + + hexrays_failure_t hf; + cfuncptr_t cfunc = decompile(f, &hf); + if (!cfunc) return false; + + treeloc_t loc; + loc.ea = target_ea; + loc.itp = itp; + + // set_user_cmt with empty/nullptr deletes the comment + cfunc->set_user_cmt(loc, (comment && comment[0]) ? comment : nullptr); + + cfunc->save_user_cmts(); + invalidate_decompiler_cache(func_addr); + return true; +} + +inline CachedTableDef define_pseudocode() { + return cached_table("pseudocode") + .estimate_rows([]() -> size_t { return get_func_qty() * 20; }) + .cache_builder([](std::vector& cache) { + collect_all_pseudocode(cache); + }) + .row_populator([](PseudocodeLine& row, int argc, sqlite3_value** argv) { + // argv[2]=func_addr, argv[3]=line_num, argv[4]=line, argv[5]=ea, argv[6]=comment, argv[7]=comment_placement + if (argc > 2) row.func_addr = static_cast(sqlite3_value_int64(argv[2])); + if (argc > 3) row.line_num = sqlite3_value_int(argv[3]); + if (argc > 5) row.ea = static_cast(sqlite3_value_int64(argv[5])); + if (argc > 7 && sqlite3_value_type(argv[7]) != SQLITE_NULL) { + const char* p = reinterpret_cast(sqlite3_value_text(argv[7])); + row.comment_placement = name_to_itp(p); + } + }) + .column_int64("func_addr", [](const PseudocodeLine& r) -> int64_t { return r.func_addr; }) + .column_int("line_num", [](const PseudocodeLine& r) -> int { return r.line_num; }) + .column_text("line", [](const PseudocodeLine& r) -> std::string { return r.text; }) + .column_int64("ea", [](const PseudocodeLine& r) -> int64_t { + return r.ea != BADADDR ? r.ea : 0; + }) + .column_text_rw("comment", + [](const PseudocodeLine& r) -> std::string { return r.comment; }, + [](PseudocodeLine& row, sqlite3_value* val) -> bool { + if (row.ea == BADADDR || row.ea == 0) return false; + const char* text = nullptr; + if (sqlite3_value_type(val) != SQLITE_NULL) { + text = reinterpret_cast(sqlite3_value_text(val)); + } + bool ok = set_decompiler_comment(row.func_addr, row.ea, text, row.comment_placement); + if (ok) { + row.comment = text ? text : ""; + } + return ok; + }) + .column_text_rw("comment_placement", + [](const PseudocodeLine& r) -> std::string { return itp_to_name(r.comment_placement); }, + [](PseudocodeLine& row, sqlite3_value* val) -> bool { + if (sqlite3_value_type(val) == SQLITE_NULL) return false; + const char* name = reinterpret_cast(sqlite3_value_text(val)); + row.comment_placement = name_to_itp(name); + return true; // just sets the field, actual comment write happens in comment setter + }) + .filter_eq("func_addr", [](int64_t func_addr) -> std::unique_ptr { + return std::make_unique(static_cast(func_addr)); + }, 50.0) + .build(); +} + +// Helper: Rename lvar by func_addr and lvar index +inline bool rename_lvar_at(ea_t func_addr, int lvar_idx, const char* new_name) { + if (!hexrays_available()) + return false; + + func_t* f = get_func(func_addr); + if (!f) + return false; + + hexrays_failure_t hf; + cfuncptr_t cfunc = decompile(f, &hf); + if (!cfunc) + return false; + + lvars_t* lvars = cfunc->get_lvars(); + if (!lvars || lvar_idx < 0 || static_cast(lvar_idx) >= lvars->size()) + return false; + + lvar_t& lv = (*lvars)[lvar_idx]; + + // Use modify_user_lvar_info to persist the name change + lvar_saved_info_t lsi; + lsi.ll = lv; // Copy lvar_locator_t + lsi.name = new_name; + lsi.flags = 0; // No special flags needed + + bool ok = modify_user_lvar_info(func_addr, MLI_NAME, lsi); + if (ok) invalidate_decompiler_cache(func_addr); + return ok; +} + +// Helper: Set lvar type by func_addr and lvar index +inline bool set_lvar_type_at(ea_t func_addr, int lvar_idx, const char* type_str) { + if (!hexrays_available()) + return false; + + func_t* f = get_func(func_addr); + if (!f) + return false; + + hexrays_failure_t hf; + cfuncptr_t cfunc = decompile(f, &hf); + if (!cfunc) + return false; + + lvars_t* lvars = cfunc->get_lvars(); + if (!lvars || lvar_idx < 0 || static_cast(lvar_idx) >= lvars->size()) + return false; + + lvar_t& lv = (*lvars)[lvar_idx]; + + // Parse type string - try named type first, then parse as declaration + tinfo_t tif; + if (!tif.get_named_type(nullptr, type_str)) { + // Use parse_decl for C declaration parsing + qstring decl; + decl.sprnt("%s __x;", type_str); + qstring out_name; + if (!parse_decl(&tif, &out_name, nullptr, decl.c_str(), PT_SIL)) + return false; + } + + // Use modify_user_lvar_info to persist the type change + lvar_saved_info_t lsi; + lsi.ll = lv; // Copy lvar_locator_t + lsi.type = tif; + lsi.flags = 0; // No special flags needed + + bool ok = modify_user_lvar_info(func_addr, MLI_TYPE, lsi); + if (ok) invalidate_decompiler_cache(func_addr); + return ok; +} + +inline VTableDef define_ctree_lvars() { + return table("ctree_lvars") + .count([]() { LvarsCache::rebuild(); return LvarsCache::get().size(); }) + .column_int64("func_addr", [](size_t i) -> int64_t { + auto& c = LvarsCache::get(); return i < c.size() ? c[i].func_addr : 0; + }) + .column_int("idx", [](size_t i) -> int { + auto& c = LvarsCache::get(); return i < c.size() ? c[i].idx : 0; + }) + .column_text_rw("name", + // Getter + [](size_t i) -> std::string { + auto& c = LvarsCache::get(); + return i < c.size() ? c[i].name : ""; + }, + // Setter - rename lvar + [](size_t i, const char* new_name) -> bool { + auto& c = LvarsCache::get(); + if (i >= c.size()) return false; + ea_t func_addr = c[i].func_addr; + int idx = c[i].idx; + bool ok = rename_lvar_at(func_addr, idx, new_name); + if (ok) { + // Update cache entry + c[i].name = new_name; + } + return ok; + }) + .column_text_rw("type", + // Getter + [](size_t i) -> std::string { + auto& c = LvarsCache::get(); + return i < c.size() ? c[i].type : ""; + }, + // Setter - change lvar type + [](size_t i, const char* new_type) -> bool { + auto& c = LvarsCache::get(); + if (i >= c.size()) return false; + ea_t func_addr = c[i].func_addr; + int idx = c[i].idx; + bool ok = set_lvar_type_at(func_addr, idx, new_type); + if (ok) { + // Update cache entry + c[i].type = new_type; + } + return ok; + }) + .column_int("size", [](size_t i) -> int { + auto& c = LvarsCache::get(); return i < c.size() ? c[i].size : 0; + }) + .column_int("is_arg", [](size_t i) -> int { + auto& c = LvarsCache::get(); return i < c.size() ? (c[i].is_arg ? 1 : 0) : 0; + }) + .column_int("is_result", [](size_t i) -> int { + auto& c = LvarsCache::get(); return i < c.size() ? (c[i].is_result ? 1 : 0) : 0; + }) + .column_int("is_stk_var", [](size_t i) -> int { + auto& c = LvarsCache::get(); return i < c.size() ? (c[i].is_stk_var ? 1 : 0) : 0; + }) + .column_int("is_reg_var", [](size_t i) -> int { + auto& c = LvarsCache::get(); return i < c.size() ? (c[i].is_reg_var ? 1 : 0) : 0; + }) + .column_int64("stkoff", [](size_t i) -> int64_t { + auto& c = LvarsCache::get(); return i < c.size() ? c[i].stkoff : 0; + }) + .column_int("mreg", [](size_t i) -> int { + auto& c = LvarsCache::get(); return i < c.size() ? c[i].mreg : 0; + }) + .filter_eq("func_addr", [](int64_t func_addr) -> std::unique_ptr { + return std::make_unique(static_cast(func_addr)); + }, 10.0) + .build(); +} + +inline GeneratorTableDef define_ctree() { + return generator_table("ctree") + // Cheap estimate for query planning (doesn't decompile) + .estimate_rows([]() -> size_t { + // Heuristic: ~50 AST items per function + return get_func_qty() * 50; + }) + // Full scan generator (decompiles one function at a time) + .generator([]() -> std::unique_ptr> { + return std::make_unique(); + }) + .column_int64("func_addr", [](const CtreeItem& r) -> int64_t { return r.func_addr; }) + .column_int("item_id", [](const CtreeItem& r) -> int { return r.item_id; }) + .column_int("is_expr", [](const CtreeItem& r) -> int { return r.is_expr ? 1 : 0; }) + .column_int("op", [](const CtreeItem& r) -> int { return r.op; }) + .column_text("op_name", [](const CtreeItem& r) -> std::string { return r.op_name; }) + .column_int64("ea", [](const CtreeItem& r) -> int64_t { return r.ea != BADADDR ? r.ea : 0; }) + .column_int("parent_id", [](const CtreeItem& r) -> int { return r.parent_id; }) + .column_int("depth", [](const CtreeItem& r) -> int { return r.depth; }) + .column_int("x_id", [](const CtreeItem& r) -> int { return r.x_id; }) + .column_int("y_id", [](const CtreeItem& r) -> int { return r.y_id; }) + .column_int("z_id", [](const CtreeItem& r) -> int { return r.z_id; }) + .column_int("cond_id", [](const CtreeItem& r) -> int { return r.cond_id; }) + .column_int("then_id", [](const CtreeItem& r) -> int { return r.then_id; }) + .column_int("else_id", [](const CtreeItem& r) -> int { return r.else_id; }) + .column_int("body_id", [](const CtreeItem& r) -> int { return r.body_id; }) + .column_int("init_id", [](const CtreeItem& r) -> int { return r.init_id; }) + .column_int("step_id", [](const CtreeItem& r) -> int { return r.step_id; }) + .column_int("var_idx", [](const CtreeItem& r) -> int { return r.var_idx; }) + .column_int64("obj_ea", [](const CtreeItem& r) -> int64_t { return r.obj_ea != BADADDR ? r.obj_ea : 0; }) + .column_int64("num_value", [](const CtreeItem& r) -> int64_t { return r.num_value; }) + .column_text("str_value", [](const CtreeItem& r) -> std::string { return r.str_value; }) + .column_text("helper_name", [](const CtreeItem& r) -> std::string { return r.helper_name; }) + .column_int("member_offset", [](const CtreeItem& r) -> int { return r.member_offset; }) + .column_text("var_name", [](const CtreeItem& r) -> std::string { return r.var_name; }) + .column_int("var_is_stk", [](const CtreeItem& r) -> int { return r.var_is_stk ? 1 : 0; }) + .column_int("var_is_reg", [](const CtreeItem& r) -> int { return r.var_is_reg ? 1 : 0; }) + .column_int("var_is_arg", [](const CtreeItem& r) -> int { return r.var_is_arg ? 1 : 0; }) + .column_text("obj_name", [](const CtreeItem& r) -> std::string { return r.obj_name; }) + .filter_eq("func_addr", [](int64_t func_addr) -> std::unique_ptr { + return std::make_unique(static_cast(func_addr)); + }, 100.0, 100.0) + .build(); +} + +inline GeneratorTableDef define_ctree_call_args() { + return generator_table("ctree_call_args") + // Cheap estimate for query planning + .estimate_rows([]() -> size_t { + // Heuristic: ~20 call args per function + return get_func_qty() * 20; + }) + // Full scan generator (decompiles one function at a time) + .generator([]() -> std::unique_ptr> { + return std::make_unique(); + }) + .column_int64("func_addr", [](const CallArgInfo& r) -> int64_t { return r.func_addr; }) + .column_int("call_item_id", [](const CallArgInfo& r) -> int { return r.call_item_id; }) + .column_int("arg_idx", [](const CallArgInfo& r) -> int { return r.arg_idx; }) + .column_int("arg_item_id", [](const CallArgInfo& r) -> int { return r.arg_item_id; }) + .column_text("arg_op", [](const CallArgInfo& r) -> std::string { return r.arg_op; }) + .column_int("arg_var_idx", [](const CallArgInfo& r) -> int { return r.arg_var_idx; }) + .column_text("arg_var_name", [](const CallArgInfo& r) -> std::string { return r.arg_var_name; }) + .column_int("arg_var_is_stk", [](const CallArgInfo& r) -> int { return r.arg_var_is_stk ? 1 : 0; }) + .column_int("arg_var_is_arg", [](const CallArgInfo& r) -> int { return r.arg_var_is_arg ? 1 : 0; }) + .column_int64("arg_obj_ea", [](const CallArgInfo& r) -> int64_t { return r.arg_obj_ea != BADADDR ? r.arg_obj_ea : 0; }) + .column_text("arg_obj_name", [](const CallArgInfo& r) -> std::string { return r.arg_obj_name; }) + .column_int64("arg_num_value", [](const CallArgInfo& r) -> int64_t { return r.arg_num_value; }) + .column_text("arg_str_value", [](const CallArgInfo& r) -> std::string { return r.arg_str_value; }) + .filter_eq("func_addr", [](int64_t func_addr) -> std::unique_ptr { + return std::make_unique(static_cast(func_addr)); + }, 100.0, 100.0) + .build(); +} + +// ============================================================================ +// Views Registration +// ============================================================================ + +inline bool register_ctree_views(xsql::Database& db) { + + const char* v_calls = R"( + CREATE VIEW IF NOT EXISTS ctree_v_calls AS + SELECT + c.func_addr, c.item_id, c.ea, + x.op_name AS callee_op, + NULLIF(x.obj_ea, 0) AS callee_addr, + x.obj_name AS callee_name, + x.helper_name, + (SELECT COUNT(*) FROM ctree_call_args a + WHERE a.func_addr = c.func_addr AND a.call_item_id = c.item_id) AS arg_count + FROM ctree c + LEFT JOIN ctree x ON x.func_addr = c.func_addr AND x.item_id = c.x_id + WHERE c.op_name = 'cot_call' + )"; + db.exec(v_calls); + + const char* v_loops = R"( + CREATE VIEW IF NOT EXISTS ctree_v_loops AS + SELECT * FROM ctree + WHERE op_name IN ('cit_for', 'cit_while', 'cit_do') + )"; + db.exec(v_loops); + + const char* v_ifs = R"( + CREATE VIEW IF NOT EXISTS ctree_v_ifs AS + SELECT * FROM ctree WHERE op_name = 'cit_if' + )"; + db.exec(v_ifs); + + const char* v_signed = R"( + CREATE VIEW IF NOT EXISTS ctree_v_signed_ops AS + SELECT * FROM ctree WHERE op_name IN ( + 'cot_sge', 'cot_sle', 'cot_sgt', 'cot_slt', + 'cot_sshr', 'cot_sdiv', 'cot_smod', + 'cot_asgsshr', 'cot_asgsdiv', 'cot_asgsmod' + ) + )"; + db.exec(v_signed); + + const char* v_cmp = R"( + CREATE VIEW IF NOT EXISTS ctree_v_comparisons AS + SELECT + c.func_addr, c.item_id, c.ea, c.op_name, + lhs.op_name AS lhs_op, lhs.var_idx AS lhs_var_idx, lhs.num_value AS lhs_num, + rhs.op_name AS rhs_op, rhs.var_idx AS rhs_var_idx, rhs.num_value AS rhs_num + FROM ctree c + LEFT JOIN ctree lhs ON lhs.func_addr = c.func_addr AND lhs.item_id = c.x_id + LEFT JOIN ctree rhs ON rhs.func_addr = c.func_addr AND rhs.item_id = c.y_id + WHERE c.op_name IN ( + 'cot_eq', 'cot_ne', + 'cot_sge', 'cot_uge', 'cot_sle', 'cot_ule', + 'cot_sgt', 'cot_ugt', 'cot_slt', 'cot_ult' + ) + )"; + db.exec(v_cmp); + + const char* v_asg = R"( + CREATE VIEW IF NOT EXISTS ctree_v_assignments AS + SELECT + c.func_addr, c.item_id, c.ea, c.op_name, + lhs.op_name AS lhs_op, lhs.var_idx AS lhs_var_idx, + lhs.var_is_stk AS lhs_is_stk, lhs.obj_ea AS lhs_obj, + rhs.op_name AS rhs_op, rhs.var_idx AS rhs_var_idx, rhs.num_value AS rhs_num + FROM ctree c + LEFT JOIN ctree lhs ON lhs.func_addr = c.func_addr AND lhs.item_id = c.x_id + LEFT JOIN ctree rhs ON rhs.func_addr = c.func_addr AND rhs.item_id = c.y_id + WHERE c.op_name LIKE 'cot_asg%' + )"; + db.exec(v_asg); + + const char* v_deref = R"( + CREATE VIEW IF NOT EXISTS ctree_v_derefs AS + SELECT + c.func_addr, c.item_id, c.ea, + x.op_name AS ptr_op, x.var_idx AS ptr_var_idx, + x.var_is_stk AS ptr_is_stk, x.var_is_arg AS ptr_is_arg + FROM ctree c + LEFT JOIN ctree x ON x.func_addr = c.func_addr AND x.item_id = c.x_id + WHERE c.op_name IN ('cot_ptr', 'cot_memptr') + )"; + db.exec(v_deref); + + const char* v_calls_in_loops = R"( + CREATE VIEW IF NOT EXISTS ctree_v_calls_in_loops AS + WITH RECURSIVE loop_contents(func_addr, item_id, loop_id, loop_op, depth) AS ( + SELECT func_addr, item_id, item_id, op_name, 0 + FROM ctree + WHERE op_name IN ('cit_for', 'cit_while', 'cit_do') + UNION ALL + SELECT c.func_addr, c.item_id, lc.loop_id, lc.loop_op, lc.depth + 1 + FROM ctree c + JOIN loop_contents lc ON c.func_addr = lc.func_addr AND c.parent_id = lc.item_id + WHERE lc.depth < 50 + ) + SELECT DISTINCT + c.func_addr, c.item_id, c.ea, c.depth AS call_depth, + lc.loop_id, lc.loop_op, + NULLIF(x.obj_ea, 0) AS callee_addr, x.obj_name AS callee_name, x.helper_name + FROM loop_contents lc + JOIN ctree c ON c.func_addr = lc.func_addr AND c.item_id = lc.item_id + LEFT JOIN ctree x ON x.func_addr = c.func_addr AND x.item_id = c.x_id + WHERE c.op_name = 'cot_call' + )"; + db.exec(v_calls_in_loops); + + const char* v_calls_in_ifs = R"( + CREATE VIEW IF NOT EXISTS ctree_v_calls_in_ifs AS + WITH RECURSIVE if_contents(func_addr, item_id, if_id, branch, depth) AS ( + SELECT c.func_addr, c.item_id, p.item_id, 'then', 0 + FROM ctree c + JOIN ctree p ON c.func_addr = p.func_addr AND c.item_id = p.then_id + WHERE p.op_name = 'cit_if' + UNION ALL + SELECT c.func_addr, c.item_id, p.item_id, 'else', 0 + FROM ctree c + JOIN ctree p ON c.func_addr = p.func_addr AND c.item_id = p.else_id + WHERE p.op_name = 'cit_if' + UNION ALL + SELECT c.func_addr, c.item_id, ic.if_id, ic.branch, ic.depth + 1 + FROM ctree c + JOIN if_contents ic ON c.func_addr = ic.func_addr AND c.parent_id = ic.item_id + WHERE ic.depth < 50 + ) + SELECT DISTINCT + c.func_addr, c.item_id, c.ea, c.depth AS call_depth, + ic.if_id, ic.branch, + NULLIF(x.obj_ea, 0) AS callee_addr, x.obj_name AS callee_name, x.helper_name + FROM if_contents ic + JOIN ctree c ON c.func_addr = ic.func_addr AND c.item_id = ic.item_id + LEFT JOIN ctree x ON x.func_addr = c.func_addr AND x.item_id = c.x_id + WHERE c.op_name = 'cot_call' + )"; + db.exec(v_calls_in_ifs); + + const char* v_leaf_funcs = R"( + CREATE VIEW IF NOT EXISTS ctree_v_leaf_funcs AS + SELECT f.address, f.name + FROM funcs f + WHERE + -- Only consider functions that Hex-Rays can decompile (avoid false "leaf" results + -- when decompilation fails and the ctree tables return empty rows). + EXISTS ( + SELECT 1 FROM ctree t + WHERE t.func_addr = f.address + LIMIT 1 + ) + AND NOT EXISTS ( + SELECT 1 FROM ctree_v_calls c + WHERE c.func_addr = f.address AND c.callee_addr IS NOT NULL + LIMIT 1 + ) + )"; + db.exec(v_leaf_funcs); + + const char* v_call_chains = R"( + CREATE VIEW IF NOT EXISTS ctree_v_call_chains AS + WITH RECURSIVE call_chain(root_func, current_func, depth) AS ( + SELECT func_addr, callee_addr, 1 + FROM ctree_v_calls + WHERE callee_addr IS NOT NULL + UNION ALL + SELECT cc.root_func, c.callee_addr, cc.depth + 1 + FROM call_chain cc + JOIN ctree_v_calls c ON c.func_addr = cc.current_func + WHERE cc.depth < 10 AND c.callee_addr IS NOT NULL + ) + SELECT root_func, current_func, depth FROM call_chain + )"; + db.exec(v_call_chains); + + // Return statements with return value details + const char* v_returns = R"( + CREATE VIEW IF NOT EXISTS ctree_v_returns AS + SELECT + ret.func_addr, + ret.item_id, + ret.ea, + val.op_name AS return_op, + val.item_id AS return_item_id, + -- Numeric return (cot_num) + CASE WHEN val.op_name = 'cot_num' THEN val.num_value ELSE NULL END AS return_num, + -- String return (cot_str) + CASE WHEN val.op_name = 'cot_str' THEN val.str_value ELSE NULL END AS return_str, + -- Variable return (cot_var) + CASE WHEN val.op_name = 'cot_var' THEN val.var_name ELSE NULL END AS return_var, + CASE WHEN val.op_name = 'cot_var' THEN val.var_idx ELSE NULL END AS return_var_idx, + CASE WHEN val.op_name = 'cot_var' THEN val.var_is_arg ELSE NULL END AS returns_arg, + CASE WHEN val.op_name = 'cot_var' THEN val.var_is_stk ELSE NULL END AS returns_stk_var, + -- Object/symbol return (cot_obj) + CASE WHEN val.op_name = 'cot_obj' THEN val.obj_name ELSE NULL END AS return_obj, + CASE WHEN val.op_name = 'cot_obj' THEN val.obj_ea ELSE NULL END AS return_obj_ea, + -- Call result return (cot_call) - returning result of another call + CASE WHEN val.op_name = 'cot_call' THEN 1 ELSE 0 END AS returns_call_result + FROM ctree ret + LEFT JOIN ctree val ON val.func_addr = ret.func_addr AND val.item_id = ret.x_id + WHERE ret.op_name = 'cit_return' + )"; + db.exec(v_returns); + + return true; +} + +// ============================================================================ +// Registry +// ============================================================================ + +struct DecompilerRegistry { + // Cached table (shared cache, write support) + CachedTableDef pseudocode; + // Index-based table + VTableDef ctree_lvars; + // Generator tables (lazy full scans) + GeneratorTableDef ctree; + GeneratorTableDef ctree_call_args; + + DecompilerRegistry() + : pseudocode(define_pseudocode()) + , ctree_lvars(define_ctree_lvars()) + , ctree(define_ctree()) + , ctree_call_args(define_ctree_call_args()) + {} + + void register_all(xsql::Database& db) { + // Initialize Hex-Rays decompiler ONCE at startup + // If unavailable, skip registering decompiler tables entirely + if (!init_hexrays()) { + // Hex-Rays not available - don't register decompiler tables + return; + } + + // Cached table (query-scoped cache, freed when no cursors reference it) + db.register_cached_table("ida_pseudocode", &pseudocode); + db.create_table("pseudocode", "ida_pseudocode"); + + // Index-based table + db.register_table("ida_ctree_lvars", &ctree_lvars); + db.create_table("ctree_lvars", "ida_ctree_lvars"); + + // Generator tables (lazy full scans, stop work early with LIMIT) + db.register_generator_table("ida_ctree", &ctree); + db.create_table("ctree", "ida_ctree"); + + db.register_generator_table("ida_ctree_call_args", &ctree_call_args); + db.create_table("ctree_call_args", "ida_ctree_call_args"); + + register_ctree_views(db); + } +}; + +} // namespace decompiler +} // namespace idasql + +``` + +`src/lib/include/idasql/disassembly.hpp`: + +```hpp +/** + * disassembly.hpp - Disassembly-level SQL tables + * + * Provides instruction-level analysis via SQLite virtual tables. + * Parallels the decompiler.hpp ctree tables but at the disassembly level. + * + * Tables: + * disasm_calls - All call instructions with callee info + * disasm_loops - Detected loops via back-edge analysis + * + * Views: + * disasm_v_leaf_funcs - Functions with no outgoing calls + * disasm_v_call_chains - Recursive call chain paths up to depth 10 + * disasm_v_calls_in_loops - Calls that occur inside detected loops + * disasm_v_funcs_with_loops - Functions that contain loops + * + * All tables support constraint pushdown on func_addr for efficient queries. + */ + +#pragma once + +#include + +#include +#include + +#include + +// IDA SDK headers +#include +#include +#include // decode_insn, insn_t, is_call_insn +#include // is_call_insn +#include // get_first_fcref_from +#include // get_name +#include // qflow_chart_t for CFG analysis + +#include +#include + +namespace idasql { +namespace disassembly { + +// ============================================================================ +// Helper functions +// ============================================================================ + +inline std::string safe_name(ea_t ea) { + qstring name; + get_name(&name, ea); + return std::string(name.c_str()); +} + +// ============================================================================ +// DISASM_CALLS Table +// All call instructions across all functions +// ============================================================================ + +struct DisasmCallInfo { + ea_t func_addr; // Function containing this call + ea_t ea; // Address of call instruction + ea_t callee_addr; // Target of call (BADADDR if unknown) + std::string callee_name; +}; + +// ============================================================================ +// DisasmCallsInFuncIterator - Constraint pushdown for func_addr = X +// Iterates calls in a single function without building the full cache +// ============================================================================ + +class DisasmCallsInFuncIterator : public xsql::RowIterator { + ea_t func_addr_; + func_t* pfn_ = nullptr; + func_item_iterator_t fii_; + bool started_ = false; + bool valid_ = false; + + // Current call info + ea_t current_ea_ = BADADDR; + ea_t callee_addr_ = BADADDR; + std::string callee_name_; + + bool find_next_call() { + while (fii_.next_code()) { + ea_t ea = fii_.current(); + insn_t insn; + if (decode_insn(&insn, ea) > 0 && is_call_insn(insn)) { + current_ea_ = ea; + callee_addr_ = get_first_fcref_from(ea); + if (callee_addr_ != BADADDR) { + callee_name_ = safe_name(callee_addr_); + } else { + callee_name_.clear(); + } + return true; + } + } + return false; + } + +public: + explicit DisasmCallsInFuncIterator(ea_t func_addr) + : func_addr_(func_addr) + { + pfn_ = get_func(func_addr_); + } + + bool next() override { + if (!pfn_) return false; + + if (!started_) { + started_ = true; + // Initialize iterator and find first code item + if (!fii_.set(pfn_)) { + valid_ = false; + return false; + } + // Check if first item is a call + ea_t ea = fii_.current(); + insn_t insn; + if (decode_insn(&insn, ea) > 0 && is_call_insn(insn)) { + current_ea_ = ea; + callee_addr_ = get_first_fcref_from(ea); + if (callee_addr_ != BADADDR) { + callee_name_ = safe_name(callee_addr_); + } else { + callee_name_.clear(); + } + valid_ = true; + return true; + } + // First item wasn't a call, find next + valid_ = find_next_call(); + return valid_; + } + + valid_ = find_next_call(); + return valid_; + } + + bool eof() const override { + return started_ && !valid_; + } + + void column(sqlite3_context* ctx, int col) override { + switch (col) { + case 0: // func_addr + sqlite3_result_int64(ctx, static_cast(func_addr_)); + break; + case 1: // ea + sqlite3_result_int64(ctx, static_cast(current_ea_)); + break; + case 2: // callee_addr + if (callee_addr_ != BADADDR) { + sqlite3_result_int64(ctx, static_cast(callee_addr_)); + } else { + sqlite3_result_int64(ctx, 0); + } + break; + case 3: // callee_name + sqlite3_result_text(ctx, callee_name_.c_str(), + static_cast(callee_name_.size()), + SQLITE_TRANSIENT); + break; + } + } + + int64_t rowid() const override { + return static_cast(current_ea_); + } +}; + +class DisasmCallsGenerator : public xsql::Generator { + size_t func_idx_ = 0; + func_t* pfn_ = nullptr; + func_item_iterator_t fii_; + bool in_func_started_ = false; + DisasmCallInfo current_; + + bool start_next_func() { + size_t func_qty = get_func_qty(); + while (func_idx_ < func_qty) { + pfn_ = getn_func(func_idx_++); + if (!pfn_) continue; + + if (fii_.set(pfn_)) { + in_func_started_ = false; + return true; + } + } + pfn_ = nullptr; + return false; + } + + bool find_next_call_in_current_func() { + if (!pfn_) return false; + + while (true) { + ea_t ea = BADADDR; + if (!in_func_started_) { + in_func_started_ = true; + ea = fii_.current(); + } else { + if (!fii_.next_code()) return false; + ea = fii_.current(); + } + + insn_t insn; + if (decode_insn(&insn, ea) > 0 && is_call_insn(insn)) { + current_.func_addr = pfn_->start_ea; + current_.ea = ea; + current_.callee_addr = get_first_fcref_from(ea); + if (current_.callee_addr != BADADDR) { + current_.callee_name = safe_name(current_.callee_addr); + } else { + current_.callee_name.clear(); + } + return true; + } + } + } + +public: + bool next() override { + while (true) { + if (!pfn_) { + if (!start_next_func()) return false; + } + + if (find_next_call_in_current_func()) return true; + pfn_ = nullptr; + } + } + + const DisasmCallInfo& current() const override { return current_; } + + sqlite3_int64 rowid() const override { return static_cast(current_.ea); } +}; + +inline GeneratorTableDef define_disasm_calls() { + return generator_table("disasm_calls") + .estimate_rows([]() -> size_t { + // Heuristic: a few calls per function + return get_func_qty() * 5; + }) + .generator([]() -> std::unique_ptr> { + return std::make_unique(); + }) + .column_int64("func_addr", [](const DisasmCallInfo& r) -> int64_t { return r.func_addr; }) + .column_int64("ea", [](const DisasmCallInfo& r) -> int64_t { return r.ea; }) + .column_int64("callee_addr", [](const DisasmCallInfo& r) -> int64_t { + return r.callee_addr != BADADDR ? static_cast(r.callee_addr) : 0; + }) + .column_text("callee_name", [](const DisasmCallInfo& r) -> std::string { return r.callee_name; }) + // Constraint pushdown: func_addr = X bypasses full scan + .filter_eq("func_addr", [](int64_t func_addr) -> std::unique_ptr { + return std::make_unique(static_cast(func_addr)); + }, 10.0) // Low cost - only iterates one function + .build(); +} + +// ============================================================================ +// DISASM_LOOPS Table +// Detected loops via back-edge analysis using qflow_chart_t +// ============================================================================ + +struct LoopInfo { + ea_t func_addr; + int loop_id; // Unique ID (header block index) + ea_t header_ea; // Loop header start address + ea_t header_end_ea; // Loop header end address + ea_t back_edge_block_ea; // Block containing the back-edge jump + ea_t back_edge_block_end; // End of back-edge block +}; + +inline void collect_loops_for_func(std::vector& loops, func_t* pfn) { + if (!pfn) return; + + qflow_chart_t fc; + fc.create("", pfn, pfn->start_ea, pfn->end_ea, FC_NOEXT); + + for (int i = 0; i < fc.size(); i++) { + const qbasic_block_t& block = fc.blocks[i]; + + // Check each successor for back-edges + for (int j = 0; j < fc.nsucc(i); j++) { + int succ_idx = fc.succ(i, j); + if (succ_idx < 0 || succ_idx >= fc.size()) continue; + + const qbasic_block_t& succ = fc.blocks[succ_idx]; + + // Back-edge: successor starts at or before current block + // This indicates a loop where succ is the header + if (succ.start_ea <= block.start_ea) { + LoopInfo li; + li.func_addr = pfn->start_ea; + li.loop_id = succ_idx; // Use header block index as loop ID + li.header_ea = succ.start_ea; + li.header_end_ea = succ.end_ea; + li.back_edge_block_ea = block.start_ea; + li.back_edge_block_end = block.end_ea; + loops.push_back(li); + } + } + } +} + +// Iterator for loops in a single function (constraint pushdown) +class LoopsInFuncIterator : public xsql::RowIterator { + std::vector loops_; + size_t idx_ = 0; + bool started_ = false; + +public: + explicit LoopsInFuncIterator(ea_t func_addr) { + func_t* pfn = get_func(func_addr); + if (pfn) { + collect_loops_for_func(loops_, pfn); + } + } + + bool next() override { + if (!started_) { + started_ = true; + if (loops_.empty()) return false; + idx_ = 0; + return true; + } + if (idx_ + 1 < loops_.size()) { ++idx_; return true; } + idx_ = loops_.size(); + return false; + } + + bool eof() const override { return started_ && idx_ >= loops_.size(); } + + void column(sqlite3_context* ctx, int col) override { + if (idx_ >= loops_.size()) { sqlite3_result_null(ctx); return; } + const auto& li = loops_[idx_]; + switch (col) { + case 0: sqlite3_result_int64(ctx, static_cast(li.func_addr)); break; + case 1: sqlite3_result_int(ctx, li.loop_id); break; + case 2: sqlite3_result_int64(ctx, static_cast(li.header_ea)); break; + case 3: sqlite3_result_int64(ctx, static_cast(li.header_end_ea)); break; + case 4: sqlite3_result_int64(ctx, static_cast(li.back_edge_block_ea)); break; + case 5: sqlite3_result_int64(ctx, static_cast(li.back_edge_block_end)); break; + } + } + + int64_t rowid() const override { return static_cast(idx_); } +}; + +class DisasmLoopsGenerator : public xsql::Generator { + size_t func_idx_ = 0; + std::vector loops_; + size_t idx_ = 0; + sqlite3_int64 rowid_ = -1; + bool started_ = false; + + bool load_next_func() { + size_t func_qty = get_func_qty(); + while (func_idx_ < func_qty) { + func_t* pfn = getn_func(func_idx_++); + if (!pfn) continue; + + loops_.clear(); + collect_loops_for_func(loops_, pfn); + if (!loops_.empty()) { + idx_ = 0; + return true; + } + } + return false; + } + +public: + bool next() override { + if (!started_) { + started_ = true; + if (!load_next_func()) return false; + rowid_ = 0; + return true; + } + + if (idx_ + 1 < loops_.size()) { + ++idx_; + ++rowid_; + return true; + } + + if (!load_next_func()) return false; + ++rowid_; + return true; + } + + const LoopInfo& current() const override { return loops_[idx_]; } + + sqlite3_int64 rowid() const override { return rowid_; } +}; + +inline GeneratorTableDef define_disasm_loops() { + return generator_table("disasm_loops") + .estimate_rows([]() -> size_t { + // Heuristic: very few loops per function + return get_func_qty() * 2; + }) + .generator([]() -> std::unique_ptr> { + return std::make_unique(); + }) + .column_int64("func_addr", [](const LoopInfo& r) -> int64_t { return r.func_addr; }) + .column_int("loop_id", [](const LoopInfo& r) -> int { return r.loop_id; }) + .column_int64("header_ea", [](const LoopInfo& r) -> int64_t { return r.header_ea; }) + .column_int64("header_end_ea", [](const LoopInfo& r) -> int64_t { return r.header_end_ea; }) + .column_int64("back_edge_block_ea", [](const LoopInfo& r) -> int64_t { return r.back_edge_block_ea; }) + .column_int64("back_edge_block_end", [](const LoopInfo& r) -> int64_t { return r.back_edge_block_end; }) + .filter_eq("func_addr", [](int64_t func_addr) -> std::unique_ptr { + return std::make_unique(static_cast(func_addr)); + }, 5.0) + .build(); +} + +// ============================================================================ +// View Registration +// ============================================================================ + +inline bool register_disasm_views(xsql::Database& db) { + + // disasm_v_leaf_funcs - Functions with no outgoing calls (terminal/leaf functions) + // Uses disasm_calls to detect calls at the disassembly level + const char* v_leaf_funcs = R"( + CREATE VIEW IF NOT EXISTS disasm_v_leaf_funcs AS + SELECT f.address, f.name + FROM funcs f + LEFT JOIN disasm_calls c ON c.func_addr = f.address + GROUP BY f.address + HAVING COUNT(c.callee_addr) = 0 + )"; + db.exec(v_leaf_funcs); + + // disasm_v_call_chains - All call chain paths (root_func -> current_func at depth N) + // Enables queries like "find functions with call chains reaching depth 6" + const char* v_call_chains = R"( + CREATE VIEW IF NOT EXISTS disasm_v_call_chains AS + WITH RECURSIVE call_chain(root_func, current_func, depth) AS ( + -- Base: direct calls from each function + SELECT DISTINCT func_addr, callee_addr, 1 + FROM disasm_calls + WHERE callee_addr IS NOT NULL AND callee_addr != 0 + + UNION ALL + + -- Recursive: follow callees deeper + SELECT cc.root_func, c.callee_addr, cc.depth + 1 + FROM call_chain cc + JOIN disasm_calls c ON c.func_addr = cc.current_func + WHERE cc.depth < 10 + AND c.callee_addr IS NOT NULL + AND c.callee_addr != 0 + ) + SELECT DISTINCT + root_func, + current_func, + depth + FROM call_chain + )"; + db.exec(v_call_chains); + + // disasm_v_calls_in_loops - Calls that occur inside detected loops + // A call is considered "in a loop" if its address is between the loop header + // and the end of the back-edge block + const char* v_calls_in_loops = R"( + CREATE VIEW IF NOT EXISTS disasm_v_calls_in_loops AS + SELECT + c.func_addr, + c.ea, + c.callee_addr, + c.callee_name, + l.loop_id, + l.header_ea as loop_header, + l.back_edge_block_ea, + l.back_edge_block_end + FROM disasm_calls c + JOIN disasm_loops l ON l.func_addr = c.func_addr + WHERE c.ea >= l.header_ea AND c.ea < l.back_edge_block_end + )"; + db.exec(v_calls_in_loops); + + // disasm_v_funcs_with_loops - Functions that contain loops + const char* v_funcs_with_loops = R"( + CREATE VIEW IF NOT EXISTS disasm_v_funcs_with_loops AS + SELECT + f.address, + f.name, + COUNT(DISTINCT l.loop_id) as loop_count + FROM funcs f + JOIN disasm_loops l ON l.func_addr = f.address + GROUP BY f.address + )"; + db.exec(v_funcs_with_loops); + + return true; +} + +// ============================================================================ +// Registry for all disassembly tables +// ============================================================================ + +struct DisassemblyRegistry { + GeneratorTableDef disasm_calls; + GeneratorTableDef disasm_loops; + + DisassemblyRegistry() + : disasm_calls(define_disasm_calls()) + , disasm_loops(define_disasm_loops()) + {} + + void register_all(xsql::Database& db) { + db.register_generator_table("ida_disasm_calls", &disasm_calls); + db.create_table("disasm_calls", "ida_disasm_calls"); + + db.register_generator_table("ida_disasm_loops", &disasm_loops); + db.create_table("disasm_loops", "ida_disasm_loops"); + + // Register views on top + register_disasm_views(db); + } +}; + +} // namespace disassembly +} // namespace idasql + +``` + +`src/lib/include/idasql/entities.hpp`: + +```hpp +/** + * ida_entities.hpp - IDA entity definitions for SQLite virtual tables + * + * Defines all IDA entities as virtual tables using the clean ida_vtable.hpp framework. + * + * Tables: + * funcs - Functions + * segments - Memory segments + * names - Named locations (from nlist) + * entries - Entry points (exports) + * imports - Imported functions + * strings - String literals + * xrefs - Cross-references (universal) + */ + +#pragma once + +#include + +#include +#include +#include + +#include + +// IDA SDK headers +#include +#include +#include // Must come before moves.hpp +#include +#include +#include +#include +#include +#include // For tinfo_t, func_type_data_t +#include +#include +#include +#include +#include // For comments (get_cmt, set_cmt) +#include // For instructions (insn_t, decode_insn) +#include // For bookmarks + +#include // For invalidate_decompiler_cache + +namespace idasql { +namespace entities { + +// ============================================================================ +// Helper: Safe string extraction from IDA +// ============================================================================ + +inline std::string safe_func_name(ea_t ea) { + qstring name; + get_func_name(&name, ea); + return std::string(name.c_str()); +} + +inline std::string safe_segm_name(segment_t* seg) { + if (!seg) return ""; + qstring name; + get_segm_name(&name, seg); + return std::string(name.c_str()); +} + +inline std::string safe_segm_class(segment_t* seg) { + if (!seg) return ""; + qstring cls; + get_segm_class(&cls, seg); + return std::string(cls.c_str()); +} + +inline std::string safe_name(ea_t ea) { + qstring name; + get_name(&name, ea); + return std::string(name.c_str()); +} + +inline std::string safe_entry_name(size_t idx) { + uval_t ord = get_entry_ordinal(idx); + qstring name; + get_entry_name(&name, ord); + return std::string(name.c_str()); +} + +// ============================================================================ +// FUNCS Table (with UPDATE/DELETE support) +// ============================================================================ + +// Helper to get function type info +inline bool get_func_tinfo(ea_t ea, tinfo_t& tif) { + return get_tinfo(&tif, ea); +} + +// Helper to get calling convention name from callcnv_t +inline const char* get_cc_name(callcnv_t cc) { + switch (cc) { + case CM_CC_CDECL: return "cdecl"; + case CM_CC_STDCALL: return "stdcall"; + case CM_CC_FASTCALL: return "fastcall"; + case CM_CC_THISCALL: return "thiscall"; + case CM_CC_PASCAL: return "pascal"; + case CM_CC_SPECIAL: return "special"; + case CM_CC_SPECIALE: return "speciale"; + case CM_CC_SPECIALP: return "specialp"; + case CM_CC_ELLIPSIS: return "ellipsis"; + default: return "unknown"; + } +} + +inline VTableDef define_funcs() { + return table("funcs") + .count([]() { return get_func_qty(); }) + .column_int64("address", [](size_t i) -> int64_t { + func_t* f = getn_func(i); + return f ? static_cast(f->start_ea) : 0; + }) + .column_text_rw("name", + // Getter + [](size_t i) -> std::string { + func_t* f = getn_func(i); + return f ? safe_func_name(f->start_ea) : ""; + }, + // Setter - rename function + [](size_t i, const char* new_name) -> bool { + auto_wait(); + func_t* f = getn_func(i); + if (!f) return false; + bool ok = set_name(f->start_ea, new_name, SN_CHECK) != 0; + if (ok) decompiler::invalidate_decompiler_cache(f->start_ea); + auto_wait(); + return ok; + }) + .column_int64("size", [](size_t i) -> int64_t { + func_t* f = getn_func(i); + return f ? static_cast(f->size()) : 0; + }) + .column_int64("end_ea", [](size_t i) -> int64_t { + func_t* f = getn_func(i); + return f ? static_cast(f->end_ea) : 0; + }) + .column_int64_rw("flags", + [](size_t i) -> int64_t { + func_t* f = getn_func(i); + return f ? static_cast(f->flags) : 0; + }, + [](size_t i, int64_t new_flags) -> bool { + func_t* f = getn_func(i); + if (!f) return false; + f->flags = static_cast(new_flags); + bool ok = update_func(f); + if (ok) decompiler::invalidate_decompiler_cache(f->start_ea); + return ok; + }) + // Prototype columns - return type + .column_text("return_type", [](size_t i) -> std::string { + func_t* f = getn_func(i); + if (!f) return ""; + tinfo_t tif; + if (!get_func_tinfo(f->start_ea, tif) || !tif.is_func()) return ""; + func_type_data_t fi; + if (!tif.get_func_details(&fi)) return ""; + qstring ret_str; + fi.rettype.print(&ret_str); + return ret_str.c_str(); + }) + .column_int("return_is_ptr", [](size_t i) -> int { + func_t* f = getn_func(i); + if (!f) return 0; + tinfo_t tif; + if (!get_func_tinfo(f->start_ea, tif) || !tif.is_func()) return 0; + func_type_data_t fi; + if (!tif.get_func_details(&fi)) return 0; + return fi.rettype.is_ptr() ? 1 : 0; + }) + .column_int("return_is_int", [](size_t i) -> int { + func_t* f = getn_func(i); + if (!f) return 0; + tinfo_t tif; + if (!get_func_tinfo(f->start_ea, tif) || !tif.is_func()) return 0; + func_type_data_t fi; + if (!tif.get_func_details(&fi)) return 0; + return fi.rettype.is_int() ? 1 : 0; + }) + .column_int("return_is_integral", [](size_t i) -> int { + func_t* f = getn_func(i); + if (!f) return 0; + tinfo_t tif; + if (!get_func_tinfo(f->start_ea, tif) || !tif.is_func()) return 0; + func_type_data_t fi; + if (!tif.get_func_details(&fi)) return 0; + return fi.rettype.is_integral() ? 1 : 0; + }) + .column_int("return_is_void", [](size_t i) -> int { + func_t* f = getn_func(i); + if (!f) return 0; + tinfo_t tif; + if (!get_func_tinfo(f->start_ea, tif) || !tif.is_func()) return 0; + func_type_data_t fi; + if (!tif.get_func_details(&fi)) return 0; + return fi.rettype.is_void() ? 1 : 0; + }) + // Prototype columns - arguments + .column_int("arg_count", [](size_t i) -> int { + func_t* f = getn_func(i); + if (!f) return 0; + tinfo_t tif; + if (!get_func_tinfo(f->start_ea, tif) || !tif.is_func()) return 0; + func_type_data_t fi; + if (!tif.get_func_details(&fi)) return 0; + return static_cast(fi.size()); + }) + .column_text("calling_conv", [](size_t i) -> std::string { + func_t* f = getn_func(i); + if (!f) return ""; + tinfo_t tif; + if (!get_func_tinfo(f->start_ea, tif) || !tif.is_func()) return ""; + func_type_data_t fi; + if (!tif.get_func_details(&fi)) return ""; + return get_cc_name(fi.get_cc()); + }) + .deletable([](size_t i) -> bool { + auto_wait(); + func_t* f = getn_func(i); + if (!f) return false; + bool ok = del_func(f->start_ea); + auto_wait(); + return ok; + }) + .insertable([](int argc, sqlite3_value** argv) -> bool { + // address (col 0) is required + if (argc < 1 || sqlite3_value_type(argv[0]) == SQLITE_NULL) + return false; + + ea_t ea = static_cast(sqlite3_value_int64(argv[0])); + + // Check if function already exists at this address + if (get_func(ea) != nullptr) + return false; + + auto_wait(); + // end_ea from col 3 if provided, else BADADDR (IDA auto-detects) + ea_t end = BADADDR; + if (argc > 3 && sqlite3_value_type(argv[3]) != SQLITE_NULL) + end = static_cast(sqlite3_value_int64(argv[3])); + + bool ok = add_func(ea, end); + auto_wait(); + + if (!ok) return false; + + // Optional: set name (col 1) after creation + if (argc > 1 && sqlite3_value_type(argv[1]) != SQLITE_NULL) { + const char* name = reinterpret_cast( + sqlite3_value_text(argv[1])); + if (name && name[0]) + set_name(ea, name, SN_CHECK); + } + + return true; + }) + .build(); +} + +// ============================================================================ +// SEGMENTS Table +// ============================================================================ + +inline VTableDef define_segments() { + return table("segments") + .count([]() { return static_cast(get_segm_qty()); }) + .column_int64("start_ea", [](size_t i) -> int64_t { + segment_t* s = getnseg(static_cast(i)); + return s ? static_cast(s->start_ea) : 0; + }) + .column_int64("end_ea", [](size_t i) -> int64_t { + segment_t* s = getnseg(static_cast(i)); + return s ? static_cast(s->end_ea) : 0; + }) + .column_text_rw("name", + // Getter + [](size_t i) -> std::string { + segment_t* s = getnseg(static_cast(i)); + return safe_segm_name(s); + }, + // Setter - rename segment + [](size_t i, const char* new_name) -> bool { + auto_wait(); + segment_t* s = getnseg(static_cast(i)); + if (!s) return false; + bool ok = set_segm_name(s, new_name) != 0; + auto_wait(); + return ok; + }) + .column_text_rw("class", + // Getter + [](size_t i) -> std::string { + segment_t* s = getnseg(static_cast(i)); + return safe_segm_class(s); + }, + // Setter - change segment class + [](size_t i, const char* new_class) -> bool { + auto_wait(); + segment_t* s = getnseg(static_cast(i)); + if (!s) return false; + bool ok = set_segm_class(s, new_class) != 0; + auto_wait(); + return ok; + }) + .column_int_rw("perm", + // Getter + [](size_t i) -> int { + segment_t* s = getnseg(static_cast(i)); + return s ? s->perm : 0; + }, + // Setter - change segment permissions + [](size_t i, int new_perm) -> bool { + auto_wait(); + segment_t* s = getnseg(static_cast(i)); + if (!s) return false; + s->perm = static_cast(new_perm); + bool ok = s->update(); + auto_wait(); + return ok; + }) + .deletable([](size_t i) -> bool { + auto_wait(); + segment_t* s = getnseg(static_cast(i)); + if (!s) return false; + bool ok = del_segm(s->start_ea, SEGMOD_KILL) != 0; + auto_wait(); + return ok; + }) + .build(); +} + +// ============================================================================ +// NAMES Table (with UPDATE/DELETE support) +// ============================================================================ + +inline VTableDef define_names() { + return table("names") + .count([]() { return get_nlist_size(); }) + .column_int64("address", [](size_t i) -> int64_t { + return static_cast(get_nlist_ea(i)); + }) + .column_text_rw("name", + // Getter + [](size_t i) -> std::string { + const char* n = get_nlist_name(i); + return n ? std::string(n) : ""; + }, + // Setter - rename the address + [](size_t i, const char* new_name) -> bool { + auto_wait(); + ea_t ea = get_nlist_ea(i); + if (ea == BADADDR) return false; + bool ok = set_name(ea, new_name, SN_CHECK) != 0; + if (ok) decompiler::invalidate_decompiler_cache(ea); + auto_wait(); + return ok; + }) + .column_int("is_public", [](size_t i) -> int { + return is_public_name(get_nlist_ea(i)) ? 1 : 0; + }) + .column_int("is_weak", [](size_t i) -> int { + return is_weak_name(get_nlist_ea(i)) ? 1 : 0; + }) + // DELETE via set_name(ea, "") - removes the name + .deletable([](size_t i) -> bool { + auto_wait(); + ea_t ea = get_nlist_ea(i); + if (ea == BADADDR) return false; + bool ok = set_name(ea, "", SN_NOWARN) != 0; + auto_wait(); + return ok; + }) + .insertable([](int argc, sqlite3_value** argv) -> bool { + // address (col 0) and name (col 1) are both required + if (argc < 2 + || sqlite3_value_type(argv[0]) == SQLITE_NULL + || sqlite3_value_type(argv[1]) == SQLITE_NULL) + return false; + + ea_t ea = static_cast(sqlite3_value_int64(argv[0])); + const char* name = reinterpret_cast( + sqlite3_value_text(argv[1])); + if (!name || !name[0]) return false; + + auto_wait(); + bool ok = set_name(ea, name, SN_CHECK) != 0; + if (ok) decompiler::invalidate_decompiler_cache(ea); + auto_wait(); + return ok; + }) + .build(); +} + +// ============================================================================ +// ENTRIES Table (entry points / exports) +// ============================================================================ + +inline VTableDef define_entries() { + return table("entries") + .count([]() { return get_entry_qty(); }) + .column_int64("ordinal", [](size_t i) -> int64_t { + return static_cast(get_entry_ordinal(i)); + }) + .column_int64("address", [](size_t i) -> int64_t { + uval_t ord = get_entry_ordinal(i); + return static_cast(get_entry(ord)); + }) + .column_text("name", [](size_t i) -> std::string { + return safe_entry_name(i); + }) + .build(); +} + +// ============================================================================ +// COMMENTS Table (with UPDATE/DELETE support) +// ============================================================================ + +// Helper to iterate addresses with comments +struct CommentIterator { + static std::vector& get_addresses() { + static std::vector addrs; + return addrs; + } + + static void rebuild() { + auto& addrs = get_addresses(); + addrs.clear(); + + ea_t ea = inf_get_min_ea(); + ea_t max_ea = inf_get_max_ea(); + + while (ea < max_ea) { + qstring cmt, rpt; + bool has_cmt = get_cmt(&cmt, ea, false) > 0; + bool has_rpt = get_cmt(&rpt, ea, true) > 0; + + if (has_cmt || has_rpt) { + addrs.push_back(ea); + } + + ea = next_head(ea, max_ea); + if (ea == BADADDR) break; + } + } +}; + +inline VTableDef define_comments() { + return table("comments") + .count([]() { + CommentIterator::rebuild(); + return CommentIterator::get_addresses().size(); + }) + .column_int64("address", [](size_t i) -> int64_t { + auto& addrs = CommentIterator::get_addresses(); + return i < addrs.size() ? addrs[i] : 0; + }) + .column_text_rw("comment", + // Getter + [](size_t i) -> std::string { + auto& addrs = CommentIterator::get_addresses(); + if (i >= addrs.size()) return ""; + qstring cmt; + get_cmt(&cmt, addrs[i], false); + return cmt.c_str(); + }, + // Setter + [](size_t i, const char* new_cmt) -> bool { + auto_wait(); + auto& addrs = CommentIterator::get_addresses(); + if (i >= addrs.size()) return false; + bool ok = set_cmt(addrs[i], new_cmt, false); + auto_wait(); + return ok; + }) + .column_text_rw("rpt_comment", + // Getter + [](size_t i) -> std::string { + auto& addrs = CommentIterator::get_addresses(); + if (i >= addrs.size()) return ""; + qstring cmt; + get_cmt(&cmt, addrs[i], true); + return cmt.c_str(); + }, + // Setter + [](size_t i, const char* new_cmt) -> bool { + auto_wait(); + auto& addrs = CommentIterator::get_addresses(); + if (i >= addrs.size()) return false; + bool ok = set_cmt(addrs[i], new_cmt, true); + auto_wait(); + return ok; + }) + .deletable([](size_t i) -> bool { + auto_wait(); + // Delete both comments at this address + auto& addrs = CommentIterator::get_addresses(); + if (i >= addrs.size()) return false; + ea_t ea = addrs[i]; + set_cmt(ea, "", false); // Delete regular + set_cmt(ea, "", true); // Delete repeatable + auto_wait(); + return true; + }) + .insertable([](int argc, sqlite3_value** argv) -> bool { + // address (col 0) is required, plus at least one comment column + if (argc < 1 || sqlite3_value_type(argv[0]) == SQLITE_NULL) + return false; + + ea_t ea = static_cast(sqlite3_value_int64(argv[0])); + + bool did_something = false; + auto_wait(); + + // Regular comment (col 1) + if (argc > 1 && sqlite3_value_type(argv[1]) != SQLITE_NULL) { + const char* cmt = reinterpret_cast( + sqlite3_value_text(argv[1])); + if (cmt) { + set_cmt(ea, cmt, false); + did_something = true; + } + } + + // Repeatable comment (col 2) + if (argc > 2 && sqlite3_value_type(argv[2]) != SQLITE_NULL) { + const char* rpt = reinterpret_cast( + sqlite3_value_text(argv[2])); + if (rpt) { + set_cmt(ea, rpt, true); + did_something = true; + } + } + + auto_wait(); + return did_something; + }) + .build(); +} + +// ============================================================================ +// IMPORTS Table +// Collects all imports across all modules into a flat table +// ============================================================================ + +struct ImportInfo { + int module_idx; + ea_t ea; + std::string name; + uval_t ord; +}; + +inline std::string get_import_module_name_safe(int idx) { + qstring name; + get_import_module_name(&name, idx); + return std::string(name.c_str()); +} + +// ============================================================================ +// STRINGS Tables - By type (ASCII, Unicode) +// ============================================================================ + +// String type encoding (from ida_nalt): +// Bits 0-1: Width (0=1B/ASCII, 1=2B/UTF-16, 2=4B/UTF-32) +// Bits 2-7: Layout (0=TERMCHR, 1=PASCAL1, 2=PASCAL2, 3=PASCAL4) +// Bits 8-15: term1 (first termination character) +// Bits 16-23: term2 (second termination character) +// Bits 24-31: encoding index + +inline int get_string_width(int strtype) { + return strtype & 0x03; // 0=ASCII, 1=UTF-16, 2=UTF-32 +} + +inline const char* get_string_width_name(int strtype) { + int width = get_string_width(strtype); + switch (width) { + case 0: return "1-byte"; + case 1: return "2-byte"; + case 2: return "4-byte"; + default: return "unknown"; + } +} + +inline const char* get_string_type_name(int strtype) { + int width = get_string_width(strtype); + switch (width) { + case 0: return "ascii"; + case 1: return "utf16"; + case 2: return "utf32"; + default: return "unknown"; + } +} + +inline int get_string_layout(int strtype) { + return (strtype >> 2) & 0x3F; // Bits 2-7 +} + +inline const char* get_string_layout_name(int strtype) { + int layout = get_string_layout(strtype); + switch (layout) { + case 0: return "termchr"; // Null-terminated (C-style) + case 1: return "pascal1"; // 1-byte length prefix + case 2: return "pascal2"; // 2-byte length prefix + case 3: return "pascal4"; // 4-byte length prefix + default: return "unknown"; + } +} + +inline int get_string_encoding(int strtype) { + return (strtype >> 24) & 0xFF; // Bits 24-31: encoding index +} + +inline std::string get_string_content(const string_info_t& si) { + qstring content; + get_strlit_contents(&content, si.ea, si.length, si.type); + return std::string(content.c_str()); +} + +// ============================================================================ +// XREFS Table (universal cross-references) +// Collects all xrefs from all functions +// ============================================================================ + +struct XrefInfo { + ea_t from_ea; + ea_t to_ea; + uint8_t type; + bool is_code; +}; + +// ============================================================================ +// Xref Iterators for Constraint Pushdown +// ============================================================================ + +/** + * Iterator for xrefs TO a specific address. + * Used when query has: WHERE to_ea = X + * Uses xrefblk_t::first_to/next_to for O(refs_to_X) instead of O(all_xrefs) + */ +class XrefsToIterator : public xsql::RowIterator { + ea_t target_; + xrefblk_t xb_; + bool started_ = false; + bool valid_ = false; + +public: + explicit XrefsToIterator(ea_t target) : target_(target) {} + + bool next() override { + if (!started_) { + started_ = true; + valid_ = xb_.first_to(target_, XREF_ALL); + } else if (valid_) { + valid_ = xb_.next_to(); + } + return valid_; + } + + bool eof() const override { + return started_ && !valid_; + } + + void column(sqlite3_context* ctx, int col) override { + if (!valid_) { + sqlite3_result_null(ctx); + return; + } + switch (col) { + case 0: sqlite3_result_int64(ctx, static_cast(xb_.from)); break; + case 1: sqlite3_result_int64(ctx, static_cast(target_)); break; + case 2: sqlite3_result_int(ctx, xb_.type); break; + case 3: sqlite3_result_int(ctx, xb_.iscode ? 1 : 0); break; + default: sqlite3_result_null(ctx); break; + } + } + + int64_t rowid() const override { + return valid_ ? static_cast(xb_.from) : 0; + } +}; + +/** + * Iterator for xrefs FROM a specific address. + * Used when query has: WHERE from_ea = X + * Uses xrefblk_t::first_from/next_from for O(refs_from_X) instead of O(all_xrefs) + */ +class XrefsFromIterator : public xsql::RowIterator { + ea_t source_; + xrefblk_t xb_; + bool started_ = false; + bool valid_ = false; + +public: + explicit XrefsFromIterator(ea_t source) : source_(source) {} + + bool next() override { + if (!started_) { + started_ = true; + valid_ = xb_.first_from(source_, XREF_ALL); + } else if (valid_) { + valid_ = xb_.next_from(); + } + return valid_; + } + + bool eof() const override { + return started_ && !valid_; + } + + void column(sqlite3_context* ctx, int col) override { + if (!valid_) { + sqlite3_result_null(ctx); + return; + } + switch (col) { + case 0: sqlite3_result_int64(ctx, static_cast(source_)); break; + case 1: sqlite3_result_int64(ctx, static_cast(xb_.to)); break; + case 2: sqlite3_result_int(ctx, xb_.type); break; + case 3: sqlite3_result_int(ctx, xb_.iscode ? 1 : 0); break; + default: sqlite3_result_null(ctx); break; + } + } + + int64_t rowid() const override { + return valid_ ? static_cast(xb_.to) : 0; + } +}; + +/** + * Xrefs table with query-scoped cache. + * + * Features: + * - Cache lives in cursor (freed when query completes) + * - Lazy cache build (only if not using constraint pushdown) + * - Row count estimation (no cache rebuild in xBestIndex) + */ +inline CachedTableDef define_xrefs() { + return cached_table("xrefs") + // Estimate row count without building cache + .estimate_rows([]() -> size_t { + // Heuristic: ~10 xrefs per function on average + return get_func_qty() * 10; + }) + // Cache builder (called lazily, only if pushdown doesn't handle query) + .cache_builder([](std::vector& cache) { + size_t func_qty = get_func_qty(); + for (size_t i = 0; i < func_qty; i++) { + func_t* func = getn_func(i); + if (!func) continue; + + // Xrefs TO this function + xrefblk_t xb; + for (bool ok = xb.first_to(func->start_ea, XREF_ALL); ok; ok = xb.next_to()) { + XrefInfo xi; + xi.from_ea = xb.from; + xi.to_ea = func->start_ea; + xi.type = xb.type; + xi.is_code = xb.iscode; + cache.push_back(xi); + } + } + }) + // Column accessors take const XrefInfo& directly + .column_int64("from_ea", [](const XrefInfo& r) -> int64_t { + return static_cast(r.from_ea); + }) + .column_int64("to_ea", [](const XrefInfo& r) -> int64_t { + return static_cast(r.to_ea); + }) + .column_int("type", [](const XrefInfo& r) -> int { + return static_cast(r.type); + }) + .column_int("is_code", [](const XrefInfo& r) -> int { + return r.is_code ? 1 : 0; + }) + // Constraint pushdown filters (same iterators as V1) + .filter_eq("to_ea", [](int64_t target) -> std::unique_ptr { + return std::make_unique(static_cast(target)); + }, 10.0, 5.0) + .filter_eq("from_ea", [](int64_t source) -> std::unique_ptr { + return std::make_unique(static_cast(source)); + }, 10.0, 5.0) + .build(); +} + +// ============================================================================ +// BLOCKS Table (basic blocks) +// ============================================================================ + +struct BlockInfo { + ea_t func_ea; + ea_t start_ea; + ea_t end_ea; +}; + +/** + * Iterator for blocks in a specific function. + * Used when query has: WHERE func_ea = X + * Uses qflow_chart_t on single function for O(func_blocks) instead of O(all_blocks) + */ +class BlocksInFuncIterator : public xsql::RowIterator { + ea_t func_ea_; + qflow_chart_t fc_; + int idx_ = -1; + bool valid_ = false; + +public: + explicit BlocksInFuncIterator(ea_t func_ea) : func_ea_(func_ea) { + func_t* pfn = get_func(func_ea); + if (pfn) { + fc_.create("", pfn, pfn->start_ea, pfn->end_ea, FC_NOEXT); + } + } + + bool next() override { + ++idx_; + valid_ = (idx_ < fc_.size()); + return valid_; + } + + bool eof() const override { + return idx_ >= 0 && !valid_; + } + + void column(sqlite3_context* ctx, int col) override { + if (!valid_ || idx_ < 0 || idx_ >= fc_.size()) { + sqlite3_result_null(ctx); + return; + } + const qbasic_block_t& bb = fc_.blocks[idx_]; + switch (col) { + case 0: sqlite3_result_int64(ctx, static_cast(func_ea_)); break; + case 1: sqlite3_result_int64(ctx, static_cast(bb.start_ea)); break; + case 2: sqlite3_result_int64(ctx, static_cast(bb.end_ea)); break; + case 3: sqlite3_result_int64(ctx, static_cast(bb.end_ea - bb.start_ea)); break; + default: sqlite3_result_null(ctx); break; + } + } + + int64_t rowid() const override { + if (!valid_ || idx_ < 0 || idx_ >= fc_.size()) return 0; + return static_cast(fc_.blocks[idx_].start_ea); + } +}; + +inline CachedTableDef define_blocks() { + return cached_table("blocks") + .estimate_rows([]() -> size_t { + // Heuristic: ~10 blocks per function + return get_func_qty() * 10; + }) + .cache_builder([](std::vector& cache) { + size_t func_qty = get_func_qty(); + for (size_t i = 0; i < func_qty; i++) { + func_t* func = getn_func(i); + if (!func) continue; + + qflow_chart_t fc; + fc.create("", func, func->start_ea, func->end_ea, FC_NOEXT); + + for (int j = 0; j < fc.size(); j++) { + const qbasic_block_t& bb = fc.blocks[j]; + BlockInfo bi; + bi.func_ea = func->start_ea; + bi.start_ea = bb.start_ea; + bi.end_ea = bb.end_ea; + cache.push_back(bi); + } + } + }) + .column_int64("func_ea", [](const BlockInfo& r) -> int64_t { + return static_cast(r.func_ea); + }) + .column_int64("start_ea", [](const BlockInfo& r) -> int64_t { + return static_cast(r.start_ea); + }) + .column_int64("end_ea", [](const BlockInfo& r) -> int64_t { + return static_cast(r.end_ea); + }) + .column_int64("size", [](const BlockInfo& r) -> int64_t { + return static_cast(r.end_ea - r.start_ea); + }) + .filter_eq("func_ea", [](int64_t func_addr) -> std::unique_ptr { + return std::make_unique(static_cast(func_addr)); + }, 10.0, 10.0) + .build(); +} + +// ============================================================================ +// IMPORTS Table (query-scoped cache) +// ============================================================================ + +// Helper struct for import enumeration callback +struct ImportEnumContext { + std::vector* cache; + int module_idx; +}; + +inline CachedTableDef define_imports() { + return cached_table("imports") + .estimate_rows([]() -> size_t { + // Estimate: ~100 imports per module + return get_import_module_qty() * 100; + }) + .cache_builder([](std::vector& cache) { + uint mod_qty = get_import_module_qty(); + for (uint m = 0; m < mod_qty; m++) { + ImportEnumContext ctx; + ctx.cache = &cache; + ctx.module_idx = static_cast(m); + + enum_import_names(m, [](ea_t ea, const char* name, uval_t ord, void* param) -> int { + auto* ctx = static_cast(param); + ImportInfo info; + info.module_idx = ctx->module_idx; + info.ea = ea; + info.name = name ? name : ""; + info.ord = ord; + ctx->cache->push_back(info); + return 1; // continue enumeration + }, &ctx); + } + }) + .column_int64("address", [](const ImportInfo& r) -> int64_t { + return static_cast(r.ea); + }) + .column_text("name", [](const ImportInfo& r) -> std::string { + return r.name; + }) + .column_int64("ordinal", [](const ImportInfo& r) -> int64_t { + return static_cast(r.ord); + }) + .column_text("module", [](const ImportInfo& r) -> std::string { + return get_import_module_name_safe(r.module_idx); + }) + .column_int("module_idx", [](const ImportInfo& r) -> int { + return r.module_idx; + }) + .build(); +} + +// ============================================================================ +// STRINGS Table (query-scoped cache) +// ============================================================================ + +inline CachedTableDef define_strings() { + return cached_table("strings") + .estimate_rows([]() -> size_t { + return get_strlist_qty(); + }) + .cache_builder([](std::vector& cache) { + size_t n = get_strlist_qty(); + for (size_t i = 0; i < n; i++) { + string_info_t si; + if (get_strlist_item(&si, i)) { + cache.push_back(si); + } + } + }) + .column_int64("address", [](const string_info_t& r) -> int64_t { + return static_cast(r.ea); + }) + .column_int("length", [](const string_info_t& r) -> int { + return static_cast(r.length); + }) + .column_int("type", [](const string_info_t& r) -> int { + return static_cast(r.type); + }) + .column_text("type_name", [](const string_info_t& r) -> std::string { + return get_string_type_name(r.type); + }) + .column_int("width", [](const string_info_t& r) -> int { + return get_string_width(r.type); + }) + .column_text("width_name", [](const string_info_t& r) -> std::string { + return get_string_width_name(r.type); + }) + .column_int("layout", [](const string_info_t& r) -> int { + return get_string_layout(r.type); + }) + .column_text("layout_name", [](const string_info_t& r) -> std::string { + return get_string_layout_name(r.type); + }) + .column_int("encoding", [](const string_info_t& r) -> int { + return get_string_encoding(r.type); + }) + .column_text("content", [](const string_info_t& r) -> std::string { + return get_string_content(r); + }) + .build(); +} + +// ============================================================================ +// BOOKMARKS Table (with UPDATE/DELETE support) +// ============================================================================ + +struct BookmarkIterator { + struct Entry { + uint32_t index; + ea_t ea; + std::string desc; + }; + + static std::vector& get_entries() { + static std::vector entries; + return entries; + } + + static void rebuild() { + auto& entries = get_entries(); + entries.clear(); + + // Get bookmarks for IDA View (disassembly) + idaplace_t idaplace(inf_get_min_ea(), 0); + renderer_info_t rinfo; + lochist_entry_t loc(&idaplace, rinfo); + + uint32_t count = bookmarks_t::size(loc, nullptr); + + for (uint32_t idx = 0; idx < count; ++idx) { + idaplace_t place(0, 0); + lochist_entry_t entry(&place, rinfo); + qstring desc; + uint32_t index = idx; + + if (bookmarks_t::get(&entry, &desc, &index, nullptr)) { + Entry e; + e.index = index; + e.ea = ((idaplace_t*)entry.place())->ea; + e.desc = desc.c_str(); + entries.push_back(e); + } + } + } +}; + +inline VTableDef define_bookmarks() { + return table("bookmarks") + .count([]() { + BookmarkIterator::rebuild(); + return BookmarkIterator::get_entries().size(); + }) + .column_int("slot", [](size_t i) -> int { + auto& entries = BookmarkIterator::get_entries(); + return i < entries.size() ? entries[i].index : 0; + }) + .column_int64("address", [](size_t i) -> int64_t { + auto& entries = BookmarkIterator::get_entries(); + return i < entries.size() ? entries[i].ea : 0; + }) + .column_text_rw("description", + // Getter + [](size_t i) -> std::string { + auto& entries = BookmarkIterator::get_entries(); + return i < entries.size() ? entries[i].desc : ""; + }, + // Setter - update bookmark description + [](size_t i, const char* new_desc) -> bool { + auto_wait(); + auto& entries = BookmarkIterator::get_entries(); + if (i >= entries.size()) return false; + + idaplace_t place(entries[i].ea, 0); + renderer_info_t rinfo; + lochist_entry_t loc(&place, rinfo); + bool ok = bookmarks_t_set_desc(qstring(new_desc), loc, entries[i].index, nullptr); + auto_wait(); + return ok; + }) + .deletable([](size_t i) -> bool { + auto_wait(); + auto& entries = BookmarkIterator::get_entries(); + if (i >= entries.size()) return false; + + idaplace_t place(entries[i].ea, 0); + renderer_info_t rinfo; + lochist_entry_t loc(&place, rinfo); + bool ok = bookmarks_t::erase(loc, entries[i].index, nullptr); + auto_wait(); + return ok; + }) + .insertable([](int argc, sqlite3_value** argv) -> bool { + // address (col 1) is required + if (argc < 2 || sqlite3_value_type(argv[1]) == SQLITE_NULL) + return false; + + ea_t ea = static_cast(sqlite3_value_int64(argv[1])); + + const char* desc = ""; + if (argc > 2 && sqlite3_value_type(argv[2]) != SQLITE_NULL) { + desc = reinterpret_cast(sqlite3_value_text(argv[2])); + if (!desc) desc = ""; + } + + auto_wait(); + + idaplace_t place(ea, 0); + renderer_info_t rinfo; + lochist_entry_t loc(&place, rinfo); + + // slot (col 0): use provided slot or auto-assign next + uint32_t slot = bookmarks_t::size(loc, nullptr); + if (argc > 0 && sqlite3_value_type(argv[0]) != SQLITE_NULL) + slot = static_cast(sqlite3_value_int(argv[0])); + + uint32_t result = bookmarks_t::mark(loc, slot, nullptr, desc, nullptr); + auto_wait(); + + return result != BADADDR32; + }) + .build(); +} + +// ============================================================================ +// HEADS Table - All defined items in the database +// ============================================================================ + +struct HeadsIterator { + static std::vector& get_addresses() { + static std::vector addrs; + return addrs; + } + + static void rebuild() { + auto& addrs = get_addresses(); + addrs.clear(); + + ea_t ea = inf_get_min_ea(); + ea_t max_ea = inf_get_max_ea(); + + while (ea < max_ea && ea != BADADDR) { + addrs.push_back(ea); + ea = next_head(ea, max_ea); + } + } +}; + +inline const char* get_item_type_str(ea_t ea) { + flags64_t f = get_flags(ea); + if (is_code(f)) return "code"; + if (is_strlit(f)) return "string"; + if (is_struct(f)) return "struct"; + if (is_align(f)) return "align"; + if (is_data(f)) return "data"; + if (is_unknown(f)) return "unknown"; + return "other"; +} + +inline VTableDef define_heads() { + return table("heads") + .count([]() { + HeadsIterator::rebuild(); + return HeadsIterator::get_addresses().size(); + }) + .column_int64("address", [](size_t i) -> int64_t { + auto& addrs = HeadsIterator::get_addresses(); + return i < addrs.size() ? addrs[i] : 0; + }) + .column_int64("size", [](size_t i) -> int64_t { + auto& addrs = HeadsIterator::get_addresses(); + if (i >= addrs.size()) return 0; + return get_item_size(addrs[i]); + }) + .column_text("type", [](size_t i) -> std::string { + auto& addrs = HeadsIterator::get_addresses(); + if (i >= addrs.size()) return ""; + return get_item_type_str(addrs[i]); + }) + .column_int64("flags", [](size_t i) -> int64_t { + auto& addrs = HeadsIterator::get_addresses(); + if (i >= addrs.size()) return 0; + return get_flags(addrs[i]); + }) + .column_text("disasm", [](size_t i) -> std::string { + auto& addrs = HeadsIterator::get_addresses(); + if (i >= addrs.size()) return ""; + qstring line; + generate_disasm_line(&line, addrs[i], GENDSM_FORCE_CODE); + tag_remove(&line); + return line.c_str(); + }) + .build(); +} + +// ============================================================================ +// INSTRUCTIONS Table - With func_addr constraint pushdown +// ============================================================================ + +// Iterator for instructions within a single function (constraint pushdown) +class InstructionsInFuncIterator : public xsql::RowIterator { + ea_t func_addr_; + func_t* pfn_ = nullptr; + func_item_iterator_t fii_; + bool started_ = false; + bool valid_ = false; + ea_t current_ea_ = BADADDR; + +public: + explicit InstructionsInFuncIterator(ea_t func_addr) + : func_addr_(func_addr) + { + pfn_ = get_func(func_addr_); + } + + bool next() override { + if (!pfn_) return false; + + if (!started_) { + started_ = true; + valid_ = fii_.set(pfn_); + if (valid_) current_ea_ = fii_.current(); + } else if (valid_) { + valid_ = fii_.next_code(); + if (valid_) current_ea_ = fii_.current(); + } + return valid_; + } + + bool eof() const override { + return started_ && !valid_; + } + + void column(sqlite3_context* ctx, int col) override { + switch (col) { + case 0: // address + sqlite3_result_int64(ctx, current_ea_); + break; + case 1: { // itype + insn_t insn; + if (decode_insn(&insn, current_ea_) > 0) { + sqlite3_result_int(ctx, insn.itype); + } else { + sqlite3_result_int(ctx, 0); + } + break; + } + case 2: { // mnemonic + qstring mnem; + print_insn_mnem(&mnem, current_ea_); + sqlite3_result_text(ctx, mnem.c_str(), -1, SQLITE_TRANSIENT); + break; + } + case 3: // size + sqlite3_result_int(ctx, get_item_size(current_ea_)); + break; + case 4: // operand0 + case 5: // operand1 + case 6: { // operand2 + qstring op; + print_operand(&op, current_ea_, col - 4); + tag_remove(&op); + sqlite3_result_text(ctx, op.c_str(), -1, SQLITE_TRANSIENT); + break; + } + case 7: { // disasm + qstring line; + generate_disasm_line(&line, current_ea_, 0); + tag_remove(&line); + sqlite3_result_text(ctx, line.c_str(), -1, SQLITE_TRANSIENT); + break; + } + case 8: // func_addr + sqlite3_result_int64(ctx, func_addr_); + break; + } + } + + int64_t rowid() const override { + return static_cast(current_ea_); + } +}; + +// Cache for full instruction scan +struct InstructionsCache { + static std::vector& get() { + static std::vector cache; + return cache; + } + + static void rebuild() { + auto& cache = get(); + cache.clear(); + + ea_t ea = inf_get_min_ea(); + ea_t max_ea = inf_get_max_ea(); + + while (ea < max_ea && ea != BADADDR) { + flags64_t f = get_flags(ea); + if (is_code(f)) { + cache.push_back(ea); + } + ea = next_head(ea, max_ea); + } + } +}; + +inline VTableDef define_instructions() { + return table("instructions") + .count([]() { + InstructionsCache::rebuild(); + return InstructionsCache::get().size(); + }) + .column_int64("address", [](size_t i) -> int64_t { + auto& cache = InstructionsCache::get(); + return i < cache.size() ? cache[i] : 0; + }) + .column_int("itype", [](size_t i) -> int { + auto& cache = InstructionsCache::get(); + if (i >= cache.size()) return 0; + insn_t insn; + if (decode_insn(&insn, cache[i]) > 0) return insn.itype; + return 0; + }) + .column_text("mnemonic", [](size_t i) -> std::string { + auto& cache = InstructionsCache::get(); + if (i >= cache.size()) return ""; + qstring mnem; + print_insn_mnem(&mnem, cache[i]); + return mnem.c_str(); + }) + .column_int("size", [](size_t i) -> int { + auto& cache = InstructionsCache::get(); + if (i >= cache.size()) return 0; + return get_item_size(cache[i]); + }) + .column_text("operand0", [](size_t i) -> std::string { + auto& cache = InstructionsCache::get(); + if (i >= cache.size()) return ""; + qstring op; + print_operand(&op, cache[i], 0); + tag_remove(&op); + return op.c_str(); + }) + .column_text("operand1", [](size_t i) -> std::string { + auto& cache = InstructionsCache::get(); + if (i >= cache.size()) return ""; + qstring op; + print_operand(&op, cache[i], 1); + tag_remove(&op); + return op.c_str(); + }) + .column_text("operand2", [](size_t i) -> std::string { + auto& cache = InstructionsCache::get(); + if (i >= cache.size()) return ""; + qstring op; + print_operand(&op, cache[i], 2); + tag_remove(&op); + return op.c_str(); + }) + .column_text("disasm", [](size_t i) -> std::string { + auto& cache = InstructionsCache::get(); + if (i >= cache.size()) return ""; + qstring line; + generate_disasm_line(&line, cache[i], 0); + tag_remove(&line); + return line.c_str(); + }) + .column_int64("func_addr", [](size_t i) -> int64_t { + auto& cache = InstructionsCache::get(); + if (i >= cache.size()) return 0; + func_t* f = get_func(cache[i]); + return f ? f->start_ea : 0; + }) + .deletable([](size_t i) -> bool { + auto_wait(); + auto& cache = InstructionsCache::get(); + if (i >= cache.size()) return false; + ea_t ea = cache[i]; + asize_t sz = get_item_size(ea); + bool ok = del_items(ea, DELIT_SIMPLE, sz); + auto_wait(); + return ok; + }) + // Constraint pushdown: func_addr = X uses optimized iterator + .filter_eq("func_addr", [](int64_t func_addr) -> std::unique_ptr { + return std::make_unique(static_cast(func_addr)); + }, 100.0) + .build(); +} + +// ============================================================================ +// Registry: All tables in one place +// ============================================================================ + +struct TableRegistry { + // Index-based tables (use IDA's indexed access, no cache needed) + VTableDef funcs; + VTableDef segments; + VTableDef names; + VTableDef entries; + VTableDef comments; + VTableDef bookmarks; + VTableDef heads; + VTableDef instructions; + + // Cached tables (query-scoped cache - memory freed after query) + CachedTableDef xrefs; + CachedTableDef blocks; + CachedTableDef imports; + CachedTableDef strings; + + // Global pointer for cache invalidation from SQL functions + static inline TableRegistry* g_instance = nullptr; + + TableRegistry() + : funcs(define_funcs()) + , segments(define_segments()) + , names(define_names()) + , entries(define_entries()) + , comments(define_comments()) + , bookmarks(define_bookmarks()) + , heads(define_heads()) + , instructions(define_instructions()) + , xrefs(define_xrefs()) + , blocks(define_blocks()) + , imports(define_imports()) + , strings(define_strings()) + { + g_instance = this; + } + + ~TableRegistry() { + if (g_instance == this) g_instance = nullptr; + } + + // Invalidate the strings cache (call after rebuild_strings) + void invalidate_strings_cache() { + strings.invalidate_cache(); + } + + // Static method for SQL functions to invalidate strings cache + static void invalidate_strings_cache_global() { + if (g_instance) g_instance->invalidate_strings_cache(); + } + + void register_all(xsql::Database& db) { + // Index-based tables (use IDA's indexed access) + register_index_table(db, "funcs", &funcs); + register_index_table(db, "segments", &segments); + register_index_table(db, "names", &names); + register_index_table(db, "entries", &entries); + register_index_table(db, "comments", &comments); + register_index_table(db, "bookmarks", &bookmarks); + register_index_table(db, "heads", &heads); + register_index_table(db, "instructions", &instructions); + + // Cached tables (query-scoped cache) + register_cached_table(db, "xrefs", &xrefs); + register_cached_table(db, "blocks", &blocks); + register_cached_table(db, "imports", &imports); + register_cached_table(db, "strings", &strings); + + // Table-valued function for entity search + search::register_jump_entities(db); + + // Create convenience views for common queries + create_helper_views(db); + } + + void create_helper_views(xsql::Database& db) { + // callers view - who calls a function + db.exec(R"( + CREATE VIEW IF NOT EXISTS callers AS + SELECT + x.to_ea as func_addr, + x.from_ea as caller_addr, + f.name as caller_name, + f.address as caller_func_addr + FROM xrefs x + LEFT JOIN funcs f ON x.from_ea >= f.address + AND x.from_ea < f.end_ea + WHERE x.is_code = 1 + )"); + + // callees view - what does a function call + db.exec(R"( + CREATE VIEW IF NOT EXISTS callees AS + SELECT + f.address as func_addr, + f.name as func_name, + x.to_ea as callee_addr, + COALESCE(f2.name, n.name, printf('sub_%X', x.to_ea)) as callee_name + FROM funcs f + JOIN xrefs x ON x.from_ea >= f.address + AND x.from_ea < f.end_ea + LEFT JOIN funcs f2 ON x.to_ea = f2.address + LEFT JOIN names n ON x.to_ea = n.address + WHERE x.is_code = 1 + )"); + + // string_refs view - which functions reference which strings + db.exec(R"( + CREATE VIEW IF NOT EXISTS string_refs AS + SELECT + s.address as string_addr, + s.content as string_value, + s.length as string_length, + x.from_ea as ref_addr, + f.address as func_addr, + f.name as func_name + FROM strings s + JOIN xrefs x ON x.to_ea = s.address + LEFT JOIN funcs f ON x.from_ea >= f.address + AND x.from_ea < f.end_ea + )"); + } + +private: + void register_index_table(xsql::Database& db, const char* name, const VTableDef* def) { + std::string module_name = std::string("ida_") + name; + db.register_table(module_name.c_str(), def); + db.create_table(name, module_name.c_str()); + } + + template + void register_cached_table(xsql::Database& db, const char* name, const CachedTableDef* def) { + std::string module_name = std::string("ida_") + name; + db.register_cached_table(module_name.c_str(), def); + db.create_table(name, module_name.c_str()); + } +}; + +} // namespace entities +} // namespace idasql + +``` + +`src/lib/include/idasql/entities_dbg.hpp`: + +```hpp +/** + * entities_dbg.hpp - Debugger-related IDA entities as virtual tables + * + * Tables: + * breakpoints - Debugger breakpoints (software, hardware, symbolic, source) + * + * Breakpoints persist in the IDB, so they're queryable even without an active + * debugger session. Supports full CRUD operations. + */ + +#pragma once + +#include + +#include +#include + +#include + +// IDA SDK headers +#include +#include +#include + +namespace idasql { +namespace debugger { + +// ============================================================================ +// Helpers +// ============================================================================ + +inline const char* bpt_type_name(bpttype_t type) { + switch (type) { + case BPT_WRITE: return "hardware_write"; + case BPT_READ: return "hardware_read"; + case BPT_RDWR: return "hardware_rdwr"; + case BPT_SOFT: return "software"; + case BPT_EXEC: return "hardware_exec"; + default: return "unknown"; + } +} + +inline const char* bpt_loc_type_name(int loc_type) { + switch (loc_type) { + case BPLT_ABS: return "absolute"; + case BPLT_REL: return "relative"; + case BPLT_SYM: return "symbolic"; + case BPLT_SRC: return "source"; + default: return "unknown"; + } +} + +inline std::string safe_bpt_group(const bpt_t& bpt) { + qstring grp; + if (get_bpt_group(&grp, bpt.loc)) + return std::string(grp.c_str()); + return ""; +} + +inline std::string safe_bpt_loc_path(const bpt_t& bpt) { + const bpt_location_t& loc = bpt.loc; + if (loc.type() == BPLT_REL || loc.type() == BPLT_SRC) { + const char* p = loc.path(); + return p ? std::string(p) : ""; + } + return ""; +} + +inline std::string safe_bpt_loc_symbol(const bpt_t& bpt) { + const bpt_location_t& loc = bpt.loc; + if (loc.type() == BPLT_SYM) { + const char* s = loc.symbol(); + return s ? std::string(s) : ""; + } + return ""; +} + +// ============================================================================ +// BREAKPOINTS Table (full CRUD) +// ============================================================================ + +inline VTableDef define_breakpoints() { + return table("breakpoints") + .count([]() { return static_cast(get_bpt_qty()); }) + // Column 0: address (R) + .column_int64("address", [](size_t i) -> int64_t { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return 0; + return static_cast(bpt.ea); + }) + // Column 1: enabled (RW) + .column_int_rw("enabled", + [](size_t i) -> int { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return 0; + return bpt.enabled() ? 1 : 0; + }, + [](size_t i, int val) -> bool { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return false; + return enable_bpt(bpt.loc, val != 0); + }) + // Column 2: type (RW) + .column_int_rw("type", + [](size_t i) -> int { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return 0; + return static_cast(bpt.type); + }, + [](size_t i, int val) -> bool { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return false; + bpt.type = static_cast(val); + return update_bpt(&bpt); + }) + // Column 3: type_name (R) + .column_text("type_name", [](size_t i) -> std::string { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return ""; + return bpt_type_name(bpt.type); + }) + // Column 4: size (RW) + .column_int_rw("size", + [](size_t i) -> int { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return 0; + return bpt.size; + }, + [](size_t i, int val) -> bool { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return false; + bpt.size = val; + return update_bpt(&bpt); + }) + // Column 5: flags (RW) + .column_int64_rw("flags", + [](size_t i) -> int64_t { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return 0; + return static_cast(bpt.flags); + }, + [](size_t i, int64_t val) -> bool { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return false; + // Preserve BPT_ENABLED from current state so flags writes + // don't undo enable_bpt() calls during batch vtable updates + uint32 cur_enabled = bpt.flags & BPT_ENABLED; + bpt.flags = (static_cast(val) & ~BPT_ENABLED) | cur_enabled; + return update_bpt(&bpt); + }) + // Column 6: pass_count (RW) + .column_int_rw("pass_count", + [](size_t i) -> int { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return 0; + return bpt.pass_count; + }, + [](size_t i, int val) -> bool { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return false; + bpt.pass_count = val; + return update_bpt(&bpt); + }) + // Column 7: condition (RW) + .column_text_rw("condition", + [](size_t i) -> std::string { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return ""; + return std::string(bpt.cndbody.c_str()); + }, + [](size_t i, const char* val) -> bool { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return false; + bpt.cndbody = val; + return update_bpt(&bpt); + }) + // Column 8: loc_type (R) + .column_int("loc_type", [](size_t i) -> int { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return 0; + return bpt.loc.type(); + }) + // Column 9: loc_type_name (R) + .column_text("loc_type_name", [](size_t i) -> std::string { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return ""; + return bpt_loc_type_name(bpt.loc.type()); + }) + // Column 10: module (R) + .column_text("module", [](size_t i) -> std::string { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return ""; + return safe_bpt_loc_path(bpt); + }) + // Column 11: symbol (R) + .column_text("symbol", [](size_t i) -> std::string { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return ""; + return safe_bpt_loc_symbol(bpt); + }) + // Column 12: offset (R) + .column_int64("offset", [](size_t i) -> int64_t { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return 0; + int lt = bpt.loc.type(); + if (lt == BPLT_REL || lt == BPLT_SYM) + return static_cast(bpt.loc.offset()); + return 0; + }) + // Column 13: source_file (R) + .column_text("source_file", [](size_t i) -> std::string { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return ""; + if (bpt.loc.type() == BPLT_SRC) { + const char* p = bpt.loc.path(); + return p ? std::string(p) : ""; + } + return ""; + }) + // Column 14: source_line (R) + .column_int("source_line", [](size_t i) -> int { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return 0; + if (bpt.loc.type() == BPLT_SRC) + return bpt.loc.lineno(); + return 0; + }) + // Column 15: is_hardware (R) + .column_int("is_hardware", [](size_t i) -> int { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return 0; + return bpt.is_hwbpt() ? 1 : 0; + }) + // Column 16: is_active (R) + .column_int("is_active", [](size_t i) -> int { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return 0; + return bpt.is_active() ? 1 : 0; + }) + // Column 17: group (RW) + .column_text_rw("group", + [](size_t i) -> std::string { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return ""; + return safe_bpt_group(bpt); + }, + [](size_t i, const char* val) -> bool { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return false; + return set_bpt_group(bpt, val); + }) + // Column 18: bptid (R) + .column_int64("bptid", [](size_t i) -> int64_t { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return 0; + return static_cast(bpt.bptid); + }) + // DELETE support + .deletable([](size_t i) -> bool { + bpt_t bpt; + if (!getn_bpt(i, &bpt)) return false; + return del_bpt(bpt.loc); + }) + // INSERT support + // argv column order: address(0), enabled(1), type(2), type_name(3), + // size(4), flags(5), pass_count(6), condition(7), loc_type(8), + // loc_type_name(9), module(10), symbol(11), offset(12), + // source_file(13), source_line(14), is_hardware(15), is_active(16), + // group(17), bptid(18) + .insertable([](int argc, sqlite3_value** argv) -> bool { + // Determine location type from which columns are non-NULL + // argv[0] = address, argv[11] = symbol, argv[10] = module, + // argv[13] = source_file + + auto is_non_null = [&](int col) -> bool { + return col < argc && sqlite3_value_type(argv[col]) != SQLITE_NULL; + }; + + auto get_text = [&](int col) -> const char* { + if (col >= argc) return nullptr; + const char* t = reinterpret_cast(sqlite3_value_text(argv[col])); + return t; + }; + + auto get_int = [&](int col, int def = 0) -> int { + if (!is_non_null(col)) return def; + return sqlite3_value_int(argv[col]); + }; + + auto get_int64 = [&](int col, int64_t def = 0) -> int64_t { + if (!is_non_null(col)) return def; + return sqlite3_value_int64(argv[col]); + }; + + bool ok = false; + + if (is_non_null(11)) { + // Symbolic breakpoint: symbol column set + const char* sym = get_text(11); + if (!sym) return false; + int64_t off = get_int64(12, 0); + bpt_t bpt; + bpt.loc.set_sym_bpt(sym, static_cast(off)); + bpt.type = static_cast(get_int(2, BPT_SOFT)); + bpt.size = get_int(4, 0); + ok = add_bpt(bpt); + } else if (is_non_null(10)) { + // Relative breakpoint: module column set + const char* mod = get_text(10); + if (!mod) return false; + int64_t off = get_int64(12, 0); + bpt_t bpt; + bpt.loc.set_rel_bpt(mod, static_cast(off)); + bpt.type = static_cast(get_int(2, BPT_SOFT)); + bpt.size = get_int(4, 0); + ok = add_bpt(bpt); + } else if (is_non_null(13)) { + // Source breakpoint: source_file column set + const char* file = get_text(13); + if (!file) return false; + int line = get_int(14, 1); + bpt_t bpt; + bpt.loc.set_src_bpt(file, line); + bpt.type = static_cast(get_int(2, BPT_SOFT)); + bpt.size = get_int(4, 0); + ok = add_bpt(bpt); + } else if (is_non_null(0)) { + // Absolute breakpoint: address column set + ea_t ea = static_cast(get_int64(0)); + int sz = get_int(4, 0); + bpttype_t tp = static_cast(get_int(2, BPT_SOFT)); + ok = add_bpt(ea, sz, tp); + } else { + return false; // No location specified + } + + if (!ok) return false; + + // Apply optional properties after creation + // We need to find the breakpoint we just created + // Re-read to get the bpt_t for the newly added breakpoint + if (is_non_null(7)) { + // condition + const char* cond = get_text(7); + if (cond) { + // Find the breakpoint and update condition + bpt_t bpt; + int n = get_bpt_qty(); + for (int j = n - 1; j >= 0; --j) { + if (getn_bpt(j, &bpt)) { + // Match by address for absolute, or just use last added + if (is_non_null(0) && bpt.ea == static_cast(get_int64(0))) { + bpt.cndbody = cond; + update_bpt(&bpt); + break; + } else if (!is_non_null(0)) { + // For non-absolute, use the last breakpoint + bpt.cndbody = cond; + update_bpt(&bpt); + break; + } + } + } + } + } + + if (is_non_null(6)) { + // pass_count + bpt_t bpt; + int n = get_bpt_qty(); + for (int j = n - 1; j >= 0; --j) { + if (getn_bpt(j, &bpt)) { + if (is_non_null(0) && bpt.ea == static_cast(get_int64(0))) { + bpt.pass_count = get_int(6); + update_bpt(&bpt); + break; + } else if (!is_non_null(0)) { + bpt.pass_count = get_int(6); + update_bpt(&bpt); + break; + } + } + } + } + + if (is_non_null(5)) { + // flags + bpt_t bpt; + int n = get_bpt_qty(); + for (int j = n - 1; j >= 0; --j) { + if (getn_bpt(j, &bpt)) { + if (is_non_null(0) && bpt.ea == static_cast(get_int64(0))) { + bpt.flags = static_cast(get_int64(5)); + update_bpt(&bpt); + break; + } else if (!is_non_null(0)) { + bpt.flags = static_cast(get_int64(5)); + update_bpt(&bpt); + break; + } + } + } + } + + if (is_non_null(1)) { + // enabled - use enable_bpt API + bool enable = get_int(1) != 0; + bpt_t bpt; + int n = get_bpt_qty(); + for (int j = n - 1; j >= 0; --j) { + if (getn_bpt(j, &bpt)) { + if (is_non_null(0) && bpt.ea == static_cast(get_int64(0))) { + enable_bpt(bpt.loc, enable); + break; + } else if (!is_non_null(0)) { + enable_bpt(bpt.loc, enable); + break; + } + } + } + } + + if (is_non_null(17)) { + // group + const char* grp = get_text(17); + if (grp) { + bpt_t bpt; + int n = get_bpt_qty(); + for (int j = n - 1; j >= 0; --j) { + if (getn_bpt(j, &bpt)) { + if (is_non_null(0) && bpt.ea == static_cast(get_int64(0))) { + set_bpt_group(bpt, grp); + break; + } else if (!is_non_null(0)) { + set_bpt_group(bpt, grp); + break; + } + } + } + } + } + + return true; + }) + .build(); +} + +// ============================================================================ +// Debugger Registry +// ============================================================================ + +struct DebuggerRegistry { + VTableDef breakpoints; + + DebuggerRegistry() + : breakpoints(define_breakpoints()) + {} + + void register_all(xsql::Database& db) { + db.register_table("ida_breakpoints", &breakpoints); + db.create_table("breakpoints", "ida_breakpoints"); + } +}; + +} // namespace debugger +} // namespace idasql + +``` + +`src/lib/include/idasql/entities_ext.hpp`: + +```hpp +/** + * ida_entities_extended.hpp - Additional IDA entities as virtual tables + * + * This file provides additional virtual tables beyond the core entities. + * These tables cover: fixups, hidden ranges, problems, function chunks, + * signatures, local types, and more. + * + * Tables: + * fixups - Relocation/fixup records + * hidden_ranges - Collapsed/hidden regions + * problems - Analysis problems + * fchunks - Function chunks (tails) + * signatures - Applied FLIRT signatures + * local_types - Local type library entries + * mappings - Address mappings + */ + +#pragma once + +#include + +#include +#include + +#include + +// IDA SDK headers +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace idasql { +namespace extended { + +// ============================================================================ +// FIXUPS Table - Relocation records +// ============================================================================ + +struct FixupEntry { + ea_t ea; + fixup_data_t data; +}; + +inline std::vector& get_fixups_cache() { + static std::vector cache; + return cache; +} + +inline void rebuild_fixups_cache() { + auto& cache = get_fixups_cache(); + cache.clear(); + + for (ea_t ea = get_first_fixup_ea(); ea != BADADDR; ea = get_next_fixup_ea(ea)) { + FixupEntry entry; + entry.ea = ea; + if (get_fixup(&entry.data, ea)) { + cache.push_back(entry); + } + } +} + +inline VTableDef define_fixups() { + return table("fixups") + .count([]() { + rebuild_fixups_cache(); + return get_fixups_cache().size(); + }) + .column_int64("address", [](size_t i) -> int64_t { + auto& cache = get_fixups_cache(); + return i < cache.size() ? cache[i].ea : 0; + }) + .column_int64("target", [](size_t i) -> int64_t { + auto& cache = get_fixups_cache(); + return i < cache.size() ? cache[i].data.off : 0; + }) + .column_int("type", [](size_t i) -> int { + auto& cache = get_fixups_cache(); + return i < cache.size() ? cache[i].data.get_type() : 0; + }) + .column_int("flags", [](size_t i) -> int { + auto& cache = get_fixups_cache(); + return i < cache.size() ? cache[i].data.get_flags() : 0; + }) + .build(); +} + +// ============================================================================ +// HIDDEN_RANGES Table - Collapsed/hidden regions +// ============================================================================ + +inline VTableDef define_hidden_ranges() { + return table("hidden_ranges") + .count([]() { + return static_cast(get_hidden_range_qty()); + }) + .column_int64("start_ea", [](size_t i) -> int64_t { + hidden_range_t* hr = getn_hidden_range(static_cast(i)); + return hr ? hr->start_ea : 0; + }) + .column_int64("end_ea", [](size_t i) -> int64_t { + hidden_range_t* hr = getn_hidden_range(static_cast(i)); + return hr ? hr->end_ea : 0; + }) + .column_int64("size", [](size_t i) -> int64_t { + hidden_range_t* hr = getn_hidden_range(static_cast(i)); + return hr ? (hr->end_ea - hr->start_ea) : 0; + }) + .column_text("description", [](size_t i) -> std::string { + hidden_range_t* hr = getn_hidden_range(static_cast(i)); + return hr && hr->description ? hr->description : ""; + }) + .column_text("header", [](size_t i) -> std::string { + hidden_range_t* hr = getn_hidden_range(static_cast(i)); + return hr && hr->header ? hr->header : ""; + }) + .column_text("footer", [](size_t i) -> std::string { + hidden_range_t* hr = getn_hidden_range(static_cast(i)); + return hr && hr->footer ? hr->footer : ""; + }) + .column_int("visible", [](size_t i) -> int { + hidden_range_t* hr = getn_hidden_range(static_cast(i)); + return hr ? hr->visible : 0; + }) + .column_int("color", [](size_t i) -> int { + hidden_range_t* hr = getn_hidden_range(static_cast(i)); + return hr ? hr->color : 0; + }) + .build(); +} + +// ============================================================================ +// PROBLEMS Table - Analysis problems +// ============================================================================ + +struct ProblemEntry { + ea_t ea; + problist_id_t type; + std::string description; + std::string type_name; +}; + +inline std::vector& get_problems_cache() { + static std::vector cache; + return cache; +} + +inline void rebuild_problems_cache() { + auto& cache = get_problems_cache(); + cache.clear(); + + // Iterate all problem types + for (int t = PR_NOBASE; t < PR_END; ++t) { + problist_id_t ptype = static_cast(t); + const char* tname = get_problem_name(ptype, true); + + for (ea_t ea = get_problem(ptype, 0); ea != BADADDR; ea = get_problem(ptype, ea + 1)) { + ProblemEntry entry; + entry.ea = ea; + entry.type = ptype; + entry.type_name = tname ? tname : ""; + + qstring desc; + if (get_problem_desc(&desc, ptype, ea) > 0) { + entry.description = desc.c_str(); + } + cache.push_back(entry); + } + } +} + +inline VTableDef define_problems() { + return table("problems") + .count([]() { + rebuild_problems_cache(); + return get_problems_cache().size(); + }) + .column_int64("address", [](size_t i) -> int64_t { + auto& cache = get_problems_cache(); + return i < cache.size() ? cache[i].ea : 0; + }) + .column_int("type_id", [](size_t i) -> int { + auto& cache = get_problems_cache(); + return i < cache.size() ? cache[i].type : 0; + }) + .column_text("type", [](size_t i) -> std::string { + auto& cache = get_problems_cache(); + return i < cache.size() ? cache[i].type_name : ""; + }) + .column_text("description", [](size_t i) -> std::string { + auto& cache = get_problems_cache(); + return i < cache.size() ? cache[i].description : ""; + }) + .build(); +} + +// ============================================================================ +// FCHUNKS Table - Function chunks (tails) +// ============================================================================ + +inline VTableDef define_fchunks() { + return table("fchunks") + .count([]() { + return get_fchunk_qty(); + }) + .column_int64("start_ea", [](size_t i) -> int64_t { + func_t* chunk = getn_fchunk(i); + return chunk ? chunk->start_ea : 0; + }) + .column_int64("end_ea", [](size_t i) -> int64_t { + func_t* chunk = getn_fchunk(i); + return chunk ? chunk->end_ea : 0; + }) + .column_int64("size", [](size_t i) -> int64_t { + func_t* chunk = getn_fchunk(i); + return chunk ? chunk->size() : 0; + }) + .column_int64("owner", [](size_t i) -> int64_t { + func_t* chunk = getn_fchunk(i); + if (!chunk) return 0; + // For tail chunks, find the owner + func_t* owner = get_func(chunk->start_ea); + return owner ? owner->start_ea : 0; + }) + .column_int("flags", [](size_t i) -> int { + func_t* chunk = getn_fchunk(i); + return chunk ? chunk->flags : 0; + }) + .column_int("is_tail", [](size_t i) -> int { + func_t* chunk = getn_fchunk(i); + // FUNC_TAIL indicates this is a tail/chunk of another function + return chunk ? ((chunk->flags & FUNC_TAIL) ? 1 : 0) : 0; + }) + .build(); +} + +// ============================================================================ +// SIGNATURES Table - Applied FLIRT signatures +// ============================================================================ + +struct SignatureEntry { + int index; + std::string name; + std::string optlibs; + int32 state; +}; + +inline std::vector& get_signatures_cache() { + static std::vector cache; + return cache; +} + +inline void rebuild_signatures_cache() { + auto& cache = get_signatures_cache(); + cache.clear(); + + int qty = get_idasgn_qty(); + for (int i = 0; i < qty; ++i) { + SignatureEntry entry; + entry.index = i; + + qstring signame, optlibs; + entry.state = get_idasgn_desc(&signame, &optlibs, i); + entry.name = signame.c_str(); + entry.optlibs = optlibs.c_str(); + cache.push_back(entry); + } +} + +inline VTableDef define_signatures() { + return table("signatures") + .count([]() { + rebuild_signatures_cache(); + return get_signatures_cache().size(); + }) + .column_int("index", [](size_t i) -> int { + auto& cache = get_signatures_cache(); + return i < cache.size() ? cache[i].index : 0; + }) + .column_text("name", [](size_t i) -> std::string { + auto& cache = get_signatures_cache(); + return i < cache.size() ? cache[i].name : ""; + }) + .column_text("optlibs", [](size_t i) -> std::string { + auto& cache = get_signatures_cache(); + return i < cache.size() ? cache[i].optlibs : ""; + }) + .column_int("state", [](size_t i) -> int { + auto& cache = get_signatures_cache(); + return i < cache.size() ? cache[i].state : 0; + }) + .build(); +} + +// ============================================================================ +// LOCAL_TYPES Table - Local type library entries +// ============================================================================ + +struct LocalTypeEntry { + uint32_t ordinal; + std::string name; + std::string type_str; + bool is_struct; + bool is_enum; + bool is_typedef; +}; + +inline std::vector& get_local_types_cache() { + static std::vector cache; + return cache; +} + +inline void rebuild_local_types_cache() { + auto& cache = get_local_types_cache(); + cache.clear(); + + til_t* ti = get_idati(); + if (!ti) return; + + // Iterate numbered types + uint32_t ord = 1; + while (true) { + const char* name = get_numbered_type_name(ti, ord); + if (!name) break; + + LocalTypeEntry entry; + entry.ordinal = ord; + entry.name = name; + + tinfo_t tif; + if (tif.get_numbered_type(ti, ord)) { + qstring ts; + tif.print(&ts); + entry.type_str = ts.c_str(); + entry.is_struct = tif.is_struct() || tif.is_union(); + entry.is_enum = tif.is_enum(); + entry.is_typedef = tif.is_typedef(); + } else { + entry.is_struct = false; + entry.is_enum = false; + entry.is_typedef = false; + } + + cache.push_back(entry); + ++ord; + } +} + +inline VTableDef define_local_types() { + return table("local_types") + .count([]() { + rebuild_local_types_cache(); + return get_local_types_cache().size(); + }) + .column_int("ordinal", [](size_t i) -> int { + auto& cache = get_local_types_cache(); + return i < cache.size() ? cache[i].ordinal : 0; + }) + .column_text("name", [](size_t i) -> std::string { + auto& cache = get_local_types_cache(); + return i < cache.size() ? cache[i].name : ""; + }) + .column_text("type", [](size_t i) -> std::string { + auto& cache = get_local_types_cache(); + return i < cache.size() ? cache[i].type_str : ""; + }) + .column_int("is_struct", [](size_t i) -> int { + auto& cache = get_local_types_cache(); + return i < cache.size() ? (cache[i].is_struct ? 1 : 0) : 0; + }) + .column_int("is_enum", [](size_t i) -> int { + auto& cache = get_local_types_cache(); + return i < cache.size() ? (cache[i].is_enum ? 1 : 0) : 0; + }) + .column_int("is_typedef", [](size_t i) -> int { + auto& cache = get_local_types_cache(); + return i < cache.size() ? (cache[i].is_typedef ? 1 : 0) : 0; + }) + .build(); +} + +// ============================================================================ +// MAPPINGS Table - Address mappings +// ============================================================================ + +inline VTableDef define_mappings() { + return table("mappings") + .count([]() { + return get_mappings_qty(); + }) + .column_int64("from_ea", [](size_t i) -> int64_t { + ea_t from, to; + asize_t size; + if (get_mapping(&from, &to, &size, i)) { + return from; + } + return 0; + }) + .column_int64("to_ea", [](size_t i) -> int64_t { + ea_t from, to; + asize_t size; + if (get_mapping(&from, &to, &size, i)) { + return to; + } + return 0; + }) + .column_int64("size", [](size_t i) -> int64_t { + ea_t from, to; + asize_t size; + if (get_mapping(&from, &to, &size, i)) { + return size; + } + return 0; + }) + .build(); +} + +// ============================================================================ +// Extended Registry +// ============================================================================ + +struct ExtendedRegistry { + VTableDef fixups; + VTableDef hidden_ranges; + VTableDef problems; + VTableDef fchunks; + VTableDef signatures; + VTableDef local_types; + VTableDef mappings; + + ExtendedRegistry() + : fixups(define_fixups()) + , hidden_ranges(define_hidden_ranges()) + , problems(define_problems()) + , fchunks(define_fchunks()) + , signatures(define_signatures()) + , local_types(define_local_types()) + , mappings(define_mappings()) + {} + + void register_all(xsql::Database& db) { + db.register_table("ida_fixups", &fixups); + db.create_table("fixups", "ida_fixups"); + + db.register_table("ida_hidden_ranges", &hidden_ranges); + db.create_table("hidden_ranges", "ida_hidden_ranges"); + + db.register_table("ida_problems", &problems); + db.create_table("problems", "ida_problems"); + + db.register_table("ida_fchunks", &fchunks); + db.create_table("fchunks", "ida_fchunks"); + + db.register_table("ida_signatures", &signatures); + db.create_table("signatures", "ida_signatures"); + + db.register_table("ida_local_types", &local_types); + db.create_table("local_types", "ida_local_types"); + + db.register_table("ida_mappings", &mappings); + db.create_table("mappings", "ida_mappings"); + } +}; + +} // namespace extended +} // namespace idasql + +``` + +`src/lib/include/idasql/entities_search.hpp`: + +```hpp +/** + * entities_search.hpp - Table-valued function for unified entity search + * + * Provides the `jump_entities` virtual table for "Jump to Anything" functionality. + * This is an eponymous virtual table that acts like a table-valued function: + * + * SELECT * FROM jump_entities('pattern', 'mode') LIMIT 10; + * + * Or equivalently: + * + * SELECT * FROM jump_entities WHERE pattern = 'main' AND mode = 'prefix' LIMIT 10; + * + * Parameters: + * pattern - Search pattern (required) + * mode - 'prefix' (LIKE 'x%') or 'contains' (LIKE '%x%') + * + * Columns returned: + * name - Entity name + * kind - 'function', 'label', 'segment', 'struct', 'union', 'enum', 'member', 'enum_member' + * address - Address (for functions, labels, segments) or NULL + * ordinal - Type ordinal (for types, members) or NULL + * parent_name - Parent type name (for members) or NULL + * full_name - Fully qualified name (parent.member for members) + * + * The table lazily iterates through source tables, stopping when LIMIT is reached. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include + +// IDA SDK +#include +#include +#include +#include +#include + +namespace idasql { +namespace search { + +// ============================================================================ +// Entity Sources - each represents one category of searchable entities +// ============================================================================ + +enum class EntitySource { + Functions = 0, + Labels, + Segments, + Structs, + Unions, + Enums, + Members, + EnumMembers, + Done +}; + +// ============================================================================ +// Entity Row - one result row +// ============================================================================ + +struct EntityRow { + std::string name; + std::string kind; + ea_t address = BADADDR; + uint32 ordinal = 0; + std::string parent_name; + std::string full_name; + bool has_address = false; + bool has_ordinal = false; +}; + +// ============================================================================ +// Entity Generator - iterates through all matching entities +// ============================================================================ + +class EntityGenerator { + std::string pattern_; + bool contains_mode_; + + EntitySource current_source_ = EntitySource::Functions; + size_t current_index_ = 0; + EntityRow current_row_; + bool has_current_ = false; + + // For type iteration + uint32 type_ordinal_ = 0; + size_t member_index_ = 0; + tinfo_t current_type_; + +public: + EntityGenerator(const std::string& pattern, bool contains_mode) + : pattern_(to_lower(pattern)), contains_mode_(contains_mode) {} + + bool next() { + has_current_ = false; + + while (current_source_ != EntitySource::Done) { + if (advance_current_source()) { + has_current_ = true; + return true; + } + // Move to next source + current_source_ = static_cast(static_cast(current_source_) + 1); + current_index_ = 0; + type_ordinal_ = 0; + member_index_ = 0; + } + return false; + } + + const EntityRow& current() const { return current_row_; } + bool eof() const { return !has_current_ && current_source_ == EntitySource::Done; } + +private: + static std::string to_lower(const std::string& s) { + std::string result; + result.reserve(s.size()); + for (char c : s) { + result += static_cast(std::tolower(static_cast(c))); + } + return result; + } + + bool matches(const std::string& name) const { + std::string lower_name = to_lower(name); + if (contains_mode_) { + return lower_name.find(pattern_) != std::string::npos; + } else { + return lower_name.compare(0, pattern_.size(), pattern_) == 0; + } + } + + bool advance_current_source() { + switch (current_source_) { + case EntitySource::Functions: return advance_functions(); + case EntitySource::Labels: return advance_labels(); + case EntitySource::Segments: return advance_segments(); + case EntitySource::Structs: return advance_structs(); + case EntitySource::Unions: return advance_unions(); + case EntitySource::Enums: return advance_enums(); + case EntitySource::Members: return advance_members(); + case EntitySource::EnumMembers: return advance_enum_members(); + case EntitySource::Done: return false; + } + return false; + } + + bool advance_functions() { + size_t count = get_func_qty(); + while (current_index_ < count) { + func_t* fn = getn_func(current_index_++); + if (!fn) continue; + + qstring name; + if (get_func_name(&name, fn->start_ea) <= 0) continue; + + std::string name_str(name.c_str()); + if (matches(name_str)) { + current_row_.name = name_str; + current_row_.kind = "function"; + current_row_.address = fn->start_ea; + current_row_.has_address = true; + current_row_.has_ordinal = false; + current_row_.parent_name.clear(); + current_row_.full_name = name_str; + return true; + } + } + return false; + } + + bool advance_labels() { + // Iterate named locations that aren't function starts + // Use get_nlist_size/get_nlist_ea/get_nlist_name + size_t count = get_nlist_size(); + while (current_index_ < count) { + ea_t ea = get_nlist_ea(current_index_); + const char* name = get_nlist_name(current_index_); + current_index_++; + + if (!name || !*name) continue; + + // Skip if it's a function start + func_t* fn = get_func(ea); + if (fn && fn->start_ea == ea) continue; + + std::string name_str(name); + if (matches(name_str)) { + current_row_.name = name_str; + current_row_.kind = "label"; + current_row_.address = ea; + current_row_.has_address = true; + current_row_.has_ordinal = false; + current_row_.parent_name.clear(); + current_row_.full_name = name_str; + return true; + } + } + return false; + } + + bool advance_segments() { + int count = get_segm_qty(); + while (static_cast(current_index_) < count) { + segment_t* seg = getnseg(static_cast(current_index_++)); + if (!seg) continue; + + qstring name; + if (get_segm_name(&name, seg) <= 0) continue; + + std::string name_str(name.c_str()); + if (matches(name_str)) { + current_row_.name = name_str; + current_row_.kind = "segment"; + current_row_.address = seg->start_ea; + current_row_.has_address = true; + current_row_.has_ordinal = false; + current_row_.parent_name.clear(); + current_row_.full_name = name_str; + return true; + } + } + return false; + } + + bool advance_types_of_kind(const char* kind, bool want_struct, bool want_union, bool want_enum) { + uint32 count = get_ordinal_count(nullptr); + while (type_ordinal_ < count) { + uint32 ord = type_ordinal_++; + tinfo_t tif; + if (!tif.get_numbered_type(nullptr, ord)) continue; + + bool is_struct = tif.is_struct(); + bool is_union = tif.is_union(); + bool is_enum = tif.is_enum(); + + if (want_struct && !is_struct) continue; + if (want_union && !is_union) continue; + if (want_enum && !is_enum) continue; + + qstring name; + if (!tif.get_type_name(&name)) continue; + + std::string name_str(name.c_str()); + if (matches(name_str)) { + current_row_.name = name_str; + current_row_.kind = kind; + current_row_.has_address = false; + current_row_.ordinal = ord; + current_row_.has_ordinal = true; + current_row_.parent_name.clear(); + current_row_.full_name = name_str; + return true; + } + } + return false; + } + + bool advance_structs() { + return advance_types_of_kind("struct", true, false, false); + } + + bool advance_unions() { + return advance_types_of_kind("union", false, true, false); + } + + bool advance_enums() { + return advance_types_of_kind("enum", false, false, true); + } + + bool advance_members() { + // Iterate struct/union members + uint32 count = get_ordinal_count(nullptr); + + while (type_ordinal_ < count) { + // Get current type if not already loaded + if (!current_type_.get_numbered_type(nullptr, type_ordinal_)) { + type_ordinal_++; + member_index_ = 0; + continue; + } + + if (!current_type_.is_struct() && !current_type_.is_union()) { + type_ordinal_++; + member_index_ = 0; + continue; + } + + udt_type_data_t udt; + if (!current_type_.get_udt_details(&udt)) { + type_ordinal_++; + member_index_ = 0; + continue; + } + + while (member_index_ < udt.size()) { + const udm_t& member = udt[member_index_++]; + std::string member_name(member.name.c_str()); + + if (matches(member_name)) { + qstring type_name; + current_type_.get_type_name(&type_name); + + current_row_.name = member_name; + current_row_.kind = "member"; + current_row_.has_address = false; + current_row_.ordinal = type_ordinal_; + current_row_.has_ordinal = true; + current_row_.parent_name = type_name.c_str(); + current_row_.full_name = std::string(type_name.c_str()) + "." + member_name; + return true; + } + } + + // Exhausted members of this type, move to next + type_ordinal_++; + member_index_ = 0; + } + return false; + } + + bool advance_enum_members() { + // Iterate enum values + uint32 count = get_ordinal_count(nullptr); + + while (type_ordinal_ < count) { + if (!current_type_.get_numbered_type(nullptr, type_ordinal_)) { + type_ordinal_++; + member_index_ = 0; + continue; + } + + if (!current_type_.is_enum()) { + type_ordinal_++; + member_index_ = 0; + continue; + } + + enum_type_data_t etd; + if (!current_type_.get_enum_details(&etd)) { + type_ordinal_++; + member_index_ = 0; + continue; + } + + while (member_index_ < etd.size()) { + const edm_t& em = etd[member_index_++]; + std::string value_name(em.name.c_str()); + + if (matches(value_name)) { + qstring type_name; + current_type_.get_type_name(&type_name); + + current_row_.name = value_name; + current_row_.kind = "enum_member"; + current_row_.has_address = false; + current_row_.ordinal = type_ordinal_; + current_row_.has_ordinal = true; + current_row_.parent_name = type_name.c_str(); + current_row_.full_name = std::string(type_name.c_str()) + "." + value_name; + return true; + } + } + + type_ordinal_++; + member_index_ = 0; + } + return false; + } +}; + +// ============================================================================ +// SQLite Virtual Table Implementation +// ============================================================================ + +// Column indices +enum { + COL_NAME = 0, + COL_KIND, + COL_ADDRESS, + COL_ORDINAL, + COL_PARENT_NAME, + COL_FULL_NAME, + COL_PATTERN, // HIDDEN + COL_MODE // HIDDEN +}; + +struct JumpEntitiesVtab : sqlite3_vtab { + // No extra data needed +}; + +struct JumpEntitiesCursor : sqlite3_vtab_cursor { + std::unique_ptr generator; + int64_t rowid = 0; +}; + +inline int je_connect(sqlite3* db, void*, int, const char* const*, + sqlite3_vtab** ppVtab, char**) { + int rc = sqlite3_declare_vtab(db, + "CREATE TABLE x(" + " name TEXT," + " kind TEXT," + " address INTEGER," + " ordinal INTEGER," + " parent_name TEXT," + " full_name TEXT," + " pattern TEXT HIDDEN," + " mode TEXT HIDDEN" + ")" + ); + if (rc != SQLITE_OK) return rc; + + auto* vtab = new JumpEntitiesVtab(); + memset(static_cast(vtab), 0, sizeof(sqlite3_vtab)); + *ppVtab = vtab; + return SQLITE_OK; +} + +inline int je_disconnect(sqlite3_vtab* pVtab) { + delete static_cast(pVtab); + return SQLITE_OK; +} + +inline int je_open(sqlite3_vtab*, sqlite3_vtab_cursor** ppCursor) { + auto* cursor = new JumpEntitiesCursor(); + memset(static_cast(cursor), 0, sizeof(sqlite3_vtab_cursor)); + *ppCursor = cursor; + return SQLITE_OK; +} + +inline int je_close(sqlite3_vtab_cursor* pCursor) { + delete static_cast(pCursor); + return SQLITE_OK; +} + +inline int je_best_index(sqlite3_vtab*, sqlite3_index_info* pInfo) { + int pattern_idx = -1; + int mode_idx = -1; + + // Look for constraints on the hidden columns + for (int i = 0; i < pInfo->nConstraint; i++) { + const auto& c = pInfo->aConstraint[i]; + if (!c.usable) continue; + if (c.op != SQLITE_INDEX_CONSTRAINT_EQ) continue; + + if (c.iColumn == COL_PATTERN) pattern_idx = i; + if (c.iColumn == COL_MODE) mode_idx = i; + } + + if (pattern_idx >= 0) { + // We have a pattern - this is usable + pInfo->aConstraintUsage[pattern_idx].argvIndex = 1; + pInfo->aConstraintUsage[pattern_idx].omit = 1; + + if (mode_idx >= 0) { + pInfo->aConstraintUsage[mode_idx].argvIndex = 2; + pInfo->aConstraintUsage[mode_idx].omit = 1; + pInfo->idxNum = 2; // Both pattern and mode + } else { + pInfo->idxNum = 1; // Pattern only (default to prefix mode) + } + + pInfo->estimatedCost = 1000.0; + pInfo->estimatedRows = 100; + } else { + // No pattern constraint - discourage full table scan + pInfo->estimatedCost = 1000000.0; + pInfo->estimatedRows = 100000; + pInfo->idxNum = 0; + } + + return SQLITE_OK; +} + +inline int je_filter(sqlite3_vtab_cursor* pCursor, int idxNum, const char*, + int argc, sqlite3_value** argv) { + auto* cursor = static_cast(pCursor); + cursor->generator.reset(); + cursor->rowid = 0; + + if (idxNum == 0 || argc < 1) { + // No pattern - return empty result + return SQLITE_OK; + } + + const char* pattern = reinterpret_cast(sqlite3_value_text(argv[0])); + if (!pattern || !*pattern) { + return SQLITE_OK; + } + + bool contains_mode = false; + if (argc >= 2 && idxNum >= 2) { + const char* mode = reinterpret_cast(sqlite3_value_text(argv[1])); + if (mode && strcmp(mode, "contains") == 0) { + contains_mode = true; + } + } + + cursor->generator = std::make_unique(pattern, contains_mode); + cursor->generator->next(); // Position to first row + + return SQLITE_OK; +} + +inline int je_next(sqlite3_vtab_cursor* pCursor) { + auto* cursor = static_cast(pCursor); + if (cursor->generator) { + cursor->generator->next(); + cursor->rowid++; + } + return SQLITE_OK; +} + +inline int je_eof(sqlite3_vtab_cursor* pCursor) { + auto* cursor = static_cast(pCursor); + if (!cursor->generator) return 1; + return cursor->generator->eof() ? 1 : 0; +} + +inline int je_column(sqlite3_vtab_cursor* pCursor, sqlite3_context* ctx, int col) { + auto* cursor = static_cast(pCursor); + if (!cursor->generator || cursor->generator->eof()) { + sqlite3_result_null(ctx); + return SQLITE_OK; + } + + const EntityRow& row = cursor->generator->current(); + + switch (col) { + case COL_NAME: + sqlite3_result_text(ctx, row.name.c_str(), -1, SQLITE_TRANSIENT); + break; + case COL_KIND: + sqlite3_result_text(ctx, row.kind.c_str(), -1, SQLITE_TRANSIENT); + break; + case COL_ADDRESS: + if (row.has_address) { + sqlite3_result_int64(ctx, static_cast(row.address)); + } else { + sqlite3_result_null(ctx); + } + break; + case COL_ORDINAL: + if (row.has_ordinal) { + sqlite3_result_int64(ctx, row.ordinal); + } else { + sqlite3_result_null(ctx); + } + break; + case COL_PARENT_NAME: + if (!row.parent_name.empty()) { + sqlite3_result_text(ctx, row.parent_name.c_str(), -1, SQLITE_TRANSIENT); + } else { + sqlite3_result_null(ctx); + } + break; + case COL_FULL_NAME: + sqlite3_result_text(ctx, row.full_name.c_str(), -1, SQLITE_TRANSIENT); + break; + case COL_PATTERN: + case COL_MODE: + // Hidden columns - return null (they're inputs, not outputs) + sqlite3_result_null(ctx); + break; + default: + sqlite3_result_null(ctx); + break; + } + return SQLITE_OK; +} + +inline int je_rowid(sqlite3_vtab_cursor* pCursor, sqlite3_int64* pRowid) { + auto* cursor = static_cast(pCursor); + *pRowid = cursor->rowid; + return SQLITE_OK; +} + +// Module definition +inline sqlite3_module& get_jump_entities_module() { + static sqlite3_module mod = { + 0, // iVersion + je_connect, // xCreate + je_connect, // xConnect + je_best_index, // xBestIndex + je_disconnect, // xDisconnect + je_disconnect, // xDestroy + je_open, // xOpen + je_close, // xClose + je_filter, // xFilter + je_next, // xNext + je_eof, // xEof + je_column, // xColumn + je_rowid, // xRowid + nullptr, // xUpdate + nullptr, // xBegin + nullptr, // xSync + nullptr, // xCommit + nullptr, // xRollback + nullptr, // xFindFunction + nullptr, // xRename + nullptr, // xSavepoint + nullptr, // xRelease + nullptr, // xRollbackTo + nullptr // xShadowName + }; + return mod; +} + +/** + * Register the jump_entities table-valued function. + * + * Usage after registration: + * SELECT * FROM jump_entities('pattern', 'prefix') LIMIT 10; + * SELECT * FROM jump_entities('main', 'contains'); + * SELECT * FROM jump_entities WHERE pattern = 'sub' AND mode = 'prefix' LIMIT 20; + */ +inline bool register_jump_entities(xsql::Database& db) { + int rc = sqlite3_create_module(db.handle(), "jump_entities", &get_jump_entities_module(), nullptr); + return rc == SQLITE_OK; +} + +} // namespace search +} // namespace idasql + +``` + +`src/lib/include/idasql/entities_types.hpp`: + +```hpp +/** + * entities_types.hpp - IDA type system tables + * + * Provides SQL tables for querying IDA's type library: + * types - All local types (structs, unions, enums, typedefs, funcs) + * types_members - Struct/union member details + * types_enum_values - Enum constant values + * types_func_args - Function prototype arguments + * + * Also provides views: + * types_v_structs - Filter: structs only + * types_v_unions - Filter: unions only + * types_v_enums - Filter: enums only + * types_v_typedefs - Filter: typedefs only + * types_v_funcs - Filter: function types only + */ + +#pragma once + +#include + +#include +#include + +#include + +// IDA SDK headers +#include +#include + +namespace idasql { +namespace types { + +inline void ida_undo_hook(const std::string&) {} + +// ============================================================================ +// Type Kind Classification +// ============================================================================ + +inline const char* get_type_kind(const tinfo_t& tif) { + if (tif.is_struct()) return "struct"; + if (tif.is_union()) return "union"; + if (tif.is_enum()) return "enum"; + if (tif.is_typedef()) return "typedef"; + if (tif.is_func()) return "func"; + if (tif.is_ptr()) return "ptr"; + if (tif.is_array()) return "array"; + return "other"; +} + +// ============================================================================ +// Type Entry Cache +// ============================================================================ + +struct TypeEntry { + uint32_t ordinal; + std::string name; + std::string kind; + int64_t size; + int alignment; + bool is_struct; + bool is_union; + bool is_enum; + bool is_typedef; + bool is_func; + bool is_ptr; + bool is_array; + std::string definition; + std::string resolved; // For typedefs: what it resolves to +}; + +inline std::vector& get_types_cache() { + static std::vector cache; + return cache; +} + +inline void rebuild_types_cache() { + auto& cache = get_types_cache(); + cache.clear(); + + til_t* ti = get_idati(); + if (!ti) return; + + uint32_t max_ord = get_ordinal_limit(ti); + if (max_ord == 0 || max_ord == uint32_t(-1)) return; + + for (uint32_t ord = 1; ord < max_ord; ++ord) { + const char* name = get_numbered_type_name(ti, ord); + if (!name) continue; // Skip gaps in ordinal space + + TypeEntry entry; + entry.ordinal = ord; + entry.name = name; + + tinfo_t tif; + if (tif.get_numbered_type(ti, ord)) { + entry.kind = get_type_kind(tif); + entry.is_struct = tif.is_struct(); + entry.is_union = tif.is_union(); + entry.is_enum = tif.is_enum(); + entry.is_typedef = tif.is_typedef(); + entry.is_func = tif.is_func(); + entry.is_ptr = tif.is_ptr(); + entry.is_array = tif.is_array(); + + // Get size + size_t sz = tif.get_size(); + entry.size = (sz != BADSIZE) ? static_cast(sz) : -1; + + // Get alignment for structs/unions + entry.alignment = 0; + if (tif.is_struct() || tif.is_union()) { + udt_type_data_t udt; + if (tif.get_udt_details(&udt)) { + entry.alignment = static_cast(udt.effalign); + } + } + + // Get definition string + qstring def_str; + tif.print(&def_str); + entry.definition = def_str.c_str(); + + // For typedefs, get the resolved type name + if (tif.is_typedef()) { + qstring res_name; + if (tif.get_final_type_name(&res_name)) { + entry.resolved = res_name.c_str(); + } + } + } else { + entry.kind = "unknown"; + entry.size = -1; + entry.alignment = 0; + entry.is_struct = false; + entry.is_union = false; + entry.is_enum = false; + entry.is_typedef = false; + entry.is_func = false; + entry.is_ptr = false; + entry.is_array = false; + } + + cache.push_back(entry); + } +} + +// ============================================================================ +// TYPES Table - All local types (enhanced) +// ============================================================================ + +inline VTableDef define_types() { + return table("types") + .on_modify(ida_undo_hook) + .count([]() { + rebuild_types_cache(); + return get_types_cache().size(); + }) + .column_int("ordinal", [](size_t i) -> int { + auto& cache = get_types_cache(); + return i < cache.size() ? cache[i].ordinal : 0; + }) + .column_text_rw("name", + // Getter + [](size_t i) -> std::string { + auto& cache = get_types_cache(); + return i < cache.size() ? cache[i].name : ""; + }, + // Setter - rename type + [](size_t i, const char* new_name) -> bool { + auto& cache = get_types_cache(); + if (i >= cache.size()) return false; + + til_t* ti = get_idati(); + if (!ti) return false; + + // Get the type + tinfo_t tif; + if (!tif.get_numbered_type(ti, cache[i].ordinal)) return false; + + // Rename it using tinfo_t::rename_type() + return tif.rename_type(new_name) == TERR_OK; + }) + .column_text("kind", [](size_t i) -> std::string { + auto& cache = get_types_cache(); + return i < cache.size() ? cache[i].kind : ""; + }) + .column_int64("size", [](size_t i) -> int64_t { + auto& cache = get_types_cache(); + return i < cache.size() ? cache[i].size : -1; + }) + .column_int("alignment", [](size_t i) -> int { + auto& cache = get_types_cache(); + return i < cache.size() ? cache[i].alignment : 0; + }) + .column_int("is_struct", [](size_t i) -> int { + auto& cache = get_types_cache(); + return i < cache.size() ? (cache[i].is_struct ? 1 : 0) : 0; + }) + .column_int("is_union", [](size_t i) -> int { + auto& cache = get_types_cache(); + return i < cache.size() ? (cache[i].is_union ? 1 : 0) : 0; + }) + .column_int("is_enum", [](size_t i) -> int { + auto& cache = get_types_cache(); + return i < cache.size() ? (cache[i].is_enum ? 1 : 0) : 0; + }) + .column_int("is_typedef", [](size_t i) -> int { + auto& cache = get_types_cache(); + return i < cache.size() ? (cache[i].is_typedef ? 1 : 0) : 0; + }) + .column_int("is_func", [](size_t i) -> int { + auto& cache = get_types_cache(); + return i < cache.size() ? (cache[i].is_func ? 1 : 0) : 0; + }) + .column_int("is_ptr", [](size_t i) -> int { + auto& cache = get_types_cache(); + return i < cache.size() ? (cache[i].is_ptr ? 1 : 0) : 0; + }) + .column_int("is_array", [](size_t i) -> int { + auto& cache = get_types_cache(); + return i < cache.size() ? (cache[i].is_array ? 1 : 0) : 0; + }) + .column_text("definition", [](size_t i) -> std::string { + auto& cache = get_types_cache(); + return i < cache.size() ? cache[i].definition : ""; + }) + .column_text("resolved", [](size_t i) -> std::string { + auto& cache = get_types_cache(); + return i < cache.size() ? cache[i].resolved : ""; + }) + .deletable([](size_t i) -> bool { + auto& cache = get_types_cache(); + if (i >= cache.size()) return false; + + til_t* ti = get_idati(); + if (!ti) return false; + + return del_numbered_type(ti, cache[i].ordinal); + }) + .insertable([](int argc, sqlite3_value** argv) -> bool { + // Column layout: ordinal(0), name(1), kind(2), ... + // name (col 1) is required + if (argc < 2 || sqlite3_value_type(argv[1]) == SQLITE_NULL) + return false; + + const char* name = reinterpret_cast( + sqlite3_value_text(argv[1])); + if (!name || !name[0]) return false; + + // kind (col 2): defaults to "struct" + std::string kind = "struct"; + if (argc > 2 && sqlite3_value_type(argv[2]) != SQLITE_NULL) { + const char* k = reinterpret_cast( + sqlite3_value_text(argv[2])); + if (k && k[0]) kind = k; + } + + til_t* ti = get_idati(); + if (!ti) return false; + + // Check if type with this name already exists + if (get_type_ordinal(ti, name) != 0) + return false; + + uint32_t ord = alloc_type_ordinal(ti); + if (ord == 0) return false; + + tinfo_t tif; + if (kind == "struct") { + udt_type_data_t udt; + udt.is_union = false; + tif.create_udt(udt); + } else if (kind == "union") { + udt_type_data_t udt; + udt.is_union = true; + tif.create_udt(udt); + } else if (kind == "enum") { + enum_type_data_t ei; + tif.create_enum(ei); + } else { + return false; + } + + return tif.set_numbered_type(ti, ord, NTF_REPLACE, name) == TERR_OK; + }) + .build(); +} + +// ============================================================================ +// TYPES_MEMBERS Table - Struct/union field details +// ============================================================================ + +struct MemberEntry { + uint32_t type_ordinal; + std::string type_name; + int member_index; + std::string member_name; + int64_t offset; + int64_t offset_bits; + int64_t size; + int64_t size_bits; + std::string member_type; + bool is_bitfield; + bool is_baseclass; + std::string comment; + // Member type classification (for efficient filtering) + bool mt_is_struct; + bool mt_is_union; + bool mt_is_enum; + bool mt_is_ptr; + bool mt_is_array; + int member_type_ordinal; // -1 if member type not in local types +}; + +inline std::vector& get_members_cache() { + static std::vector cache; + return cache; +} + +// Helper to get ordinal of a type by name +inline int get_type_ordinal_by_name(til_t* ti, const char* type_name) { + if (!ti || !type_name || !type_name[0]) return -1; + uint32_t ord = get_type_ordinal(ti, type_name); + return (ord != 0) ? static_cast(ord) : -1; +} + +// Helper to classify member type and get ordinal +inline void classify_member_type(const tinfo_t& mtype, til_t* ti, + bool& is_struct, bool& is_union, bool& is_enum, + bool& is_ptr, bool& is_array, int& type_ordinal) { + is_struct = false; + is_union = false; + is_enum = false; + is_ptr = mtype.is_ptr(); + is_array = mtype.is_array(); + type_ordinal = -1; + + // Get the base type (dereference pointers/arrays to find underlying type) + tinfo_t base_type = mtype; + if (mtype.is_ptr()) { + base_type = mtype.get_pointed_object(); + } else if (mtype.is_array()) { + base_type = mtype.get_array_element(); + } + + // Classify the base type + is_struct = base_type.is_struct(); + is_union = base_type.is_union(); + is_enum = base_type.is_enum(); + + // Try to get ordinal of the base type + qstring type_name; + if (base_type.get_type_name(&type_name) && !type_name.empty()) { + type_ordinal = get_type_ordinal_by_name(ti, type_name.c_str()); + } +} + +inline void rebuild_members_cache() { + auto& cache = get_members_cache(); + cache.clear(); + + til_t* ti = get_idati(); + if (!ti) return; + + uint32_t max_ord = get_ordinal_limit(ti); + if (max_ord == 0 || max_ord == uint32_t(-1)) return; + + for (uint32_t ord = 1; ord < max_ord; ++ord) { + const char* name = get_numbered_type_name(ti, ord); + if (!name) continue; // Skip gaps in ordinal space + + tinfo_t tif; + if (tif.get_numbered_type(ti, ord)) { + if (tif.is_struct() || tif.is_union()) { + udt_type_data_t udt; + if (tif.get_udt_details(&udt)) { + for (size_t i = 0; i < udt.size(); i++) { + const udm_t& m = udt[i]; + MemberEntry entry; + entry.type_ordinal = ord; + entry.type_name = name; + entry.member_index = static_cast(i); + entry.member_name = m.name.c_str(); + entry.offset = static_cast(m.offset / 8); + entry.offset_bits = static_cast(m.offset); + entry.size = static_cast(m.size / 8); + entry.size_bits = static_cast(m.size); + entry.is_bitfield = m.is_bitfield(); + entry.is_baseclass = m.is_baseclass(); + entry.comment = m.cmt.c_str(); + + qstring type_str; + m.type.print(&type_str); + entry.member_type = type_str.c_str(); + + // Classify member type + classify_member_type(m.type, ti, + entry.mt_is_struct, entry.mt_is_union, entry.mt_is_enum, + entry.mt_is_ptr, entry.mt_is_array, entry.member_type_ordinal); + + cache.push_back(entry); + } + } + } + } + } +} + +/** + * Iterator for members of a specific type. + * Used when query has: WHERE type_ordinal = X + */ +class MembersInTypeIterator : public xsql::RowIterator { + uint32_t type_ordinal_; + std::string type_name_; + udt_type_data_t udt_; + int idx_ = -1; + bool valid_ = false; + bool has_data_ = false; + +public: + explicit MembersInTypeIterator(uint32_t ordinal) : type_ordinal_(ordinal) { + til_t* ti = get_idati(); + if (!ti) return; + + const char* name = get_numbered_type_name(ti, type_ordinal_); + if (!name) return; + type_name_ = name; + + tinfo_t tif; + if (tif.get_numbered_type(ti, type_ordinal_)) { + if (tif.is_struct() || tif.is_union()) { + has_data_ = tif.get_udt_details(&udt_); + } + } + } + + bool next() override { + if (!has_data_) return false; + ++idx_; + valid_ = (idx_ >= 0 && static_cast(idx_) < udt_.size()); + return valid_; + } + + bool eof() const override { + return idx_ >= 0 && !valid_; + } + + void column(sqlite3_context* ctx, int col) override { + if (!valid_ || idx_ < 0 || static_cast(idx_) >= udt_.size()) { + sqlite3_result_null(ctx); + return; + } + const udm_t& m = udt_[idx_]; + switch (col) { + case 0: sqlite3_result_int(ctx, type_ordinal_); break; + case 1: sqlite3_result_text(ctx, type_name_.c_str(), -1, SQLITE_TRANSIENT); break; + case 2: sqlite3_result_int(ctx, idx_); break; + case 3: sqlite3_result_text(ctx, m.name.c_str(), -1, SQLITE_TRANSIENT); break; + case 4: sqlite3_result_int64(ctx, static_cast(m.offset / 8)); break; + case 5: sqlite3_result_int64(ctx, static_cast(m.offset)); break; + case 6: sqlite3_result_int64(ctx, static_cast(m.size / 8)); break; + case 7: sqlite3_result_int64(ctx, static_cast(m.size)); break; + case 8: { + qstring type_str; + m.type.print(&type_str); + sqlite3_result_text(ctx, type_str.c_str(), -1, SQLITE_TRANSIENT); + break; + } + case 9: sqlite3_result_int(ctx, m.is_bitfield() ? 1 : 0); break; + case 10: sqlite3_result_int(ctx, m.is_baseclass() ? 1 : 0); break; + case 11: sqlite3_result_text(ctx, m.cmt.c_str(), -1, SQLITE_TRANSIENT); break; + // Member type classification columns + case 12: case 13: case 14: case 15: case 16: case 17: { + // Classify the member type on-the-fly for iterator + bool mt_is_struct, mt_is_union, mt_is_enum, mt_is_ptr, mt_is_array; + int mt_ordinal; + classify_member_type(m.type, get_idati(), + mt_is_struct, mt_is_union, mt_is_enum, + mt_is_ptr, mt_is_array, mt_ordinal); + switch (col) { + case 12: sqlite3_result_int(ctx, mt_is_struct ? 1 : 0); break; + case 13: sqlite3_result_int(ctx, mt_is_union ? 1 : 0); break; + case 14: sqlite3_result_int(ctx, mt_is_enum ? 1 : 0); break; + case 15: sqlite3_result_int(ctx, mt_is_ptr ? 1 : 0); break; + case 16: sqlite3_result_int(ctx, mt_is_array ? 1 : 0); break; + case 17: sqlite3_result_int(ctx, mt_ordinal); break; + } + break; + } + default: sqlite3_result_null(ctx); break; + } + } + + int64_t rowid() const override { + return static_cast(type_ordinal_) * 10000 + idx_; + } +}; + +// Helper to get type and member by ordinal/index (for write operations) +struct TypeMemberRef { + tinfo_t tif; + udt_type_data_t udt; + bool valid; + uint32_t ordinal; + + TypeMemberRef(uint32_t ord) : valid(false), ordinal(ord) { + til_t* ti = get_idati(); + if (!ti) return; + if (tif.get_numbered_type(ti, ord)) { + if (tif.is_struct() || tif.is_union()) { + valid = tif.get_udt_details(&udt); + } + } + } + + bool save() { + if (!valid) return false; + tinfo_t new_tif; + new_tif.create_udt(udt, tif.is_union() ? BTF_UNION : BTF_STRUCT); + return new_tif.set_numbered_type(get_idati(), ordinal, NTF_REPLACE, nullptr); + } +}; + +inline VTableDef define_types_members() { + return table("types_members") + .on_modify(ida_undo_hook) + .count([]() { + rebuild_members_cache(); + return get_members_cache().size(); + }) + .column_int("type_ordinal", [](size_t i) -> int { + auto& cache = get_members_cache(); + return i < cache.size() ? cache[i].type_ordinal : 0; + }) + .column_text("type_name", [](size_t i) -> std::string { + auto& cache = get_members_cache(); + return i < cache.size() ? cache[i].type_name : ""; + }) + .column_int("member_index", [](size_t i) -> int { + auto& cache = get_members_cache(); + return i < cache.size() ? cache[i].member_index : 0; + }) + .column_text_rw("member_name", + // Getter + [](size_t i) -> std::string { + auto& cache = get_members_cache(); + return i < cache.size() ? cache[i].member_name : ""; + }, + // Setter - rename member + [](size_t i, const char* new_name) -> bool { + auto& cache = get_members_cache(); + if (i >= cache.size()) return false; + + TypeMemberRef ref(cache[i].type_ordinal); + if (!ref.valid) return false; + + int idx = cache[i].member_index; + if (idx < 0 || static_cast(idx) >= ref.udt.size()) return false; + + ref.udt[idx].name = new_name; + return ref.save(); + }) + .column_int64("offset", [](size_t i) -> int64_t { + auto& cache = get_members_cache(); + return i < cache.size() ? cache[i].offset : 0; + }) + .column_int64("offset_bits", [](size_t i) -> int64_t { + auto& cache = get_members_cache(); + return i < cache.size() ? cache[i].offset_bits : 0; + }) + .column_int64("size", [](size_t i) -> int64_t { + auto& cache = get_members_cache(); + return i < cache.size() ? cache[i].size : 0; + }) + .column_int64("size_bits", [](size_t i) -> int64_t { + auto& cache = get_members_cache(); + return i < cache.size() ? cache[i].size_bits : 0; + }) + .column_text("member_type", [](size_t i) -> std::string { + auto& cache = get_members_cache(); + return i < cache.size() ? cache[i].member_type : ""; + }) + .column_int("is_bitfield", [](size_t i) -> int { + auto& cache = get_members_cache(); + return i < cache.size() ? (cache[i].is_bitfield ? 1 : 0) : 0; + }) + .column_int("is_baseclass", [](size_t i) -> int { + auto& cache = get_members_cache(); + return i < cache.size() ? (cache[i].is_baseclass ? 1 : 0) : 0; + }) + .column_text_rw("comment", + // Getter + [](size_t i) -> std::string { + auto& cache = get_members_cache(); + return i < cache.size() ? cache[i].comment : ""; + }, + // Setter - update member comment + [](size_t i, const char* new_comment) -> bool { + auto& cache = get_members_cache(); + if (i >= cache.size()) return false; + + TypeMemberRef ref(cache[i].type_ordinal); + if (!ref.valid) return false; + + int idx = cache[i].member_index; + if (idx < 0 || static_cast(idx) >= ref.udt.size()) return false; + + ref.udt[idx].cmt = new_comment; + return ref.save(); + }) + // Member type classification columns (for efficient filtering) + .column_int("mt_is_struct", [](size_t i) -> int { + auto& cache = get_members_cache(); + return i < cache.size() ? (cache[i].mt_is_struct ? 1 : 0) : 0; + }) + .column_int("mt_is_union", [](size_t i) -> int { + auto& cache = get_members_cache(); + return i < cache.size() ? (cache[i].mt_is_union ? 1 : 0) : 0; + }) + .column_int("mt_is_enum", [](size_t i) -> int { + auto& cache = get_members_cache(); + return i < cache.size() ? (cache[i].mt_is_enum ? 1 : 0) : 0; + }) + .column_int("mt_is_ptr", [](size_t i) -> int { + auto& cache = get_members_cache(); + return i < cache.size() ? (cache[i].mt_is_ptr ? 1 : 0) : 0; + }) + .column_int("mt_is_array", [](size_t i) -> int { + auto& cache = get_members_cache(); + return i < cache.size() ? (cache[i].mt_is_array ? 1 : 0) : 0; + }) + .column_int("member_type_ordinal", [](size_t i) -> int { + auto& cache = get_members_cache(); + return i < cache.size() ? cache[i].member_type_ordinal : -1; + }) + .deletable([](size_t i) -> bool { + auto& cache = get_members_cache(); + if (i >= cache.size()) return false; + + TypeMemberRef ref(cache[i].type_ordinal); + if (!ref.valid) return false; + + int idx = cache[i].member_index; + if (idx < 0 || static_cast(idx) >= ref.udt.size()) return false; + + ref.udt.erase(ref.udt.begin() + idx); + return ref.save(); + }) + .insertable([](int argc, sqlite3_value** argv) -> bool { + // Column layout: type_ordinal(0), type_name(1), member_index(2), + // member_name(3), offset(4), ..., member_type(8), ..., comment(11) + // type_ordinal (col 0) and member_name (col 3) are required + if (argc < 4 + || sqlite3_value_type(argv[0]) == SQLITE_NULL + || sqlite3_value_type(argv[3]) == SQLITE_NULL) + return false; + + uint32_t ordinal = static_cast(sqlite3_value_int(argv[0])); + const char* member_name = reinterpret_cast( + sqlite3_value_text(argv[3])); + if (!member_name || !member_name[0]) return false; + + TypeMemberRef ref(ordinal); + if (!ref.valid) return false; + + // Build the new member + udm_t new_member; + new_member.name = member_name; + + // member_type (col 8): parse type string, default to "int" + std::string type_str = "int"; + if (argc > 8 && sqlite3_value_type(argv[8]) != SQLITE_NULL) { + const char* mt = reinterpret_cast( + sqlite3_value_text(argv[8])); + if (mt && mt[0]) type_str = mt; + } + + // Parse the type string into a tinfo_t + tinfo_t member_type; + qstring parsed_name; + if (parse_decl(&member_type, &parsed_name, nullptr, + (type_str + " x;").c_str(), PT_SIL)) { + new_member.type = member_type; + new_member.size = member_type.get_size() * 8; // size in bits + } else { + // Fallback: default to int (4 bytes) + new_member.type = tinfo_t(BT_INT32); + new_member.size = 32; + } + + // comment (col 11) + if (argc > 11 && sqlite3_value_type(argv[11]) != SQLITE_NULL) { + const char* cmt = reinterpret_cast( + sqlite3_value_text(argv[11])); + if (cmt) new_member.cmt = cmt; + } + + // Compute offset: append after last member + if (!ref.udt.empty()) { + const udm_t& last = ref.udt.back(); + new_member.offset = last.offset + last.size; + } else { + new_member.offset = 0; + } + + ref.udt.push_back(new_member); + return ref.save(); + }) + // Constraint pushdown: type_ordinal = X + .filter_eq("type_ordinal", [](int64_t ordinal) -> std::unique_ptr { + return std::make_unique(static_cast(ordinal)); + }, 10.0, 5.0) + .build(); +} + +// ============================================================================ +// TYPES_ENUM_VALUES Table - Enum constants +// ============================================================================ + +struct EnumValueEntry { + uint32_t type_ordinal; + std::string type_name; + int value_index; + std::string value_name; + int64_t value; + uint64_t uvalue; + std::string comment; +}; + +inline std::vector& get_enum_values_cache() { + static std::vector cache; + return cache; +} + +inline void rebuild_enum_values_cache() { + auto& cache = get_enum_values_cache(); + cache.clear(); + + til_t* ti = get_idati(); + if (!ti) return; + + uint32_t max_ord = get_ordinal_limit(ti); + if (max_ord == 0 || max_ord == uint32_t(-1)) return; + + for (uint32_t ord = 1; ord < max_ord; ++ord) { + const char* name = get_numbered_type_name(ti, ord); + if (!name) continue; // Skip gaps in ordinal space + + tinfo_t tif; + if (tif.get_numbered_type(ti, ord)) { + if (tif.is_enum()) { + enum_type_data_t ei; + if (tif.get_enum_details(&ei)) { + for (size_t i = 0; i < ei.size(); i++) { + const edm_t& e = ei[i]; + EnumValueEntry entry; + entry.type_ordinal = ord; + entry.type_name = name; + entry.value_index = static_cast(i); + entry.value_name = e.name.c_str(); + entry.value = static_cast(e.value); + entry.uvalue = e.value; + entry.comment = e.cmt.c_str(); + cache.push_back(entry); + } + } + } + } + } +} + +/** + * Iterator for enum values of a specific enum type. + * Used when query has: WHERE type_ordinal = X + */ +class EnumValuesInTypeIterator : public xsql::RowIterator { + uint32_t type_ordinal_; + std::string type_name_; + enum_type_data_t ei_; + int idx_ = -1; + bool valid_ = false; + bool has_data_ = false; + +public: + explicit EnumValuesInTypeIterator(uint32_t ordinal) : type_ordinal_(ordinal) { + til_t* ti = get_idati(); + if (!ti) return; + + const char* name = get_numbered_type_name(ti, type_ordinal_); + if (!name) return; + type_name_ = name; + + tinfo_t tif; + if (tif.get_numbered_type(ti, type_ordinal_)) { + if (tif.is_enum()) { + has_data_ = tif.get_enum_details(&ei_); + } + } + } + + bool next() override { + if (!has_data_) return false; + ++idx_; + valid_ = (idx_ >= 0 && static_cast(idx_) < ei_.size()); + return valid_; + } + + bool eof() const override { + return idx_ >= 0 && !valid_; + } + + void column(sqlite3_context* ctx, int col) override { + if (!valid_ || idx_ < 0 || static_cast(idx_) >= ei_.size()) { + sqlite3_result_null(ctx); + return; + } + const edm_t& e = ei_[idx_]; + switch (col) { + case 0: sqlite3_result_int(ctx, type_ordinal_); break; + case 1: sqlite3_result_text(ctx, type_name_.c_str(), -1, SQLITE_TRANSIENT); break; + case 2: sqlite3_result_int(ctx, idx_); break; + case 3: sqlite3_result_text(ctx, e.name.c_str(), -1, SQLITE_TRANSIENT); break; + case 4: sqlite3_result_int64(ctx, static_cast(e.value)); break; + case 5: sqlite3_result_int64(ctx, static_cast(e.value)); break; // uvalue + case 6: sqlite3_result_text(ctx, e.cmt.c_str(), -1, SQLITE_TRANSIENT); break; + default: sqlite3_result_null(ctx); break; + } + } + + int64_t rowid() const override { + return static_cast(type_ordinal_) * 10000 + idx_; + } +}; + +// Helper to get enum type by ordinal (for write operations) +struct EnumTypeRef { + tinfo_t tif; + enum_type_data_t ei; + bool valid; + uint32_t ordinal; + + EnumTypeRef(uint32_t ord) : valid(false), ordinal(ord) { + til_t* ti = get_idati(); + if (!ti) return; + if (tif.get_numbered_type(ti, ord)) { + if (tif.is_enum()) { + valid = tif.get_enum_details(&ei); + } + } + } + + bool save() { + if (!valid) return false; + tinfo_t new_tif; + new_tif.create_enum(ei); + return new_tif.set_numbered_type(get_idati(), ordinal, NTF_REPLACE, nullptr); + } +}; + +inline VTableDef define_types_enum_values() { + return table("types_enum_values") + .on_modify(ida_undo_hook) + .count([]() { + rebuild_enum_values_cache(); + return get_enum_values_cache().size(); + }) + .column_int("type_ordinal", [](size_t i) -> int { + auto& cache = get_enum_values_cache(); + return i < cache.size() ? cache[i].type_ordinal : 0; + }) + .column_text("type_name", [](size_t i) -> std::string { + auto& cache = get_enum_values_cache(); + return i < cache.size() ? cache[i].type_name : ""; + }) + .column_int("value_index", [](size_t i) -> int { + auto& cache = get_enum_values_cache(); + return i < cache.size() ? cache[i].value_index : 0; + }) + .column_text_rw("value_name", + // Getter + [](size_t i) -> std::string { + auto& cache = get_enum_values_cache(); + return i < cache.size() ? cache[i].value_name : ""; + }, + // Setter - rename enum value + [](size_t i, const char* new_name) -> bool { + auto& cache = get_enum_values_cache(); + if (i >= cache.size()) return false; + + EnumTypeRef ref(cache[i].type_ordinal); + if (!ref.valid) return false; + + int idx = cache[i].value_index; + if (idx < 0 || static_cast(idx) >= ref.ei.size()) return false; + + ref.ei[idx].name = new_name; + return ref.save(); + }) + .column_int64_rw("value", + // Getter + [](size_t i) -> int64_t { + auto& cache = get_enum_values_cache(); + return i < cache.size() ? cache[i].value : 0; + }, + // Setter - change enum value + [](size_t i, int64_t new_value) -> bool { + auto& cache = get_enum_values_cache(); + if (i >= cache.size()) return false; + + EnumTypeRef ref(cache[i].type_ordinal); + if (!ref.valid) return false; + + int idx = cache[i].value_index; + if (idx < 0 || static_cast(idx) >= ref.ei.size()) return false; + + ref.ei[idx].value = static_cast(new_value); + return ref.save(); + }) + .column_int64("uvalue", [](size_t i) -> int64_t { + auto& cache = get_enum_values_cache(); + return i < cache.size() ? static_cast(cache[i].uvalue) : 0; + }) + .column_text_rw("comment", + // Getter + [](size_t i) -> std::string { + auto& cache = get_enum_values_cache(); + return i < cache.size() ? cache[i].comment : ""; + }, + // Setter - update enum value comment + [](size_t i, const char* new_comment) -> bool { + auto& cache = get_enum_values_cache(); + if (i >= cache.size()) return false; + + EnumTypeRef ref(cache[i].type_ordinal); + if (!ref.valid) return false; + + int idx = cache[i].value_index; + if (idx < 0 || static_cast(idx) >= ref.ei.size()) return false; + + ref.ei[idx].cmt = new_comment; + return ref.save(); + }) + .deletable([](size_t i) -> bool { + auto& cache = get_enum_values_cache(); + if (i >= cache.size()) return false; + + EnumTypeRef ref(cache[i].type_ordinal); + if (!ref.valid) return false; + + int idx = cache[i].value_index; + if (idx < 0 || static_cast(idx) >= ref.ei.size()) return false; + + ref.ei.erase(ref.ei.begin() + idx); + return ref.save(); + }) + .insertable([](int argc, sqlite3_value** argv) -> bool { + // Column layout: type_ordinal(0), type_name(1), value_index(2), + // value_name(3), value(4), uvalue(5), comment(6) + // type_ordinal (col 0) and value_name (col 3) are required + if (argc < 4 + || sqlite3_value_type(argv[0]) == SQLITE_NULL + || sqlite3_value_type(argv[3]) == SQLITE_NULL) + return false; + + uint32_t ordinal = static_cast(sqlite3_value_int(argv[0])); + const char* value_name = reinterpret_cast( + sqlite3_value_text(argv[3])); + if (!value_name || !value_name[0]) return false; + + EnumTypeRef ref(ordinal); + if (!ref.valid) return false; + + // Build the new enum member + edm_t new_edm; + new_edm.name = value_name; + + // value (col 4): default to 0 + if (argc > 4 && sqlite3_value_type(argv[4]) != SQLITE_NULL) { + new_edm.value = static_cast(sqlite3_value_int64(argv[4])); + } else { + // Auto-assign: next value after last member + if (!ref.ei.empty()) { + new_edm.value = ref.ei.back().value + 1; + } else { + new_edm.value = 0; + } + } + + // comment (col 6) + if (argc > 6 && sqlite3_value_type(argv[6]) != SQLITE_NULL) { + const char* cmt = reinterpret_cast( + sqlite3_value_text(argv[6])); + if (cmt) new_edm.cmt = cmt; + } + + ref.ei.push_back(new_edm); + return ref.save(); + }) + // Constraint pushdown: type_ordinal = X + .filter_eq("type_ordinal", [](int64_t ordinal) -> std::unique_ptr { + return std::make_unique(static_cast(ordinal)); + }, 10.0, 10.0) + .build(); +} + +// ============================================================================ +// TYPES_FUNC_ARGS Table - Function prototype arguments +// ============================================================================ + +// Type classification info (surface + resolved) +struct TypeClassification { + // Surface-level classification (literal type as written) + bool is_ptr = false; + bool is_int = false; // Exactly int type + bool is_integral = false; // Int-like family (int, long, short, char, bool) + bool is_float = false; + bool is_void = false; + bool is_struct = false; + bool is_array = false; + int ptr_depth = 0; + std::string base_type; // Type name with pointers stripped + + // Resolved classification (after typedef resolution) + bool is_ptr_resolved = false; + bool is_int_resolved = false; + bool is_integral_resolved = false; + bool is_float_resolved = false; + bool is_void_resolved = false; + int ptr_depth_resolved = 0; + std::string base_type_resolved; +}; + +// Get pointer depth (int** -> 2, int* -> 1, int -> 0) +inline int get_ptr_depth(tinfo_t tif) { + int depth = 0; + while (tif.is_ptr()) { + depth++; + tif = tif.get_pointed_object(); + } + return depth; +} + +// Get base type name (strips pointers/arrays) +inline std::string get_base_type_name(tinfo_t tif) { + // Strip pointers + while (tif.is_ptr()) { + tif = tif.get_pointed_object(); + } + // Strip arrays + while (tif.is_array()) { + tif = tif.get_array_element(); + } + qstring name; + tif.print(&name); + return name.c_str(); +} + +// Classify a single tinfo_t (surface or resolved) +inline void classify_tinfo(const tinfo_t& tif, + bool& is_ptr, bool& is_int, bool& is_integral, + bool& is_float, bool& is_void, bool& is_struct, + bool& is_array, int& ptr_depth, std::string& base_type) { + is_ptr = tif.is_ptr(); + is_array = tif.is_array(); + is_struct = tif.is_struct() || tif.is_union(); + is_void = tif.is_void(); + is_float = tif.is_float() || tif.is_double() || tif.is_ldouble() || + tif.is_floating(); + + // For int classification, we need to check the actual type + // is_int = exactly "int" type + // is_integral = int-like family + is_integral = tif.is_integral(); // IDA SDK: int, char, short, long, bool, etc. + is_int = tif.is_int(); // IDA SDK: exactly int32/int64 + + ptr_depth = get_ptr_depth(tif); + base_type = get_base_type_name(tif); +} + +// Check if type is a typedef (type reference) at surface level +inline bool is_surface_typedef(const tinfo_t& tif) { + return tif.is_typeref(); +} + +// Classify surface-level type (WITHOUT typedef resolution) +// If tif is a typedef, surface classification shows it as "other" not the underlying type +inline void classify_surface(const tinfo_t& tif, + bool& is_ptr, bool& is_int, bool& is_integral, + bool& is_float, bool& is_void, bool& is_struct, + bool& is_array, int& ptr_depth, std::string& base_type) { + // If it's a typedef, surface level is NOT a ptr/int/etc - it's a typedef + if (is_surface_typedef(tif)) { + is_ptr = false; + is_int = false; + is_integral = false; + is_float = false; + is_void = false; + is_struct = false; + is_array = false; + ptr_depth = 0; + // Get the typedef name as base_type + qstring name; + if (tif.get_type_name(&name)) { + base_type = name.c_str(); + } else { + tif.print(&name); + base_type = name.c_str(); + } + return; + } + + // Not a typedef - classify directly + classify_tinfo(tif, is_ptr, is_int, is_integral, is_float, + is_void, is_struct, is_array, ptr_depth, base_type); +} + +// Full type classification (surface + resolved) +inline TypeClassification classify_arg_type(const tinfo_t& tif) { + TypeClassification tc; + + // Surface classification (without typedef resolution) + classify_surface(tif, + tc.is_ptr, tc.is_int, tc.is_integral, tc.is_float, + tc.is_void, tc.is_struct, tc.is_array, + tc.ptr_depth, tc.base_type); + + // Resolved classification (with typedef resolution) + // IDA SDK's is_ptr(), is_integral(), etc. already resolve typedefs via get_realtype() + classify_tinfo(tif, + tc.is_ptr_resolved, tc.is_int_resolved, tc.is_integral_resolved, + tc.is_float_resolved, tc.is_void_resolved, + tc.is_struct, tc.is_array, // Reuse - struct/array handled by classify_tinfo + tc.ptr_depth_resolved, tc.base_type_resolved); + + return tc; +} + +struct FuncArgEntry { + uint32_t type_ordinal; + std::string type_name; + int arg_index; // -1 for return type + std::string arg_name; + std::string arg_type; + std::string calling_conv; // Only set on arg_index=-1 row + + // Type classification + TypeClassification tc; +}; + +inline std::vector& get_func_args_cache() { + static std::vector cache; + return cache; +} + +inline const char* get_calling_convention_name(cm_t cc) { + // Extract calling convention from cm_t (using CM_CC_MASK) + callcnv_t conv = cc & CM_CC_MASK; + switch (conv) { + case CM_CC_CDECL: return "cdecl"; + case CM_CC_STDCALL: return "stdcall"; + case CM_CC_FASTCALL: return "fastcall"; + case CM_CC_THISCALL: return "thiscall"; + case CM_CC_PASCAL: return "pascal"; + case CM_CC_ELLIPSIS: return "ellipsis"; + case CM_CC_SPECIAL: return "usercall"; + case CM_CC_SPECIALE: return "usercall_ellipsis"; + case CM_CC_SPECIALP: return "usercall_purged"; + case CM_CC_VOIDARG: return "voidarg"; + case CM_CC_UNKNOWN: return "unknown"; + case CM_CC_INVALID: return "invalid"; + default: return "other"; + } +} + +inline void rebuild_func_args_cache() { + auto& cache = get_func_args_cache(); + cache.clear(); + + til_t* ti = get_idati(); + if (!ti) return; + + uint32_t max_ord = get_ordinal_limit(ti); + if (max_ord == 0 || max_ord == uint32_t(-1)) return; + + for (uint32_t ord = 1; ord < max_ord; ++ord) { + const char* name = get_numbered_type_name(ti, ord); + if (!name) continue; // Skip gaps in ordinal space + + tinfo_t tif; + if (tif.get_numbered_type(ti, ord)) { + if (tif.is_func()) { + func_type_data_t fi; + if (tif.get_func_details(&fi)) { + // Return type (arg_index = -1) + FuncArgEntry ret_entry; + ret_entry.type_ordinal = ord; + ret_entry.type_name = name; + ret_entry.arg_index = -1; + ret_entry.arg_name = "(return)"; + + qstring ret_str; + fi.rettype.print(&ret_str); + ret_entry.arg_type = ret_str.c_str(); + ret_entry.calling_conv = get_calling_convention_name(fi.get_cc()); + ret_entry.tc = classify_arg_type(fi.rettype); + cache.push_back(ret_entry); + + // Arguments + for (size_t i = 0; i < fi.size(); i++) { + const funcarg_t& a = fi[i]; + FuncArgEntry entry; + entry.type_ordinal = ord; + entry.type_name = name; + entry.arg_index = static_cast(i); + entry.arg_name = a.name.empty() ? "" : a.name.c_str(); + + qstring type_str; + a.type.print(&type_str); + entry.arg_type = type_str.c_str(); + entry.tc = classify_arg_type(a.type); + // calling_conv only on return type row + cache.push_back(entry); + } + } + } + } + } +} + +/** + * Iterator for function args of a specific function type. + * Used when query has: WHERE type_ordinal = X + */ +class FuncArgsInTypeIterator : public xsql::RowIterator { + uint32_t type_ordinal_; + std::string type_name_; + func_type_data_t fi_; + int idx_ = -2; // Start at -2, first next() moves to -1 (return type) + bool valid_ = false; + bool has_data_ = false; + +public: + explicit FuncArgsInTypeIterator(uint32_t ordinal) : type_ordinal_(ordinal) { + til_t* ti = get_idati(); + if (!ti) return; + + const char* name = get_numbered_type_name(ti, type_ordinal_); + if (!name) return; + type_name_ = name; + + tinfo_t tif; + if (tif.get_numbered_type(ti, type_ordinal_)) { + if (tif.is_func()) { + has_data_ = tif.get_func_details(&fi_); + } + } + } + + bool next() override { + if (!has_data_) return false; + ++idx_; + // idx_=-1 is return type, then 0..n-1 are args + valid_ = (idx_ >= -1 && static_cast(idx_) < fi_.size() + 1); + // Adjust: idx_=-1 is return, idx_=0 is first arg, etc. + // Total items = 1 (return) + fi_.size() (args) + valid_ = (idx_ >= -1 && idx_ < static_cast(fi_.size())); + // Correction: idx=-1 is return, idx=0..fi_.size()-1 are args + // So valid when idx >= -1 and idx < fi_.size() + // Actually: return type is one row, args are fi_.size() rows + // Total rows = 1 + fi_.size() + valid_ = (idx_ >= -1 && idx_ <= static_cast(fi_.size()) - 1 + 1 - 1); + // Simpler: idx=-1 valid, idx=0..fi_.size()-1 valid + valid_ = (idx_ == -1) || (idx_ >= 0 && static_cast(idx_) < fi_.size()); + return valid_; + } + + bool eof() const override { + return idx_ >= -1 && !valid_; + } + + void column(sqlite3_context* ctx, int col) override { + if (!valid_) { + sqlite3_result_null(ctx); + return; + } + + // Get the type for classification (computed on-the-fly for iterator) + auto get_current_type = [&]() -> tinfo_t { + if (idx_ == -1) return fi_.rettype; + if (static_cast(idx_) < fi_.size()) return fi_[idx_].type; + return tinfo_t(); + }; + + switch (col) { + case 0: // type_ordinal + sqlite3_result_int(ctx, type_ordinal_); + break; + case 1: // type_name + sqlite3_result_text(ctx, type_name_.c_str(), -1, SQLITE_TRANSIENT); + break; + case 2: // arg_index + sqlite3_result_int(ctx, idx_); + break; + case 3: // arg_name + if (idx_ == -1) { + sqlite3_result_text(ctx, "(return)", -1, SQLITE_STATIC); + } else if (static_cast(idx_) < fi_.size()) { + sqlite3_result_text(ctx, fi_[idx_].name.c_str(), -1, SQLITE_TRANSIENT); + } else { + sqlite3_result_null(ctx); + } + break; + case 4: // arg_type + if (idx_ == -1) { + qstring ret_str; + fi_.rettype.print(&ret_str); + sqlite3_result_text(ctx, ret_str.c_str(), -1, SQLITE_TRANSIENT); + } else if (static_cast(idx_) < fi_.size()) { + qstring type_str; + fi_[idx_].type.print(&type_str); + sqlite3_result_text(ctx, type_str.c_str(), -1, SQLITE_TRANSIENT); + } else { + sqlite3_result_null(ctx); + } + break; + case 5: // calling_conv + if (idx_ == -1) { + sqlite3_result_text(ctx, get_calling_convention_name(fi_.get_cc()), -1, SQLITE_STATIC); + } else { + sqlite3_result_text(ctx, "", -1, SQLITE_STATIC); + } + break; + // Type classification columns (computed on-the-fly) + case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: + case 15: case 16: case 17: case 18: case 19: case 20: case 21: { + TypeClassification tc = classify_arg_type(get_current_type()); + switch (col) { + case 6: sqlite3_result_int(ctx, tc.is_ptr ? 1 : 0); break; + case 7: sqlite3_result_int(ctx, tc.is_int ? 1 : 0); break; + case 8: sqlite3_result_int(ctx, tc.is_integral ? 1 : 0); break; + case 9: sqlite3_result_int(ctx, tc.is_float ? 1 : 0); break; + case 10: sqlite3_result_int(ctx, tc.is_void ? 1 : 0); break; + case 11: sqlite3_result_int(ctx, tc.is_struct ? 1 : 0); break; + case 12: sqlite3_result_int(ctx, tc.is_array ? 1 : 0); break; + case 13: sqlite3_result_int(ctx, tc.ptr_depth); break; + case 14: sqlite3_result_text(ctx, tc.base_type.c_str(), -1, SQLITE_TRANSIENT); break; + case 15: sqlite3_result_int(ctx, tc.is_ptr_resolved ? 1 : 0); break; + case 16: sqlite3_result_int(ctx, tc.is_int_resolved ? 1 : 0); break; + case 17: sqlite3_result_int(ctx, tc.is_integral_resolved ? 1 : 0); break; + case 18: sqlite3_result_int(ctx, tc.is_float_resolved ? 1 : 0); break; + case 19: sqlite3_result_int(ctx, tc.is_void_resolved ? 1 : 0); break; + case 20: sqlite3_result_int(ctx, tc.ptr_depth_resolved); break; + case 21: sqlite3_result_text(ctx, tc.base_type_resolved.c_str(), -1, SQLITE_TRANSIENT); break; + } + break; + } + default: + sqlite3_result_null(ctx); + break; + } + } + + int64_t rowid() const override { + return static_cast(type_ordinal_) * 10000 + (idx_ + 1); + } +}; + +inline VTableDef define_types_func_args() { + return table("types_func_args") + .count([]() { + rebuild_func_args_cache(); + return get_func_args_cache().size(); + }) + .column_int("type_ordinal", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? cache[i].type_ordinal : 0; + }) + .column_text("type_name", [](size_t i) -> std::string { + auto& cache = get_func_args_cache(); + return i < cache.size() ? cache[i].type_name : ""; + }) + .column_int("arg_index", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? cache[i].arg_index : 0; + }) + .column_text("arg_name", [](size_t i) -> std::string { + auto& cache = get_func_args_cache(); + return i < cache.size() ? cache[i].arg_name : ""; + }) + .column_text("arg_type", [](size_t i) -> std::string { + auto& cache = get_func_args_cache(); + return i < cache.size() ? cache[i].arg_type : ""; + }) + .column_text("calling_conv", [](size_t i) -> std::string { + auto& cache = get_func_args_cache(); + return i < cache.size() ? cache[i].calling_conv : ""; + }) + // Surface-level type classification + .column_int("is_ptr", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? (cache[i].tc.is_ptr ? 1 : 0) : 0; + }) + .column_int("is_int", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? (cache[i].tc.is_int ? 1 : 0) : 0; + }) + .column_int("is_integral", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? (cache[i].tc.is_integral ? 1 : 0) : 0; + }) + .column_int("is_float", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? (cache[i].tc.is_float ? 1 : 0) : 0; + }) + .column_int("is_void", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? (cache[i].tc.is_void ? 1 : 0) : 0; + }) + .column_int("is_struct", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? (cache[i].tc.is_struct ? 1 : 0) : 0; + }) + .column_int("is_array", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? (cache[i].tc.is_array ? 1 : 0) : 0; + }) + .column_int("ptr_depth", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? cache[i].tc.ptr_depth : 0; + }) + .column_text("base_type", [](size_t i) -> std::string { + auto& cache = get_func_args_cache(); + return i < cache.size() ? cache[i].tc.base_type : ""; + }) + // Resolved type classification (after typedef resolution) + .column_int("is_ptr_resolved", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? (cache[i].tc.is_ptr_resolved ? 1 : 0) : 0; + }) + .column_int("is_int_resolved", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? (cache[i].tc.is_int_resolved ? 1 : 0) : 0; + }) + .column_int("is_integral_resolved", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? (cache[i].tc.is_integral_resolved ? 1 : 0) : 0; + }) + .column_int("is_float_resolved", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? (cache[i].tc.is_float_resolved ? 1 : 0) : 0; + }) + .column_int("is_void_resolved", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? (cache[i].tc.is_void_resolved ? 1 : 0) : 0; + }) + .column_int("ptr_depth_resolved", [](size_t i) -> int { + auto& cache = get_func_args_cache(); + return i < cache.size() ? cache[i].tc.ptr_depth_resolved : 0; + }) + .column_text("base_type_resolved", [](size_t i) -> std::string { + auto& cache = get_func_args_cache(); + return i < cache.size() ? cache[i].tc.base_type_resolved : ""; + }) + // Constraint pushdown: type_ordinal = X + .filter_eq("type_ordinal", [](int64_t ordinal) -> std::unique_ptr { + return std::make_unique(static_cast(ordinal)); + }, 10.0, 5.0) + .build(); +} + +// ============================================================================ +// Types Registry +// ============================================================================ + +struct TypesRegistry { + VTableDef types; + VTableDef types_members; + VTableDef types_enum_values; + VTableDef types_func_args; + + TypesRegistry() + : types(define_types()) + , types_members(define_types_members()) + , types_enum_values(define_types_enum_values()) + , types_func_args(define_types_func_args()) + {} + + void register_all(xsql::Database& db) { + // Register tables + db.register_table("ida_types", &types); + db.create_table("types", "ida_types"); + + db.register_table("ida_types_members", &types_members); + db.create_table("types_members", "ida_types_members"); + + db.register_table("ida_types_enum_values", &types_enum_values); + db.create_table("types_enum_values", "ida_types_enum_values"); + + db.register_table("ida_types_func_args", &types_func_args); + db.create_table("types_func_args", "ida_types_func_args"); + + // Create views + create_views(db); + } + +private: + void create_views(xsql::Database& db) { + // Filtering views + db.exec("CREATE VIEW IF NOT EXISTS types_v_structs AS SELECT * FROM types WHERE is_struct = 1"); + db.exec("CREATE VIEW IF NOT EXISTS types_v_unions AS SELECT * FROM types WHERE is_union = 1"); + db.exec("CREATE VIEW IF NOT EXISTS types_v_enums AS SELECT * FROM types WHERE is_enum = 1"); + db.exec("CREATE VIEW IF NOT EXISTS types_v_typedefs AS SELECT * FROM types WHERE is_typedef = 1"); + db.exec("CREATE VIEW IF NOT EXISTS types_v_funcs AS SELECT * FROM types WHERE is_func = 1"); + } +}; + +} // namespace types +} // namespace idasql + +``` + +`src/lib/include/idasql/functions.hpp`: + +```hpp +/** + * ida_sql_functions.hpp - Custom SQL functions for IDA operations + * + * Query Functions: + * - disasm(address) - Get disassembly line at address + * - disasm(address, count) - Get multiple disassembly lines + * - decompile(address) - Get decompiled pseudocode for function + * - bytes(address, count) - Get bytes as hex string + * - bytes_raw(address, count) - Get bytes as blob + * - name_at(address) - Get name at address + * - func_at(address) - Get function name containing address + * - func_start(address) - Get start address of function containing address + * - func_end(address) - Get end address of function containing address + * - xrefs_to(address) - Get xrefs to address (JSON array) + * - xrefs_from(address) - Get xrefs from address (JSON array) + * - segment_at(address) - Get segment name containing address + * - comment_at(address) - Get comment at address + * - set_comment(address, text) - Set comment at address + * - set_name(address, name) - Set name at address + * + * Function Index Functions (O(1) access): + * - func_qty() - Get total function count + * - func_at_index(n) - Get function address at index n + * + * Instruction Decoding Functions: + * - itype(address) - Get instruction type code at address + * - decode_insn(address) - Get full instruction info as JSON + * - operand_type(address, n) - Get operand type (0-5) + * - operand_value(address, n) - Get operand value/address + * + * File Generation Functions: + * - gen_asm_file(ea1, ea2, path) - Generate assembly file + * - gen_lst_file(ea1, ea2, path) - Generate listing file (with addresses) + * - gen_map_file(path) - Generate MAP file + * - gen_idc_file(ea1, ea2, path) - Generate IDC script + * - gen_html_file(ea1, ea2, path) - Generate HTML listing + * - gen_cfg_dot(address) - Generate CFG as DOT (returns string) + * - gen_cfg_dot_file(address, path) - Generate CFG DOT to file + * - gen_schema_dot() - Generate schema diagram as DOT + * + * Database Persistence: + * - save_database() - Persist changes to .i64 file (returns 1/0) + * + * Introspection (standard SQLite): + * - SELECT * FROM sqlite_master WHERE type='table' + * - PRAGMA table_info(tablename) + * - PRAGMA table_xinfo(tablename) + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include + +// IDA SDK headers (order matters) +#include +#include +#include // Must come early +#include // insn_t, decode_insn for instruction decoding +#include +#include +#include +#include +#include +#include +#include // gen_file +#include // qfile_t for file operations +#include // FlowChart for CFG generation +#include // String list functions +#include // String type constants + +// Hex-Rays decompiler - always included, runtime detection +#include +#include // For hexrays_available() + +namespace idasql { +namespace functions { + +// ============================================================================ +// Disassembly Functions +// ============================================================================ + +// disasm(address) - Get single disassembly line +// disasm(address, count) - Get multiple lines +static void sql_disasm(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("disasm requires at least 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + int count = (argc >= 2) ? argv[1].as_int() : 1; + if (count < 1) count = 1; + if (count > 1000) count = 1000; // Safety limit + + std::ostringstream result; + for (int i = 0; i < count && ea != BADADDR; i++) { + qstring line; + if (generate_disasm_line(&line, ea, GENDSM_FORCE_CODE)) { + // Strip color codes + tag_remove(&line); + if (i > 0) result << "\n"; + result << std::hex << ea << ": " << line.c_str(); + } + ea = next_head(ea, BADADDR); + } + + std::string str = result.str(); + ctx.result_text(str); +} + +// ============================================================================ +// Bytes Functions +// ============================================================================ + +// bytes(address, count) - Get bytes as hex string +static void sql_bytes_hex(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 2) { + ctx.result_error("bytes requires 2 arguments (address, count)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + size_t count = static_cast(argv[1].as_int()); + if (count > 4096) count = 4096; // Safety limit + + std::ostringstream result; + result << std::hex << std::setfill('0'); + for (size_t i = 0; i < count; i++) { + if (i > 0) result << " "; + uchar byte = get_byte(ea + i); + result << std::setw(2) << static_cast(byte); + } + + std::string str = result.str(); + ctx.result_text(str); +} + +// bytes_raw(address, count) - Get bytes as blob +static void sql_bytes_raw(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 2) { + ctx.result_error("bytes_raw requires 2 arguments (address, count)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + size_t count = static_cast(argv[1].as_int()); + if (count > 4096) count = 4096; // Safety limit + + std::vector data(count); + for (size_t i = 0; i < count; i++) { + data[i] = get_byte(ea + i); + } + + ctx.result_blob(data.data(), static_cast(data.size())); +} + +// ============================================================================ +// Name Functions +// ============================================================================ + +// name_at(address) - Get name at address +static void sql_name_at(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("name_at requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + qstring name; + if (get_name(&name, ea) > 0 && !name.empty()) { + ctx.result_text(name.c_str()); + } else { + ctx.result_null(); + } +} + +// func_at(address) - Get function name containing address +static void sql_func_at(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("func_at requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + func_t* func = get_func(ea); + if (func) { + qstring name; + if (get_func_name(&name, func->start_ea) > 0) { + ctx.result_text(name.c_str()); + return; + } + } + ctx.result_null(); +} + +// func_start(address) - Get start address of function containing address +static void sql_func_start(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("func_start requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + func_t* func = get_func(ea); + if (func) { + ctx.result_int64(func->start_ea); + } else { + ctx.result_null(); + } +} + +// func_end(address) - Get end address of function containing address +static void sql_func_end(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("func_end requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + func_t* func = get_func(ea); + if (func) { + ctx.result_int64(func->end_ea); + } else { + ctx.result_null(); + } +} + +// ============================================================================ +// Function Index Functions (O(1) access) +// ============================================================================ + +// func_qty() - Get total function count +static void sql_func_qty(xsql::FunctionContext& ctx, int, xsql::FunctionArg*) { + ctx.result_int64(get_func_qty()); +} + +// func_at_index(n) - Get function address at index n +static void sql_func_at_index(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("func_at_index requires 1 argument (index)"); + return; + } + + size_t idx = static_cast(argv[0].as_int64()); + size_t qty = get_func_qty(); + + if (idx >= qty) { + ctx.result_null(); + return; + } + + func_t* f = getn_func(idx); + if (f) { + ctx.result_int64(f->start_ea); + } else { + ctx.result_null(); + } +} + +// ============================================================================ +// Name Modification Functions +// ============================================================================ + +// set_name(address, name) - Set name at address +static void sql_set_name(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 2) { + ctx.result_error("set_name requires 2 arguments (address, name)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + const char* name = argv[1].as_c_str(); + + bool success = set_name(ea, name, SN_CHECK) != 0; + if (success) decompiler::invalidate_decompiler_cache(ea); + ctx.result_int(success ? 1 : 0); +} + +// ============================================================================ +// Segment Functions +// ============================================================================ + +// segment_at(address) - Get segment name containing address +static void sql_segment_at(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("segment_at requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + segment_t* seg = getseg(ea); + if (seg) { + qstring name; + if (get_segm_name(&name, seg) > 0) { + ctx.result_text(name.c_str()); + return; + } + } + ctx.result_null(); +} + +// ============================================================================ +// Comment Functions +// ============================================================================ + +// comment_at(address) - Get comment at address +static void sql_comment_at(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("comment_at requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + qstring cmt; + if (get_cmt(&cmt, ea, false) > 0) { + ctx.result_text(cmt.c_str()); + } else if (get_cmt(&cmt, ea, true) > 0) { + // Try repeatable comment + ctx.result_text(cmt.c_str()); + } else { + ctx.result_null(); + } +} + +// set_comment(address, text) - Set comment at address +// set_comment(address, text, repeatable) - Set comment with type +static void sql_set_comment(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 2) { + ctx.result_error("set_comment requires 2-3 arguments (address, text, [repeatable])"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + const char* cmt = argv[1].as_c_str(); + bool repeatable = (argc >= 3) ? argv[2].as_int() != 0 : false; + + bool success = set_cmt(ea, cmt ? cmt : "", repeatable); + ctx.result_int(success ? 1 : 0); +} + +// ============================================================================ +// Cross-Reference Functions +// ============================================================================ + +// xrefs_to(address) - Get xrefs to address as JSON array +static void sql_xrefs_to(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("xrefs_to requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + + xsql::json arr = xsql::json::array(); + xrefblk_t xb; + for (bool ok = xb.first_to(ea, XREF_ALL); ok; ok = xb.next_to()) { + arr.push_back({{"from", xb.from}, {"type", static_cast(xb.type)}}); + } + + std::string str = arr.dump(); + ctx.result_text(str); +} + +// xrefs_from(address) - Get xrefs from address as JSON array +static void sql_xrefs_from(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("xrefs_from requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + + xsql::json arr = xsql::json::array(); + xrefblk_t xb; + for (bool ok = xb.first_from(ea, XREF_ALL); ok; ok = xb.next_from()) { + arr.push_back({{"to", xb.to}, {"type", static_cast(xb.type)}}); + } + + std::string str = arr.dump(); + ctx.result_text(str); +} + +// ============================================================================ +// Decompiler Functions (Optional - requires Hex-Rays) +// ============================================================================ + +// Render pseudocode lines with ea prefixes +static std::string render_pseudocode(cfuncptr_t& cfunc) { + const strvec_t& sv = cfunc->get_pseudocode(); + std::ostringstream result; + for (size_t i = 0; i < sv.size(); i++) { + ea_t line_ea = decompiler::extract_line_ea(&*cfunc, sv[i].line); + qstring line = sv[i].line; + tag_remove(&line); + if (i > 0) result << "\n"; + char prefix[48]; + if (line_ea != 0 && line_ea != BADADDR) + qsnprintf(prefix, sizeof(prefix), "/* %a */ ", line_ea); + else + qsnprintf(prefix, sizeof(prefix), "/* */ "); + result << prefix << line.c_str(); + } + return result.str(); +} + +// decompile(address) - Get decompiled pseudocode (runtime Hex-Rays detection) +// Uses decompiler::hexrays_available() set during DecompilerRegistry::register_all() +static void sql_decompile(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("decompile requires 1 argument (address)"); + return; + } + + // Check cached Hex-Rays availability (set during DecompilerRegistry::register_all) + if (!decompiler::hexrays_available()) { + ctx.result_error("Decompiler not available (requires Hex-Rays license)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + + func_t* func = get_func(ea); + if (!func) { + ctx.result_error("No function at address"); + return; + } + + hexrays_failure_t hf; + cfuncptr_t cfunc = decompile(func, &hf); + if (!cfunc) { + std::string err = "Decompilation failed: " + std::string(hf.desc().c_str()); + ctx.result_error(err); + return; + } + + std::string str = render_pseudocode(cfunc); + ctx.result_text(str); +} + +// decompile(address, refresh) - Get decompiled pseudocode with optional cache invalidation +// When refresh=1, invalidates the cached decompilation before decompiling. +// Use after renaming functions or local variables to get fresh pseudocode. +static void sql_decompile_2(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 2) { + ctx.result_error("decompile requires 2 arguments (address, refresh)"); + return; + } + + if (!decompiler::hexrays_available()) { + ctx.result_error("Decompiler not available (requires Hex-Rays license)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + int refresh = argv[1].as_int(); + + func_t* func = get_func(ea); + if (!func) { + ctx.result_error("No function at address"); + return; + } + + if (refresh) { + mark_cfunc_dirty(func->start_ea, false); + } + + hexrays_failure_t hf; + cfuncptr_t cfunc = decompile(func, &hf); + if (!cfunc) { + std::string err = "Decompilation failed: " + std::string(hf.desc().c_str()); + ctx.result_error(err); + return; + } + + std::string str = render_pseudocode(cfunc); + ctx.result_text(str); +} + +// ============================================================================ +// Address Utility Functions +// ============================================================================ + +// next_head(address) - Get next defined head +static void sql_next_head(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("next_head requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + ea_t next = next_head(ea, BADADDR); + if (next != BADADDR) { + ctx.result_int64(next); + } else { + ctx.result_null(); + } +} + +// prev_head(address) - Get previous defined head +static void sql_prev_head(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("prev_head requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + ea_t prev = prev_head(ea, 0); + if (prev != BADADDR) { + ctx.result_int64(prev); + } else { + ctx.result_null(); + } +} + +// hex(value) - Format integer as hex string +static void sql_hex(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("hex requires 1 argument (value)"); + return; + } + + int64_t val = argv[0].as_int64(); + std::ostringstream result; + result << "0x" << std::hex << val; + std::string str = result.str(); + ctx.result_text(str); +} + +// ============================================================================ +// Item Query Functions +// ============================================================================ + +// item_type(address) - Get type of item at address +static void sql_item_type(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("item_type requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + flags64_t f = get_flags(ea); + + const char* type = "unknown"; + if (is_code(f)) type = "code"; + else if (is_strlit(f)) type = "string"; + else if (is_struct(f)) type = "struct"; + else if (is_align(f)) type = "align"; + else if (is_data(f)) type = "data"; + + ctx.result_text_static(type); +} + +// item_size(address) - Get size of item at address +static void sql_item_size(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("item_size requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + asize_t size = get_item_size(ea); + ctx.result_int64(size); +} + +// is_code(address) - Check if address is code +static void sql_is_code(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("is_code requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + ctx.result_int(is_code(get_flags(ea)) ? 1 : 0); +} + +// is_data(address) - Check if address is data +static void sql_is_data(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("is_data requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + ctx.result_int(is_data(get_flags(ea)) ? 1 : 0); +} + +// mnemonic(address) - Get instruction mnemonic +static void sql_mnemonic(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("mnemonic requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + if (!is_code(get_flags(ea))) { + ctx.result_null(); + return; + } + + qstring mnem; + print_insn_mnem(&mnem, ea); + ctx.result_text(mnem.c_str()); +} + +// operand(address, n) - Get operand text +static void sql_operand(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 2) { + ctx.result_error("operand requires 2 arguments (address, operand_num)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + int n = argv[1].as_int(); + + if (!is_code(get_flags(ea)) || n < 0 || n > 5) { + ctx.result_null(); + return; + } + + qstring op; + print_operand(&op, ea, n); + tag_remove(&op); + if (op.empty()) { + ctx.result_null(); + } else { + ctx.result_text(op.c_str()); + } +} + +// flags_at(address) - Get raw flags at address +static void sql_flags_at(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("flags_at requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + ctx.result_int64(get_flags(ea)); +} + +// ============================================================================ +// Instruction Decoding Functions +// ============================================================================ + +// Operand type names +static const char* get_optype_name(optype_t type) { + switch (type) { + case o_void: return "void"; + case o_reg: return "reg"; + case o_mem: return "mem"; + case o_phrase: return "phrase"; + case o_displ: return "displ"; + case o_imm: return "imm"; + case o_far: return "far"; + case o_near: return "near"; + default: return "idpspec"; + } +} + +// itype(address) - Get instruction type code +static void sql_itype(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("itype requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + + if (!is_code(get_flags(ea))) { + ctx.result_null(); + return; + } + + insn_t insn; + if (decode_insn(&insn, ea) > 0) { + ctx.result_int(insn.itype); + } else { + ctx.result_null(); + } +} + +// decode_insn(address) - Get full instruction info as JSON +static void sql_decode_insn(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("decode_insn requires 1 argument (address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + + if (!is_code(get_flags(ea))) { + ctx.result_null(); + return; + } + + insn_t insn; + int len = decode_insn(&insn, ea); + if (len <= 0) { + ctx.result_null(); + return; + } + + // Get mnemonic + qstring mnem; + print_insn_mnem(&mnem, ea); + + // Build JSON using xsql::json + xsql::json result = { + {"ea", insn.ea}, + {"itype", insn.itype}, + {"size", insn.size}, + {"mnemonic", mnem.c_str()} + }; + + // Operands array + xsql::json ops = xsql::json::array(); + for (int i = 0; i < UA_MAXOP; i++) { + const op_t& op = insn.ops[i]; + if (op.type == o_void) break; + + // Get operand text + qstring op_text; + print_operand(&op_text, ea, i); + tag_remove(&op_text); + + ops.push_back({ + {"n", i}, + {"type", static_cast(op.type)}, + {"type_name", get_optype_name(op.type)}, + {"dtype", static_cast(op.dtype)}, + {"reg", op.reg}, + {"addr", op.addr}, + {"value", op.value}, + {"text", op_text.c_str()} // nlohmann auto-escapes + }); + } + result["operands"] = ops; + + std::string str = result.dump(); + ctx.result_text(str); +} + +// operand_type(address, n) - Get operand type +static void sql_operand_type(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 2) { + ctx.result_error("operand_type requires 2 arguments (address, operand_num)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + int n = argv[1].as_int(); + + if (!is_code(get_flags(ea)) || n < 0 || n >= UA_MAXOP) { + ctx.result_null(); + return; + } + + insn_t insn; + if (decode_insn(&insn, ea) <= 0) { + ctx.result_null(); + return; + } + + const op_t& op = insn.ops[n]; + if (op.type == o_void) { + ctx.result_null(); + } else { + ctx.result_text_static(get_optype_name(op.type)); + } +} + +// operand_value(address, n) - Get operand value (immediate or address) +static void sql_operand_value(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 2) { + ctx.result_error("operand_value requires 2 arguments (address, operand_num)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + int n = argv[1].as_int(); + + if (!is_code(get_flags(ea)) || n < 0 || n >= UA_MAXOP) { + ctx.result_null(); + return; + } + + insn_t insn; + if (decode_insn(&insn, ea) <= 0) { + ctx.result_null(); + return; + } + + const op_t& op = insn.ops[n]; + switch (op.type) { + case o_void: + ctx.result_null(); + break; + case o_imm: + ctx.result_int64(op.value); + break; + case o_mem: + case o_near: + case o_far: + case o_displ: + ctx.result_int64(op.addr); + break; + case o_reg: + ctx.result_int(op.reg); + break; + default: + ctx.result_int64(op.value); + break; + } +} + +// ============================================================================ +// File Generation Functions +// ============================================================================ + +// Helper: Generate file using ida_loader.gen_file +static int gen_file_helper(ofile_type_t ofile_type, const char* filepath, ea_t ea1, ea_t ea2, int flags) { + qstring path(filepath); + FILE* fp = qfopen(path.c_str(), "w"); + if (!fp) return -1; + + int result = gen_file(ofile_type, fp, ea1, ea2, flags); + qfclose(fp); + return result; +} + +// gen_asm_file(ea1, ea2, path) - Generate assembly file +static void sql_gen_asm_file(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 3) { + ctx.result_error("gen_asm_file requires 3 arguments (ea1, ea2, path)"); + return; + } + + ea_t ea1 = static_cast(argv[0].as_int64()); + ea_t ea2 = static_cast(argv[1].as_int64()); + const char* path = argv[2].as_c_str(); + if (!path) { + ctx.result_error("Invalid path"); + return; + } + + int result = gen_file_helper(OFILE_ASM, path, ea1, ea2, 0); + ctx.result_int(result); +} + +// gen_lst_file(ea1, ea2, path) - Generate listing file with addresses +static void sql_gen_lst_file(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 3) { + ctx.result_error("gen_lst_file requires 3 arguments (ea1, ea2, path)"); + return; + } + + ea_t ea1 = static_cast(argv[0].as_int64()); + ea_t ea2 = static_cast(argv[1].as_int64()); + const char* path = argv[2].as_c_str(); + if (!path) { + ctx.result_error("Invalid path"); + return; + } + + int result = gen_file_helper(OFILE_LST, path, ea1, ea2, 0); + ctx.result_int(result); +} + +// gen_map_file(path) - Generate MAP file +static void sql_gen_map_file(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("gen_map_file requires 1 argument (path)"); + return; + } + + const char* path = argv[0].as_c_str(); + if (!path) { + ctx.result_error("Invalid path"); + return; + } + + // MAP files ignore ea1/ea2, use GENFLG_MAPSEG | GENFLG_MAPNAME + int flags = GENFLG_MAPSEG | GENFLG_MAPNAME | GENFLG_MAPDMNG; + int result = gen_file_helper(OFILE_MAP, path, 0, BADADDR, flags); + ctx.result_int(result); +} + +// gen_idc_file(ea1, ea2, path) - Generate IDC script +static void sql_gen_idc_file(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 3) { + ctx.result_error("gen_idc_file requires 3 arguments (ea1, ea2, path)"); + return; + } + + ea_t ea1 = static_cast(argv[0].as_int64()); + ea_t ea2 = static_cast(argv[1].as_int64()); + const char* path = argv[2].as_c_str(); + if (!path) { + ctx.result_error("Invalid path"); + return; + } + + int result = gen_file_helper(OFILE_IDC, path, ea1, ea2, 0); + ctx.result_int(result); +} + +// gen_html_file(ea1, ea2, path) - Generate HTML listing +static void sql_gen_html_file(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 3) { + ctx.result_error("gen_html_file requires 3 arguments (ea1, ea2, path)"); + return; + } + + ea_t ea1 = static_cast(argv[0].as_int64()); + ea_t ea2 = static_cast(argv[1].as_int64()); + const char* path = argv[2].as_c_str(); + if (!path) { + ctx.result_error("Invalid path"); + return; + } + + int result = gen_file_helper(OFILE_LST, path, ea1, ea2, GENFLG_GENHTML); + ctx.result_int(result); +} + +// gen_cfg_dot(address) - Generate CFG as DOT string +static void sql_gen_cfg_dot(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("gen_cfg_dot requires 1 argument (func_address)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + func_t* func = get_func(ea); + if (!func) { + ctx.result_error("No function at address"); + return; + } + + // Build DOT representation using FlowChart + qflow_chart_t fc; + fc.create("", func, func->start_ea, func->end_ea, FC_NOEXT); + + qstring func_name; + get_func_name(&func_name, func->start_ea); + if (func_name.empty()) { + func_name.sprnt("sub_%llX", (uint64)func->start_ea); + } + + std::ostringstream dot; + dot << "digraph CFG {\n"; + dot << " node [shape=box, fontname=\"Courier\"];\n"; + dot << " label=\"" << func_name.c_str() << "\";\n\n"; + + // Emit nodes + for (int i = 0; i < fc.size(); i++) { + const qbasic_block_t& bb = fc.blocks[i]; + dot << " n" << i << " [label=\""; + dot << std::hex << "0x" << bb.start_ea << " - 0x" << bb.end_ea; + dot << "\"];\n"; + } + + dot << "\n"; + + // Emit edges + for (int i = 0; i < fc.size(); i++) { + const qbasic_block_t& bb = fc.blocks[i]; + for (int j = 0; j < bb.succ.size(); j++) { + dot << " n" << i << " -> n" << bb.succ[j] << ";\n"; + } + } + + dot << "}\n"; + + std::string str = dot.str(); + ctx.result_text(str); +} + +// gen_cfg_dot_file(address, path) - Generate CFG DOT to file +static void sql_gen_cfg_dot_file(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 2) { + ctx.result_error("gen_cfg_dot_file requires 2 arguments (func_address, path)"); + return; + } + + ea_t ea = static_cast(argv[0].as_int64()); + const char* path = argv[1].as_c_str(); + if (!path) { + ctx.result_error("Invalid path"); + return; + } + + func_t* func = get_func(ea); + if (!func) { + ctx.result_error("No function at address"); + return; + } + + // Build DOT using FlowChart + qflow_chart_t fc; + fc.create("", func, func->start_ea, func->end_ea, FC_NOEXT); + + qstring func_name; + get_func_name(&func_name, func->start_ea); + if (func_name.empty()) { + func_name.sprnt("sub_%llX", (uint64)func->start_ea); + } + + FILE* fp = qfopen(path, "w"); + if (!fp) { + ctx.result_error("Failed to open file"); + return; + } + + qfprintf(fp, "digraph CFG {\n"); + qfprintf(fp, " node [shape=box, fontname=\"Courier\"];\n"); + qfprintf(fp, " label=\"%s\";\n\n", func_name.c_str()); + + // Emit nodes + for (int i = 0; i < fc.size(); i++) { + const qbasic_block_t& bb = fc.blocks[i]; + qfprintf(fp, " n%d [label=\"0x%llX - 0x%llX\"];\n", + i, (uint64)bb.start_ea, (uint64)bb.end_ea); + } + + qfprintf(fp, "\n"); + + // Emit edges + for (int i = 0; i < fc.size(); i++) { + const qbasic_block_t& bb = fc.blocks[i]; + for (int j = 0; j < bb.succ.size(); j++) { + qfprintf(fp, " n%d -> n%d;\n", i, bb.succ[j]); + } + } + + qfprintf(fp, "}\n"); + qfclose(fp); + + ctx.result_int(1); // Success +} + +// gen_schema_dot(db) - Generate DOT diagram of all tables +// This uses SQLite introspection to build the schema +static void sql_gen_schema_dot(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + sqlite3* db = ctx.db_handle(); + + std::ostringstream dot; + dot << "digraph IDASQL_Schema {\n"; + dot << " rankdir=TB;\n"; + dot << " node [shape=record, fontname=\"Helvetica\", fontsize=10];\n"; + dot << " edge [fontname=\"Helvetica\", fontsize=8];\n\n"; + + // Query all tables from sqlite_master + sqlite3_stmt* stmt; + const char* sql = "SELECT name, type FROM sqlite_master WHERE type IN ('table', 'view') ORDER BY name"; + if (sqlite3_prepare_v2(db, sql, -1, &stmt, nullptr) != SQLITE_OK) { + ctx.result_error("Failed to query schema"); + return; + } + + std::vector tables; + while (sqlite3_step(stmt) == SQLITE_ROW) { + const char* name = (const char*)sqlite3_column_text(stmt, 0); + const char* type = (const char*)sqlite3_column_text(stmt, 1); + if (name) { + tables.push_back(name); + + // Get column info for this table + std::string pragma = "PRAGMA table_info(" + std::string(name) + ")"; + sqlite3_stmt* col_stmt; + if (sqlite3_prepare_v2(db, pragma.c_str(), -1, &col_stmt, nullptr) == SQLITE_OK) { + dot << " " << name << " [label=\"{" << name; + if (type && strcmp(type, "view") == 0) dot << " (view)"; + dot << "|"; + + bool first = true; + while (sqlite3_step(col_stmt) == SQLITE_ROW) { + const char* col_name = (const char*)sqlite3_column_text(col_stmt, 1); + const char* col_type = (const char*)sqlite3_column_text(col_stmt, 2); + if (!first) dot << "\\l"; + first = false; + dot << (col_name ? col_name : "?"); + if (col_type && strlen(col_type) > 0) { + dot << " : " << col_type; + } + } + dot << "\\l}\"];\n"; + sqlite3_finalize(col_stmt); + } + } + } + sqlite3_finalize(stmt); + + // Add relationships based on naming conventions + dot << "\n // Relationships (inferred from naming)\n"; + + // Common relationships in IDA + for (const auto& t : tables) { + if (t == "funcs" || t == "funcs_live") { + dot << " segments -> " << t << " [label=\"contains\"];\n"; + } + if (t == "names" || t == "names_live") { + dot << " segments -> " << t << " [label=\"contains\"];\n"; + } + if (t == "strings") { + dot << " segments -> strings [label=\"contains\"];\n"; + } + if (t == "xrefs") { + dot << " funcs -> xrefs [label=\"has\"];\n"; + dot << " xrefs -> names [label=\"references\"];\n"; + } + if (t == "blocks") { + dot << " funcs -> blocks [label=\"contains\"];\n"; + } + if (t == "comments_live") { + dot << " funcs -> comments_live [label=\"has\"];\n"; + } + } + + dot << "}\n"; + + std::string str = dot.str(); + ctx.result_text(str); +} + +// ============================================================================ +// Decompiler Lvar Functions (requires Hex-Rays) +// ============================================================================ + +// rename_lvar(func_addr, lvar_idx, new_name) - Rename a local variable +// Uses locator-based rename_lvar_at() for precise identification by index. +// Returns JSON with result details. +static void sql_rename_lvar(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 3) { + ctx.result_error("rename_lvar requires 3 arguments (func_addr, lvar_idx, new_name)"); + return; + } + + ea_t func_addr = static_cast(argv[0].as_int64()); + int lvar_idx = argv[1].as_int(); + const char* new_name = argv[2].as_c_str(); + + if (!new_name) { + ctx.result_error("Invalid name"); + return; + } + + bool success = decompiler::rename_lvar_at(func_addr, lvar_idx, new_name); + + xsql::json result = { + {"func_addr", func_addr}, + {"lvar_idx", lvar_idx}, + {"new_name", new_name}, + {"success", success} + }; + if (!success) { + result["error"] = "rename failed"; + } + std::string str = result.dump(); + ctx.result_text(str); +} + +// list_lvars(func_addr) - List local variables for a function as JSON +static void sql_list_lvars(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("list_lvars requires 1 argument (func_addr)"); + return; + } + + ea_t func_addr = static_cast(argv[0].as_int64()); + + // Check cached Hex-Rays availability + if (!decompiler::hexrays_available()) { + ctx.result_error("Hex-Rays not available"); + return; + } + + func_t* f = get_func(func_addr); + if (!f) { + ctx.result_error("Function not found"); + return; + } + + hexrays_failure_t hf; + cfuncptr_t cfunc = decompile(f, &hf); + if (!cfunc) { + std::string err = "Decompilation failed: " + std::string(hf.str.c_str()); + ctx.result_error(err); + return; + } + + lvars_t* lvars = cfunc->get_lvars(); + if (!lvars) { + ctx.result_text_static("[]"); + return; + } + + xsql::json arr = xsql::json::array(); + for (size_t i = 0; i < lvars->size(); i++) { + const lvar_t& lv = (*lvars)[i]; + + qstring type_str; + lv.type().print(&type_str); + + arr.push_back({ + {"idx", i}, + {"name", lv.name.c_str()}, + {"type", type_str.c_str()}, + {"size", lv.width}, + {"is_arg", lv.is_arg_var()}, + {"is_result", lv.is_result_var()} + }); + } + + std::string str = arr.dump(); + ctx.result_text(str); +} + +// ============================================================================ +// Jump Search Functions (unified entity search) +// ============================================================================ + +// Build dynamic SQL query for entity search +// prefix: search pattern +// contains: if true, use '%prefix%', otherwise 'prefix%' +// limit: max results +// offset: pagination offset +inline std::string build_jump_query(const std::string& prefix, bool contains, int limit, int offset) { + if (prefix.empty()) return ""; + + // Escape single quotes in prefix + std::string escaped; + for (char c : prefix) { + if (c == '\'') escaped += "''"; + else escaped += std::tolower(c); + } + + std::string pattern = contains + ? ("'%" + escaped + "%'") + : ("'" + escaped + "%'"); + + std::ostringstream sql; + sql << "SELECT name, kind, address, ordinal, parent_name, full_name FROM (\n"; + + // Functions + sql << " SELECT name, 'function' as kind, address, NULL as ordinal,\n"; + sql << " NULL as parent_name, name as full_name\n"; + sql << " FROM funcs WHERE LOWER(name) LIKE " << pattern << "\n"; + sql << " UNION ALL\n"; + + // Labels (exclude function starts) + sql << " SELECT name, 'label', address, NULL, NULL, name\n"; + sql << " FROM names n WHERE LOWER(name) LIKE " << pattern << "\n"; + sql << " AND NOT EXISTS (SELECT 1 FROM funcs f WHERE f.address = n.address)\n"; + sql << " UNION ALL\n"; + + // Segments + sql << " SELECT name, 'segment', start_ea, NULL, NULL, name\n"; + sql << " FROM segments WHERE LOWER(name) LIKE " << pattern << "\n"; + sql << " UNION ALL\n"; + + // Structs + sql << " SELECT name, 'struct', NULL, ordinal, NULL, name\n"; + sql << " FROM types WHERE is_struct = 1 AND LOWER(name) LIKE " << pattern << "\n"; + sql << " UNION ALL\n"; + + // Unions + sql << " SELECT name, 'union', NULL, ordinal, NULL, name\n"; + sql << " FROM types WHERE is_union = 1 AND LOWER(name) LIKE " << pattern << "\n"; + sql << " UNION ALL\n"; + + // Enums + sql << " SELECT name, 'enum', NULL, ordinal, NULL, name\n"; + sql << " FROM types WHERE is_enum = 1 AND LOWER(name) LIKE " << pattern << "\n"; + sql << " UNION ALL\n"; + + // Struct/union members + sql << " SELECT member_name, 'member', NULL, type_ordinal,\n"; + sql << " type_name, type_name || '.' || member_name\n"; + sql << " FROM types_members WHERE LOWER(member_name) LIKE " << pattern << "\n"; + sql << " UNION ALL\n"; + + // Enum members + sql << " SELECT value_name, 'enum_member', NULL, type_ordinal,\n"; + sql << " type_name, type_name || '.' || value_name\n"; + sql << " FROM types_enum_values WHERE LOWER(value_name) LIKE " << pattern << "\n"; + + sql << ")\n"; + sql << "ORDER BY kind, name\n"; + sql << "LIMIT " << limit << " OFFSET " << offset; + + return sql.str(); +} + +// jump_search(prefix, mode, limit, offset) - Search entities, return JSON array +// mode: 'prefix' or 'contains' +static void sql_jump_search(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 4) { + ctx.result_error("jump_search requires 4 arguments (prefix, mode, limit, offset)"); + return; + } + + const char* prefix = argv[0].as_c_str(); + const char* mode = argv[1].as_c_str(); + int limit = argv[2].as_int(); + int offset = argv[3].as_int(); + + if (!prefix || !mode) { + ctx.result_error("Invalid arguments"); + return; + } + + bool contains = (strcmp(mode, "contains") == 0); + std::string query = build_jump_query(prefix, contains, limit, offset); + + if (query.empty()) { + ctx.result_text_static("[]"); + return; + } + + // Execute query and build JSON result + sqlite3* db = ctx.db_handle(); + sqlite3_stmt* stmt; + + if (sqlite3_prepare_v2(db, query.c_str(), -1, &stmt, nullptr) != SQLITE_OK) { + std::string err = "Query error: " + std::string(sqlite3_errmsg(db)); + ctx.result_error(err); + return; + } + + xsql::json arr = xsql::json::array(); + + while (sqlite3_step(stmt) == SQLITE_ROW) { + const char* name = (const char*)sqlite3_column_text(stmt, 0); + const char* kind = (const char*)sqlite3_column_text(stmt, 1); + int64_t address = sqlite3_column_int64(stmt, 2); + int ordinal = sqlite3_column_int(stmt, 3); + const char* parent = (const char*)sqlite3_column_text(stmt, 4); + const char* full_name = (const char*)sqlite3_column_text(stmt, 5); + + xsql::json obj = { + {"name", name ? name : ""}, + {"kind", kind ? kind : ""}, + {"full_name", full_name ? full_name : ""} + }; + + // Handle nullable fields + if (sqlite3_column_type(stmt, 2) != SQLITE_NULL) { + obj["address"] = address; + } else { + obj["address"] = nullptr; + } + + if (sqlite3_column_type(stmt, 3) != SQLITE_NULL) { + obj["ordinal"] = ordinal; + } else { + obj["ordinal"] = nullptr; + } + + obj["parent_name"] = parent ? xsql::json(parent) : xsql::json(nullptr); + + arr.push_back(obj); + } + + sqlite3_finalize(stmt); + + std::string result = arr.dump(); + ctx.result_text(result); +} + +// jump_query(prefix, mode, limit, offset) - Return the SQL query string +static void sql_jump_query(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 4) { + ctx.result_error("jump_query requires 4 arguments (prefix, mode, limit, offset)"); + return; + } + + const char* prefix = argv[0].as_c_str(); + const char* mode = argv[1].as_c_str(); + int limit = argv[2].as_int(); + int offset = argv[3].as_int(); + + if (!prefix || !mode) { + ctx.result_error("Invalid arguments"); + return; + } + + bool contains = (strcmp(mode, "contains") == 0); + std::string query = build_jump_query(prefix, contains, limit, offset); + + ctx.result_text(query); +} + +// ============================================================================ +// String List Functions +// ============================================================================ + +// rebuild_strings() - Rebuild IDA's string list +// Returns: number of strings found +// +// Args (all optional): +// min_len: minimum string length (default 5) +// types: string types bitmask (default 3 = ASCII + UTF-16) +// 1 = ASCII (STRTYPE_C) +// 2 = UTF-16 (STRTYPE_C_16) +// 4 = UTF-32 (STRTYPE_C_32) +// 3 = ASCII + UTF-16 (default) +// 7 = all types +// +// Example: +// SELECT rebuild_strings(); -- Default: ASCII + UTF-16, minlen 5 +// SELECT rebuild_strings(4); -- ASCII + UTF-16, minlen 4 +// SELECT rebuild_strings(5, 1); -- ASCII only, minlen 5 +// SELECT rebuild_strings(5, 7); -- All types, minlen 5 +static void sql_rebuild_strings(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + int min_len = 5; + int types_mask = 3; // Default: ASCII + UTF-16 + + if (argc >= 1 && !argv[0].is_null()) { + min_len = argv[0].as_int(); + if (min_len < 1) min_len = 1; + if (min_len > 1000) min_len = 1000; + } + if (argc >= 2 && !argv[1].is_null()) { + types_mask = argv[1].as_int(); + } + + // Get the options pointer - despite 'const', it IS modifiable (same as Python bindings) + strwinsetup_t* opts = const_cast(get_strlist_options()); + + // Configure string types based on mask + opts->strtypes.clear(); + if (types_mask & 1) opts->strtypes.push_back(STRTYPE_C); // ASCII + if (types_mask & 2) opts->strtypes.push_back(STRTYPE_C_16); // UTF-16 + if (types_mask & 4) opts->strtypes.push_back(STRTYPE_C_32); // UTF-32 + + // Set minimum length + opts->minlen = min_len; + + // Allow extended ASCII + opts->only_7bit = 0; + + // Clear and rebuild with new settings + clear_strlist(); + build_strlist(); + + // Invalidate the strings virtual table cache so queries see new data + entities::TableRegistry::invalidate_strings_cache_global(); + + // Return the count + size_t count = get_strlist_qty(); + ctx.result_int64(static_cast(count)); +} + +// string_count() - Get current count of strings in IDA's cached list (no rebuild) +static void sql_string_count(xsql::FunctionContext& ctx, int /*argc*/, xsql::FunctionArg* /*argv*/) { + ctx.result_int64(static_cast(get_strlist_qty())); +} + +// ============================================================================ +// Database Persistence +// ============================================================================ + +// save_database() - Persist changes to the IDA database file +// Returns: 1 on success, 0 on failure +static void sql_save_database(xsql::FunctionContext& ctx, int /*argc*/, xsql::FunctionArg* /*argv*/) { + bool ok = save_database(); // IDA API: save to current file with default flags + ctx.result_int(ok ? 1 : 0); +} + +// ============================================================================ +// Registration +// ============================================================================ + +inline bool register_sql_functions(xsql::Database& db) { + // Disassembly + db.register_function("disasm", 1, xsql::ScalarFn(sql_disasm)); + db.register_function("disasm", 2, xsql::ScalarFn(sql_disasm)); + + // Bytes + db.register_function("bytes", 2, xsql::ScalarFn(sql_bytes_hex)); + db.register_function("bytes_raw", 2, xsql::ScalarFn(sql_bytes_raw)); + + // Names + db.register_function("name_at", 1, xsql::ScalarFn(sql_name_at)); + db.register_function("func_at", 1, xsql::ScalarFn(sql_func_at)); + db.register_function("func_start", 1, xsql::ScalarFn(sql_func_start)); + db.register_function("func_end", 1, xsql::ScalarFn(sql_func_end)); + db.register_function("set_name", 2, xsql::ScalarFn(sql_set_name)); + + // Function index (O(1) access) + db.register_function("func_qty", 0, xsql::ScalarFn(sql_func_qty)); + db.register_function("func_at_index", 1, xsql::ScalarFn(sql_func_at_index)); + + // Segments + db.register_function("segment_at", 1, xsql::ScalarFn(sql_segment_at)); + + // Comments + db.register_function("comment_at", 1, xsql::ScalarFn(sql_comment_at)); + db.register_function("set_comment", 2, xsql::ScalarFn(sql_set_comment)); + db.register_function("set_comment", 3, xsql::ScalarFn(sql_set_comment)); + + // Cross-references + db.register_function("xrefs_to", 1, xsql::ScalarFn(sql_xrefs_to)); + db.register_function("xrefs_from", 1, xsql::ScalarFn(sql_xrefs_from)); + + // Decompiler (only registered if Hex-Rays is available) + if (decompiler::hexrays_available()) { + db.register_function("decompile", 1, xsql::ScalarFn(sql_decompile)); + db.register_function("decompile", 2, xsql::ScalarFn(sql_decompile_2)); + db.register_function("list_lvars", 1, xsql::ScalarFn(sql_list_lvars)); + db.register_function("rename_lvar", 3, xsql::ScalarFn(sql_rename_lvar)); + } + + // Address utilities + db.register_function("next_head", 1, xsql::ScalarFn(sql_next_head)); + db.register_function("prev_head", 1, xsql::ScalarFn(sql_prev_head)); + db.register_function("hex", 1, xsql::ScalarFn(sql_hex)); + + // Item query functions + db.register_function("item_type", 1, xsql::ScalarFn(sql_item_type)); + db.register_function("item_size", 1, xsql::ScalarFn(sql_item_size)); + db.register_function("is_code", 1, xsql::ScalarFn(sql_is_code)); + db.register_function("is_data", 1, xsql::ScalarFn(sql_is_data)); + db.register_function("mnemonic", 1, xsql::ScalarFn(sql_mnemonic)); + db.register_function("operand", 2, xsql::ScalarFn(sql_operand)); + db.register_function("flags_at", 1, xsql::ScalarFn(sql_flags_at)); + + // Instruction decoding + db.register_function("itype", 1, xsql::ScalarFn(sql_itype)); + db.register_function("decode_insn", 1, xsql::ScalarFn(sql_decode_insn)); + db.register_function("operand_type", 2, xsql::ScalarFn(sql_operand_type)); + db.register_function("operand_value", 2, xsql::ScalarFn(sql_operand_value)); + + // File generation + db.register_function("gen_asm_file", 3, xsql::ScalarFn(sql_gen_asm_file)); + db.register_function("gen_lst_file", 3, xsql::ScalarFn(sql_gen_lst_file)); + db.register_function("gen_map_file", 1, xsql::ScalarFn(sql_gen_map_file)); + db.register_function("gen_idc_file", 3, xsql::ScalarFn(sql_gen_idc_file)); + db.register_function("gen_html_file", 3, xsql::ScalarFn(sql_gen_html_file)); + + // Graph generation + db.register_function("gen_cfg_dot", 1, xsql::ScalarFn(sql_gen_cfg_dot)); + db.register_function("gen_cfg_dot_file", 2, xsql::ScalarFn(sql_gen_cfg_dot_file)); + db.register_function("gen_schema_dot", 0, xsql::ScalarFn(sql_gen_schema_dot)); + + // Jump search + db.register_function("jump_search", 4, xsql::ScalarFn(sql_jump_search)); + db.register_function("jump_query", 4, xsql::ScalarFn(sql_jump_query)); + + // String list functions + db.register_function("rebuild_strings", 0, xsql::ScalarFn(sql_rebuild_strings)); + db.register_function("rebuild_strings", 1, xsql::ScalarFn(sql_rebuild_strings)); + db.register_function("rebuild_strings", 2, xsql::ScalarFn(sql_rebuild_strings)); + db.register_function("string_count", 0, xsql::ScalarFn(sql_string_count)); + + // Database persistence + db.register_function("save_database", 0, xsql::ScalarFn(sql_save_database)); + + return true; +} + +} // namespace functions +} // namespace idasql + +``` + +`src/lib/include/idasql/idasql.hpp`: + +```hpp +/** + * idasql.hpp - Main include header for IDASQL library + * + * This is the convenience header that includes all IDASQL components. + * + * Usage: + * #include + * + * idasql::Database db; + * db.open("database.i64"); + * db.exec("SELECT * FROM funcs", callback, nullptr); + * db.close(); + */ + +#pragma once + +// Core virtual table framework +#include + +// Entity tables +#include +#include +#include + +// Decompiler tables (requires Hex-Rays) +#include + +// Metadata tables +#include + +// SQL functions +#include + +// Database wrapper class +#include + +``` + +`src/lib/include/idasql/metadata.hpp`: + +```hpp +/** + * ida_metadata.hpp - IDA database metadata as virtual tables + * + * These tables provide metadata about the database itself, not entities within it. + * Many of these work even without a fully loaded database. + * + * Tables: + * db_info - Database information (processor, file type, etc.) + * ida_info - IDA analysis settings and flags + */ + +#pragma once + +#include + +#include +#include + +#include + +// IDA SDK headers +#include +#include +#include + +namespace idasql { +namespace metadata { + +// ============================================================================ +// Helper: Key-Value pair for metadata tables +// ============================================================================ + +struct MetadataItem { + std::string key; + std::string value; + std::string type; // "string", "int", "hex", "bool" +}; + +// ============================================================================ +// DB_INFO Table - Database information +// ============================================================================ + +inline std::vector& get_db_info_cache() { + static std::vector cache; + return cache; +} + +inline void rebuild_db_info_cache() { + auto& cache = get_db_info_cache(); + cache.clear(); + + auto add_str = [&](const char* k, const std::string& v) { + cache.push_back({k, v, "string"}); + }; + auto add_int = [&](const char* k, int64_t v) { + cache.push_back({k, std::to_string(v), "int"}); + }; + auto add_hex = [&](const char* k, uint64_t v) { + char buf[32]; + qsnprintf(buf, sizeof(buf), "0x%llX", (unsigned long long)v); + cache.push_back({k, buf, "hex"}); + }; + auto add_bool = [&](const char* k, bool v) { + cache.push_back({k, v ? "true" : "false", "bool"}); + }; + + // Processor info + add_str("processor", inf_get_procname().c_str()); + add_int("filetype", inf_get_filetype()); + add_int("ostype", inf_get_ostype()); + add_int("apptype", inf_get_apptype()); + + // Address info + add_hex("min_ea", inf_get_min_ea()); + add_hex("max_ea", inf_get_max_ea()); + add_hex("start_ea", inf_get_start_ea()); + add_hex("main_ea", inf_get_main()); + + // Addressing + add_int("cc_id", inf_get_cc_id()); + add_bool("is_32bit", !inf_is_64bit()); + add_bool("is_64bit", inf_is_64bit()); + add_bool("is_be", inf_is_be()); + + // Database info + add_int("database_change_count", inf_get_database_change_count()); + add_int("version", IDA_SDK_VERSION); +} + +inline VTableDef define_db_info() { + return table("db_info") + .count([]() { + rebuild_db_info_cache(); + return get_db_info_cache().size(); + }) + .column_text("key", [](size_t i) -> std::string { + auto& cache = get_db_info_cache(); + return i < cache.size() ? cache[i].key : ""; + }) + .column_text("value", [](size_t i) -> std::string { + auto& cache = get_db_info_cache(); + return i < cache.size() ? cache[i].value : ""; + }) + .column_text("type", [](size_t i) -> std::string { + auto& cache = get_db_info_cache(); + return i < cache.size() ? cache[i].type : ""; + }) + .build(); +} + +// ============================================================================ +// IDA_INFO Table - IDA analysis flags (from inf structure) +// ============================================================================ + +inline std::vector& get_ida_info_cache() { + static std::vector cache; + return cache; +} + +inline void rebuild_ida_info_cache() { + auto& cache = get_ida_info_cache(); + cache.clear(); + + auto add_bool = [&](const char* k, bool v) { + cache.push_back({k, v ? "1" : "0", "bool"}); + }; + auto add_int = [&](const char* k, int64_t v) { + cache.push_back({k, std::to_string(v), "int"}); + }; + + // Analysis flags + add_bool("show_auto", inf_should_create_stkvars()); // approximate + add_bool("show_void", inf_is_graph_view()); + add_bool("is_dll", inf_is_dll()); + add_bool("is_flat", inf_is_flat_off32()); + add_bool("wide_fids", inf_is_wide_high_byte_first()); + + // Naming + add_int("long_demnames", inf_get_long_demnames()); + add_int("short_demnames", inf_get_short_demnames()); + add_int("demnames", inf_get_demnames()); + + // Limits + add_int("max_autoname_len", inf_get_max_autoname_len()); +} + +inline VTableDef define_ida_info() { + return table("ida_info") + .count([]() { + rebuild_ida_info_cache(); + return get_ida_info_cache().size(); + }) + .column_text("key", [](size_t i) -> std::string { + auto& cache = get_ida_info_cache(); + return i < cache.size() ? cache[i].key : ""; + }) + .column_text("value", [](size_t i) -> std::string { + auto& cache = get_ida_info_cache(); + return i < cache.size() ? cache[i].value : ""; + }) + .column_text("type", [](size_t i) -> std::string { + auto& cache = get_ida_info_cache(); + return i < cache.size() ? cache[i].type : ""; + }) + .build(); +} + +// ============================================================================ +// Metadata Registry +// ============================================================================ + +struct MetadataRegistry { + VTableDef db_info; + VTableDef ida_info; + + MetadataRegistry() + : db_info(define_db_info()) + , ida_info(define_ida_info()) + {} + + void register_all(xsql::Database& db) { + db.register_table("ida_db_info", &db_info); + db.create_table("db_info", "ida_db_info"); + + db.register_table("ida_ida_info", &ida_info); + db.create_table("ida_info", "ida_ida_info"); + } +}; + +} // namespace metadata +} // namespace idasql + +``` + +`src/lib/include/idasql/platform.hpp`: + +```hpp +/** + * platform.hpp - Platform fixups for IDA SDK compatibility + * + * macOS Mach kernel headers define processor_t and token_t as integer + * typedefs (via ), which conflict with IDA SDK's + * struct processor_t. Since #undef cannot remove typedefs, we redirect + * them via #define before any system headers are included. + * + * Usage (every header or .cpp that includes both system and IDA headers): + * + * #include // FIRST: redirect macOS typedefs + * #include // system headers see harmless names + * #include // clean up before IDA headers + * #include // IDA sees clean namespace + */ + +#pragma once + +#ifdef __APPLE__ +#define processor_t __mach_processor_t +#define token_t __mach_token_t +#endif + +``` + +`src/lib/include/idasql/platform_undef.hpp`: + +```hpp +/** + * platform_undef.hpp - Undo platform.hpp redirects before IDA headers + * + * Include this after all system/library headers and before any IDA SDK + * headers. See platform.hpp for details. + * + * This file is intentionally NOT guarded with #pragma once so it can + * be included multiple times (each header pair needs its own cleanup). + */ + +#ifdef __APPLE__ +#undef processor_t +#undef token_t +#endif + +``` + +`src/lib/include/idasql/search_bytes.hpp`: + +```hpp +// Copyright (c) 2025 Elias Bachaalany +// SPDX-License-Identifier: MIT + +/** + * search_bytes.hpp - Binary pattern search functions for IDASQL + * + * Provides search_bytes() and search_first() functions for finding byte patterns. + * + * Pattern syntax (IDA native): + * - "48 8B 05" - Exact bytes (hex, space-separated) + * - "48 ? 05" - ? = any byte wildcard (whole byte only) + * - "48 ?? 05" - ?? = same as ? (any byte) + * - "(01 02 03)" - Alternatives (match any of these bytes) + * + * SQL usage: + * SELECT search_bytes('48 8B ? 00'); -- Returns JSON array + * SELECT search_bytes('48 8B ? 00', 0x401000, 0x402000); -- With range + * SELECT search_first('48 8B ? 00'); -- Returns first address + * + * Unlike Binary Ninja: + * - No nibble wildcards (? always means full byte) + * - No regex support + * - Supports alternatives like (01 02 03) + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +// IDA SDK +#include +#include +#include +#include +#include + +namespace idasql { +namespace search { + +// ============================================================================ +// Search Result +// ============================================================================ + +struct ByteSearchResult { + ea_t address; + std::vector matched_bytes; + std::string matched_hex; +}; + +// ============================================================================ +// Binary Pattern Search Implementation +// ============================================================================ + +/** + * Find all matches for a byte pattern in the given range. + * + * @param pattern Pattern string like "48 8B ? 00" or "48 ?? (01 02) 00" + * @param start_ea Start of search range (default: inf_get_min_ea()) + * @param end_ea End of search range (default: inf_get_max_ea()) + * @param results Vector to store results + * @param max_results Maximum results to return (0 = unlimited) + * @return Number of matches found + */ +inline size_t find_byte_pattern( + const char* pattern, + ea_t start_ea, + ea_t end_ea, + std::vector& results, + size_t max_results = 0) +{ + if (!pattern || !*pattern) return 0; + + // Parse the pattern string + compiled_binpat_vec_t binpat; + qstring errbuf; + + if (!parse_binpat_str(&binpat, start_ea, pattern, 16, PBSENC_DEF1BPU, &errbuf)) { + // Pattern parse failed + return 0; + } + + if (binpat.empty()) return 0; + + // Get pattern length for reading matched bytes + size_t pattern_len = binpat[0].bytes.size(); + + ea_t ea = start_ea; + size_t count = 0; + + while (ea < end_ea) { + ea_t found = bin_search(ea, end_ea, binpat, BIN_SEARCH_FORWARD); + if (found == BADADDR) break; + + ByteSearchResult result; + result.address = found; + + // Read matched bytes + result.matched_bytes.resize(pattern_len); + for (size_t i = 0; i < pattern_len; i++) { + result.matched_bytes[i] = get_byte(found + i); + } + + // Build hex string + std::ostringstream hex; + hex << std::hex << std::setfill('0'); + for (size_t i = 0; i < pattern_len; i++) { + if (i > 0) hex << " "; + hex << std::setw(2) << static_cast(result.matched_bytes[i]); + } + result.matched_hex = hex.str(); + + results.push_back(std::move(result)); + count++; + + if (max_results > 0 && count >= max_results) break; + + ea = found + 1; // Move past this match + } + + return count; +} + +/** + * Find first match for a byte pattern. + * + * @return Address of first match, or BADADDR if not found + */ +inline ea_t find_first_pattern(const char* pattern, ea_t start_ea, ea_t end_ea) { + if (!pattern || !*pattern) return BADADDR; + + compiled_binpat_vec_t binpat; + qstring errbuf; + + if (!parse_binpat_str(&binpat, start_ea, pattern, 16, PBSENC_DEF1BPU, &errbuf)) { + return BADADDR; + } + + if (binpat.empty()) return BADADDR; + + return bin_search(start_ea, end_ea, binpat, BIN_SEARCH_FORWARD); +} + +// ============================================================================ +// SQL Function Registration +// ============================================================================ + +// search_bytes(pattern) - Returns JSON array of all matches +static void sql_search_bytes_1(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("search_bytes requires pattern argument"); + return; + } + + const char* pattern = argv[0].as_c_str(); + if (!pattern) { + ctx.result_error("Invalid pattern"); + return; + } + + ea_t start_ea = inf_get_min_ea(); + ea_t end_ea = inf_get_max_ea(); + + std::vector results; + find_byte_pattern(pattern, start_ea, end_ea, results); + + // Build JSON array + xsql::json arr = xsql::json::array(); + for (const auto& r : results) { + arr.push_back({ + {"address", r.address}, + {"matched_hex", r.matched_hex}, + {"size", r.matched_bytes.size()} + }); + } + + std::string result = arr.dump(); + ctx.result_text(result); +} + +// search_bytes(pattern, start, end) - Returns JSON array within range +static void sql_search_bytes_3(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 3) { + ctx.result_error("search_bytes requires (pattern, start, end) arguments"); + return; + } + + const char* pattern = argv[0].as_c_str(); + if (!pattern) { + ctx.result_error("Invalid pattern"); + return; + } + + ea_t start_ea = static_cast(argv[1].as_int64()); + ea_t end_ea = static_cast(argv[2].as_int64()); + + std::vector results; + find_byte_pattern(pattern, start_ea, end_ea, results); + + // Build JSON array + xsql::json arr = xsql::json::array(); + for (const auto& r : results) { + arr.push_back({ + {"address", r.address}, + {"matched_hex", r.matched_hex}, + {"size", r.matched_bytes.size()} + }); + } + + std::string result = arr.dump(); + ctx.result_text(result); +} + +// search_first(pattern) - Returns first match address +static void sql_search_first_1(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("search_first requires pattern argument"); + return; + } + + const char* pattern = argv[0].as_c_str(); + if (!pattern) { + ctx.result_error("Invalid pattern"); + return; + } + + ea_t result = find_first_pattern(pattern, inf_get_min_ea(), inf_get_max_ea()); + if (result != BADADDR) { + ctx.result_int64(static_cast(result)); + } else { + ctx.result_null(); + } +} + +// search_first(pattern, start, end) - Returns first match in range +static void sql_search_first_3(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 3) { + ctx.result_error("search_first requires (pattern, start, end) arguments"); + return; + } + + const char* pattern = argv[0].as_c_str(); + if (!pattern) { + ctx.result_error("Invalid pattern"); + return; + } + + ea_t start_ea = static_cast(argv[1].as_int64()); + ea_t end_ea = static_cast(argv[2].as_int64()); + + ea_t result = find_first_pattern(pattern, start_ea, end_ea); + if (result != BADADDR) { + ctx.result_int64(static_cast(result)); + } else { + ctx.result_null(); + } +} + +/** + * Register all search_bytes SQL functions. + */ +inline bool register_search_bytes(xsql::Database& db) { + db.register_function("search_bytes", 1, xsql::ScalarFn(sql_search_bytes_1)); + db.register_function("search_bytes", 3, xsql::ScalarFn(sql_search_bytes_3)); + db.register_function("search_first", 1, xsql::ScalarFn(sql_search_first_1)); + db.register_function("search_first", 3, xsql::ScalarFn(sql_search_first_3)); + return true; +} + +} // namespace search +} // namespace idasql + +``` + +`src/lib/include/idasql/vtable.hpp`: + +```hpp +/** + * vtable.hpp - SQLite Virtual Table framework for IDA + * + * This file re-exports the xsql virtual table framework types into the idasql + * namespace for convenience. + * + * Two table patterns are available: + * + * 1. Index-based tables (for IDA's indexed access like getn_func): + * + * auto funcs_table = idasql::table("funcs") + * .count([]() { return get_func_qty(); }) + * .column_int64("address", [](size_t i) { return getn_func(i)->start_ea; }) + * .build(); + * + * 2. Cached tables (for enumeration-based data, cache freed after query): + * + * auto xrefs_table = idasql::cached_table("xrefs") + * .estimate_rows([]() { return get_func_qty() * 10; }) + * .cache_builder([](auto& cache) { ... populate ... }) + * .column_int64("from_ea", [](const XrefInfo& r) { return r.from_ea; }) + * .build(); + * + * 3. Generator tables (for expensive full scans that must be lazy): + * + * auto ctree_table = idasql::generator_table("ctree") + * .estimate_rows([]() { return get_func_qty() * 50; }) + * .generator([]() { return std::make_unique(); }) + * .column_int64("func_addr", [](const CtreeItem& r) { return r.func_addr; }) + * .build(); + */ + +#pragma once + +#include + +namespace idasql { + +// ============================================================================ +// Re-export xsql types into idasql namespace +// ============================================================================ + +// Column types +using xsql::ColumnType; +using xsql::column_type_sql; + +// Column definition (index-based) +using xsql::ColumnDef; + +// Virtual table definition (index-based) +using xsql::VTableDef; + +// SQLite virtual table implementation +using xsql::Vtab; +using xsql::Cursor; + +// Registration helpers +using xsql::register_vtable; +using xsql::create_vtable; + +// Index-based table builder +using xsql::VTableBuilder; +using xsql::table; + +// ============================================================================ +// Cached Table API (query-scoped cache, freed after query) +// ============================================================================ + +// Row iterator for constraint pushdown +using xsql::RowIterator; +using xsql::FilterDef; +using xsql::FILTER_NONE; + +// Cached column definition (row-typed) +template +using CachedColumnDef = xsql::CachedColumnDef; + +// Cached table definition +template +using CachedTableDef = xsql::CachedTableDef; + +// Cached cursor (owns cache) +template +using CachedCursor = xsql::CachedCursor; + +// Cached table registration +template +inline bool register_cached_vtable(sqlite3* db, const char* module_name, + const CachedTableDef* def) { + return xsql::register_cached_vtable(db, module_name, def); +} + +// Cached table builder +template +using CachedTableBuilder = xsql::CachedTableBuilder; + +template +inline CachedTableBuilder cached_table(const char* name) { + return xsql::cached_table(name); +} + +// ============================================================================ +// Generator Table API (streaming, no full-cache materialization) +// ============================================================================ + +template +using Generator = xsql::Generator; + +template +using GeneratorTableDef = xsql::GeneratorTableDef; + +template +using GeneratorCursor = xsql::GeneratorCursor; + +template +inline bool register_generator_vtable(sqlite3* db, const char* module_name, + const GeneratorTableDef* def) { + return xsql::register_generator_vtable(db, module_name, def); +} + +template +using GeneratorTableBuilder = xsql::GeneratorTableBuilder; + +template +inline GeneratorTableBuilder generator_table(const char* name) { + return xsql::generator_table(name); +} + +} // namespace idasql + +// ============================================================================ +// Convenience Macros (namespace-qualified for idasql) +// ============================================================================ + +#define COLUMN_INT64(name, getter) \ + .column_int64(#name, getter) + +#define COLUMN_INT(name, getter) \ + .column_int(#name, getter) + +#define COLUMN_TEXT(name, getter) \ + .column_text(#name, getter) + +#define COLUMN_DOUBLE(name, getter) \ + .column_double(#name, getter) + +``` + +`src/lib/include/idasql/vtable_policy.hpp`: + +```hpp +/** + * ida_vtable_policy.hpp - Policy and configuration system for IDASQL + * + * This allows passing options to virtual tables via: + * 1. Module arguments: CREATE VIRTUAL TABLE funcs USING ida_funcs(cache=off) + * 2. Global configuration via SQL function: SELECT idasql_config('cache', 'off') + * 3. Per-session settings stored in a config table + * + * Supported policies: + * - cache: 'on'|'off' - Enable/disable result caching + * - undo: 'on'|'off' - Create undo points for modifications + * - batch: 'on'|'off' - Batch multiple operations into one undo point + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace idasql { +namespace policy { + +// ============================================================================ +// Policy Values +// ============================================================================ + +enum class CachePolicy { + Off, // No caching, always fetch live data + Session, // Cache for the duration of SQL statement + Persistent // Cache until invalidated +}; + +enum class UndoPolicy { + Off, // No undo points + PerRow, // Undo point per row modification + PerStatement // One undo point per SQL statement (recommended) +}; + +// ============================================================================ +// Global Configuration +// ============================================================================ + +struct IdasqlConfig { + CachePolicy cache = CachePolicy::Off; // Default: live data + UndoPolicy undo = UndoPolicy::PerStatement; // Default: one undo per statement + bool batch_operations = true; // Batch ops under one undo + bool verbose = false; // Debug output + + static IdasqlConfig& instance() { + static IdasqlConfig config; + return config; + } + +private: + IdasqlConfig() = default; +}; + +// ============================================================================ +// Configuration Parsing from Module Arguments +// ============================================================================ + +struct ModuleOptions { + CachePolicy cache = CachePolicy::Off; + UndoPolicy undo = UndoPolicy::PerStatement; + + // Parse from CREATE VIRTUAL TABLE ... USING module(key=value, ...) + static ModuleOptions parse(int argc, const char* const* argv) { + ModuleOptions opts; + + // argv[0] = module name, argv[1] = database name, argv[2] = table name + // argv[3..] = additional arguments + for (int i = 3; i < argc; ++i) { + std::string arg = argv[i]; + size_t eq = arg.find('='); + if (eq == std::string::npos) continue; + + std::string key = arg.substr(0, eq); + std::string val = arg.substr(eq + 1); + + // Trim quotes if present + if (val.size() >= 2 && val.front() == '\'' && val.back() == '\'') { + val = val.substr(1, val.size() - 2); + } + + if (key == "cache") { + if (val == "off" || val == "0" || val == "false") { + opts.cache = CachePolicy::Off; + } else if (val == "session") { + opts.cache = CachePolicy::Session; + } else if (val == "on" || val == "1" || val == "true" || val == "persistent") { + opts.cache = CachePolicy::Persistent; + } + } else if (key == "undo") { + if (val == "off" || val == "0" || val == "false") { + opts.undo = UndoPolicy::Off; + } else if (val == "row" || val == "perrow") { + opts.undo = UndoPolicy::PerRow; + } else if (val == "on" || val == "1" || val == "true" || val == "statement") { + opts.undo = UndoPolicy::PerStatement; + } + } + } + + return opts; + } +}; + +// ============================================================================ +// SQL Configuration Function +// ============================================================================ + +// Register: SELECT idasql_config('key', 'value') to set +// Register: SELECT idasql_config('key') to get +inline void idasql_config_func(xsql::FunctionContext& ctx, int argc, xsql::FunctionArg* argv) { + if (argc < 1) { + ctx.result_error("idasql_config requires at least 1 argument"); + return; + } + + const char* key = argv[0].as_c_str(); + if (!key) { + ctx.result_null(); + return; + } + + auto& config = IdasqlConfig::instance(); + + // Setter mode + if (argc >= 2) { + const char* val = argv[1].as_c_str(); + if (!val) val = ""; + + if (strcmp(key, "cache") == 0) { + if (strcmp(val, "off") == 0 || strcmp(val, "0") == 0) { + config.cache = CachePolicy::Off; + } else if (strcmp(val, "session") == 0) { + config.cache = CachePolicy::Session; + } else { + config.cache = CachePolicy::Persistent; + } + ctx.result_text(val); + } else if (strcmp(key, "undo") == 0) { + if (strcmp(val, "off") == 0 || strcmp(val, "0") == 0) { + config.undo = UndoPolicy::Off; + } else if (strcmp(val, "row") == 0) { + config.undo = UndoPolicy::PerRow; + } else { + config.undo = UndoPolicy::PerStatement; + } + ctx.result_text(val); + } else if (strcmp(key, "verbose") == 0) { + config.verbose = (strcmp(val, "on") == 0 || strcmp(val, "1") == 0); + ctx.result_int(config.verbose ? 1 : 0); + } else { + ctx.result_error("Unknown config key"); + } + return; + } + + // Getter mode + if (strcmp(key, "cache") == 0) { + const char* val = "off"; + if (config.cache == CachePolicy::Session) val = "session"; + else if (config.cache == CachePolicy::Persistent) val = "persistent"; + ctx.result_text_static(val); + } else if (strcmp(key, "undo") == 0) { + const char* val = "statement"; + if (config.undo == UndoPolicy::Off) val = "off"; + else if (config.undo == UndoPolicy::PerRow) val = "row"; + ctx.result_text_static(val); + } else if (strcmp(key, "verbose") == 0) { + ctx.result_int(config.verbose ? 1 : 0); + } else { + ctx.result_null(); + } +} + +// Register the config function with SQLite +inline bool register_config_function(xsql::Database& db) { + return db.register_function("idasql_config", -1, xsql::ScalarFn(idasql_config_func)) == SQLITE_OK; +} + +// ============================================================================ +// Configuration Table (Alternative approach) +// ============================================================================ + +inline bool create_config_table(xsql::Database& db) { + const char* sql = R"( + CREATE TABLE IF NOT EXISTS idasql_settings ( + key TEXT PRIMARY KEY, + value TEXT, + description TEXT + ); + + INSERT OR IGNORE INTO idasql_settings VALUES + ('cache', 'off', 'Cache policy: off, session, persistent'), + ('undo', 'statement', 'Undo policy: off, row, statement'), + ('verbose', '0', 'Debug output: 0 or 1'); + )"; + + return db.exec(sql) == SQLITE_OK; +} + +// Sync config from table to memory +inline bool load_config_from_table(xsql::Database& db) { + auto& config = IdasqlConfig::instance(); + + auto result = db.query("SELECT key, value FROM idasql_settings"); + if (!result.ok()) return false; + + for (const auto& row : result) { + const auto& key = row[0]; + const auto& val = row[1]; + + if (key == "cache") { + if (val == "off") config.cache = CachePolicy::Off; + else if (val == "session") config.cache = CachePolicy::Session; + else config.cache = CachePolicy::Persistent; + } else if (key == "undo") { + if (val == "off") config.undo = UndoPolicy::Off; + else if (val == "row") config.undo = UndoPolicy::PerRow; + else config.undo = UndoPolicy::PerStatement; + } else if (key == "verbose") { + config.verbose = (val == "1"); + } + } + + return true; +} + +// ============================================================================ +// Initialization Helper +// ============================================================================ + +inline bool init_policy_system(xsql::Database& db) { + bool ok = true; + ok = ok && register_config_function(db); + ok = ok && create_config_table(db); + ok = ok && load_config_from_table(db); + return ok; +} + +} // namespace policy +} // namespace idasql + +``` + +`src/plugin/CMakeLists.txt`: + +```txt +# idasql Plugin - IDA Pro plugin for SQL queries +# Built as part of the main idasql project + +# Plugin sources +ida_add_plugin(idasql_plugin + SOURCES main.cpp +) + +# Output as idasql.dll (not idasql_plugin.dll) +set_target_properties(idasql_plugin PROPERTIES OUTPUT_NAME "idasql") + +# Include directories and link dependencies +target_include_directories(idasql_plugin PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../lib/include +) +target_link_libraries(idasql_plugin PRIVATE xsql::xsql) + +# USE_DANGEROUS_FUNCTIONS: Disable IDA's safe function macros (pro.h: strcpy, sprintf, getenv, etc.) +# USE_STANDARD_FILE_FUNCTIONS: Disable IDA's file function macros (fpro.h: fgetc, fputc, etc.) +# Required for std::getenv and file I/O in agent_settings.hpp +target_compile_definitions(idasql_plugin PRIVATE + USE_DANGEROUS_FUNCTIONS + USE_STANDARD_FILE_FUNCTIONS +) + +# Windows-specific settings +if(WIN32) + target_link_libraries(idasql_plugin PRIVATE ws2_32) + # WIN32_LEAN_AND_MEAN: Exclude rarely-used Windows headers + # NOMINMAX: Prevent Windows.h from defining min/max macros + target_compile_definitions(idasql_plugin PRIVATE + WIN32_LEAN_AND_MEAN + NOMINMAX + ) +endif() + +# Hex-Rays support +target_compile_definitions(idasql_plugin PRIVATE USE_HEXRAYS) + +# ============================================================================ +# AI Agent support (from parent project) +# ============================================================================ + +if(IDASQL_WITH_AI_AGENT) + # Add ai_agent and mcp_server source files + target_sources(idasql_plugin PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../common/ai_agent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../common/mcp_server.cpp + ) + target_include_directories(idasql_plugin PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../common + ${CMAKE_CURRENT_SOURCE_DIR}/../../external/libagents/external/fastmcpp/include + ) + + # Link against libagents and fastmcpp + target_link_libraries(idasql_plugin PRIVATE libagents fastmcpp_core) + + # Define the feature flag + target_compile_definitions(idasql_plugin PRIVATE IDASQL_HAS_AI_AGENT) + + # Add dependency on prompt generation + if(TARGET generate_prompt) + add_dependencies(idasql_plugin generate_prompt) + endif() + + message(STATUS "idasql_plugin: AI agent support enabled") +endif() + +# ============================================================================ +# HTTP Server support +# ============================================================================ + +target_sources(idasql_plugin PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../common/http_server.cpp +) +target_include_directories(idasql_plugin PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../common +) +target_compile_definitions(idasql_plugin PRIVATE XSQL_HAS_THINCLIENT) + +# cpp-httplib: provided transitively by xsql::xsql when XSQL_WITH_THINCLIENT=ON. +# Only fetch standalone if the target doesn't already exist. +if(NOT TARGET httplib) + include(FetchContent) + FetchContent_Declare( + cpp_httplib + GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git + GIT_TAG v0.15.3 + ) + FetchContent_MakeAvailable(cpp_httplib) + target_include_directories(idasql_plugin PRIVATE ${cpp_httplib_SOURCE_DIR}) +endif() + +``` + +`src/plugin/main.cpp`: + +```cpp +/** + * idasql_plugin - IDA plugin providing SQL interface to IDA databases + * + * The plugin auto-installs a CLI (command line interface) on load. + * Use dot commands: .http, .mcp, .help + * + * The plugin is hidden from the Edit > Plugins menu (PLUGIN_HIDE). + * See plugin_control.hpp for run() arg codes. + */ + +// ============================================================================= +// CRITICAL: Include order matters on Windows! +// 1. nlohmann/json before IDA headers (IDA macros can interfere) +// 2. Standard library headers +// 3. IDA headers +// +// Note: USE_DANGEROUS_FUNCTIONS and USE_STANDARD_FILE_FUNCTIONS are defined +// via CMakeLists.txt to disable IDA's safe function macros that conflict +// with MSVC standard library (__msvc_filebuf.hpp uses fgetc/fputc). +// ============================================================================= + +#include + +// Standard library includes +#include +#include +#include + +// Platform-specific include order: +// - Windows: json before IDA (IDA poisons stdlib functions) +// - macOS/Linux: IDA before json +#include + +#ifdef _WIN32 +// Include shlobj.h BEFORE IDA headers: agent_settings.hpp pulls in +// which defines CM_MASK/CM_STATE enums in shobjidl_core.h. IDA's typeinf.hpp +// also defines CM_MASK (const uchar). Including Windows headers first lets +// IDA's definition shadow the Windows enum without conflict. +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +// IDASQL CLI (command line interface) +#include "../common/idasql_cli.hpp" + +// Plugin control codes +#include "../common/plugin_control.hpp" + +// Version info +#include "../common/idasql_version.hpp" + +// MCP server (when AI agent is enabled) +#ifdef IDASQL_HAS_AI_AGENT +#include "../common/mcp_server.hpp" +#include "../common/ai_agent.hpp" +#endif + +// HTTP server for .http REPL command +#include "../common/http_server.hpp" + +//============================================================================= +// IDA execute_sync wrapper +//============================================================================= + +namespace { + +struct query_request_t : public exec_request_t +{ + idasql::QueryEngine* engine; + std::string sql; + idasql::QueryResult result; + + query_request_t(idasql::QueryEngine* e, const std::string& s) + : engine(e), sql(s) {} + + virtual ssize_t idaapi execute() override + { + result = engine->query(sql); + return result.success ? 0 : -1; + } +}; + +} // anonymous namespace + +//============================================================================= +// IDA Plugin +//============================================================================= + +struct idasql_plugmod_t : public plugmod_t +{ + std::unique_ptr engine_; + std::unique_ptr cli_; + +#ifdef IDASQL_HAS_AI_AGENT + idasql::IDAMCPServer mcp_server_; + std::unique_ptr mcp_agent_; // AI agent for MCP +#endif + + idasql::IDAHTTPServer http_server_; + + idasql_plugmod_t() + { + engine_ = std::make_unique(); + if (engine_->is_valid()) { + msg("IDASQL v" IDASQL_VERSION_STRING ": Query engine initialized\n"); + + // SQL executor that uses execute_sync for thread safety + auto sql_executor = [this](const std::string& sql) -> std::string { + query_request_t req(engine_.get(), sql); + execute_sync(req, MFF_WRITE); + if (req.result.success) { + return req.result.to_string(); + } else { + return "Error: " + req.result.error; + } + }; + + // Create CLI with execute_sync wrapper for thread safety + cli_ = std::make_unique(sql_executor); + +#ifdef IDASQL_HAS_AI_AGENT + // Setup MCP callbacks + cli_->session().callbacks().mcp_status = [this]() -> std::string { + if (mcp_server_.is_running()) { + return idasql::format_mcp_status(mcp_server_.port(), true); + } else { + // Auto-start if not running + return start_mcp_server(); + } + }; + + cli_->session().callbacks().mcp_start = [this](int port, const std::string& bind_addr) -> std::string { + return start_mcp_server(port, bind_addr); + }; + + cli_->session().callbacks().mcp_stop = [this]() -> std::string { + if (mcp_server_.is_running()) { + mcp_server_.stop(); + mcp_agent_.reset(); + return "MCP server stopped"; + } else { + return "MCP server not running"; + } + }; +#endif + + // Setup HTTP server callbacks + cli_->session().callbacks().http_status = [this]() -> std::string { + if (http_server_.is_running()) { + return idasql::format_http_status(http_server_.port(), true); + } else { + return "HTTP server not running\nUse '.http start' to start\n"; + } + }; + + cli_->session().callbacks().http_start = [this](int port, const std::string& bind_addr) -> std::string { + return start_http_server(port, bind_addr); + }; + + cli_->session().callbacks().http_stop = [this]() -> std::string { + if (http_server_.is_running()) { + http_server_.stop(); + return "HTTP server stopped"; + } else { + return "HTTP server not running"; + } + }; + + // Auto-install CLI so it's available immediately + // User can still toggle it off with run(23) if desired + cli_->install(); + } else { + msg("IDASQL: Failed to init engine: %s\n", engine_->error().c_str()); + } + } + +#ifdef IDASQL_HAS_AI_AGENT + std::string start_mcp_server(int req_port = 0, const std::string& bind_addr = "127.0.0.1") + { + if (mcp_server_.is_running()) { + return idasql::format_mcp_status(mcp_server_.port(), true); + } + + // SQL executor that uses execute_sync for thread safety + auto sql_executor = [this](const std::string& sql) -> std::string { + query_request_t req(engine_.get(), sql); + execute_sync(req, MFF_WRITE); + if (req.result.success) { + return req.result.to_string(); + } else { + return "Error: " + req.result.error; + } + }; + + // Create AI agent for MCP (runs on MCP thread, SQL via execute_sync) + mcp_agent_ = std::make_unique(sql_executor); + mcp_agent_->start(); + + // MCP ask callback - agent runs on MCP thread + idasql::AskCallback ask_cb = [this](const std::string& question) -> std::string { + if (!mcp_agent_) return "Error: AI agent not available"; + return mcp_agent_->query(question); + }; + + // Start MCP server + int port = mcp_server_.start(req_port, sql_executor, ask_cb, bind_addr); + if (port <= 0) { + mcp_agent_.reset(); + return "Error: Failed to start MCP server"; + } + + return idasql::format_mcp_info(port, true); + } +#endif + + std::string start_http_server(int req_port = 0, const std::string& bind_addr = "127.0.0.1") + { + if (http_server_.is_running()) { + return idasql::format_http_status(http_server_.port(), true); + } + + // SQL executor that uses execute_sync for thread safety and returns JSON + idasql::HTTPQueryCallback sql_cb = [this](const std::string& sql) -> std::string { + query_request_t req(engine_.get(), sql); + execute_sync(req, MFF_WRITE); + + xsql::json j = {{"success", req.result.success}}; + if (req.result.success) { + j["columns"] = req.result.columns; + xsql::json rows = xsql::json::array(); + for (const auto& row : req.result.rows) { + rows.push_back(row.values); + } + j["rows"] = rows; + j["row_count"] = req.result.rows.size(); + } else { + j["error"] = req.result.error; + } + return j.dump(); + }; + + // Start HTTP server, no queue (plugin mode) + int port = http_server_.start(req_port, sql_cb, bind_addr); + if (port <= 0) { + return "Error: Failed to start HTTP server"; + } + + return idasql::format_http_info(port, "Type '.http stop' to stop the server."); + } + + ~idasql_plugmod_t() + { +#ifdef IDASQL_HAS_AI_AGENT + // Stop MCP server before destroying engine + if (mcp_server_.is_running()) { + mcp_server_.stop(); + } + mcp_agent_.reset(); +#endif + // Stop HTTP server before destroying engine + if (http_server_.is_running()) { + http_server_.stop(); + } + if (cli_) cli_->uninstall(); + engine_.reset(); + msg("IDASQL: Plugin terminated\n"); + } + + virtual bool idaapi run(size_t arg) override + { + using namespace idasql; + + switch (arg) { + case 0: + msg("IDASQL v" IDASQL_VERSION_STRING " - SQL interface for IDA database\n"); + msg("Use dot commands: .http, .mcp, .help\n"); + return true; + + case PLUGIN_ARG_TOGGLE_CLI: + if (cli_) { + if (cli_->is_installed()) { + cli_->uninstall(); + } else { + cli_->install(); + } + } + return true; + + default: + return false; + } + } +}; + +//============================================================================= +// Plugin Entry Points +//============================================================================= + +static plugmod_t* idaapi init() +{ + // Skip loading when running under idalib (e.g., idasql CLI) + if (is_ida_library()) { + msg("IDASQL: Running under idalib, plugin skipped\n"); + return nullptr; + } + + return new idasql_plugmod_t(); +} + +plugin_t PLUGIN = +{ + IDP_INTERFACE_VERSION, + PLUGIN_MULTI | PLUGIN_HIDE, + init, + nullptr, + nullptr, + "IDASQL - SQL interface for IDA database", + "IDASQL Plugin\n" + "\n" + "Auto-installs CLI on load. Use dot commands:\n" + " .http start/stop - HTTP REST server\n" + " .mcp start/stop - MCP server\n" + " .help - Show all commands\n" + "\n" + "run(23): Toggle CLI (command line interface)", + "IDASQL", + "" +}; + +``` \ No newline at end of file diff --git a/archive/cristeigabriela/IDAFind.txt b/archive/cristeigabriela/IDAFind.txt new file mode 100644 index 00000000..798fbbce --- /dev/null +++ b/archive/cristeigabriela/IDAFind.txt @@ -0,0 +1,1211 @@ +Project Path: arc_cristeigabriela_IDAFind_q3fp9f5t + +Source Tree: + +```txt +arc_cristeigabriela_IDAFind_q3fp9f5t +├── IDAFind.py +├── LICENSE +├── README.md +└── media + ├── Demo.gif + └── LongDemo.mp4 + +``` + +`IDAFind.py`: + +```py +import re +import threading +import time +import urllib.request + +import ida_hexrays +import ida_kernwin +import ida_lines +import ida_moves +import ida_netnode +from PyQt5.QtCore import QEvent, QObject, Qt +from PyQt5.QtGui import QColor +from PyQt5.QtWidgets import ( + QApplication, + QCheckBox, + QColorDialog, + QHBoxLayout, + QLabel, + QLineEdit, + QPushButton, + QVBoxLayout, + QWidget, +) + +# Plugin constants +PLUGIN_NAME = "IdaFind" +PLUGIN_VERSION = "1.0.1" +PLUGIN_DEBUG = False # Prints some debug stuff. Not useful for usage. + +# Plugin constants (Repository) +PLUGIN_REPO = "cristeigabriela/IDAFind" +PLUGIN_REPO_BRANCH = "main" +PLUGIN_REPO_URL = f"https://github.com/{PLUGIN_REPO}" +PLUGIN_REPO_RAW_URL = ( + f"https://raw.githubusercontent.com/{PLUGIN_REPO}/{PLUGIN_REPO_BRANCH}" +) + +# Plugin constants (Action - Open Search) +PLUGIN_ACTION_OPEN_NAME = f"{PLUGIN_NAME}:OpenPseudocodeSearch" +PLUGIN_ACTION_OPEN_LABEL = "Find in Pseudocode" +PLUGIN_ACTION_OPEN_KEY = "Ctrl+F" +PLUGIN_ACTION_OPEN_TOOLTIP = "Key to open the pseudocode search menu." + +# Default highlight color (ABGR format for IDA: 0xAABBGGRR) +PLUGIN_HIGHLIGHT_COLOR = 0x3300FFFF # Yellow (R=FF, G=FF, B=00) with alpha=33 +PLUGIN_HIGHLIGHT_COLOR_DIM = ( + 0x1500FFFF # Same color but lower alpha for non-current matches +) + +# Netnode name for persistent (IDB-level) settings +PLUGIN_SETTINGS_NETNODE_NAME = "$ pseudocode_search_settings" + +# Global hooks instance +PLUGIN_HIGHLIGHT_HOOKS = None + +# Global search dialog instance +PLUGIN_SEARCH_DIALOG = None + +# Timestamp of last Ctrl+F press when dialog was active (for double-tap to close) +PLUGIN_LAST_HOTKEY_TIME = 0 + +# Update availability (set by background check) +PLUGIN_UPDATE_AVAILABLE = None + + +def __plugin_print(id): + print(f">> [{PLUGIN_NAME}] [{id}] ", end="") + + +def plugin_debug(*args): + """Debug printing utility. Only prints when PLUGIN_DEBUG is on.""" + if not PLUGIN_DEBUG: + return + + __plugin_print("debug") + print(*args) + + +def plugin_info(*args): + """Info printing utility. Tells you what's happening, thought you might care.""" + __plugin_print("info ") + print(*args) + + +def plugin_warn(*args): + """Warning printing utility. Tells you the answer to why you're confused.""" + __plugin_print("warn ") + print(*args) + + +def plugin_error(*args): + """Error printing utility. Oops.""" + __plugin_print("error") + print(*args) + + +def check_for_updates(): + """Check for updates in a background thread. Non-blocking, silent on failure.""" + + def _check(): + global PLUGIN_UPDATE_AVAILABLE + try: + url = f"{PLUGIN_REPO_RAW_URL}/IDAFind.py" + cache_header = None + with urllib.request.urlopen(url, timeout=5) as response: + cache_header = response.getheader("X-Cache") + content = response.read().decode("utf-8", errors="ignore") + + match = re.search(r'PLUGIN_VERSION\s*=\s*["\']([^"\']+)["\']', content) + if match: + remote_version = match.group(1) + if remote_version != PLUGIN_VERSION: + PLUGIN_UPDATE_AVAILABLE = remote_version + plugin_info( + f"Update available: {PLUGIN_VERSION} -> {remote_version}. " + f"Visit {PLUGIN_REPO_URL}" + ) + if isinstance(cache_header, str) and cache_header.lower() == "hit": + plugin_warn( + "Plugin version check was given cached data, might be inaccurate." + ) + except Exception as e: + plugin_warn("Failed version update check...") + plugin_warn(e) + pass + + thread = threading.Thread(target=_check, daemon=True) + thread.start() + + +def load_settings(): + """Load persistent settings from IDB netnode.""" + global PLUGIN_HIGHLIGHT_COLOR, PLUGIN_HIGHLIGHT_COLOR_DIM + + defaults = { + "immediate_search": True, + "transparent_on_unfocus": True, + "highlight_enabled": True, + "wildcard_search": False, + "case_insensitive": True, + "highlight_color": PLUGIN_HIGHLIGHT_COLOR, + "highlight_color_dim": PLUGIN_HIGHLIGHT_COLOR_DIM, + } + + try: + node = ida_netnode.netnode(PLUGIN_SETTINGS_NETNODE_NAME, 0, True) + blob = node.getblob(0, "S") + if blob: + import json + + settings = json.loads(blob.decode("utf-8")) + # Update globals for colors + if "highlight_color" in settings: + PLUGIN_HIGHLIGHT_COLOR = settings["highlight_color"] + if "highlight_color_dim" in settings: + PLUGIN_HIGHLIGHT_COLOR_DIM = settings["highlight_color_dim"] + # Merge with defaults for any missing keys + for key, value in defaults.items(): + if key not in settings: + settings[key] = value + return settings + except Exception as e: + plugin_warn(f"Failed to load settings: {e}") + + return defaults + + +def save_settings(settings): + """Save persistent settings to IDB netnode.""" + try: + import json + + node = ida_netnode.netnode(PLUGIN_SETTINGS_NETNODE_NAME, 0, True) + blob = json.dumps(settings).encode("utf-8") + node.setblob(blob, 0, "S") + except Exception as e: + plugin_warn(f"Failed to save settings: {e}") + + +def get_pseudocode_vdui(): + """Get the vdui object from the current pseudocode window.""" + if not ida_hexrays.init_hexrays_plugin(): + plugin_error("Hex-Rays decompiler not available") + return None, None + + # NOTE(gabriela): is this even possible to happen with register_action? + widget = ida_kernwin.get_current_widget() + if widget is None: + plugin_warn("No active widget") + return None, None + + # NOTE(gabriela): ditto + widget_type = ida_kernwin.get_widget_type(widget) + if widget_type != ida_kernwin.BWN_PSEUDOCODE: + plugin_warn(f"Current window is not pseudocode (type: {widget_type})") + return None, None + + vdui = ida_hexrays.get_widget_vdui(widget) + if vdui is None: + plugin_warn("Could not get decompiler view") + return None, None + + return vdui, widget + + +def get_current_position(widget): + """Get current cursor line and column.""" + loc = ida_moves.lochist_entry_t() + if ida_kernwin.get_custom_viewer_location(loc, widget): + place = loc.place() + sl_place = ida_kernwin.place_t.as_simpleline_place_t(place) + line_num = sl_place.n + col = loc.renderer_info().pos.cx + return line_num, col + return 0, 0 + + +def search_and_jump( + query, vdui, widget, direction=0, use_wildcard=False, case_insensitive=True +): + """Search for query in pseudocode and jump to it. + + direction: 0 = first match, 1 = next, -1 = previous + use_wildcard: if True, use wildcard matching (* matches any characters) + case_insensitive: if True, ignore case when matching + """ + if not query: + return False + + # Get all matches + matches = find_all_matches(query, vdui, use_wildcard, case_insensitive) + if not matches: + return False + + current_line, current_col = get_current_position(widget) + + if direction == 0: + # Jump to first match + line_num, col = matches[0] + jump_to_position(widget, line_num, col) + return True + + elif direction == 1: + # Find next match after cursor + for line_num, col in matches: + if line_num > current_line or ( + line_num == current_line and col > current_col + ): + jump_to_position(widget, line_num, col) + return True + # Wrap around to first match + line_num, col = matches[0] + jump_to_position(widget, line_num, col) + return True + + elif direction == -1: + # Find previous match before cursor + for line_num, col in reversed(matches): + if line_num < current_line or ( + line_num == current_line and col < current_col + ): + jump_to_position(widget, line_num, col) + return True + # Wrap around to last match + line_num, col = matches[-1] + jump_to_position(widget, line_num, col) + return True + + return False + + +def jump_to_position(widget, line_num, col): + """Jump cursor to specified line and column.""" + loc = ida_moves.lochist_entry_t() + if ida_kernwin.get_custom_viewer_location(loc, widget): + place = loc.place() + sl_place = ida_kernwin.place_t.as_simpleline_place_t(place) + sl_place.n = line_num + loc.set_place(sl_place) + loc.renderer_info().pos.cx = col + ida_kernwin.custom_viewer_jump(widget, loc, ida_kernwin.CVNF_LAZY) + + +def find_all_matches(query, vdui, use_wildcard=False, case_insensitive=True): + """Find all occurrences of query in pseudocode. + + Returns list of (line_num, col) tuples. + """ + if not query: + return [] + + matches = [] + cfunc = vdui.cfunc + sv = cfunc.get_pseudocode() + + # Prepare query for case-insensitive search + search_query = query.lower() if case_insensitive else query + + for line_num, sline in enumerate(sv): + line_text = ida_lines.tag_remove(sline.line) + # Prepare line text for case-insensitive search + search_text = line_text.lower() if case_insensitive else line_text + + if use_wildcard: + # Find all wildcard matches in this line + start = 0 + while start < len(search_text): + col = find_wildcard_match(search_query, search_text, start) + if col == -1: + break + matches.append((line_num, col)) + start = col + 1 + else: + # Find all exact occurrences in this line + start = 0 + while True: + col = search_text.find(search_query, start) + if col == -1: + break + matches.append((line_num, col)) + start = col + 1 + + return matches + + +def find_wildcard_match(pattern, text, start=0): + """ + Find the first position in text (starting from start) where the pattern matches. + Returns the column index or -1 if not found. + + Wildcards: + * - matches zero or more characters + ? - matches exactly one character + + Note: text is assumed to be a single line (no newlines). + """ + if not pattern: + return -1 + + # Skip leading * wildcards - they match anything before first literal + # (but not ? since that requires exactly one char) + pattern_start = 0 + while pattern_start < len(pattern) and pattern[pattern_start] == "*": + pattern_start += 1 + + # Pattern is all * wildcards - matches at start position + if pattern_start >= len(pattern): + return start if start < len(text) else -1 + + # Get the first non-wildcard character for fast skipping + first_char = pattern[pattern_start] + first_is_literal = first_char != "?" + + for col in range(start, len(text)): + # Quick skip: if first char is literal, it must match + if first_is_literal and text[col] != first_char: + continue + + # Try to match pattern (without leading stars) starting at this position + if try_wildcard_match_at(pattern, pattern_start, text, col): + return col + + return -1 + + +def try_wildcard_match_at(pattern, p_start, text, t_start): + """ + Try to match pattern[p_start:] against text starting at t_start. + Uses recursive backtracking with memoization. + Returns True if a match is found. + + Wildcards: + * - matches zero or more characters + ? - matches exactly one character + + Text is assumed to be a single line (no newlines). + """ + pat_len = len(pattern) + text_len = len(text) + + # Memoization cache: (p_idx, t_idx) -> result + memo = {} + + def match(p_idx, t_idx): + """Recursive helper for pattern matching.""" + # Check memo first + key = (p_idx, t_idx) + if key in memo: + return memo[key] + + # If we've consumed the entire pattern, we have a match + if p_idx >= pat_len: + memo[key] = True + return True + + # If we hit end of text, check if remaining pattern is all stars + if t_idx >= text_len: + while p_idx < pat_len: + if pattern[p_idx] != "*": + memo[key] = False + return False + p_idx += 1 + memo[key] = True + return True + + # Current pattern character + p_char = pattern[p_idx] + + if p_char == "*": + # '*' can match zero or more characters + # Try matching zero characters (skip the star) + if match(p_idx + 1, t_idx): + memo[key] = True + return True + # Try matching one character and keep the star active + if match(p_idx, t_idx + 1): + memo[key] = True + return True + memo[key] = False + return False + elif p_char == "?": + # '?' matches exactly one character + result = match(p_idx + 1, t_idx + 1) + memo[key] = result + return result + else: + # Literal character - must match exactly + if p_char == text[t_idx]: + result = match(p_idx + 1, t_idx + 1) + memo[key] = result + return result + memo[key] = False + return False + + return match(p_start, t_start) + + +def get_current_match_index(matches, widget): + """Determine which match we're currently on (1-based index).""" + if not matches: + return 0, 0 + + current_line, current_col = get_current_position(widget) + + for i, (line_num, col) in enumerate(matches): + if line_num == current_line and col == current_col: + return i + 1, len(matches) + + # If not exactly on a match, find the closest one after cursor + for i, (line_num, col) in enumerate(matches): + if line_num > current_line or (line_num == current_line and col >= current_col): + return i + 1, len(matches) + + return 1, len(matches) + + +class SearchHighlightHooks(ida_kernwin.UI_Hooks): + """UI hooks to highlight search matches in pseudocode and intercept Escape.""" + + def __init__(self): + ida_kernwin.UI_Hooks.__init__(self) + self.highlights = [] # List of (func_ea, line_num, col_start, length) + self.func_ea = None + self.current_line = None # Line number of the current match + self.current_col = None # Column of the current match + + def preprocess_action(self, action_name): + """Intercept actions before they execute. Return 1 to block the action.""" + global PLUGIN_SEARCH_DIALOG + # Block IDA's "Return" action (triggered by Escape) when our dialog is visible + if action_name == "Return": + if PLUGIN_SEARCH_DIALOG is not None and PLUGIN_SEARCH_DIALOG.isVisible(): + plugin_debug(f"Blocking '{action_name}' action, closing search dialog.") + PLUGIN_SEARCH_DIALOG.close() + return 1 # Block the action + return 0 # Allow the action + + def set_highlights( + self, func_ea, matches, query_len, current_line=None, current_col=None + ): + """Set the matches to highlight.""" + self.func_ea = func_ea + self.highlights = [ + (func_ea, line_num, col, query_len) for line_num, col in matches + ] + self.current_line = current_line + self.current_col = current_col + + def clear_highlights(self): + """Clear all highlights.""" + self.highlights = [] + self.func_ea = None + self.current_line = None + self.current_col = None + + def get_lines_rendering_info(self, out, widget, rin): + """Called by IDA to get line rendering info.""" + vu = ida_hexrays.get_widget_vdui(widget) + if vu: + entry_ea = vu.cfunc.entry_ea + for section_lines in rin.sections_lines: + for line in section_lines: + line_num = ida_kernwin.place_t.as_simpleline_place_t(line.at).n + # Check if this line should be highlighted + for func_ea, h_line, h_col, h_len in self.highlights: + if func_ea == entry_ea and h_line == line_num: + e = ida_kernwin.line_rendering_output_entry_t(line) + # Use brighter color for current match, dimmer for others + if ( + h_line == self.current_line + and h_col == self.current_col + ): + e.bg_color = PLUGIN_HIGHLIGHT_COLOR + else: + e.bg_color = PLUGIN_HIGHLIGHT_COLOR_DIM + out.entries.push_back(e) + break # Only one highlight per line needed + + +class EscapeEventFilter(QObject): + """Event filter that intercepts Escape key to close the search dialog.""" + + def __init__(self, dialog): + super().__init__() + self.dialog = dialog + + def eventFilter(self, obj, event): + if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Escape: + if self.dialog is not None and self.dialog.isVisible(): + plugin_debug("Escape intercepted by event filter, closing dialog.") + self.dialog.close() + return True # Event handled, don't propagate + return False # Let other events pass through + + +class SearchLineEdit(QLineEdit): + """QLineEdit that forwards Enter to parent widget.""" + + def keyPressEvent(self, event): + if event.key() in (Qt.Key_Return, Qt.Key_Enter): + # Forward to parent widget + self.parent().keyPressEvent(event) + else: + super().keyPressEvent(event) + + +class SearchDialog(QWidget): + """Qt widget with optional real-time search - non-blocking, stays on top.""" + + def __init__(self, vdui, widget, parent=None): + super().__init__(parent) + self.vdui = vdui + self.widget = widget + + # Event filter to intercept Escape key at application level + self.escape_filter = EscapeEventFilter(self) + + # Load persistent settings + settings = load_settings() + self.immediate_search = settings["immediate_search"] + self.transparent_on_unfocus = settings["transparent_on_unfocus"] + self.highlight_enabled = settings["highlight_enabled"] + self.wildcard_search = settings["wildcard_search"] + self.case_insensitive = settings["case_insensitive"] + + self.setWindowTitle("pseudocode search") + # Tool window: stays on top, doesn't block focus, no taskbar entry + self.setWindowFlags(Qt.Tool | Qt.WindowStaysOnTopHint) + self.setMinimumWidth(400) + + # Widget layout + layout = QVBoxLayout() + + # Search box information row (enter/ctrl+enter + status) + info_row = QHBoxLayout() + + self.label = QLabel("enter: next, ctrl+enter: prev") + info_row.addWidget(self.label) + + info_row.addStretch() + + self.status = QLabel("") + info_row.addWidget(self.status) + + layout.addLayout(info_row) + + # Search box + self.input = SearchLineEdit(self) + self.input.setPlaceholderText("Enter search query...") + self.input.textChanged.connect(self.on_text_changed) + self.input.setFocusPolicy(Qt.StrongFocus) + layout.addWidget(self.input) + + # Immediate search + self.immediate_search_btn = QCheckBox("immediate search") + self.immediate_search_btn.setChecked(self.immediate_search) + self.immediate_search_btn.toggled.connect(self.checked_immediate_search) + layout.addWidget(self.immediate_search_btn) + + # Transparent on unfocus + self.transparent_btn = QCheckBox("transparent on unfocus") + self.transparent_btn.setChecked(self.transparent_on_unfocus) + self.transparent_btn.toggled.connect(self.checked_transparent_on_unfocus) + layout.addWidget(self.transparent_btn) + + # Wildcard search + self.wildcard_btn = QCheckBox("wildcard search (*?)") + self.wildcard_btn.setChecked(self.wildcard_search) + self.wildcard_btn.toggled.connect(self.checked_wildcard_search) + layout.addWidget(self.wildcard_btn) + + # Case insensitive search + self.case_insensitive_btn = QCheckBox("case insensitive") + self.case_insensitive_btn.setChecked(self.case_insensitive) + self.case_insensitive_btn.toggled.connect(self.checked_case_insensitive) + layout.addWidget(self.case_insensitive_btn) + + # Highlight row (checkbox + color button) + highlight_row = QHBoxLayout() + + self.highlight_btn = QCheckBox("highlight") + self.highlight_btn.setChecked(self.highlight_enabled) + self.highlight_btn.toggled.connect(self.checked_highlight_enabled) + highlight_row.addWidget(self.highlight_btn) + + highlight_row.addStretch(1) + + self.color_btn = QPushButton() + self.color_btn.setFixedSize(24, 24) + self.color_btn.clicked.connect(self.pick_color) + self.update_color_button() + highlight_row.addWidget(self.color_btn) + + highlight_row.addStretch() + layout.addLayout(highlight_row) + + # Update notification (only shown if update is available) + if PLUGIN_UPDATE_AVAILABLE is not None: + update_row = QHBoxLayout() + current_version_label = QLabel(f"current ver: {PLUGIN_VERSION}") + current_version_label.setStyleSheet( + "background-color: black; color: white;" + ) + update_row.addWidget(current_version_label) + + update_row.addStretch(1) + + latest_version_label = QLabel(f"latest ver: {PLUGIN_UPDATE_AVAILABLE}") + latest_version_label.setStyleSheet("background-color: white; color: black;") + update_row.addWidget(latest_version_label) + layout.addLayout(update_row) + + update_label2 = QLabel("you can update at:") + layout.addWidget(update_label2) + + update_link = QLabel(f'{PLUGIN_REPO_URL}') + update_link.setOpenExternalLinks(True) + layout.addWidget(update_link) + + self.setLayout(layout) + + def pick_color(self): + """Open color picker dialog.""" + global PLUGIN_HIGHLIGHT_COLOR, PLUGIN_HIGHLIGHT_COLOR_DIM + + r = (PLUGIN_HIGHLIGHT_COLOR >> 0) & 0xFF + g = (PLUGIN_HIGHLIGHT_COLOR >> 8) & 0xFF + b = (PLUGIN_HIGHLIGHT_COLOR >> 16) & 0xFF + alpha = (PLUGIN_HIGHLIGHT_COLOR >> 24) & 0xFF + alpha_dim = (PLUGIN_HIGHLIGHT_COLOR_DIM >> 24) & 0xFF + current_color = QColor(r, g, b) + + color = QColorDialog.getColor(current_color, self, "Select Highlight Color") + if color.isValid(): + # Convert RGB to ABGR for IDA, preserve alpha + r = color.red() + g = color.green() + b = color.blue() + PLUGIN_HIGHLIGHT_COLOR = alpha << 24 | b << 16 | g << 8 | r + PLUGIN_HIGHLIGHT_COLOR_DIM = alpha_dim << 24 | b << 16 | g << 8 | r + self.update_color_button() + self.save_current_settings() + # Refresh highlights with new color + text = self.input.text() + if text: + self.update_highlights(text) + + def update_color_button(self): + """Update color button to show current color.""" + # Extract color (format: 0xRRGGBBAA) + r = (PLUGIN_HIGHLIGHT_COLOR >> 0) & 0xFF + g = (PLUGIN_HIGHLIGHT_COLOR >> 8) & 0xFF + b = (PLUGIN_HIGHLIGHT_COLOR >> 16) & 0xFF + self.color_btn.setStyleSheet( + f"background-color: rgb({r}, {g}, {b}); border: 1px solid gray;" + ) + + def save_current_settings(self): + """Save current settings to persistent storage.""" + global PLUGIN_HIGHLIGHT_COLOR, PLUGIN_HIGHLIGHT_COLOR_DIM + settings = { + "immediate_search": self.immediate_search, + "transparent_on_unfocus": self.transparent_on_unfocus, + "highlight_enabled": self.highlight_enabled, + "wildcard_search": self.wildcard_search, + "case_insensitive": self.case_insensitive, + "highlight_color": PLUGIN_HIGHLIGHT_COLOR, + "highlight_color_dim": PLUGIN_HIGHLIGHT_COLOR_DIM, + } + save_settings(settings) + + def checked_immediate_search(self, checked): + """Updates whether we should immediate search or not""" + self.immediate_search = checked + self.save_current_settings() + + def checked_transparent_on_unfocus(self, checked): + """Updates whether window should go transparent on unfocus""" + self.transparent_on_unfocus = checked + if not checked: + self.setWindowOpacity(1.0) + self.save_current_settings() + + def checked_wildcard_search(self, checked): + """Updates whether wildcard search is enabled""" + self.wildcard_search = checked + self.save_current_settings() + # Re-run search with new mode + text = self.input.text() + if text: + if self.immediate_search: + found = search_and_jump( + text, + self.vdui, + self.widget, + direction=0, + use_wildcard=checked, + case_insensitive=self.case_insensitive, + ) + self.update_status(text, found) + self.update_highlights(text) + + def checked_case_insensitive(self, checked): + """Updates whether case-insensitive search is enabled""" + self.case_insensitive = checked + self.save_current_settings() + # Re-run search with new mode + text = self.input.text() + if text: + if self.immediate_search: + found = search_and_jump( + text, + self.vdui, + self.widget, + direction=0, + use_wildcard=self.wildcard_search, + case_insensitive=checked, + ) + self.update_status(text, found) + self.update_highlights(text) + + def checked_highlight_enabled(self, checked): + """Updates whether highlights are enabled""" + global PLUGIN_HIGHLIGHT_HOOKS + + self.highlight_enabled = checked + self.save_current_settings() + if checked: + text = self.input.text() + if text: + self.update_highlights(text) + else: + if PLUGIN_HIGHLIGHT_HOOKS: + PLUGIN_HIGHLIGHT_HOOKS.clear_highlights() + ida_kernwin.refresh_custom_viewer(self.widget) + + def on_text_changed(self, text): + """Called on every keystroke.""" + global PLUGIN_HIGHLIGHT_HOOKS + + if not text: + self.status.setText("") + # Clear highlights + if PLUGIN_HIGHLIGHT_HOOKS: + PLUGIN_HIGHLIGHT_HOOKS.clear_highlights() + ida_kernwin.refresh_custom_viewer(self.widget) + return + + # Only search immediately if immediate_search is enabled + if self.immediate_search: + # First search starts from beginning + found = search_and_jump( + text, + self.vdui, + self.widget, + direction=0, + use_wildcard=self.wildcard_search, + case_insensitive=self.case_insensitive, + ) + self.update_status(text, found) + self.update_highlights(text) + else: + # Just update highlights without jumping + self.update_highlights(text) + matches = find_all_matches( + text, self.vdui, self.wildcard_search, self.case_insensitive + ) + if matches: + self.status.setText(f"{len(matches)} matches") + self.status.setStyleSheet("color: green;") + else: + self.status.setText("No results") + self.status.setStyleSheet("color: red;") + + def find_next(self): + """Find next occurrence.""" + text = self.input.text() + if not text: + return + + found = search_and_jump( + text, + self.vdui, + self.widget, + direction=1, + use_wildcard=self.wildcard_search, + case_insensitive=self.case_insensitive, + ) + self.update_status(text, found) + self.update_highlights(text) + + def find_prev(self): + """Find previous occurrence.""" + text = self.input.text() + if not text: + return + + found = search_and_jump( + text, + self.vdui, + self.widget, + direction=-1, + use_wildcard=self.wildcard_search, + case_insensitive=self.case_insensitive, + ) + self.update_status(text, found) + self.update_highlights(text) + + def update_status(self, text, found): + """Update status label with match count.""" + if found: + matches = find_all_matches( + text, self.vdui, self.wildcard_search, self.case_insensitive + ) + current, total = get_current_match_index(matches, self.widget) + self.status.setText(f"{current} of {total}") + self.status.setStyleSheet("color: green;") + else: + self.status.setText("No results") + self.status.setStyleSheet("color: red;") + + def refresh_status(self): + """Update status to reflect current cursor position.""" + text = self.input.text() + if text: + matches = find_all_matches( + text, self.vdui, self.wildcard_search, self.case_insensitive + ) + if matches: + current, total = get_current_match_index(matches, self.widget) + self.status.setText(f"{current} of {total}") + self.status.setStyleSheet("color: green;") + else: + self.status.setText("No results") + self.status.setStyleSheet("color: red;") + + def refresh_target_vdui(self): + plugin_debug("Refreshing target vdui.") + + vdui, widget = get_pseudocode_vdui() + if vdui is None: + plugin_error("Couldn't get vdui.") + return + + self.vdui = vdui + self.widget = widget + + # Accurate status + self.refresh_status() + + def update_highlights(self, text): + """Update the highlighted matches.""" + global PLUGIN_HIGHLIGHT_HOOKS + + if not PLUGIN_HIGHLIGHT_HOOKS: + return + + if not self.highlight_enabled: + PLUGIN_HIGHLIGHT_HOOKS.clear_highlights() + ida_kernwin.refresh_custom_viewer(self.widget) + return + + matches = find_all_matches( + text, self.vdui, self.wildcard_search, self.case_insensitive + ) + if matches: + func_ea = self.vdui.cfunc.entry_ea + current_line, current_col = get_current_position(self.widget) + PLUGIN_HIGHLIGHT_HOOKS.set_highlights( + func_ea, matches, len(text), current_line, current_col + ) + else: + PLUGIN_HIGHLIGHT_HOOKS.clear_highlights() + + ida_kernwin.refresh_custom_viewer(self.widget) + + def keyPressEvent(self, event): + """Handle Enter/Ctrl+Enter/Escape.""" + if event.key() == Qt.Key_Escape: + self.close() + elif event.key() in (Qt.Key_Return, Qt.Key_Enter): + if event.modifiers() & Qt.ControlModifier: + self.find_prev() + else: + self.find_next() + else: + super().keyPressEvent(event) + + def showEvent(self, event): + """Install escape filter and refresh status when shown.""" + super().showEvent(event) + # Install event filter at application level to intercept Escape before IDA + QApplication.instance().installEventFilter(self.escape_filter) + self.refresh_status() + + def changeEvent(self, event): + """ + Responsible for catching 'activation change' events. + + Checks if an 'activation change' event happened, and if the current window is the active window. + - If it is, it refreshes the window opacity to 100%. + - If it isn't, and the user asks for it through configuration, makes windows opaque. + """ + super().changeEvent(event) + if event.type() == QEvent.ActivationChange: + if self.isActiveWindow(): + self.setWindowOpacity(1.0) + else: + if self.transparent_on_unfocus: + self.setWindowOpacity(0.8) + + def closeEvent(self, event): + """Remove escape filter and clear highlights when closing.""" + global PLUGIN_HIGHLIGHT_HOOKS + + # Remove event filter + QApplication.instance().removeEventFilter(self.escape_filter) + + if PLUGIN_HIGHLIGHT_HOOKS: + PLUGIN_HIGHLIGHT_HOOKS.clear_highlights() + ida_kernwin.refresh_custom_viewer(self.widget) + super().closeEvent(event) + + +def show_search_dialog(): + """Show the search dialog for the pseudocode window.""" + global PLUGIN_SEARCH_DIALOG, PLUGIN_LAST_HOTKEY_TIME + + if PLUGIN_SEARCH_DIALOG is not None: + if PLUGIN_SEARCH_DIALOG.isActiveWindow(): + current_time = time.time() + if current_time - PLUGIN_LAST_HOTKEY_TIME < 1.0: + plugin_debug("Double-tap detected, closing window.") + PLUGIN_SEARCH_DIALOG.close() + PLUGIN_LAST_HOTKEY_TIME = 0 + return + plugin_debug("Window already active.") + PLUGIN_LAST_HOTKEY_TIME = current_time + PLUGIN_SEARCH_DIALOG.refresh_status() + return + + vdui, widget = get_pseudocode_vdui() + if vdui is None: + return + + # NOTE(gabriela): shouldn't be possible, but let's keep safe for the future + widget_type = ida_kernwin.get_widget_type(widget) + if widget_type != ida_kernwin.BWN_PSEUDOCODE: + plugin_warn(f"Current window is not pseudocode (type: {widget_type})") + return + + # Create new search dialog if it's needed. + if PLUGIN_SEARCH_DIALOG is None: + PLUGIN_SEARCH_DIALOG = SearchDialog(vdui, widget) + else: + PLUGIN_SEARCH_DIALOG.refresh_target_vdui() + PLUGIN_SEARCH_DIALOG.show() + PLUGIN_SEARCH_DIALOG.activateWindow() + PLUGIN_SEARCH_DIALOG.input.setFocus() + PLUGIN_SEARCH_DIALOG.input.selectAll() + + +class OpenSearchCallback(ida_kernwin.action_handler_t): + """Callback to open the pseudocode search dialog.""" + + def __init__(self): + ida_kernwin.action_handler_t.__init__(self) + + def activate(self, ctx): + show_search_dialog() + return 1 + + def update(self, ctx): + return ( + ida_kernwin.AST_ENABLE_FOR_WIDGET + if ctx.widget_type == ida_kernwin.BWN_PSEUDOCODE + else ida_kernwin.AST_DISABLE_FOR_WIDGET + ) + + +class CloseSearchCallback(ida_kernwin.action_handler_t): + """Callback to close the pseudocode search dialog.""" + + def __init__(self): + ida_kernwin.action_handler_t.__init__(self) + + def activate(self, ctx): + global PLUGIN_SEARCH_DIALOG + if PLUGIN_SEARCH_DIALOG is not None: + plugin_debug("here!") + PLUGIN_SEARCH_DIALOG.close() + return 1 + + def update(self, ctx): + global PLUGIN_SEARCH_DIALOG + if PLUGIN_SEARCH_DIALOG is not None and PLUGIN_SEARCH_DIALOG.isActiveWindow(): + return ida_kernwin.AST_ENABLE_FOR_WIDGET + return ida_kernwin.AST_DISABLE_FOR_WIDGET + + +def register_hotkey(action_name, action_label, callback, hotkey, tooltip): + """Register a hotkey action.""" + if ida_kernwin.register_action( + ida_kernwin.action_desc_t( + action_name, + action_label, + callback, + hotkey, + tooltip, + ) + ): + plugin_debug(f"Registered hotkey action '{action_name}' ({hotkey})!") + return True + + plugin_error(f"Failed to register hotkey action '{action_name}' ({hotkey})!") + unregister_hotkey(action_name) + return False + + +def unregister_hotkey(action_name): + """Unregister a hotkey action.""" + plugin_debug(f"Unregistering hotkey action '{action_name}'!") + ida_kernwin.unregister_action(action_name) + + +def init_hooks(): + """Initialize the highlight hooks.""" + global PLUGIN_HIGHLIGHT_HOOKS + + if PLUGIN_HIGHLIGHT_HOOKS is None: + PLUGIN_HIGHLIGHT_HOOKS = SearchHighlightHooks() + PLUGIN_HIGHLIGHT_HOOKS.hook() + plugin_debug("Search highlight hooks installed") + + +def cleanup_hooks(): + """Remove the highlight hooks.""" + global PLUGIN_HIGHLIGHT_HOOKS + + if PLUGIN_HIGHLIGHT_HOOKS is not None: + PLUGIN_HIGHLIGHT_HOOKS.unhook() + PLUGIN_HIGHLIGHT_HOOKS = None + plugin_debug("Search highlight hooks removed") + + +def cleanup_search_dialog(): + """Remove the search dialog.""" + global PLUGIN_SEARCH_DIALOG + + if PLUGIN_SEARCH_DIALOG is not None: + PLUGIN_SEARCH_DIALOG.deleteLater() + PLUGIN_SEARCH_DIALOG.close() + PLUGIN_SEARCH_DIALOG = None + plugin_debug("Search dialog removed") + + +# Cleanup previous instance if reloading script +cleanup_hooks() +cleanup_search_dialog() +unregister_hotkey(PLUGIN_ACTION_OPEN_NAME) + +# Load persistent settings on startup +load_settings() + +# Initialize on script load +init_hooks() +register_hotkey( + PLUGIN_ACTION_OPEN_NAME, + PLUGIN_ACTION_OPEN_LABEL, + OpenSearchCallback(), + PLUGIN_ACTION_OPEN_KEY, + PLUGIN_ACTION_OPEN_TOOLTIP, +) + +# Check for updates in background +check_for_updates() + +``` + +`LICENSE`: + +``` +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + +``` + +`README.md`: + +```md +# IDAFind + +**Pictured:** a simple example of IDAFind, with fuzzy search, highlight found lines, and case sensitive/insensitive search. +![Short, GIF demo of IDAFind for non-collapsed presentation](./media/Demo.gif) +> For a more detailed demo, including shortcut action configuration, please see here: +>
+> +> https://github.com/user-attachments/assets/19bf8f4b-7a7c-4f2b-8c83-6bb99c368fa6 +> +>
+ +## What is this? + +This is a plugin for IDA that implements rich, responsive search functionality to pseudocode widgets, making searching for what you're looking for less cumbersome. + +## Versions tested + +- IDA Pro 8.3 + +## Features + +- Save your search settings: your last search settings are saved in your **.idb** file, including your highlight color! +- Doesn't block focus: you can keep your search on top at all times, and press your hotkey **(default: Ctrl+F)** to regrab focus on the fly. +- Immediate search: search as you write, and never leave your searchbox. Go back and forth with **Ctrl+Enter**/**Enter** respectively. +- Transparent on unfocus: because we don't block focus, you can switch back to your pseudocode window seamlessly. To make that experience even better, we offer the option to make the window see-through as you do that. +- Wildcard search: functionally the same as the `-Like` functionality of PowerShell. Most Windows programmers are familiar with it already. +- Case insensitive search: does not account for character case in matching. +- **Highlights**: as you type, all the matched lines of your search query (including wildcard search ones) will be highlighted. To help you focus, **the active match (as seen in the search window) pops out, as other matches are dimmed**. You can even pick your preferred color! +- **Plugin Update checks**: commit `0a1a9446f64965f4cfd89b65c76920b4823f729b` introduced a check to see if you're running the latest version of **IDAFind**, and declared the first stable version of `1.0.0`. + +## Installation + +Go to your **IDA folder**, in the **plugins** folder, and put **IDAFind.py** there. + +## AI disclaimer +I put together most of this with Claude. Well, like 80% of it. It was great at early iteration but at some point it was faster for me to start implementing and writing some of the stuff, and handle refactoring. It was kind of really awesome though. + +## License +[The Unlicense/PUBLIC DOMAIN](./LICENSE) + +``` \ No newline at end of file diff --git a/archive/not1cyyy/Kiroshi.txt b/archive/not1cyyy/Kiroshi.txt new file mode 100644 index 00000000..65a37992 --- /dev/null +++ b/archive/not1cyyy/Kiroshi.txt @@ -0,0 +1,2653 @@ +Project Path: arc_not1cyyy_Kiroshi_kn2cqvvm + +Source Tree: + +```txt +arc_not1cyyy_Kiroshi_kn2cqvvm +├── LICENSE +├── README.md +├── Release +│ └── anticheat_detector64.dll +├── anticheat_detector.cpp +├── anticheat_detector.h +├── ida-plugin.json +├── makefile +└── obj + └── x64_win_vc_64_opt + ├── anticheat_detector.obj + └── anticheat_detector64.dll.map + +``` + +`LICENSE`: + +``` + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +``` + +`README.md`: + +```md +# Kiroshi: an Anti-Cheat Artifacts Detector + +An **IDA Pro plugin** that statically detects **Anti-Cheat Artifacts** in Windows binaries. +meant to help reverse engineers and security researchers quickly identify the presence of +commercial or custom anti-cheat mechanisms during analysis. + +> ⚠️ This plugin is for **reverse engineering and academic research only**. + +--- + +Screenshot 2026-01-02 023528 + +## ✨ Features + +- 🔍 **Static detection of Anti-Cheat Artifacts** + - Known Anti-Cheat strings (EAC, BattlEye, Vanguard, FACEIT, XIGNCODE, etc.) + - Suspicious Windows APIs commonly used by Anti-Cheats + - Anti-debugging and Anti-VM related imports +- 🧠 **Function-level scanning** +- 📊 **Interactive results view** + - Clickable results that jump directly to the artifact in IDA +- 🧩 **Extensible rule-based design** + - Easy to add new signatures and heuristics +- ⚡ Built on the **IDA SDK (C++)** + +--- + +## 🛠️ Detection Logic (Overview) + +The plugin analyzes the binary for common Anti-Cheat techniques, including: + +### 1. Known Anti-Cheat Strings +Examples: +- `EasyAntiCheat` +- `BEService` +- `vgk.sys` +- `FACEIT AC` +- `XIGNCODE` + +### 2. Suspicious Imports / APIs +Examples: +- `NtQueryInformationProcess` +- `ZwQuerySystemInformation` +- `IsDebuggerPresent` +- `CheckRemoteDebuggerPresent` +- `OutputDebugString` +- `NtSetInformationThread` + +### 3. Heuristic Indicators +- Unusual driver-related strings +- Anti-debug patterns +- Process / thread inspection behavior + +> This is **static analysis only**, no runtime hooking or bypassing is done. + +--- + +## 📦 Installation + +You can download the pre-built plugin from the [releases](https://github.com/not1cyyy/Anti-Cheat-Artifacts-Detector/releases) page. +Just place the plugin in the IDA Pro plugins directory. + +## 📦 Building from Source + +### Requirements +- IDA Pro 9.0 +- IDA SDK +- Windows SDK (10.0.26100.0) +- Visual Studio (MSVC) + +### Build Steps +1. Clone the repository: + ```bash + git clone https://github.com/not1cyyy/Anti-Cheat-Artifacts-Detector.git + cd Anti-Cheat-Artifacts-Detector + ``` +2. Build the plugin: + ```bash + mkdir build + cd build + cmake .. + cmake --build . + ``` +## 🚀 Usage + +1. Open IDA Pro and load the binary you want to analyze. +2. Go to the "Plugins" menu and select "Anti-Cheat Artifacts Detector". +3. Choose if you want to scan the current function or all functions. +4. The plugin will scan the binary and display the results in a chooser window. +5. You can then double click on the results to jump to the artifact in IDA. + +## 📄 License + +This project is licensed under the [GNU General Public License v3.0](LICENSE). + +## 📝 Contributing + +Contributions are welcome! Please open an issue or submit a pull request. + +## ⭐ Acknowledgements + +- [IDA SDK](https://github.com/HexRaysSA/ida-sdk) +- [IDA Pro](https://www.hex-rays.com/products/ida/) +- Reverse engineering & game security research community + +``` + +`anticheat_detector.cpp`: + +```cpp +#include "anticheat_detector.h" +#include + +int data_id; + +//-------------------------------------------------------------------------- +// API patterns commonly used by anti-cheat systems +const anticheat_detector_t::api_pattern_t anticheat_detector_t::api_patterns[] = { + // Debugger detection + { "IsDebuggerPresent", AC_DEBUGGER_DETECT, "Checks if debugger is attached", 5 }, + { "CheckRemoteDebuggerPresent", AC_DEBUGGER_DETECT, "Checks for remote debugger", 5 }, + { "NtQueryInformationProcess", AC_DEBUGGER_DETECT, "Can detect debugger via ProcessDebugPort", 4 }, + { "NtSetInformationThread", AC_DEBUGGER_DETECT, "Can hide thread from debugger", 4 }, + { "OutputDebugStringA", AC_DEBUGGER_DETECT, "Used in anti-debug tricks", 3 }, + { "OutputDebugStringW", AC_DEBUGGER_DETECT, "Used in anti-debug tricks", 3 }, + { "ZwQueryInformationProcess", AC_DEBUGGER_DETECT, "Native API debugger check", 4 }, + + // VM detection + { "NtQuerySystemInformation", AC_VM_DETECT, "Can detect VM artifacts", 3 }, + { "GetSystemFirmwareTable", AC_VM_DETECT, "Checks BIOS/firmware for VM signatures", 4 }, + { "SetupDiGetDeviceRegistryPropertyA", AC_VM_DETECT, "Enumerates hardware for VM detection", 3 }, + + // Integrity checks + { "CryptHashData", AC_INTEGRITY_CHECK, "Hashing function for integrity checks", 3 }, + { "CreateToolhelp32Snapshot", AC_INTEGRITY_CHECK, "Memory/process enumeration", 4 }, + { "VirtualProtect", AC_INTEGRITY_CHECK, "Memory protection changes", 3 }, + { "VirtualQuery", AC_INTEGRITY_CHECK, "Query memory regions", 3 }, + { "ReadProcessMemory", AC_INTEGRITY_CHECK, "Read process memory", 4 }, + + // Timing checks + { "QueryPerformanceCounter", AC_TIMING_CHECK, "High-resolution timing", 3 }, + { "GetTickCount", AC_TIMING_CHECK, "Tick count for timing attacks", 2 }, + { "GetTickCount64", AC_TIMING_CHECK, "64-bit tick count", 2 }, + { "timeGetTime", AC_TIMING_CHECK, "Multimedia timer", 2 }, + { "NtQueryPerformanceCounter", AC_TIMING_CHECK, "Native timing function", 3 }, + + // Hardware breakpoint detection + { "GetThreadContext", AC_HARDWARE_BREAKPOINT, "Can read debug registers", 5 }, + { "SetThreadContext", AC_HARDWARE_BREAKPOINT, "Can manipulate debug registers", 5 }, + { "NtGetContextThread", AC_HARDWARE_BREAKPOINT, "Native context function", 5 }, + { "NtSetContextThread", AC_HARDWARE_BREAKPOINT, "Native context function", 5 }, + + // Process/Module enumeration + { "EnumProcesses", AC_PROCESS_ENUM, "Enumerate running processes", 3 }, + { "Process32First", AC_PROCESS_ENUM, "Process enumeration", 3 }, + { "Process32Next", AC_PROCESS_ENUM, "Process enumeration", 3 }, + { "EnumProcessModules", AC_MODULE_ENUM, "Module enumeration", 3 }, + { "Module32First", AC_MODULE_ENUM, "Module enumeration", 3 }, + { "Module32Next", AC_MODULE_ENUM, "Module enumeration", 3 }, + { "GetModuleHandleA", AC_MODULE_ENUM, "Get module handle", 2 }, + { "GetModuleHandleW", AC_MODULE_ENUM, "Get module handle", 2 }, + { "LoadLibraryA", AC_MODULE_ENUM, "Load library check", 2 }, + { "LoadLibraryW", AC_MODULE_ENUM, "Load library check", 2 }, + + // Protection systems + { "VirtualProtectEx", AC_PROTECTION_CHECK, "External memory protection", 3 }, + { "NtProtectVirtualMemory", AC_PROTECTION_CHECK, "Native memory protection", 3 }, +}; + +//-------------------------------------------------------------------------- +// String patterns +const anticheat_detector_t::string_pattern_t anticheat_detector_t::string_patterns[] = { + { "WINE", AC_VM_DETECT, "Wine compatibility layer detection", 4 }, + { "VMware", AC_VM_DETECT, "VMware detection", 4 }, + { "VBox", AC_VM_DETECT, "VirtualBox detection", 4 }, + { "VBOX", AC_VM_DETECT, "VirtualBox detection", 4 }, + { "Xen", AC_VM_DETECT, "Xen hypervisor detection", 4 }, + { "QEMU", AC_VM_DETECT, "QEMU detection", 4 }, + { "Cheat Engine", AC_PROCESS_ENUM, "Cheat Engine detection", 5 }, + { "OllyDbg", AC_DEBUGGER_DETECT, "OllyDbg detection", 5 }, + { "x64dbg", AC_DEBUGGER_DETECT, "x64dbg detection", 5 }, + { "IDA", AC_DEBUGGER_DETECT, "IDA Pro detection", 5 }, + { "Wireshark", AC_PROCESS_ENUM, "Network analysis tool", 3 }, + { "ProcessHacker", AC_PROCESS_ENUM, "Process analysis tool", 4 }, + { "ProcessExplorer", AC_PROCESS_ENUM, "Process analysis tool", 4 }, + { "SeDebugPrivilege", AC_PROTECTION_CHECK, "Debug privilege check", 4 }, +}; + +//-------------------------------------------------------------------------- +plugin_ctx_t::plugin_ctx_t() + : show_detector_ah(*this), + main_action(ACTION_DESC_LITERAL_PLUGMOD( + ACTION_NAME, + ACTION_LABEL, + &show_detector_ah, + this, + "Ctrl+Shift+A", + nullptr, -1)), + detector(*this) +{ +} + +//-------------------------------------------------------------------------- +bool plugin_ctx_t::register_main_action() +{ + return register_action(main_action) + && attach_action_to_menu("Search/", + ACTION_NAME, SETMENU_APP); +} + +//-------------------------------------------------------------------------- +int idaapi show_detector_ah_t::activate(action_activation_ctx_t *) +{ + ctx.run(0); + return 0; +} + +//-------------------------------------------------------------------------- +anticheat_detector_t::anticheat_detector_t(plugin_ctx_t &_ctx) : ctx(_ctx) +{ +} + +//-------------------------------------------------------------------------- +const char *anticheat_detector_t::get_category_name(artifact_category_t cat) +{ + switch (cat) + { + case AC_DEBUGGER_DETECT: return "Debugger Detection"; + case AC_VM_DETECT: return "VM Detection"; + case AC_INTEGRITY_CHECK: return "Integrity Check"; + case AC_TIMING_CHECK: return "Timing Check"; + case AC_HARDWARE_BREAKPOINT: return "Hardware Breakpoint"; + case AC_PROCESS_ENUM: return "Process Enumeration"; + case AC_MODULE_ENUM: return "Module Enumeration"; + case AC_THREAD_CONTEXT: return "Thread Context"; + case AC_PROTECTION_CHECK: return "Protection Check"; + default: return "Unknown"; + } +} + +//-------------------------------------------------------------------------- +bgcolor_t anticheat_detector_t::get_category_color(artifact_category_t cat) +{ + switch (cat) + { + case AC_DEBUGGER_DETECT: return 0xFF6B6B; // Red + case AC_VM_DETECT: return 0xFFA500; // Orange + case AC_INTEGRITY_CHECK: return 0xFFFF00; // Yellow + case AC_TIMING_CHECK: return 0x90EE90; // Light Green + case AC_HARDWARE_BREAKPOINT: return 0xFF1493; // Deep Pink + case AC_PROCESS_ENUM: return 0x87CEEB; // Sky Blue + case AC_MODULE_ENUM: return 0xDDA0DD; // Plum + case AC_THREAD_CONTEXT: return 0xFF69B4; // Hot Pink + case AC_PROTECTION_CHECK: return 0xFFA07A; // Light Salmon + default: return 0xCCCCCC; // Gray + } +} + +//-------------------------------------------------------------------------- +bool anticheat_detector_t::is_suspicious_constant(uint64 val) +{ + // Common constants used in anti-debug/anti-vm + static const uint64 suspicious_constants[] = { + 0x564D5868, // VMware I/O port + 0x40000000, // VirtualBox CPUID + 0x7FFE0000, // KUSER_SHARED_DATA + 0xC0000000, // Kernel address range + 0x2C, // ProcessDebugPort + 0x1F, // ProcessDebugObjectHandle + }; + + for (size_t i = 0; i < qnumber(suspicious_constants); i++) + { + if (val == suspicious_constants[i]) + return true; + } + return false; +} + +//-------------------------------------------------------------------------- +bool anticheat_detector_t::check_api_calls(ea_t ea, func_t *func) +{ + bool found = false; + func_item_iterator_t fii; + + for (bool fi_ok = fii.set(func); fi_ok; fi_ok = fii.next_code()) + { + xrefblk_t xb; + for (bool xb_ok = xb.first_from(fii.current(), XREF_FAR); + xb_ok && xb.iscode; + xb_ok = xb.next_from()) + { + qstring name; + if (get_name(&name, xb.to) > 0) + { + // Check against our API patterns + for (size_t i = 0; i < qnumber(api_patterns); i++) + { + if (strstr(name.c_str(), api_patterns[i].api_name) != nullptr) + { + detected_artifact_t artifact; + artifact.address = fii.current(); + artifact.category = api_patterns[i].category; + artifact.description = api_patterns[i].description; + artifact.severity = api_patterns[i].severity; + artifact.api_used = name; + artifact.instruction = name; // Store the API name as instruction + get_func_name(&artifact.function_name, func->start_ea); + + artifacts.push_back(artifact); + found = true; + + // Set comment to mark the artifact (only if not already marked) + qstring cmt; + get_cmt(&cmt, fii.current(), false); + if (strstr(cmt.c_str(), "[ACAD]") == nullptr) + { + if (!cmt.empty()) + cmt.append(" "); + cmt.append("[ACAD] "); + cmt.append(artifact.description); + set_cmt(fii.current(), cmt.c_str(), false); + } + } + } + } + } + } + + return found; +} + +//-------------------------------------------------------------------------- +bool anticheat_detector_t::check_string_references(ea_t ea, func_t *func) +{ + bool found = false; + func_item_iterator_t fii; + + for (bool fi_ok = fii.set(func); fi_ok; fi_ok = fii.next_code()) + { + xrefblk_t xb; + for (bool xb_ok = xb.first_from(fii.current(), XREF_DATA); + xb_ok; + xb_ok = xb.next_from()) + { + qstring str; + if (get_strlit_contents(&str, xb.to, -1, STRTYPE_C) > 0) + { + // Check against string patterns + for (size_t i = 0; i < qnumber(string_patterns); i++) + { + if (stristr(str.c_str(), string_patterns[i].pattern) != nullptr) + { + detected_artifact_t artifact; + artifact.address = fii.current(); + artifact.category = string_patterns[i].category; + artifact.description.sprnt("%s (string: \"%s\")", + string_patterns[i].description, + string_patterns[i].pattern); + artifact.severity = string_patterns[i].severity; + artifact.instruction.sprnt("String: \"%s\"", string_patterns[i].pattern); + get_func_name(&artifact.function_name, func->start_ea); + + artifacts.push_back(artifact); + found = true; + + // Set comment to mark the artifact (only if not already marked) + qstring cmt; + get_cmt(&cmt, fii.current(), false); + if (strstr(cmt.c_str(), "[ACAD]") == nullptr) + { + if (!cmt.empty()) + cmt.append(" "); + cmt.append("[ACAD] "); + cmt.append(artifact.description); + set_cmt(fii.current(), cmt.c_str(), false); + } + } + } + } + } + } + + return found; +} + +//-------------------------------------------------------------------------- +bool anticheat_detector_t::check_inline_detection(ea_t ea, func_t *func) +{ + bool found = false; + func_item_iterator_t fii; + + for (bool fi_ok = fii.set(func); fi_ok; fi_ok = fii.next_code()) + { + ea_t current = fii.current(); + + // Check for specific instructions + if (is_code(get_flags(current))) + { + insn_t insn; + if (decode_insn(&insn, current) > 0) + { + qstring mnem; + print_insn_mnem(&mnem, current); + + // CPUID (VM detection) + if (mnem == "cpuid") + { + detected_artifact_t artifact; + artifact.address = current; + artifact.category = AC_VM_DETECT; + artifact.description = "CPUID instruction (VM detection)"; + artifact.severity = 4; + artifact.instruction = "cpuid"; + get_func_name(&artifact.function_name, func->start_ea); + + artifacts.push_back(artifact); + found = true; + + // Set comment to mark the artifact (only if not already marked) + qstring cmt; + get_cmt(&cmt, current, false); + if (strstr(cmt.c_str(), "[ACAD]") == nullptr) + { + if (!cmt.empty()) + cmt.append(" "); + cmt.append("[ACAD] "); + cmt.append(artifact.description); + set_cmt(current, cmt.c_str(), false); + } + } + + // RDTSC/RDTSCP (timing check) + else if (mnem == "rdtsc" || mnem == "rdtscp") + { + detected_artifact_t artifact; + artifact.address = current; + artifact.category = AC_TIMING_CHECK; + artifact.description = "RDTSC instruction (timing check)"; + artifact.severity = 3; + artifact.instruction = mnem; + get_func_name(&artifact.function_name, func->start_ea); + + artifacts.push_back(artifact); + found = true; + + // Set comment to mark the artifact (only if not already marked) + qstring cmt; + get_cmt(&cmt, current, false); + if (strstr(cmt.c_str(), "[ACAD]") == nullptr) + { + if (!cmt.empty()) + cmt.append(" "); + cmt.append("[ACAD] "); + cmt.append(artifact.description); + set_cmt(current, cmt.c_str(), false); + } + } + + // INT 2D (anti-debug) or INT 3 + else if (mnem == "int") + { + if (insn.ops[0].type == o_imm && + (insn.ops[0].value == 0x2D || insn.ops[0].value == 0x03)) + { + detected_artifact_t artifact; + artifact.address = current; + artifact.category = AC_DEBUGGER_DETECT; + artifact.description.sprnt("INT %Xh instruction (anti-debug)", insn.ops[0].value); + artifact.severity = 5; + artifact.instruction.sprnt("int %Xh", insn.ops[0].value); + get_func_name(&artifact.function_name, func->start_ea); + + artifacts.push_back(artifact); + found = true; + + // Set comment to mark the artifact (only if not already marked) + qstring cmt; + get_cmt(&cmt, current, false); + if (strstr(cmt.c_str(), "[ACAD]") == nullptr) + { + if (!cmt.empty()) + cmt.append(" "); + cmt.append("[ACAD] "); + cmt.append(artifact.description); + set_cmt(current, cmt.c_str(), false); + } + } + } + + // Check for PEB access (fs:[30h] or gs:[60h] on x64) + else if (mnem == "mov" && insn.ops[0].type == o_reg) + { + // Check if accessing segment registers with specific offsets + if (insn.ops[1].type == o_mem || insn.ops[1].type == o_displ) + { + // Common PEB offsets: 0x30 (x86 fs), 0x60 (x64 gs) + if (insn.ops[1].addr == 0x30 || insn.ops[1].addr == 0x60) + { + detected_artifact_t artifact; + artifact.address = current; + artifact.category = AC_DEBUGGER_DETECT; + artifact.description = "PEB access (potential BeingDebugged check)"; + artifact.severity = 4; + + // Get the full instruction text + qstring insn_text; + generate_disasm_line(&insn_text, current, GENDSM_FORCE_CODE); + tag_remove(&insn_text); + artifact.instruction = insn_text; + + get_func_name(&artifact.function_name, func->start_ea); + + artifacts.push_back(artifact); + found = true; + + // Set comment to mark the artifact (only if not already marked) + qstring cmt; + get_cmt(&cmt, current, false); + if (strstr(cmt.c_str(), "[ACAD]") == nullptr) + { + if (!cmt.empty()) + cmt.append(" "); + cmt.append("[ACAD] "); + cmt.append(artifact.description); + set_cmt(current, cmt.c_str(), false); + } + } + } + } + + // Check for suspicious constants in operands + for (int i = 0; i < UA_MAXOP; i++) + { + if (insn.ops[i].type == o_imm) + { + if (is_suspicious_constant(insn.ops[i].value)) + { + detected_artifact_t artifact; + artifact.address = current; + artifact.category = AC_UNKNOWN; + artifact.description.sprnt("Suspicious constant: 0x%llX", insn.ops[i].value); + artifact.severity = 3; + artifact.instruction.sprnt("0x%llX", insn.ops[i].value); + get_func_name(&artifact.function_name, func->start_ea); + + artifacts.push_back(artifact); + found = true; + } + } + } + } + } + } + + return found; +} + +//-------------------------------------------------------------------------- +void anticheat_detector_t::scan_function(func_t *func) +{ + if (func == nullptr) + return; + + msg("Scanning function at %a\n", func->start_ea); + + check_api_calls(func->start_ea, func); + check_string_references(func->start_ea, func); + check_inline_detection(func->start_ea, func); +} + +//-------------------------------------------------------------------------- +void anticheat_detector_t::scan_all_functions() +{ + clear_results(); + msg("Starting anti-cheat artifact scan...\n"); + + size_t func_qty = get_func_qty(); + for (size_t i = 0; i < func_qty; i++) + { + func_t *func = getn_func(i); + if (func != nullptr) + scan_function(func); + + if ((i % 100) == 0) + msg("Progress: %d/%d functions\n", i, func_qty); + } + + msg("Scan complete! Found %d artifacts\n", artifacts.size()); + show_results(); +} + +//-------------------------------------------------------------------------- +void anticheat_detector_t::scan_current_function() +{ + clear_results(); + + func_t *func = get_func(get_screen_ea()); + if (func == nullptr) + { + warning("Please position cursor in a function!"); + return; + } + + msg("Scanning current function...\n"); + scan_function(func); + msg("Found %d artifacts in current function\n", artifacts.size()); + show_results(); +} + +//-------------------------------------------------------------------------- +void anticheat_detector_t::clear_results() +{ + artifacts.clear(); +} + +//-------------------------------------------------------------------------- +void anticheat_detector_t::show_results() +{ + if (artifacts.empty()) + { + info("No anti-cheat artifacts detected!"); + return; + } + + artifact_chooser_t chooser(*this); + chooser.choose(); +} + +//-------------------------------------------------------------------------- +void anticheat_detector_t::export_results(const char *filename) +{ + FILE *f = qfopen(filename, "w"); + if (f == nullptr) + { + warning("Failed to open file for writing!"); + return; + } + + qfprintf(f, "Anti-Cheat Artifact Detection Report\n"); + qfprintf(f, "=====================================\n\n"); + qfprintf(f, "Total artifacts found: %d\n\n", artifacts.size()); + + for (const auto &artifact : artifacts) + { + qfprintf(f, "Address: %a\n", artifact.address); + qfprintf(f, "Function: %s\n", artifact.function_name.c_str()); + qfprintf(f, "Instruction: %s\n", artifact.instruction.c_str()); + qfprintf(f, "Category: %s\n", get_category_name(artifact.category)); + qfprintf(f, "Severity: %d/5\n", artifact.severity); + qfprintf(f, "Description: %s\n", artifact.description.c_str()); + if (!artifact.api_used.empty()) + qfprintf(f, "API: %s\n", artifact.api_used.c_str()); + qfprintf(f, "\n"); + } + + qfclose(f); + msg("Report exported to %s\n", filename); +} + +//-------------------------------------------------------------------------- +// Artifact chooser implementation +const int artifact_chooser_t::widths_[] = +{ + CHCOL_HEX | 10, // Address + 15, // Function + 25, // Instruction + 20, // Category + 5, // Severity + 40, // Description +}; + +const char *const artifact_chooser_t::header_[] = +{ + "Address", + "Function", + "Instruction", + "Category", + "Severity", + "Description", +}; + +artifact_chooser_t::artifact_chooser_t(anticheat_detector_t &det) + : chooser_t(CH_MODAL | CH_KEEP, + qnumber(widths_), widths_, header_, + "Anti-Cheat Artifacts"), + detector(det) +{ +} + +size_t idaapi artifact_chooser_t::get_count() const +{ + return detector.artifacts.size(); +} + +void idaapi artifact_chooser_t::get_row( + qstrvec_t *cols, + int *icon_, + chooser_item_attrs_t *attrs, + size_t n) const +{ + const detected_artifact_t &artifact = detector.artifacts[n]; + qstrvec_t &cols_ref = *cols; + + cols_ref[0].sprnt("%a", artifact.address); + cols_ref[1] = artifact.function_name; + cols_ref[2] = artifact.instruction; + cols_ref[3] = detector.get_category_name(artifact.category); + cols_ref[4].sprnt("%d/5", artifact.severity); + cols_ref[5] = artifact.description; + + if (attrs != nullptr) + attrs->color = detector.get_category_color(artifact.category); +} + +chooser_t::cbret_t idaapi artifact_chooser_t::enter(size_t n) +{ + if (n < detector.artifacts.size()) + jumpto(detector.artifacts[n].address); + return cbret_t(0, chooser_base_t::ALL_CHANGED); +} + +//-------------------------------------------------------------------------- +bool idaapi plugin_ctx_t::run(size_t arg) +{ + static const char form[] = + "Anti-Cheat Artifact Detector\n" + "\n" + "Select scan mode:\n" + "\n" + " <~S~can current function:R>\n" + " >\n" + "\n"; + + int scan_mode = 0; + if (!ask_form(form, &scan_mode)) + return false; + + if (scan_mode == 0) + detector.scan_current_function(); + else + detector.scan_all_functions(); + + return true; +} + +//-------------------------------------------------------------------------- +static plugmod_t *idaapi init() +{ + if (!is_idaq()) + return nullptr; + + plugin_ctx_t *ctx = new plugin_ctx_t; + if (!ctx->register_main_action()) + { + msg("Failed to register menu item for <" ACTION_LABEL "> plugin!\n"); + delete ctx; + return nullptr; + } + + set_module_data(&data_id, ctx); + msg("Anti-Cheat Artifact Detector loaded!\n"); + msg("Use " ACTION_LABEL " from Search menu or press Ctrl+Shift+A\n"); + + return ctx; +} + +//-------------------------------------------------------------------------- +plugin_t PLUGIN = +{ + IDP_INTERFACE_VERSION, + PLUGIN_MULTI, + init, + nullptr, + nullptr, + "Anti-Cheat Artifact Detector - Identifies debugger checks, VM detection, and other anti-analysis techniques", + "Anti-Cheat Artifact Detector plugin for IDA Pro\n" + "\n" + "Detects common anti-cheat techniques including:\n" + "- Debugger detection APIs\n" + "- VM detection methods\n" + "- Integrity checks\n" + "- Timing-based detection\n" + "- Hardware breakpoint checks\n" + "- Process/module enumeration\n", + ACTION_LABEL, + "" +}; +``` + +`anticheat_detector.h`: + +```h +#ifndef __ANTICHEAT_DETECTOR__ +#define __ANTICHEAT_DETECTOR__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ACTION_NAME "anticheat:DetectArtifacts" +#define ACTION_LABEL "Anti-Cheat Artifacts Detector" + +// Anti-cheat artifact categories +enum artifact_category_t +{ + AC_DEBUGGER_DETECT, + AC_VM_DETECT, + AC_INTEGRITY_CHECK, + AC_TIMING_CHECK, + AC_HARDWARE_BREAKPOINT, + AC_PROCESS_ENUM, + AC_MODULE_ENUM, + AC_THREAD_CONTEXT, + AC_PROTECTION_CHECK, + AC_UNKNOWN +}; + +// Detected artifact structure +struct detected_artifact_t +{ + ea_t address; + artifact_category_t category; + qstring description; + qstring function_name; + int severity; // 1-5, 5 being highest + qstring api_used; + qstring instruction; // The actual instruction/API at this location +}; + +struct plugin_ctx_t; + +//-------------------------------------------------------------------------- +// Anti-cheat artifact detector class +class anticheat_detector_t +{ +public: + plugin_ctx_t &ctx; + std::vector artifacts; + +private: + // API patterns to detect + struct api_pattern_t + { + const char *api_name; + artifact_category_t category; + const char *description; + int severity; + }; + + static const api_pattern_t api_patterns[]; + + // String patterns that indicate anti-cheat + struct string_pattern_t + { + const char *pattern; + artifact_category_t category; + const char *description; + int severity; + }; + + static const string_pattern_t string_patterns[]; + + // Instruction patterns for inline checks + struct insn_pattern_t + { + const char *description; + artifact_category_t category; + int severity; + }; + + bool check_api_calls(ea_t ea, func_t *func); + bool check_string_references(ea_t ea, func_t *func); + bool check_inline_detection(ea_t ea, func_t *func); + bool is_suspicious_constant(uint64 val); + +public: + anticheat_detector_t(plugin_ctx_t &_ctx); + + void scan_function(func_t *func); + void scan_all_functions(); + void scan_current_function(); + void clear_results(); + + const char *get_category_name(artifact_category_t cat); + bgcolor_t get_category_color(artifact_category_t cat); + + void show_results(); + void export_results(const char *filename); +}; + +//-------------------------------------------------------------------------- +// Chooser for displaying detected artifacts +class artifact_chooser_t : public chooser_t +{ +protected: + static const int widths_[]; + static const char *const header_[]; + anticheat_detector_t &detector; + +public: + artifact_chooser_t(anticheat_detector_t &det); + + virtual size_t idaapi get_count() const override; + virtual void idaapi get_row( + qstrvec_t *cols, + int *icon_, + chooser_item_attrs_t *attrs, + size_t n) const override; + virtual cbret_t idaapi enter(size_t n) override; +}; + +//-------------------------------------------------------------------------- +// Main action handler +struct show_detector_ah_t : public action_handler_t +{ + plugin_ctx_t &ctx; + show_detector_ah_t(plugin_ctx_t &_ctx) : ctx(_ctx) {} + virtual int idaapi activate(action_activation_ctx_t *) override; + virtual action_state_t idaapi update(action_update_ctx_t *) override + { + return AST_ENABLE_ALWAYS; + } +}; + +//-------------------------------------------------------------------------- +// Plugin context +struct plugin_ctx_t : public plugmod_t +{ + show_detector_ah_t show_detector_ah; + const action_desc_t main_action; + anticheat_detector_t detector; + + plugin_ctx_t(); + bool register_main_action(); + virtual bool idaapi run(size_t arg) override; +}; + +#endif +``` + +`ida-plugin.json`: + +```json +{ + "IDAMetadataDescriptorVersion": 1, + "plugin": { + "name": "Anti-Cheat Artifacts Detector", + "version": "1.0.0", + "entryPoint": "Release/anticheat_detector64.dll", + "urls": { + "repository": "https://github.com/not1cyyy/Anti-Cheat-Artifacts-Detector" + }, + "authors": [ + { + "name": "not1cyyy", + "email": "not1cyyy@proton.me" + } + ] + } +} +``` + +`makefile`: + +``` +PROC=anticheat_detector + +include ../plugin.mak + +# MAKEDEP dependency list ------------------ +$(F)anticheat_detector$(O): $(I)bytes.hpp $(I)config.hpp $(I)fpro.h \ + $(I)funcs.hpp $(I)ida.hpp $(I)idp.hpp \ + $(I)kernwin.hpp $(I)lines.hpp $(I)loader.hpp \ + $(I)nalt.hpp $(I)name.hpp $(I)netnode.hpp \ + $(I)pro.h $(I)segment.hpp $(I)ua.hpp \ + $(I)xref.hpp $(I)allins.hpp $(I)diskio.hpp \ + anticheat_detector.cpp anticheat_detector.h +``` + +`obj/x64_win_vc_64_opt/anticheat_detector64.dll.map`: + +```map + anticheat_detector64 + + Timestamp is b43712d4 (reproducible build file hash) + + Preferred load address is 0000000180000000 + + Start Length Name Class + 0001:00000000 00004810H .text$mn CODE + 0001:00004810 00000040H .text$mn$00 CODE + 0001:00004850 00000365H .text$x CODE + 0002:00000000 00000270H .idata$5 DATA + 0002:00000270 00000028H .00cfg DATA + 0002:00000298 00000008H .CRT$XCA DATA + 0002:000002a0 00000008H .CRT$XCZ DATA + 0002:000002a8 00000008H .CRT$XIA DATA + 0002:000002b0 00000008H .CRT$XIZ DATA + 0002:000002b8 00000008H .CRT$XPA DATA + 0002:000002c0 00000008H .CRT$XPZ DATA + 0002:000002c8 00000008H .CRT$XTA DATA + 0002:000002d0 00000010H .CRT$XTZ DATA + 0002:000002d8 00000000H .gehcont$y DATA + 0002:000002d8 00000000H .gfids$y DATA + 0002:000002e0 000019a0H .rdata DATA + 0002:00001c80 00000000H .rdata$CastGuardVftablesA DATA + 0002:00001c80 00000000H .rdata$CastGuardVftablesC DATA + 0002:00001c80 000001f4H .rdata$r DATA + 0002:00001e74 0000002cH .rdata$voltmd DATA + 0002:00001ea0 000002e8H .rdata$zzzdbg DATA + 0002:00002188 00000008H .rtc$IAA DATA + 0002:00002190 00000008H .rtc$IZZ DATA + 0002:00002198 00000008H .rtc$TAA DATA + 0002:000021a0 00000008H .rtc$TZZ DATA + 0002:000021a8 00000810H .xdata DATA + 0002:000029b8 000000f8H .xdata$x DATA + 0002:00002ab0 00000054H .edata DATA + 0002:00002b04 0000008cH .idata$2 DATA + 0002:00002b90 00000018H .idata$3 DATA + 0002:00002ba8 00000270H .idata$4 DATA + 0002:00002e18 000005d4H .idata$6 DATA + 0003:00000000 00000080H .data DATA + 0003:00000080 00000080H .data$r DATA + 0003:00000100 00000020H .data$rs DATA + 0003:00000120 000005f8H .bss DATA + 0004:00000000 0000045cH .pdata DATA + + Address Publics by Value Rva+Base Lib:Object + + 0000:00000000 __guard_iat_table 0000000000000000 + 0000:00000000 __arm64x_redirection_metadata 0000000000000000 + 0000:00000000 __guard_longjmp_count 0000000000000000 + 0000:00000000 __dynamic_value_reloc_table 0000000000000000 + 0000:00000000 __x64_code_ranges_to_entry_points_count 0000000000000000 + 0000:00000000 __hybrid_auxiliary_iat 0000000000000000 + 0000:00000000 __hybrid_auxiliary_iat_copy 0000000000000000 + 0000:00000000 ___safe_se_handler_table 0000000000000000 + 0000:00000000 __AbsoluteZero 0000000000000000 + 0000:00000000 __guard_iat_count 0000000000000000 + 0000:00000000 __guard_longjmp_table 0000000000000000 + 0000:00000000 __guard_check_icall_a64n_fptr 0000000000000000 + 0000:00000000 __x64_code_ranges_to_entry_points 0000000000000000 + 0000:00000000 __hybrid_code_map_count 0000000000000000 + 0000:00000000 __guard_fids_table 0000000000000000 + 0000:00000000 __guard_fids_count 0000000000000000 + 0000:00000000 __arm64x_extra_rfe_table 0000000000000000 + 0000:00000000 __hybrid_code_map 0000000000000000 + 0000:00000000 __arm64x_extra_rfe_table_size 0000000000000000 + 0000:00000000 ___safe_se_handler_count 0000000000000000 + 0000:00000000 __arm64x_redirection_metadata_count 0000000000000000 + 0000:00000000 __enclave_config 0000000000000000 + 0000:00000000 __guard_eh_cont_table 0000000000000000 + 0000:00000000 __arm64x_native_entrypoint 0000000000000000 + 0000:00000000 __guard_eh_cont_count 0000000000000000 + 0000:00000100 __guard_flags 0000000000000100 + 0000:00000000 __ImageBase 0000000180000000 + 0001:000001d0 ??$_Emplace_reallocate@AEBUdetected_artifact_t@@@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAAPEAUdetected_artifact_t@@QEAU2@AEBU2@@Z 00000001800011d0 f i anticheat_detector.obj + 0001:00000480 ??0?$_qstring@D@@QEAA@XZ 0000000180001480 f i anticheat_detector.obj + 0001:000004a0 ??0bad_alloc@std@@QEAA@AEBV01@@Z 00000001800014a0 f i anticheat_detector.obj + 0001:000004e0 ??0bad_array_new_length@std@@QEAA@AEBV01@@Z 00000001800014e0 f i anticheat_detector.obj + 0001:00000520 ??0bad_array_new_length@std@@QEAA@XZ 0000000180001520 f i anticheat_detector.obj + 0001:00000550 ??0detected_artifact_t@@QEAA@AEBU0@@Z 0000000180001550 f i anticheat_detector.obj + 0001:000006a0 ??0exception@std@@QEAA@AEBV01@@Z 00000001800016a0 f i anticheat_detector.obj + 0001:000006e0 ??1?$_Uninitialized_backout_al@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAA@XZ 00000001800016e0 f i anticheat_detector.obj + 0001:00000740 ??1?$_qstring@D@@QEAA@XZ 0000000180001740 f i anticheat_detector.obj + 0001:00000760 ??1?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAA@XZ 0000000180001760 f i anticheat_detector.obj + 0001:00000840 ??1artifact_chooser_t@@UEAA@XZ 0000000180001840 f i anticheat_detector.obj + 0001:00000870 ??1bad_array_new_length@std@@UEAA@XZ 0000000180001870 f i anticheat_detector.obj + 0001:00000870 ??1bad_alloc@std@@UEAA@XZ 0000000180001870 f i anticheat_detector.obj + 0001:00000890 ??1detected_artifact_t@@QEAA@XZ 0000000180001890 f i anticheat_detector.obj + 0001:000008d0 ??1func_item_iterator_t@@QEAA@XZ 00000001800018d0 f i anticheat_detector.obj + 0001:000008f0 ??3modctx_t@@SAXPEAX@Z 00000001800018f0 f i anticheat_detector.obj + 0001:00000900 ??_Gaction_handler_t@@UEAAPEAXI@Z 0000000180001900 f i anticheat_detector.obj + 0001:00000900 ??_Eaction_handler_t@@UEAAPEAXI@Z 0000000180001900 f i anticheat_detector.obj + 0001:00000940 ??_Gartifact_chooser_t@@UEAAPEAXI@Z 0000000180001940 f i anticheat_detector.obj + 0001:00000940 ??_Eartifact_chooser_t@@UEAAPEAXI@Z 0000000180001940 f i anticheat_detector.obj + 0001:000009c0 ??_Gexception@std@@UEAAPEAXI@Z 00000001800019c0 f i anticheat_detector.obj + 0001:000009c0 ??_Gbad_alloc@std@@UEAAPEAXI@Z 00000001800019c0 f i anticheat_detector.obj + 0001:000009c0 ??_Gbad_array_new_length@std@@UEAAPEAXI@Z 00000001800019c0 f i anticheat_detector.obj + 0001:000009c0 ??_Ebad_array_new_length@std@@UEAAPEAXI@Z 00000001800019c0 f i anticheat_detector.obj + 0001:000009c0 ??_Eexception@std@@UEAAPEAXI@Z 00000001800019c0 f i anticheat_detector.obj + 0001:000009c0 ??_Ebad_alloc@std@@UEAAPEAXI@Z 00000001800019c0 f i anticheat_detector.obj + 0001:00000a10 ??_Echooser_base_t@@UEAAPEAXI@Z 0000000180001a10 f i anticheat_detector.obj + 0001:00000a10 ??_Gchooser_base_t@@UEAAPEAXI@Z 0000000180001a10 f i anticheat_detector.obj + 0001:00000a90 ??_Eplugin_ctx_t@@UEAAPEAXI@Z 0000000180001a90 f i anticheat_detector.obj + 0001:00000a90 ??_Gplugin_ctx_t@@UEAAPEAXI@Z 0000000180001a90 f i anticheat_detector.obj + 0001:00000b00 ??_Gplugmod_t@@UEAAPEAXI@Z 0000000180001b00 f i anticheat_detector.obj + 0001:00000b00 ??_Eplugmod_t@@UEAAPEAXI@Z 0000000180001b00 f i anticheat_detector.obj + 0001:00000b40 ??_Eshow_detector_ah_t@@UEAAPEAXI@Z 0000000180001b40 f i anticheat_detector.obj + 0001:00000b40 ??_Gshow_detector_ah_t@@UEAAPEAXI@Z 0000000180001b40 f i anticheat_detector.obj + 0001:00000b80 ?_Destroy@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@AEAAXPEAUdetected_artifact_t@@0@Z 0000000180001b80 f i anticheat_detector.obj + 0001:00000be0 ?__scrt_throw_std_bad_array_new_length@@YAXXZ 0000000180001be0 f i MSVCRT:throw_bad_alloc.obj + 0001:00000be0 ?_Throw_bad_array_new_length@std@@YAXXZ 0000000180001be0 f i anticheat_detector.obj + 0001:00000c00 ?_Umove@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@AEAAPEAUdetected_artifact_t@@PEAU3@00@Z 0000000180001c00 f i anticheat_detector.obj + 0001:00000cf0 ?_Xlength@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@CAXXZ 0000000180001cf0 f i anticheat_detector.obj + 0001:00000d10 ?__global_delete@@YAXPEAX_K@Z 0000000180001d10 f i anticheat_detector.obj + 0001:00000d10 ?select@chooser_t@@UEBAX_J@Z 0000000180001d10 f i anticheat_detector.obj + 0001:00000d10 ?closed@chooser_base_t@@UEAAXXZ 0000000180001d10 f i anticheat_detector.obj + 0001:00000d10 _guard_check_icall_nop 0000000180001d10 f i MSVCRT:guard_support.obj + 0001:00000d10 ?__empty_global_delete@@YAXPEAX_K@Z 0000000180001d10 f i anticheat_detector.obj + 0001:00000d20 ?activate@show_detector_ah_t@@UEAAHPEAUaction_ctx_base_t@@@Z 0000000180001d20 f anticheat_detector.obj + 0001:00000d40 ?append@?$_qstring@D@@QEAAAEAV1@PEBD_K@Z 0000000180001d40 f i anticheat_detector.obj + 0001:00000de0 ?ask_form@@YAHPEBDZZ 0000000180001de0 f i anticheat_detector.obj + 0001:00000e20 ?assign@?$_qstring@D@@AEAAXPEBD_K@Z 0000000180001e20 f i anticheat_detector.obj + 0001:00000e90 ?check_api_calls@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 0000000180001e90 f anticheat_detector.obj + 0001:00001520 ?check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 0000000180002520 f anticheat_detector.obj + 0001:000027b0 ?check_string_references@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 00000001800037b0 f anticheat_detector.obj + 0001:00002d00 ?clear_results@anticheat_detector_t@@QEAAXXZ 0000000180003d00 f anticheat_detector.obj + 0001:00002d80 ?deallocate@?$allocator@Udetected_artifact_t@@@std@@QEAAXQEAUdetected_artifact_t@@_K@Z 0000000180003d80 f i anticheat_detector.obj + 0001:00002dd0 ?edit@chooser_t@@UEAA?AUcbret_t@1@_K@Z 0000000180003dd0 f i anticheat_detector.obj + 0001:00002dd0 ?del@chooser_t@@UEAA?AUcbret_t@1@_K@Z 0000000180003dd0 f i anticheat_detector.obj + 0001:00002dd0 ?ins@chooser_t@@UEAA?AUcbret_t@1@_J@Z 0000000180003dd0 f i anticheat_detector.obj + 0001:00002df0 ?get_stdact_descs@chooser_t@@UEAA_NQEAPEAUchooser_stdact_desc_t@@@Z 0000000180003df0 f i anticheat_detector.obj + 0001:00002df0 ?do_lazy_load_dir@chooser_t@@UEAA_NPEAVdirtree_t@@AEBV?$_qstring@D@@@Z 0000000180003df0 f i anticheat_detector.obj + 0001:00002e00 ?enter@artifact_chooser_t@@UEAA?AUcbret_t@chooser_t@@_K@Z 0000000180003e00 f anticheat_detector.obj + 0001:00002e80 ?f_is_code@@YA_N_KPEAX@Z 0000000180003e80 f i anticheat_detector.obj + 0001:00002ea0 ?get_category_name@anticheat_detector_t@@QEAAPEBDW4artifact_category_t@@@Z 0000000180003ea0 f anticheat_detector.obj + 0001:00002f30 ?get_chooser_obj@chooser_base_t@@UEAAPEAXXZ 0000000180003f30 f i anticheat_detector.obj + 0001:00002f40 ?get_count@artifact_chooser_t@@UEBA_KXZ 0000000180003f40 f anticheat_detector.obj + 0001:00002f70 ?get_dirtree@chooser_t@@UEAAPEAVdirtree_t@@XZ 0000000180003f70 f i anticheat_detector.obj + 0001:00002f70 _is_c_termination_complete 0000000180003f70 f i MSVCRT:ucrt_stubs.obj + 0001:00002f70 __scrt_stub_for_is_c_termination_complete 0000000180003f70 f i MSVCRT:ucrt_stubs.obj + 0001:00002f70 ?update@show_detector_ah_t@@UEAA?AW4action_state_t@@PEAUaction_ctx_base_t@@@Z 0000000180003f70 f i anticheat_detector.obj + 0001:00002f80 ?get_ea@chooser_base_t@@UEBA_K_K@Z 0000000180003f80 f i anticheat_detector.obj + 0001:00002f80 ?inode_to_index@chooser_t@@UEBA_K_K@Z 0000000180003f80 f i anticheat_detector.obj + 0001:00002f80 ?index_to_diffpos@chooser_t@@UEBA_K_K@Z 0000000180003f80 f i anticheat_detector.obj + 0001:00002f80 ?index_to_inode@chooser_t@@UEBA_K_K@Z 0000000180003f80 f i anticheat_detector.obj + 0001:00002f90 ?get_item_index@chooser_t@@UEBA_JPEBX@Z 0000000180003f90 f i anticheat_detector.obj + 0001:00002fa0 ?get_obj_id@chooser_base_t@@UEBAPEBXPEA_K@Z 0000000180003fa0 f i anticheat_detector.obj + 0001:00002fb0 ?get_row@artifact_chooser_t@@UEBAXPEAV?$qvector@V?$_qstring@D@@@@PEAHPEAUchooser_item_attrs_t@@_K@Z 0000000180003fb0 f anticheat_detector.obj + 0001:00003190 ?info@@YAXPEBDZZ 0000000180004190 f i anticheat_detector.obj + 0001:000031d0 __scrt_stub_for_acrt_uninitialize_critical 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __acrt_initialize 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __vcrt_thread_attach 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __scrt_stub_for_acrt_thread_detach 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __vcrt_uninitialize_critical 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __acrt_thread_attach 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __vcrt_uninitialize 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __scrt_stub_for_acrt_initialize 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 ?init@chooser_base_t@@UEAA_NXZ 00000001800041d0 f i anticheat_detector.obj + 0001:000031d0 __acrt_thread_detach 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __vcrt_initialize 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __acrt_uninitialize 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __acrt_uninitialize_critical 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __scrt_stub_for_acrt_uninitialize 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __scrt_stub_for_acrt_thread_attach 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031d0 __vcrt_thread_detach 00000001800041d0 f i MSVCRT:ucrt_stubs.obj + 0001:000031e0 ?msg@@YAHPEBDZZ 00000001800041e0 f i anticheat_detector.obj + 0001:00003220 ?refresh@chooser_t@@UEAA?AUcbret_t@1@_J@Z 0000000180004220 f i anticheat_detector.obj + 0001:00003230 ?resize_noinit@?$qvector@D@@QEAAX_K@Z 0000000180004230 f i anticheat_detector.obj + 0001:00003280 ?run@plugin_ctx_t@@UEAA_N_K@Z 0000000180004280 f anticheat_detector.obj + 0001:000034a0 ?show_results@anticheat_detector_t@@QEAAXXZ 00000001800044a0 f anticheat_detector.obj + 0001:00003600 ?sprnt@?$_qstring@D@@QEAAAEAV1@PEBDZZ 0000000180004600 f i anticheat_detector.obj + 0001:000036b0 ?warning@@YAXPEBDZZ 00000001800046b0 f i anticheat_detector.obj + 0001:000036f0 ?what@exception@std@@UEBAPEBDXZ 00000001800046f0 f i anticheat_detector.obj + 0001:00003703 qfree 0000000180004703 f ida:ida64.dll + 0001:00003709 stristr 0000000180004709 f ida:ida64.dll + 0001:0000370f qvsnprintf 000000018000470f f ida:ida64.dll + 0001:00003715 qalloc_or_throw 0000000180004715 f ida:ida64.dll + 0001:0000371b qvector_reserve 000000018000471b f ida:ida64.dll + 0001:00003721 tag_remove 0000000180004721 f ida:ida64.dll + 0001:00003727 generate_disasm_line 0000000180004727 f ida:ida64.dll + 0001:0000372d get_flags_ex 000000018000472d f ida:ida64.dll + 0001:00003733 get_strlit_contents 0000000180004733 f ida:ida64.dll + 0001:00003739 set_cmt 0000000180004739 f ida:ida64.dll + 0001:0000373f get_cmt 000000018000473f f ida:ida64.dll + 0001:00003745 lock_func_range 0000000180004745 f ida:ida64.dll + 0001:0000374b get_func 000000018000474b f ida:ida64.dll + 0001:00003751 getn_func 0000000180004751 f ida:ida64.dll + 0001:00003757 get_func_qty 0000000180004757 f ida:ida64.dll + 0001:0000375d get_func_name 000000018000475d f ida:ida64.dll + 0001:00003763 func_tail_iterator_set 0000000180004763 f ida:ida64.dll + 0001:00003769 func_item_iterator_next 0000000180004769 f ida:ida64.dll + 0001:0000376f xrefblk_t_first_from 000000018000476f f ida:ida64.dll + 0001:00003775 xrefblk_t_next_from 0000000180004775 f ida:ida64.dll + 0001:0000377b print_insn_mnem 000000018000477b f ida:ida64.dll + 0001:00003781 decode_insn 0000000180004781 f ida:ida64.dll + 0001:00003787 get_ph 0000000180004787 f ida:ida64.dll + 0001:0000378d get_ash 000000018000478d f ida:ida64.dll + 0001:00003793 get_eah 0000000180004793 f ida:ida64.dll + 0001:00003799 set_module_data 0000000180004799 f ida:ida64.dll + 0001:0000379f get_ea_name 000000018000479f f ida:ida64.dll + 0001:000037a5 ?_Xlength_error@std@@YAXPEBD@Z 00000001800047a5 f msvcprt:MSVCP140.dll + 0001:000037ac ??_L@YAXPEAX_K1P6AX0@Z2@Z 00000001800047ac f MSVCRT:ehvecctr.obj + 0001:0000381c ??_M@YAXPEAX_K1P6AX0@Z@Z 000000018000481c f MSVCRT:ehvecdtr.obj + 0001:00003888 ?__ArrayUnwind@@YAXPEAX_K1P6AX0@Z@Z 0000000180004888 f MSVCRT:ehvecdtr.obj + 0001:000038ec ??2@YAPEAX_K@Z 00000001800048ec f MSVCRT:new_scalar.obj + 0001:00003928 ??3@YAXPEAX_K@Z 0000000180004928 f MSVCRT:delete_scalar_size.obj + 0001:00003930 __GSHandlerCheck_EH4 0000000180004930 f MSVCRT:gshandlereh4.obj + 0001:000039c0 __security_check_cookie 00000001800049c0 f MSVCRT:amdsecgs.obj + 0001:000039e0 ??_Etype_info@@UEAAPEAXI@Z 00000001800049e0 f i MSVCRT:std_type_info_static.obj + 0001:000039e0 ??_Gtype_info@@UEAAPEAXI@Z 00000001800049e0 f i MSVCRT:std_type_info_static.obj + 0001:00003d2c _DllMainCRTStartup 0000000180004d2c f MSVCRT:dll_dllmain.obj + 0001:00003d6c ??0bad_alloc@std@@QEAA@XZ 0000000180004d6c f i MSVCRT:throw_bad_alloc.obj + 0001:00003d8c ?__scrt_throw_std_bad_alloc@@YAXXZ 0000000180004d8c f MSVCRT:throw_bad_alloc.obj + 0001:00003dac ??3@YAXPEAX@Z 0000000180004dac f MSVCRT:delete_scalar.obj + 0001:00003db4 __GSHandlerCheckCommon 0000000180004db4 f MSVCRT:gshandler.obj + 0001:00003e10 __raise_securityfailure 0000000180004e10 f MSVCRT:gs_report.obj + 0001:00003e44 __report_gsfailure 0000000180004e44 f MSVCRT:gs_report.obj + 0001:00003f8c __security_init_cookie 0000000180004f8c f MSVCRT:gs_support.obj + 0001:00004038 DllMain 0000000180005038 f MSVCRT:dll_dllmain_stub.obj + 0001:0000405c ?__scrt_initialize_type_info@@YAXXZ 000000018000505c f MSVCRT:tncleanup.obj + 0001:0000406c ?__scrt_uninitialize_type_info@@YAXXZ 000000018000506c f MSVCRT:tncleanup.obj + 0001:00004078 __local_stdio_printf_options 0000000180005078 f i MSVCRT:default_local_stdio_options.obj + 0001:00004080 __local_stdio_scanf_options 0000000180005080 f i MSVCRT:default_local_stdio_options.obj + 0001:00004088 __scrt_initialize_default_local_stdio_options 0000000180005088 f MSVCRT:default_local_stdio_options.obj + 0001:000040a4 __scrt_acquire_startup_lock 00000001800050a4 f MSVCRT:utility.obj + 0001:000040e0 __scrt_dllmain_after_initialize_c 00000001800050e0 f MSVCRT:utility.obj + 0001:00004114 __scrt_dllmain_before_initialize_c 0000000180005114 f MSVCRT:utility.obj + 0001:0000412c __scrt_dllmain_crt_thread_attach 000000018000512c f MSVCRT:utility.obj + 0001:00004154 __scrt_dllmain_crt_thread_detach 0000000180005154 f MSVCRT:utility.obj + 0001:0000416c __scrt_dllmain_exception_filter 000000018000516c f MSVCRT:utility.obj + 0001:000041cc __scrt_dllmain_uninitialize_c 00000001800051cc f MSVCRT:utility.obj + 0001:000041fc __scrt_dllmain_uninitialize_critical 00000001800051fc f MSVCRT:utility.obj + 0001:00004210 __scrt_initialize_crt 0000000180005210 f MSVCRT:utility.obj + 0001:0000425c __scrt_initialize_onexit_tables 000000018000525c f MSVCRT:utility.obj + 0001:000042e8 __scrt_is_nonwritable_in_current_image 00000001800052e8 f MSVCRT:utility.obj + 0001:00004380 __scrt_release_startup_lock 0000000180005380 f MSVCRT:utility.obj + 0001:000043a4 __scrt_uninitialize_crt 00000001800053a4 f MSVCRT:utility.obj + 0001:000043d0 __scrt_get_dyn_tls_init_callback 00000001800053d0 f MSVCRT:dyn_tls_init.obj + 0001:000043d8 __crt_debugger_hook 00000001800053d8 f MSVCRT:utility_desktop.obj + 0001:000043e0 __scrt_fastfail 00000001800053e0 f MSVCRT:utility_desktop.obj + 0001:0000452c _RTC_Initialize 000000018000552c f MSVCRT:initsect.obj + 0001:00004568 _RTC_Terminate 0000000180005568 f MSVCRT:initsect.obj + 0001:000045b0 __isa_available_init 00000001800055b0 f MSVCRT:cpu_disp.obj + 0001:00004754 _get_startup_argv_mode 0000000180005754 f MSVCRT:argv_mode.obj + 0001:0000475c __scrt_is_ucrt_dll_in_use 000000018000575c f MSVCRT:ucrt_detection.obj + 0001:00004768 _purecall 0000000180005768 f vcruntime:VCRUNTIME140.dll + 0001:0000476e __std_terminate 000000018000576e f vcruntime:VCRUNTIME140.dll + 0001:00004774 __std_exception_copy 0000000180005774 f vcruntime:VCRUNTIME140.dll + 0001:0000477a __std_exception_destroy 000000018000577a f vcruntime:VCRUNTIME140.dll + 0001:00004780 strstr 0000000180005780 f vcruntime:VCRUNTIME140.dll + 0001:00004786 _CxxThrowException 0000000180005786 f vcruntime:VCRUNTIME140.dll + 0001:0000478c __CxxFrameHandler4 000000018000578c f vcruntime:VCRUNTIME140_1.dll + 0001:00004792 memcpy 0000000180005792 f vcruntime:VCRUNTIME140.dll + 0001:00004798 memmove 0000000180005798 f vcruntime:VCRUNTIME140.dll + 0001:0000479e __C_specific_handler 000000018000579e f vcruntime:VCRUNTIME140.dll + 0001:000047a4 __current_exception 00000001800057a4 f vcruntime:VCRUNTIME140.dll + 0001:000047aa __current_exception_context 00000001800057aa f vcruntime:VCRUNTIME140.dll + 0001:000047b0 __std_type_info_destroy_list 00000001800057b0 f vcruntime:VCRUNTIME140.dll + 0001:000047b6 memset 00000001800057b6 f vcruntime:VCRUNTIME140.dll + 0001:000047bc terminate 00000001800057bc f ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0001:000047c2 _callnewh 00000001800057c2 f ucrt:api-ms-win-crt-heap-l1-1-0.dll + 0001:000047c8 malloc 00000001800057c8 f ucrt:api-ms-win-crt-heap-l1-1-0.dll + 0001:000047ce _initterm 00000001800057ce f ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0001:000047d4 _initterm_e 00000001800057d4 f ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0001:000047da free 00000001800057da f ucrt:api-ms-win-crt-heap-l1-1-0.dll + 0001:000047e0 _seh_filter_dll 00000001800057e0 f ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0001:000047e6 _configure_narrow_argv 00000001800057e6 f ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0001:000047ec _initialize_narrow_environment 00000001800057ec f ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0001:000047f2 _initialize_onexit_table 00000001800057f2 f ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0001:000047f8 _execute_onexit_table 00000001800057f8 f ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0001:000047fe _cexit 00000001800057fe f ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0001:00004820 _guard_dispatch_icall_nop 0000000180005820 f MSVCRT:guard_dispatch.obj + 0001:00004840 _guard_xfg_dispatch_icall_nop 0000000180005840 f MSVCRT:guard_xfg_dispatch.obj + 0002:00000000 __imp_GetCurrentProcessId 0000000180006000 kernel32:KERNEL32.dll + 0002:00000008 __imp_SetUnhandledExceptionFilter 0000000180006008 kernel32:KERNEL32.dll + 0002:00000010 __imp_UnhandledExceptionFilter 0000000180006010 kernel32:KERNEL32.dll + 0002:00000018 __imp_RtlVirtualUnwind 0000000180006018 kernel32:KERNEL32.dll + 0002:00000020 __imp_RtlLookupFunctionEntry 0000000180006020 kernel32:KERNEL32.dll + 0002:00000028 __imp_RtlCaptureContext 0000000180006028 kernel32:KERNEL32.dll + 0002:00000030 __imp_TerminateProcess 0000000180006030 kernel32:KERNEL32.dll + 0002:00000038 __imp_IsProcessorFeaturePresent 0000000180006038 kernel32:KERNEL32.dll + 0002:00000040 __imp_QueryPerformanceCounter 0000000180006040 kernel32:KERNEL32.dll + 0002:00000048 __imp_GetCurrentThreadId 0000000180006048 kernel32:KERNEL32.dll + 0002:00000050 __imp_GetSystemTimeAsFileTime 0000000180006050 kernel32:KERNEL32.dll + 0002:00000058 __imp_DisableThreadLibraryCalls 0000000180006058 kernel32:KERNEL32.dll + 0002:00000060 __imp_InitializeSListHead 0000000180006060 kernel32:KERNEL32.dll + 0002:00000068 __imp_IsDebuggerPresent 0000000180006068 kernel32:KERNEL32.dll + 0002:00000070 __imp_GetCurrentProcess 0000000180006070 kernel32:KERNEL32.dll + 0002:00000078 \177KERNEL32_NULL_THUNK_DATA 0000000180006078 kernel32:KERNEL32.dll + 0002:00000080 __imp_?_Xlength_error@std@@YAXPEBD@Z 0000000180006080 msvcprt:MSVCP140.dll + 0002:00000088 \177MSVCP140_NULL_THUNK_DATA 0000000180006088 msvcprt:MSVCP140.dll + 0002:00000090 __imp_strstr 0000000180006090 vcruntime:VCRUNTIME140.dll + 0002:00000098 __imp__CxxThrowException 0000000180006098 vcruntime:VCRUNTIME140.dll + 0002:000000a0 __imp___std_exception_destroy 00000001800060a0 vcruntime:VCRUNTIME140.dll + 0002:000000a8 __imp__purecall 00000001800060a8 vcruntime:VCRUNTIME140.dll + 0002:000000b0 __imp_memcpy 00000001800060b0 vcruntime:VCRUNTIME140.dll + 0002:000000b8 __imp___std_terminate 00000001800060b8 vcruntime:VCRUNTIME140.dll + 0002:000000c0 __imp___C_specific_handler 00000001800060c0 vcruntime:VCRUNTIME140.dll + 0002:000000c8 __imp___current_exception 00000001800060c8 vcruntime:VCRUNTIME140.dll + 0002:000000d0 __imp___std_exception_copy 00000001800060d0 vcruntime:VCRUNTIME140.dll + 0002:000000d8 __imp___current_exception_context 00000001800060d8 vcruntime:VCRUNTIME140.dll + 0002:000000e0 __imp___std_type_info_destroy_list 00000001800060e0 vcruntime:VCRUNTIME140.dll + 0002:000000e8 __imp_memset 00000001800060e8 vcruntime:VCRUNTIME140.dll + 0002:000000f0 __imp_memmove 00000001800060f0 vcruntime:VCRUNTIME140.dll + 0002:000000f8 \177VCRUNTIME140_NULL_THUNK_DATA 00000001800060f8 vcruntime:VCRUNTIME140.dll + 0002:00000100 __imp___CxxFrameHandler4 0000000180006100 vcruntime:VCRUNTIME140_1.dll + 0002:00000108 \177VCRUNTIME140_1_NULL_THUNK_DATA 0000000180006108 vcruntime:VCRUNTIME140_1.dll + 0002:00000110 __imp_malloc 0000000180006110 ucrt:api-ms-win-crt-heap-l1-1-0.dll + 0002:00000118 __imp_free 0000000180006118 ucrt:api-ms-win-crt-heap-l1-1-0.dll + 0002:00000120 __imp__callnewh 0000000180006120 ucrt:api-ms-win-crt-heap-l1-1-0.dll + 0002:00000128 \177api-ms-win-crt-heap-l1-1-0_NULL_THUNK_DATA 0000000180006128 ucrt:api-ms-win-crt-heap-l1-1-0.dll + 0002:00000130 __imp__cexit 0000000180006130 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:00000138 __imp__execute_onexit_table 0000000180006138 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:00000140 __imp__initialize_onexit_table 0000000180006140 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:00000148 __imp__initialize_narrow_environment 0000000180006148 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:00000150 __imp__configure_narrow_argv 0000000180006150 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:00000158 __imp__seh_filter_dll 0000000180006158 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:00000160 __imp__initterm_e 0000000180006160 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:00000168 __imp__initterm 0000000180006168 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:00000170 __imp_terminate 0000000180006170 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:00000178 __imp__invalid_parameter_noinfo_noreturn 0000000180006178 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:00000180 \177api-ms-win-crt-runtime-l1-1-0_NULL_THUNK_DATA 0000000180006180 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:00000188 __imp_get_func_qty 0000000180006188 ida:ida64.dll + 0002:00000190 __imp_getn_func 0000000180006190 ida:ida64.dll + 0002:00000198 __imp_get_func 0000000180006198 ida:ida64.dll + 0002:000001a0 __imp_lock_func_range 00000001800061a0 ida:ida64.dll + 0002:000001a8 __imp_get_cmt 00000001800061a8 ida:ida64.dll + 0002:000001b0 __imp_set_cmt 00000001800061b0 ida:ida64.dll + 0002:000001b8 __imp_get_strlit_contents 00000001800061b8 ida:ida64.dll + 0002:000001c0 __imp_get_flags_ex 00000001800061c0 ida:ida64.dll + 0002:000001c8 __imp_get_func_name 00000001800061c8 ida:ida64.dll + 0002:000001d0 __imp_qfree 00000001800061d0 ida:ida64.dll + 0002:000001d8 __imp_qvector_reserve 00000001800061d8 ida:ida64.dll + 0002:000001e0 __imp_func_tail_iterator_set 00000001800061e0 ida:ida64.dll + 0002:000001e8 __imp_func_item_iterator_next 00000001800061e8 ida:ida64.dll + 0002:000001f0 __imp_qalloc_or_throw 00000001800061f0 ida:ida64.dll + 0002:000001f8 __imp_qvsnprintf 00000001800061f8 ida:ida64.dll + 0002:00000200 __imp_xrefblk_t_first_from 0000000180006200 ida:ida64.dll + 0002:00000208 __imp_xrefblk_t_next_from 0000000180006208 ida:ida64.dll + 0002:00000210 __imp_stristr 0000000180006210 ida:ida64.dll + 0002:00000218 __imp_print_insn_mnem 0000000180006218 ida:ida64.dll + 0002:00000220 __imp_decode_insn 0000000180006220 ida:ida64.dll + 0002:00000228 __imp_get_ph 0000000180006228 ida:ida64.dll + 0002:00000230 __imp_get_ash 0000000180006230 ida:ida64.dll + 0002:00000238 __imp_get_eah 0000000180006238 ida:ida64.dll + 0002:00000240 __imp_set_module_data 0000000180006240 ida:ida64.dll + 0002:00000248 __imp_get_ea_name 0000000180006248 ida:ida64.dll + 0002:00000250 __imp_callui 0000000180006250 ida:ida64.dll + 0002:00000258 __imp_generate_disasm_line 0000000180006258 ida:ida64.dll + 0002:00000260 __imp_tag_remove 0000000180006260 ida:ida64.dll + 0002:00000268 \177ida64_NULL_THUNK_DATA 0000000180006268 ida:ida64.dll + 0002:00000270 __guard_check_icall_fptr 0000000180006270 MSVCRT:guard_support.obj + 0002:00000278 __guard_xfg_check_icall_fptr 0000000180006278 MSVCRT:guard_support.obj + 0002:00000280 __guard_dispatch_icall_fptr 0000000180006280 MSVCRT:guard_support.obj + 0002:00000288 __guard_xfg_dispatch_icall_fptr 0000000180006288 MSVCRT:guard_support.obj + 0002:00000290 __guard_xfg_table_dispatch_icall_fptr 0000000180006290 MSVCRT:guard_support.obj + 0002:00000298 __xc_a 0000000180006298 MSVCRT:initializers.obj + 0002:000002a0 __xc_z 00000001800062a0 MSVCRT:initializers.obj + 0002:000002a8 __xi_a 00000001800062a8 MSVCRT:initializers.obj + 0002:000002b0 __xi_z 00000001800062b0 MSVCRT:initializers.obj + 0002:000002b8 __xp_a 00000001800062b8 MSVCRT:initializers.obj + 0002:000002c0 __xp_z 00000001800062c0 MSVCRT:initializers.obj + 0002:000002c8 __xt_a 00000001800062c8 MSVCRT:initializers.obj + 0002:000002d0 __xt_z 00000001800062d0 MSVCRT:initializers.obj + 0002:000002e0 ?api_patterns@anticheat_detector_t@@0QBUapi_pattern_t@1@B 00000001800062e0 anticheat_detector.obj + 0002:00000760 ?string_patterns@anticheat_detector_t@@0QBUstring_pattern_t@1@B 0000000180006760 anticheat_detector.obj + 0002:00000920 ?widths_@artifact_chooser_t@@1QBHB 0000000180006920 anticheat_detector.obj + 0002:00000938 ?header_@artifact_chooser_t@@1QBQEBDB 0000000180006938 anticheat_detector.obj + 0002:00000968 ??_C@_00CNPNBAHC@@ 0000000180006968 anticheat_detector.obj + 0002:00000970 ??_C@_0BC@KNBMEBEO@IsDebuggerPresent@ 0000000180006970 anticheat_detector.obj + 0002:00000988 ??_C@_0BP@PADELDLL@Checks?5if?5debugger?5is?5attached@ 0000000180006988 anticheat_detector.obj + 0002:000009a8 ??_C@_0BL@FJLANNDC@CheckRemoteDebuggerPresent@ 00000001800069a8 anticheat_detector.obj + 0002:000009c8 ??_C@_0BL@BIIGBEFN@Checks?5for?5remote?5debugger@ 00000001800069c8 anticheat_detector.obj + 0002:000009e8 ??_C@_0BK@EGDDIFMK@NtQueryInformationProcess@ 00000001800069e8 anticheat_detector.obj + 0002:00000a08 ??_C@_0CJ@GDPPFPKF@Can?5detect?5debugger?5via?5Process@ 0000000180006a08 anticheat_detector.obj + 0002:00000a38 ??_C@_0BH@KPLDILJD@NtSetInformationThread@ 0000000180006a38 anticheat_detector.obj + 0002:00000a50 ??_C@_0BO@IAEPBNGE@Can?5hide?5thread?5from?5debugger@ 0000000180006a50 anticheat_detector.obj + 0002:00000a70 ??_C@_0BD@ILGKEMFC@OutputDebugStringA@ 0000000180006a70 anticheat_detector.obj + 0002:00000a88 ??_C@_0BK@JIHBNEIG@Used?5in?5anti?9debug?5tricks@ 0000000180006a88 anticheat_detector.obj + 0002:00000aa8 ??_C@_0BD@JHPCPJIF@OutputDebugStringW@ 0000000180006aa8 anticheat_detector.obj + 0002:00000ac0 ??_C@_0BK@PDBIPODP@ZwQueryInformationProcess@ 0000000180006ac0 anticheat_detector.obj + 0002:00000ae0 ??_C@_0BK@MCMOCAOA@Native?5API?5debugger?5check@ 0000000180006ae0 anticheat_detector.obj + 0002:00000b00 ??_C@_0BJ@NDLOPGCH@NtQuerySystemInformation@ 0000000180006b00 anticheat_detector.obj + 0002:00000b20 ??_C@_0BI@NDFPLIME@Can?5detect?5VM?5artifacts@ 0000000180006b20 anticheat_detector.obj + 0002:00000b38 ??_C@_0BH@CIFPAPFL@GetSystemFirmwareTable@ 0000000180006b38 anticheat_detector.obj + 0002:00000b50 ??_C@_0CH@FKAJBKPM@Checks?5BIOS?1firmware?5for?5VM?5sig@ 0000000180006b50 anticheat_detector.obj + 0002:00000b78 ??_C@_0CC@GLCBFBDA@SetupDiGetDeviceRegistryPropert@ 0000000180006b78 anticheat_detector.obj + 0002:00000ba0 ??_C@_0CF@EMMBDDIM@Enumerates?5hardware?5for?5VM?5dete@ 0000000180006ba0 anticheat_detector.obj + 0002:00000bc8 ??_C@_0O@DAIMODJH@CryptHashData@ 0000000180006bc8 anticheat_detector.obj + 0002:00000bd8 ??_C@_0CG@MPAKLPFK@Hashing?5function?5for?5integrity?5@ 0000000180006bd8 anticheat_detector.obj + 0002:00000c00 ??_C@_0BJ@JEFKDHMD@CreateToolhelp32Snapshot@ 0000000180006c00 anticheat_detector.obj + 0002:00000c20 ??_C@_0BL@GFGNCPOI@Memory?1process?5enumeration@ 0000000180006c20 anticheat_detector.obj + 0002:00000c40 ??_C@_0P@IGDMCLEE@VirtualProtect@ 0000000180006c40 anticheat_detector.obj + 0002:00000c50 ??_C@_0BK@KCFCPHHL@Memory?5protection?5changes@ 0000000180006c50 anticheat_detector.obj + 0002:00000c70 ??_C@_0N@MAAAEOL@VirtualQuery@ 0000000180006c70 anticheat_detector.obj + 0002:00000c80 ??_C@_0BF@OJGJBIEE@Query?5memory?5regions@ 0000000180006c80 anticheat_detector.obj + 0002:00000c98 ??_C@_0BC@LFNIPHGA@ReadProcessMemory@ 0000000180006c98 anticheat_detector.obj + 0002:00000cb0 ??_C@_0BE@MLBHGHNC@Read?5process?5memory@ 0000000180006cb0 anticheat_detector.obj + 0002:00000cc8 ??_C@_0BI@LFICGGHO@QueryPerformanceCounter@ 0000000180006cc8 anticheat_detector.obj + 0002:00000ce0 ??_C@_0BH@FOHPIHON@High?9resolution?5timing@ 0000000180006ce0 anticheat_detector.obj + 0002:00000cf8 ??_C@_0N@JOMACIEF@GetTickCount@ 0000000180006cf8 anticheat_detector.obj + 0002:00000d08 ??_C@_0BO@HLFAHDOC@Tick?5count?5for?5timing?5attacks@ 0000000180006d08 anticheat_detector.obj + 0002:00000d28 ??_C@_0P@CDHHOHKP@GetTickCount64@ 0000000180006d28 anticheat_detector.obj + 0002:00000d38 ??_C@_0BC@JPJPDFEH@64?9bit?5tick?5count@ 0000000180006d38 anticheat_detector.obj + 0002:00000d50 ??_C@_0M@DEPJBPP@timeGetTime@ 0000000180006d50 anticheat_detector.obj + 0002:00000d60 ??_C@_0BB@FDDPJMBO@Multimedia?5timer@ 0000000180006d60 anticheat_detector.obj + 0002:00000d78 ??_C@_0BK@LPCLDNPC@NtQueryPerformanceCounter@ 0000000180006d78 anticheat_detector.obj + 0002:00000d98 ??_C@_0BH@DDBNAGD@Native?5timing?5function@ 0000000180006d98 anticheat_detector.obj + 0002:00000db0 ??_C@_0BB@MBPKHMON@GetThreadContext@ 0000000180006db0 anticheat_detector.obj + 0002:00000dc8 ??_C@_0BJ@GHPBEKNM@Can?5read?5debug?5registers@ 0000000180006dc8 anticheat_detector.obj + 0002:00000de8 ??_C@_0BB@KFKDMCFP@SetThreadContext@ 0000000180006de8 anticheat_detector.obj + 0002:00000e00 ??_C@_0BP@CIEJNNAG@Can?5manipulate?5debug?5registers@ 0000000180006e00 anticheat_detector.obj + 0002:00000e20 ??_C@_0BD@LHJBPAIN@NtGetContextThread@ 0000000180006e20 anticheat_detector.obj + 0002:00000e38 ??_C@_0BI@DOGGJPBK@Native?5context?5function@ 0000000180006e38 anticheat_detector.obj + 0002:00000e50 ??_C@_0BD@NDMIEODP@NtSetContextThread@ 0000000180006e50 anticheat_detector.obj + 0002:00000e68 ??_C@_0O@DJPJEFJP@EnumProcesses@ 0000000180006e68 anticheat_detector.obj + 0002:00000e78 ??_C@_0BM@BOGKCHJI@Enumerate?5running?5processes@ 0000000180006e78 anticheat_detector.obj + 0002:00000e98 ??_C@_0P@ILGHDLOE@Process32First@ 0000000180006e98 anticheat_detector.obj + 0002:00000ea8 ??_C@_0BE@LECOJMIC@Process?5enumeration@ 0000000180006ea8 anticheat_detector.obj + 0002:00000ec0 ??_C@_0O@LGJLFMBH@Process32Next@ 0000000180006ec0 anticheat_detector.obj + 0002:00000ed0 ??_C@_0BD@FIEJBLME@EnumProcessModules@ 0000000180006ed0 anticheat_detector.obj + 0002:00000ee8 ??_C@_0BD@IKLLHHFO@Module?5enumeration@ 0000000180006ee8 anticheat_detector.obj + 0002:00000f00 ??_C@_0O@CNMFJJGO@Module32First@ 0000000180006f00 anticheat_detector.obj + 0002:00000f10 ??_C@_0N@KIGOLBLC@Module32Next@ 0000000180006f10 anticheat_detector.obj + 0002:00000f20 ??_C@_0BB@HNEJOHCL@GetModuleHandleA@ 0000000180006f20 anticheat_detector.obj + 0002:00000f38 ??_C@_0BC@OFLCPBAL@Get?5module?5handle@ 0000000180006f38 anticheat_detector.obj + 0002:00000f50 ??_C@_0BB@GBNBFCPM@GetModuleHandleW@ 0000000180006f50 anticheat_detector.obj + 0002:00000f68 ??_C@_0N@LOMLCOFC@LoadLibraryA@ 0000000180006f68 anticheat_detector.obj + 0002:00000f78 ??_C@_0BD@DOINKEEF@Load?5library?5check@ 0000000180006f78 anticheat_detector.obj + 0002:00000f90 ??_C@_0N@KCFDJLIF@LoadLibraryW@ 0000000180006f90 anticheat_detector.obj + 0002:00000fa0 ??_C@_0BB@KJBOEJKI@VirtualProtectEx@ 0000000180006fa0 anticheat_detector.obj + 0002:00000fb8 ??_C@_0BL@LJAMJOHI@External?5memory?5protection@ 0000000180006fb8 anticheat_detector.obj + 0002:00000fd8 ??_C@_0BH@EDMKDLIP@NtProtectVirtualMemory@ 0000000180006fd8 anticheat_detector.obj + 0002:00000ff0 ??_C@_0BJ@DMFNMPHO@Native?5memory?5protection@ 0000000180006ff0 anticheat_detector.obj + 0002:0000100c ??_C@_04KCPIBHAE@WINE@ 000000018000700c anticheat_detector.obj + 0002:00001018 ??_C@_0CD@BILEOFNB@Wine?5compatibility?5layer?5detect@ 0000000180007018 anticheat_detector.obj + 0002:0000103c ??_C@_06BGMHGKNL@VMware@ 000000018000703c anticheat_detector.obj + 0002:00001048 ??_C@_0BB@NPMIMCAG@VMware?5detection@ 0000000180007048 anticheat_detector.obj + 0002:0000105c ??_C@_04BLPONNNM@VBox@ 000000018000705c anticheat_detector.obj + 0002:00001068 ??_C@_0BF@IADCNKEE@VirtualBox?5detection@ 0000000180007068 anticheat_detector.obj + 0002:00001080 ??_C@_04LGDHLPJO@VBOX@ 0000000180007080 anticheat_detector.obj + 0002:00001088 ??_C@_03GFGAEIEM@Xen@ 0000000180007088 anticheat_detector.obj + 0002:00001090 ??_C@_0BJ@DLGHECMB@Xen?5hypervisor?5detection@ 0000000180007090 anticheat_detector.obj + 0002:000010ac ??_C@_04CPOKPBBE@QEMU@ 00000001800070ac anticheat_detector.obj + 0002:000010b8 ??_C@_0P@EBDENMMP@QEMU?5detection@ 00000001800070b8 anticheat_detector.obj + 0002:000010c8 ??_C@_0N@IGDPKFLN@Cheat?5Engine@ 00000001800070c8 anticheat_detector.obj + 0002:000010d8 ??_C@_0BH@KHAJADGL@Cheat?5Engine?5detection@ 00000001800070d8 anticheat_detector.obj + 0002:000010f0 ??_C@_07NIBLLGJN@OllyDbg@ 00000001800070f0 anticheat_detector.obj + 0002:000010f8 ??_C@_0BC@HDMPIAEJ@OllyDbg?5detection@ 00000001800070f8 anticheat_detector.obj + 0002:0000110c ??_C@_06OODNMGEB@x64dbg@ 000000018000710c anticheat_detector.obj + 0002:00001118 ??_C@_0BB@DCOLNHOC@x64dbg?5detection@ 0000000180007118 anticheat_detector.obj + 0002:0000112c ??_C@_03KGFGGMAM@IDA@ 000000018000712c anticheat_detector.obj + 0002:00001130 ??_C@_0BC@JOILOIIC@IDA?5Pro?5detection@ 0000000180007130 anticheat_detector.obj + 0002:00001148 ??_C@_09PGJEBFDP@Wireshark@ 0000000180007148 anticheat_detector.obj + 0002:00001158 ??_C@_0BG@HOEMHOPH@Network?5analysis?5tool@ 0000000180007158 anticheat_detector.obj + 0002:00001170 ??_C@_0O@EJMEKJLM@ProcessHacker@ 0000000180007170 anticheat_detector.obj + 0002:00001180 ??_C@_0BG@BGIDHFCK@Process?5analysis?5tool@ 0000000180007180 anticheat_detector.obj + 0002:00001198 ??_C@_0BA@CHFHMCJO@ProcessExplorer@ 0000000180007198 anticheat_detector.obj + 0002:000011a8 ??_C@_0BB@ICFLKOPM@SeDebugPrivilege@ 00000001800071a8 anticheat_detector.obj + 0002:000011c0 ??_C@_0BG@LNNACHDD@Debug?5privilege?5check@ 00000001800071c0 anticheat_detector.obj + 0002:000011d8 ??_C@_0BO@JNDBAJEO@Anti?9Cheat?5Artifacts?5Detector@ 00000001800071d8 anticheat_detector.obj + 0002:000011f8 ??_C@_07EODJIPPN@Address@ 00000001800071f8 anticheat_detector.obj + 0002:00001200 ??_C@_08PHKOHANG@Function@ 0000000180007200 anticheat_detector.obj + 0002:00001210 ??_C@_0M@LHLPIFKK@Instruction@ 0000000180007210 anticheat_detector.obj + 0002:00001220 ??_C@_08EDGJCMOO@Category@ 0000000180007220 anticheat_detector.obj + 0002:00001230 ??_C@_08HFJKEKNA@Severity@ 0000000180007230 anticheat_detector.obj + 0002:00001240 ??_C@_0M@OHKMKKDH@Description@ 0000000180007240 anticheat_detector.obj + 0002:00001250 ??_C@_0GM@CLNDHAEI@Anti?9Cheat?5Artifact?5Detector?5?9?5@ 0000000180007250 anticheat_detector.obj + 0002:000012c0 ??_C@_0PJ@BAENDNAG@Anti?9Cheat?5Artifact?5Detector?5pl@ 00000001800072c0 anticheat_detector.obj + 0002:000013c0 ??_C@_0BC@EOODALEL@Unknown?5exception@ 00000001800073c0 anticheat_detector.obj + 0002:000013d8 ??_C@_0BF@KINCDENJ@bad?5array?5new?5length@ 00000001800073d8 anticheat_detector.obj + 0002:000013f0 ??_7chooser_base_t@@6B@ 00000001800073f0 anticheat_detector.obj + 0002:00001430 ??_7action_handler_t@@6B@ 0000000180007430 anticheat_detector.obj + 0002:00001448 ??_7plugmod_t@@6B@ 0000000180007448 anticheat_detector.obj + 0002:00001458 ??_7artifact_chooser_t@@6B@ 0000000180007458 anticheat_detector.obj + 0002:00001500 ??_7show_detector_ah_t@@6B@ 0000000180007500 anticheat_detector.obj + 0002:00001518 ??_7plugin_ctx_t@@6B@ 0000000180007518 anticheat_detector.obj + 0002:00001528 ??_C@_0BK@EJJNKJBG@anticheat?3DetectArtifacts@ 0000000180007528 anticheat_detector.obj + 0002:00001548 ??_C@_0N@NJOPCHHH@Ctrl?$CLShift?$CLA@ 0000000180007548 anticheat_detector.obj + 0002:00001558 ??_C@_07KFALCCLJ@Search?1@ 0000000180007558 anticheat_detector.obj + 0002:00001560 ??_C@_0BD@OBIGJHJF@Debugger?5Detection@ 0000000180007560 anticheat_detector.obj + 0002:00001578 ??_C@_0N@HNGMLOEH@VM?5Detection@ 0000000180007578 anticheat_detector.obj + 0002:00001588 ??_C@_0BA@EJGGKAJA@Integrity?5Check@ 0000000180007588 anticheat_detector.obj + 0002:00001598 ??_C@_0N@FLCNAIOE@Timing?5Check@ 0000000180007598 anticheat_detector.obj + 0002:000015a8 ??_C@_0BE@FBGIBBNE@Hardware?5Breakpoint@ 00000001800075a8 anticheat_detector.obj + 0002:000015c0 ??_C@_0BE@KMBPEHHK@Process?5Enumeration@ 00000001800075c0 anticheat_detector.obj + 0002:000015d8 ??_C@_0BD@JCIKKMKG@Module?5Enumeration@ 00000001800075d8 anticheat_detector.obj + 0002:000015f0 ??_C@_0P@MLLKACNM@Thread?5Context@ 00000001800075f0 anticheat_detector.obj + 0002:00001600 ??_C@_0BB@IHEFEGOF@Protection?5Check@ 0000000180007600 anticheat_detector.obj + 0002:00001618 ??_C@_07NBCGADJA@Unknown@ 0000000180007618 anticheat_detector.obj + 0002:00001650 ??_C@_06MDDIFPMB@?$FLACAD?$FN@ 0000000180007650 anticheat_detector.obj + 0002:00001658 ??_C@_01CLKCMJKC@?5@ 0000000180007658 anticheat_detector.obj + 0002:00001660 ??_C@_07HJCEOONL@?$FLACAD?$FN?5@ 0000000180007660 anticheat_detector.obj + 0002:00001668 ??_C@_0BC@CAHCGKHF@?$CFs?5?$CIstring?3?5?$CC?$CFs?$CC?$CJ@ 0000000180007668 anticheat_detector.obj + 0002:00001680 ??_C@_0N@HAODCDDO@String?3?5?$CC?$CFs?$CC@ 0000000180007680 anticheat_detector.obj + 0002:00001690 ??_C@_05CDPJMNMF@cpuid@ 0000000180007690 anticheat_detector.obj + 0002:00001698 ??_C@_0CB@JDADGKBA@CPUID?5instruction?5?$CIVM?5detection@ 0000000180007698 anticheat_detector.obj + 0002:000016bc ??_C@_05JIFPPKLN@rdtsc@ 00000001800076bc anticheat_detector.obj + 0002:000016c4 ??_C@_06JKHHOJDN@rdtscp@ 00000001800076c4 anticheat_detector.obj + 0002:000016d0 ??_C@_0CB@BHIPPFJK@RDTSC?5instruction?5?$CItiming?5check@ 00000001800076d0 anticheat_detector.obj + 0002:000016f4 ??_C@_03JBIPMCLC@int@ 00000001800076f4 anticheat_detector.obj + 0002:000016f8 ??_C@_0CB@JOGGAPMB@INT?5?$CFXh?5instruction?5?$CIanti?9debug@ 00000001800076f8 anticheat_detector.obj + 0002:00001720 ??_C@_07IHOFANMG@int?5?$CFXh@ 0000000180007720 anticheat_detector.obj + 0002:00001728 ??_C@_03CNBJFNFA@mov@ 0000000180007728 anticheat_detector.obj + 0002:00001730 ??_C@_0CL@FELJJLEE@PEB?5access?5?$CIpotential?5BeingDebu@ 0000000180007730 anticheat_detector.obj + 0002:00001760 ??_C@_0BM@IDEMFBDL@Suspicious?5constant?3?50x?$CFllX@ 0000000180007760 anticheat_detector.obj + 0002:0000177c ??_C@_06GGPPAHKI@0x?$CFllX@ 000000018000777c anticheat_detector.obj + 0002:00001788 ??_C@_0BJ@FHBKMJHK@Scanning?5function?5at?5?$CFa?6@ 0000000180007788 anticheat_detector.obj + 0002:000017a8 ??_C@_0CG@ICHBANFC@Starting?5anti?9cheat?5artifact?5sc@ 00000001800077a8 anticheat_detector.obj + 0002:000017d0 ??_C@_0BL@GEOGGBPA@Progress?3?5?$CFd?1?$CFd?5functions?6@ 00000001800077d0 anticheat_detector.obj + 0002:000017f0 ??_C@_0CD@KAGGMJJP@Scan?5complete?$CB?5Found?5?$CFd?5artifac@ 00000001800077f0 anticheat_detector.obj + 0002:00001818 ??_C@_0CG@LIBABJFE@Please?5position?5cursor?5in?5a?5fun@ 0000000180007818 anticheat_detector.obj + 0002:00001840 ??_C@_0BO@BOLBPCBE@Scanning?5current?5function?4?4?4?6@ 0000000180007840 anticheat_detector.obj + 0002:00001860 ??_C@_0CI@PFCGGOBC@Found?5?$CFd?5artifacts?5in?5current?5f@ 0000000180007860 anticheat_detector.obj + 0002:00001888 ??_C@_0CC@ECOFOKDO@No?5anti?9cheat?5artifacts?5detecte@ 0000000180007888 anticheat_detector.obj + 0002:000018b0 ??_C@_0BF@DMBKKPDA@Anti?9Cheat?5Artifacts@ 00000001800078b0 anticheat_detector.obj + 0002:000018c8 ??_C@_02ECNOPIAA@?$CFa@ 00000001800078c8 anticheat_detector.obj + 0002:000018cc ??_C@_04BFJIALKJ@?$CFd?15@ 00000001800078cc anticheat_detector.obj + 0002:00001950 ??_C@_0EK@HMLNDCAO@Failed?5to?5register?5menu?5item?5fo@ 0000000180007950 anticheat_detector.obj + 0002:000019a0 ??_C@_0CG@PNOCBFJI@Anti?9Cheat?5Artifact?5Detector?5lo@ 00000001800079a0 anticheat_detector.obj + 0002:000019d0 ??_C@_0EK@OONEFNAP@Use?5Anti?9Cheat?5Artifacts?5Detect@ 00000001800079d0 anticheat_detector.obj + 0002:00001a1a ?nullstr@?1??c_str@?$_qstring@D@@QEBAPEBDXZ@4QBDB 0000000180007a1a anticheat_detector.obj + 0002:00001a20 ??_C@_0BA@FOIKENOD@vector?5too?5long@ 0000000180007a20 anticheat_detector.obj + 0002:00001a30 __xmm@ffffffffffffffffffffffffffffffff 0000000180007a30 anticheat_detector.obj + 0002:00001a48 ??_7type_info@@6B@ 0000000180007a48 MSVCRT:std_type_info_static.obj + 0002:00001a50 _pRawDllMain 0000000180007a50 MSVCRT:dll_dllmain.obj + 0002:00001a50 _pDefaultRawDllMain 0000000180007a50 MSVCRT:dll_dllmain.obj + 0002:00001a60 ??_7exception@std@@6B@ 0000000180007a60 MSVCRT:throw_bad_alloc.obj + 0002:00001a78 ??_7bad_alloc@std@@6B@ 0000000180007a78 MSVCRT:throw_bad_alloc.obj + 0002:00001a88 ??_C@_0P@GHFPNOJB@bad?5allocation@ 0000000180007a88 MSVCRT:throw_bad_alloc.obj + 0002:00001aa0 ??_7bad_array_new_length@std@@6B@ 0000000180007aa0 MSVCRT:throw_bad_alloc.obj + 0002:00001b30 _load_config_used 0000000180007b30 MSVCRT:loadcfg.obj + 0002:00001c80 ??_R4type_info@@6B@ 0000000180007c80 MSVCRT:std_type_info_static.obj + 0002:00001ca8 ??_R3type_info@@8 0000000180007ca8 MSVCRT:std_type_info_static.obj + 0002:00001cc0 ??_R2type_info@@8 0000000180007cc0 MSVCRT:std_type_info_static.obj + 0002:00001cd0 ??_R1A@?0A@EA@type_info@@8 0000000180007cd0 MSVCRT:std_type_info_static.obj + 0002:00001cf8 ??_R4exception@std@@6B@ 0000000180007cf8 MSVCRT:throw_bad_alloc.obj + 0002:00001d20 ??_R3exception@std@@8 0000000180007d20 MSVCRT:throw_bad_alloc.obj + 0002:00001d38 ??_R2exception@std@@8 0000000180007d38 MSVCRT:throw_bad_alloc.obj + 0002:00001d48 ??_R1A@?0A@EA@exception@std@@8 0000000180007d48 MSVCRT:throw_bad_alloc.obj + 0002:00001d70 ??_R4bad_alloc@std@@6B@ 0000000180007d70 MSVCRT:throw_bad_alloc.obj + 0002:00001d98 ??_R3bad_alloc@std@@8 0000000180007d98 MSVCRT:throw_bad_alloc.obj + 0002:00001db0 ??_R2bad_alloc@std@@8 0000000180007db0 MSVCRT:throw_bad_alloc.obj + 0002:00001dc8 ??_R1A@?0A@EA@bad_alloc@std@@8 0000000180007dc8 MSVCRT:throw_bad_alloc.obj + 0002:00001df0 ??_R4bad_array_new_length@std@@6B@ 0000000180007df0 MSVCRT:throw_bad_alloc.obj + 0002:00001e18 ??_R3bad_array_new_length@std@@8 0000000180007e18 MSVCRT:throw_bad_alloc.obj + 0002:00001e30 ??_R2bad_array_new_length@std@@8 0000000180007e30 MSVCRT:throw_bad_alloc.obj + 0002:00001e50 ??_R1A@?0A@EA@bad_array_new_length@std@@8 0000000180007e50 MSVCRT:throw_bad_alloc.obj + 0002:00001e74 __volatile_metadata 0000000180007e74 + 0002:00002188 __rtc_iaa 0000000180008188 MSVCRT:initsect.obj + 0002:00002190 __rtc_izz 0000000180008190 MSVCRT:initsect.obj + 0002:00002198 __rtc_taa 0000000180008198 MSVCRT:initsect.obj + 0002:000021a0 __rtc_tzz 00000001800081a0 MSVCRT:initsect.obj + 0002:000029b8 _TI3?AVbad_array_new_length@std@@ 00000001800089b8 anticheat_detector.obj + 0002:000029d8 _CTA3?AVbad_array_new_length@std@@ 00000001800089d8 anticheat_detector.obj + 0002:000029f8 _CT??_R0?AVbad_array_new_length@std@@@8??0bad_array_new_length@std@@QEAA@AEBV01@@Z24 00000001800089f8 anticheat_detector.obj + 0002:00002a20 _CT??_R0?AVbad_alloc@std@@@8??0bad_alloc@std@@QEAA@AEBV01@@Z24 0000000180008a20 anticheat_detector.obj + 0002:00002a48 _CT??_R0?AVexception@std@@@8??0exception@std@@QEAA@AEBV01@@Z24 0000000180008a48 anticheat_detector.obj + 0002:00002a70 _TI2?AVbad_alloc@std@@ 0000000180008a70 MSVCRT:throw_bad_alloc.obj + 0002:00002a90 _CTA2?AVbad_alloc@std@@ 0000000180008a90 MSVCRT:throw_bad_alloc.obj + 0002:00002b04 __IMPORT_DESCRIPTOR_ida64 0000000180008b04 ida:ida64.dll + 0002:00002b18 __IMPORT_DESCRIPTOR_MSVCP140 0000000180008b18 msvcprt:MSVCP140.dll + 0002:00002b2c __IMPORT_DESCRIPTOR_KERNEL32 0000000180008b2c kernel32:KERNEL32.dll + 0002:00002b40 __IMPORT_DESCRIPTOR_VCRUNTIME140 0000000180008b40 vcruntime:VCRUNTIME140.dll + 0002:00002b54 __IMPORT_DESCRIPTOR_VCRUNTIME140_1 0000000180008b54 vcruntime:VCRUNTIME140_1.dll + 0002:00002b68 __IMPORT_DESCRIPTOR_api-ms-win-crt-runtime-l1-1-0 0000000180008b68 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:00002b7c __IMPORT_DESCRIPTOR_api-ms-win-crt-heap-l1-1-0 0000000180008b7c ucrt:api-ms-win-crt-heap-l1-1-0.dll + 0002:00002b90 __NULL_IMPORT_DESCRIPTOR 0000000180008b90 ida:ida64.dll + 0003:00000000 PLUGIN 000000018000a000 anticheat_detector.obj + 0003:00000040 __security_cookie_complement 000000018000a040 MSVCRT:gs_cookie.obj + 0003:00000048 __security_cookie 000000018000a048 MSVCRT:gs_cookie.obj + 0003:00000050 __scrt_native_dllmain_reason 000000018000a050 MSVCRT:utility.obj + 0003:00000060 __isa_available 000000018000a060 MSVCRT:cpu_disp.obj + 0003:00000064 __isa_enabled 000000018000a064 MSVCRT:cpu_disp.obj + 0003:00000068 __memcpy_nt_iters 000000018000a068 MSVCRT:cpu_disp.obj + 0003:00000070 __memset_nt_iters 000000018000a070 MSVCRT:cpu_disp.obj + 0003:00000078 __scrt_ucrt_dll_is_in_use 000000018000a078 MSVCRT:ucrt_stubs.obj + 0003:00000080 ??_R0?AVbad_array_new_length@std@@@8 000000018000a080 anticheat_detector.obj + 0003:000000b0 ??_R0?AVbad_alloc@std@@@8 000000018000a0b0 anticheat_detector.obj + 0003:000000d8 ??_R0?AVexception@std@@@8 000000018000a0d8 anticheat_detector.obj + 0003:00000100 ??_R0?AVtype_info@@@8 000000018000a100 MSVCRT:std_type_info_static.obj + 0003:00000120 ?data_id@@3HA 000000018000a120 anticheat_detector.obj + 0003:00000128 __castguard_check_failure_os_handled_fptr 000000018000a128 MSVCRT:guard_support.obj + 0003:000006a0 ?__type_info_root_node@@3U__type_info_node@@A 000000018000a6a0 MSVCRT:tncleanup.obj + 0003:000006b0 ?_OptionsStorage@?1??__local_stdio_printf_options@@9@4_KA 000000018000a6b0 MSVCRT:default_local_stdio_options.obj + 0003:000006b8 ?_OptionsStorage@?1??__local_stdio_scanf_options@@9@4_KA 000000018000a6b8 MSVCRT:default_local_stdio_options.obj + 0003:000006c0 __scrt_current_native_startup_state 000000018000a6c0 MSVCRT:utility.obj + 0003:000006c8 __scrt_native_startup_lock 000000018000a6c8 MSVCRT:utility.obj + 0003:00000708 __scrt_debugger_hook_flag 000000018000a708 MSVCRT:utility_desktop.obj + 0003:0000070c __favor 000000018000a70c MSVCRT:cpu_disp.obj + 0003:00000710 __dyn_tls_init_callback 000000018000a710 + + entry point at 0001:00003d2c + + Static symbols + + 0000:ffff3000 .debug$S 0000000180000000 anticheat_detector64.exp + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 ida:ida64.dll + 0000:ffff3000 .debug$S 0000000180000000 msvcprt:MSVCP140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140.dll + 0000:ffff3000 .debug$S 0000000180000000 vcruntime:VCRUNTIME140_1.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-heap-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-heap-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-heap-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0000:ffff3000 .debug$S 0000000180000000 kernel32:KERNEL32.dll + 0001:00000000 ?init@@YAPEAUplugmod_t@@XZ 0000000180001000 f anticheat_detector.obj + 0001:00002f0c $LN15 0000000180003f0c anticheat_detector.obj + 0001:00003168 $LN67 0000000180004168 anticheat_detector.obj + 0001:000039b0 $$000000 00000001800049b0 MSVCRT:amdsecgs.obj + 0001:00003a0c ?dllmain_crt_dispatch@@YAHQEAUHINSTANCE__@@KQEAX@Z 0000000180004a0c f MSVCRT:dll_dllmain.obj + 0001:00003a5c ?dllmain_crt_process_attach@@YAHQEAUHINSTANCE__@@QEAX@Z 0000000180004a5c f MSVCRT:dll_dllmain.obj + 0001:00003b74 ?dllmain_crt_process_detach@@YAH_N@Z 0000000180004b74 f MSVCRT:dll_dllmain.obj + 0001:00003bf8 ?dllmain_dispatch@@YAHQEAUHINSTANCE__@@KQEAX@Z 0000000180004bf8 f MSVCRT:dll_dllmain.obj + 0001:00003f18 capture_previous_context 0000000180004f18 f MSVCRT:gs_report.obj + 0001:00004810 $$000000 0000000180005810 MSVCRT:guard_dispatch.obj + 0001:00004830 $$000000 0000000180005830 MSVCRT:guard_xfg_dispatch.obj + 0001:00004850 ?dtor$0@?0???$_Emplace_reallocate@AEBUdetected_artifact_t@@@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAAPEAUdetected_artifact_t@@QEAU2@AEBU2@@Z@4HA 0000000180005850 f anticheat_detector.obj + 0001:00004860 ?catch$22@?0???$_Emplace_reallocate@AEBUdetected_artifact_t@@@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAAPEAUdetected_artifact_t@@QEAU2@AEBU2@@Z@4HA 0000000180005860 f anticheat_detector.obj + 0001:0000486d __catch$??$_Emplace_reallocate@AEBUdetected_artifact_t@@@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAAPEAUdetected_artifact_t@@QEAU2@AEBU2@@Z$0 000000018000586d f anticheat_detector.obj + 0001:000048a0 ?dtor$0@?0???0detected_artifact_t@@QEAA@AEBU0@@Z@4HA 00000001800058a0 f anticheat_detector.obj + 0001:000048b0 ?dtor$1@?0???0detected_artifact_t@@QEAA@AEBU0@@Z@4HA 00000001800058b0 f anticheat_detector.obj + 0001:000048c0 ?dtor$2@?0???0detected_artifact_t@@QEAA@AEBU0@@Z@4HA 00000001800058c0 f anticheat_detector.obj + 0001:000048d0 ?dtor$0@?0??check_api_calls@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 00000001800058d0 f anticheat_detector.obj + 0001:000048e0 ?dtor$1@?0??check_api_calls@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 00000001800058e0 f anticheat_detector.obj + 0001:000048f0 ?dtor$2@?0??check_api_calls@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 00000001800058f0 f anticheat_detector.obj + 0001:00004900 ?dtor$10@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005900 f anticheat_detector.obj + 0001:00004900 ?dtor$3@?0??check_api_calls@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005900 f anticheat_detector.obj + 0001:00004900 ?dtor$3@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005900 f anticheat_detector.obj + 0001:00004900 ?dtor$5@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005900 f anticheat_detector.obj + 0001:00004900 ?dtor$7@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005900 f anticheat_detector.obj + 0001:00004910 ?dtor$4@?0??check_api_calls@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005910 f anticheat_detector.obj + 0001:00004920 ?dtor$5@?0??check_api_calls@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005920 f anticheat_detector.obj + 0001:00004930 ?dtor$6@?0??check_api_calls@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005930 f anticheat_detector.obj + 0001:00004940 ?dtor$0@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005940 f anticheat_detector.obj + 0001:00004950 ?dtor$1@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005950 f anticheat_detector.obj + 0001:00004960 ?dtor$11@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005960 f anticheat_detector.obj + 0001:00004960 ?dtor$2@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005960 f anticheat_detector.obj + 0001:00004960 ?dtor$4@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005960 f anticheat_detector.obj + 0001:00004960 ?dtor$6@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005960 f anticheat_detector.obj + 0001:00004960 ?dtor$8@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005960 f anticheat_detector.obj + 0001:00004970 ?dtor$9@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005970 f anticheat_detector.obj + 0001:00004980 ?dtor$12@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005980 f anticheat_detector.obj + 0001:00004990 ?dtor$13@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005990 f anticheat_detector.obj + 0001:000049a0 ?dtor$14@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 00000001800059a0 f anticheat_detector.obj + 0001:000049b0 ?dtor$31@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 00000001800059b0 f anticheat_detector.obj + 0001:000049b0 ?dtor$50@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 00000001800059b0 f anticheat_detector.obj + 0001:000049c0 ?dtor$32@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 00000001800059c0 f anticheat_detector.obj + 0001:000049c0 ?dtor$51@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 00000001800059c0 f anticheat_detector.obj + 0001:000049d0 ?dtor$33@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 00000001800059d0 f anticheat_detector.obj + 0001:000049d0 ?dtor$52@?0??check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 00000001800059d0 f anticheat_detector.obj + 0001:000049e0 ?dtor$0@?0??check_string_references@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 00000001800059e0 f anticheat_detector.obj + 0001:000049f0 ?dtor$1@?0??check_string_references@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 00000001800059f0 f anticheat_detector.obj + 0001:00004a00 ?dtor$2@?0??check_string_references@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005a00 f anticheat_detector.obj + 0001:00004a10 ?dtor$3@?0??check_string_references@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005a10 f anticheat_detector.obj + 0001:00004a20 ?dtor$4@?0??check_string_references@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005a20 f anticheat_detector.obj + 0001:00004a30 ?dtor$5@?0??check_string_references@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005a30 f anticheat_detector.obj + 0001:00004a40 ?dtor$6@?0??check_string_references@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z@4HA 0000000180005a40 f anticheat_detector.obj + 0001:00004a50 ?dtor$0@?0??init@@YAPEAUplugmod_t@@XZ@4HA 0000000180005a50 f anticheat_detector.obj + 0001:00004a60 ?dtor$0@?0??show_results@anticheat_detector_t@@QEAAXXZ@4HA 0000000180005a60 f anticheat_detector.obj + 0001:00004a6c ?fin$0@?0???_L@YAXPEAX_K1P6AX0@Z2@Z@4HA 0000000180005a6c f MSVCRT:ehvecctr.obj + 0001:00004a98 ?fin$0@?0???_M@YAXPEAX_K1P6AX0@Z@Z@4HA 0000000180005a98 f MSVCRT:ehvecdtr.obj + 0001:00004ac4 ?filt$0@?0??__ArrayUnwind@@YAXPEAX_K1P6AX0@Z@Z@4HA 0000000180005ac4 f MSVCRT:ehvecdtr.obj + 0001:00004b23 ?fin$0@?0??dllmain_crt_process_attach@@YAHQEAUHINSTANCE__@@QEAX@Z@4HA 0000000180005b23 f MSVCRT:dll_dllmain.obj + 0001:00004b3a ?fin$0@?0??dllmain_crt_process_detach@@YAH_N@Z@4HA 0000000180005b3a f MSVCRT:dll_dllmain.obj + 0001:00004b53 ?fin$1@?0??dllmain_crt_process_detach@@YAH_N@Z@4HA 0000000180005b53 f MSVCRT:dll_dllmain.obj + 0001:00004b67 ?filt$0@?0??dllmain_dispatch@@YAHQEAUHINSTANCE__@@KQEAX@Z@4HA 0000000180005b67 f MSVCRT:dll_dllmain.obj + 0001:00004b9d __scrt_is_nonwritable_in_current_image$filt$0 0000000180005b9d f MSVCRT:utility.obj + 0002:00001620 ?suspicious_constants@?1??is_suspicious_constant@anticheat_detector_t@@AEAA_N_K@Z@4QB_KB 0000000180007620 anticheat_detector.obj + 0002:000018e0 ?form@?1??run@plugin_ctx_t@@UEAA_N_K@Z@4QBDB 00000001800078e0 anticheat_detector.obj + 0002:00001ab0 GS_ExceptionPointers 0000000180007ab0 MSVCRT:gs_report.obj + 0002:000021a8 $unwind$??0exception@std@@QEAA@AEBV01@@Z 00000001800081a8 anticheat_detector.obj + 0002:000021b8 $cppxdata$??0exception@std@@QEAA@AEBV01@@Z 00000001800081b8 anticheat_detector.obj + 0002:000021bd $ip2state$??0exception@std@@QEAA@AEBV01@@Z 00000001800081bd anticheat_detector.obj + 0002:000021c0 $unwind$??_Gbad_alloc@std@@UEAAPEAXI@Z 00000001800081c0 anticheat_detector.obj + 0002:000021c0 $unwind$??_Gbad_array_new_length@std@@UEAAPEAXI@Z 00000001800081c0 anticheat_detector.obj + 0002:000021c0 $unwind$??_Gexception@std@@UEAAPEAXI@Z 00000001800081c0 anticheat_detector.obj + 0002:000021d4 $cppxdata$??_Gaction_handler_t@@UEAAPEAXI@Z 00000001800081d4 anticheat_detector.obj + 0002:000021d4 $cppxdata$??_Gbad_alloc@std@@UEAAPEAXI@Z 00000001800081d4 anticheat_detector.obj + 0002:000021d4 $cppxdata$??_Gbad_array_new_length@std@@UEAAPEAXI@Z 00000001800081d4 anticheat_detector.obj + 0002:000021d4 $cppxdata$??_Gexception@std@@UEAAPEAXI@Z 00000001800081d4 anticheat_detector.obj + 0002:000021d4 $cppxdata$??_Gplugmod_t@@UEAAPEAXI@Z 00000001800081d4 anticheat_detector.obj + 0002:000021d4 $cppxdata$??_Gshow_detector_ah_t@@UEAAPEAXI@Z 00000001800081d4 anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??0bad_alloc@std@@QEAA@AEBV01@@Z 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??0bad_array_new_length@std@@QEAA@AEBV01@@Z 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??1?$_Uninitialized_backout_al@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAA@XZ 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??1?$_qstring@D@@QEAA@XZ 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??1?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAA@XZ 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??1artifact_chooser_t@@UEAA@XZ 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??1bad_alloc@std@@UEAA@XZ 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??1bad_array_new_length@std@@UEAA@XZ 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??1detected_artifact_t@@QEAA@XZ 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??1func_item_iterator_t@@QEAA@XZ 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??_Gaction_handler_t@@UEAAPEAXI@Z 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??_Gartifact_chooser_t@@UEAAPEAXI@Z 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??_Gbad_alloc@std@@UEAAPEAXI@Z 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??_Gbad_array_new_length@std@@UEAAPEAXI@Z 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??_Gchooser_base_t@@UEAAPEAXI@Z 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??_Gexception@std@@UEAAPEAXI@Z 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??_Gplugin_ctx_t@@UEAAPEAXI@Z 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??_Gplugmod_t@@UEAAPEAXI@Z 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$??_Gshow_detector_ah_t@@UEAAPEAXI@Z 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$?_Destroy@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@AEAAXPEAUdetected_artifact_t@@0@Z 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$?clear_results@anticheat_detector_t@@QEAAXXZ 00000001800081dd anticheat_detector.obj + 0002:000021dd $stateUnwindMap$?deallocate@?$allocator@Udetected_artifact_t@@@std@@QEAAXQEAUdetected_artifact_t@@_K@Z 00000001800081dd anticheat_detector.obj + 0002:000021e3 $ip2state$??_Gaction_handler_t@@UEAAPEAXI@Z 00000001800081e3 anticheat_detector.obj + 0002:000021e3 $ip2state$??_Gbad_alloc@std@@UEAAPEAXI@Z 00000001800081e3 anticheat_detector.obj + 0002:000021e3 $ip2state$??_Gbad_array_new_length@std@@UEAAPEAXI@Z 00000001800081e3 anticheat_detector.obj + 0002:000021e3 $ip2state$??_Gexception@std@@UEAAPEAXI@Z 00000001800081e3 anticheat_detector.obj + 0002:000021e3 $ip2state$??_Gplugmod_t@@UEAAPEAXI@Z 00000001800081e3 anticheat_detector.obj + 0002:000021e3 $ip2state$??_Gshow_detector_ah_t@@UEAAPEAXI@Z 00000001800081e3 anticheat_detector.obj + 0002:000021e8 $unwind$??1bad_alloc@std@@UEAA@XZ 00000001800081e8 anticheat_detector.obj + 0002:000021e8 $unwind$??1bad_array_new_length@std@@UEAA@XZ 00000001800081e8 anticheat_detector.obj + 0002:000021f8 $cppxdata$??1bad_alloc@std@@UEAA@XZ 00000001800081f8 anticheat_detector.obj + 0002:000021f8 $cppxdata$??1bad_array_new_length@std@@UEAA@XZ 00000001800081f8 anticheat_detector.obj + 0002:00002201 $ip2state$??1bad_alloc@std@@UEAA@XZ 0000000180008201 anticheat_detector.obj + 0002:00002201 $ip2state$??1bad_array_new_length@std@@UEAA@XZ 0000000180008201 anticheat_detector.obj + 0002:00002204 $unwind$??0bad_alloc@std@@QEAA@AEBV01@@Z 0000000180008204 anticheat_detector.obj + 0002:00002204 $unwind$??0bad_array_new_length@std@@QEAA@AEBV01@@Z 0000000180008204 anticheat_detector.obj + 0002:00002214 $cppxdata$??0bad_alloc@std@@QEAA@AEBV01@@Z 0000000180008214 anticheat_detector.obj + 0002:00002214 $cppxdata$??0bad_array_new_length@std@@QEAA@AEBV01@@Z 0000000180008214 anticheat_detector.obj + 0002:0000221d $ip2state$??0bad_alloc@std@@QEAA@AEBV01@@Z 000000018000821d anticheat_detector.obj + 0002:0000221d $ip2state$??0bad_array_new_length@std@@QEAA@AEBV01@@Z 000000018000821d anticheat_detector.obj + 0002:00002220 $unwind$?_Throw_bad_array_new_length@std@@YAXXZ 0000000180008220 anticheat_detector.obj + 0002:00002220 $unwind$?__scrt_throw_std_bad_alloc@@YAXXZ 0000000180008220 MSVCRT:throw_bad_alloc.obj + 0002:00002220 $unwind$?__scrt_throw_std_bad_array_new_length@@YAXXZ 0000000180008220 MSVCRT:throw_bad_alloc.obj + 0002:00002228 $unwind$?resize_noinit@?$qvector@D@@QEAAX_K@Z 0000000180008228 anticheat_detector.obj + 0002:00002228 $unwind$_RTC_Initialize 0000000180008228 MSVCRT:initsect.obj + 0002:00002228 $unwind$_RTC_Terminate 0000000180008228 MSVCRT:initsect.obj + 0002:00002234 $unwind$?assign@?$_qstring@D@@AEAAXPEBD_K@Z 0000000180008234 anticheat_detector.obj + 0002:00002234 $unwind$__scrt_dllmain_exception_filter 0000000180008234 MSVCRT:utility.obj + 0002:00002248 $unwind$?append@?$_qstring@D@@QEAAAEAV1@PEBD_K@Z 0000000180008248 anticheat_detector.obj + 0002:00002248 $unwind$?get_row@artifact_chooser_t@@UEBAXPEAV?$qvector@V?$_qstring@D@@@@PEAHPEAUchooser_item_attrs_t@@_K@Z 0000000180008248 anticheat_detector.obj + 0002:00002260 $unwind$?sprnt@?$_qstring@D@@QEAAAEAV1@PEBDZZ 0000000180008260 anticheat_detector.obj + 0002:00002270 $unwind$??1?$_qstring@D@@QEAA@XZ 0000000180008270 anticheat_detector.obj + 0002:00002280 $cppxdata$??1?$_qstring@D@@QEAA@XZ 0000000180008280 anticheat_detector.obj + 0002:00002289 $ip2state$??1?$_qstring@D@@QEAA@XZ 0000000180008289 anticheat_detector.obj + 0002:0000228c $unwind$??1func_item_iterator_t@@QEAA@XZ 000000018000828c anticheat_detector.obj + 0002:0000229c $cppxdata$??1func_item_iterator_t@@QEAA@XZ 000000018000829c anticheat_detector.obj + 0002:000022a5 $ip2state$??1func_item_iterator_t@@QEAA@XZ 00000001800082a5 anticheat_detector.obj + 0002:000022a8 $unwind$??_Gartifact_chooser_t@@UEAAPEAXI@Z 00000001800082a8 anticheat_detector.obj + 0002:000022a8 $unwind$??_Gchooser_base_t@@UEAAPEAXI@Z 00000001800082a8 anticheat_detector.obj + 0002:000022bc $cppxdata$??_Gartifact_chooser_t@@UEAAPEAXI@Z 00000001800082bc anticheat_detector.obj + 0002:000022bc $cppxdata$??_Gchooser_base_t@@UEAAPEAXI@Z 00000001800082bc anticheat_detector.obj + 0002:000022c5 $ip2state$??_Gartifact_chooser_t@@UEAAPEAXI@Z 00000001800082c5 anticheat_detector.obj + 0002:000022c5 $ip2state$??_Gchooser_base_t@@UEAAPEAXI@Z 00000001800082c5 anticheat_detector.obj + 0002:000022cc $unwind$??_Gaction_handler_t@@UEAAPEAXI@Z 00000001800082cc anticheat_detector.obj + 0002:000022cc $unwind$??_Gplugmod_t@@UEAAPEAXI@Z 00000001800082cc anticheat_detector.obj + 0002:000022cc $unwind$??_Gshow_detector_ah_t@@UEAAPEAXI@Z 00000001800082cc anticheat_detector.obj + 0002:000022dc $unwind$?ask_form@@YAHPEBDZZ 00000001800082dc anticheat_detector.obj + 0002:000022dc $unwind$?info@@YAXPEBDZZ 00000001800082dc anticheat_detector.obj + 0002:000022dc $unwind$?msg@@YAHPEBDZZ 00000001800082dc anticheat_detector.obj + 0002:000022dc $unwind$?warning@@YAXPEBDZZ 00000001800082dc anticheat_detector.obj + 0002:000022e4 $unwind$??3modctx_t@@SAXPEAX@Z 00000001800082e4 anticheat_detector.obj + 0002:000022f4 $cppxdata$??3modctx_t@@SAXPEAX@Z 00000001800082f4 anticheat_detector.obj + 0002:000022f9 $ip2state$??3modctx_t@@SAXPEAX@Z 00000001800082f9 anticheat_detector.obj + 0002:000022fc $unwind$??1detected_artifact_t@@QEAA@XZ 00000001800082fc anticheat_detector.obj + 0002:0000230c $cppxdata$??1detected_artifact_t@@QEAA@XZ 000000018000830c anticheat_detector.obj + 0002:00002315 $ip2state$??1detected_artifact_t@@QEAA@XZ 0000000180008315 anticheat_detector.obj + 0002:0000231c $unwind$??0detected_artifact_t@@QEAA@AEBU0@@Z 000000018000831c anticheat_detector.obj + 0002:00002338 $cppxdata$??0detected_artifact_t@@QEAA@AEBU0@@Z 0000000180008338 anticheat_detector.obj + 0002:00002341 $stateUnwindMap$??0detected_artifact_t@@QEAA@AEBU0@@Z 0000000180008341 anticheat_detector.obj + 0002:00002351 $ip2state$??0detected_artifact_t@@QEAA@AEBU0@@Z 0000000180008351 anticheat_detector.obj + 0002:0000235c $unwind$?deallocate@?$allocator@Udetected_artifact_t@@@std@@QEAAXQEAUdetected_artifact_t@@_K@Z 000000018000835c anticheat_detector.obj + 0002:0000236c $cppxdata$?deallocate@?$allocator@Udetected_artifact_t@@@std@@QEAAXQEAUdetected_artifact_t@@_K@Z 000000018000836c anticheat_detector.obj + 0002:00002375 $ip2state$?deallocate@?$allocator@Udetected_artifact_t@@@std@@QEAAXQEAUdetected_artifact_t@@_K@Z 0000000180008375 anticheat_detector.obj + 0002:00002378 $unwind$??1?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAA@XZ 0000000180008378 anticheat_detector.obj + 0002:00002390 $cppxdata$??1?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAA@XZ 0000000180008390 anticheat_detector.obj + 0002:00002399 $ip2state$??1?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAA@XZ 0000000180008399 anticheat_detector.obj + 0002:000023a0 $unwind$?_Destroy@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@AEAAXPEAUdetected_artifact_t@@0@Z 00000001800083a0 anticheat_detector.obj + 0002:000023b4 $cppxdata$?_Destroy@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@AEAAXPEAUdetected_artifact_t@@0@Z 00000001800083b4 anticheat_detector.obj + 0002:000023bd $ip2state$?_Destroy@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@AEAAXPEAUdetected_artifact_t@@0@Z 00000001800083bd anticheat_detector.obj + 0002:000023c4 $unwind$?_Xlength@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@CAXXZ 00000001800083c4 anticheat_detector.obj + 0002:000023c4 $unwind$?activate@show_detector_ah_t@@UEAAHPEAUaction_ctx_base_t@@@Z 00000001800083c4 anticheat_detector.obj + 0002:000023c4 $unwind$__scrt_initialize_default_local_stdio_options 00000001800083c4 MSVCRT:default_local_stdio_options.obj + 0002:000023c4 $unwind$?dllmain_crt_dispatch@@YAHQEAUHINSTANCE__@@KQEAX@Z 00000001800083c4 MSVCRT:dll_dllmain.obj + 0002:000023c4 $unwind$DllMain 00000001800083c4 MSVCRT:dll_dllmain_stub.obj + 0002:000023c4 $unwind$__scrt_acquire_startup_lock 00000001800083c4 MSVCRT:utility.obj + 0002:000023c4 $unwind$__scrt_dllmain_after_initialize_c 00000001800083c4 MSVCRT:utility.obj + 0002:000023c4 $unwind$__scrt_dllmain_before_initialize_c 00000001800083c4 MSVCRT:utility.obj + 0002:000023c4 $unwind$__scrt_dllmain_crt_thread_attach 00000001800083c4 MSVCRT:utility.obj + 0002:000023c4 $unwind$__scrt_dllmain_crt_thread_detach 00000001800083c4 MSVCRT:utility.obj + 0002:000023c4 $unwind$__scrt_dllmain_uninitialize_c 00000001800083c4 MSVCRT:utility.obj + 0002:000023c4 $unwind$__scrt_dllmain_uninitialize_critical 00000001800083c4 MSVCRT:utility.obj + 0002:000023cc $unwind$?check_api_calls@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 00000001800083cc anticheat_detector.obj + 0002:000023f4 $cppxdata$?check_api_calls@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 00000001800083f4 anticheat_detector.obj + 0002:000023fd $stateUnwindMap$?check_api_calls@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 00000001800083fd anticheat_detector.obj + 0002:0000243d $ip2state$?check_api_calls@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 000000018000843d anticheat_detector.obj + 0002:00002460 $unwind$?check_string_references@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 0000000180008460 anticheat_detector.obj + 0002:00002488 $cppxdata$?check_string_references@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 0000000180008488 anticheat_detector.obj + 0002:00002491 $stateUnwindMap$?check_string_references@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 0000000180008491 anticheat_detector.obj + 0002:000024d2 $ip2state$?check_string_references@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 00000001800084d2 anticheat_detector.obj + 0002:000024f4 $unwind$?check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 00000001800084f4 anticheat_detector.obj + 0002:0000251c $cppxdata$?check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 000000018000851c anticheat_detector.obj + 0002:00002525 $stateUnwindMap$?check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 0000000180008525 anticheat_detector.obj + 0002:00002599 $ip2state$?check_inline_detection@anticheat_detector_t@@AEAA_N_KPEAVfunc_t@@@Z 0000000180008599 anticheat_detector.obj + 0002:00002600 $unwind$?clear_results@anticheat_detector_t@@QEAAXXZ 0000000180008600 anticheat_detector.obj + 0002:00002618 $cppxdata$?clear_results@anticheat_detector_t@@QEAAXXZ 0000000180008618 anticheat_detector.obj + 0002:00002621 $ip2state$?clear_results@anticheat_detector_t@@QEAAXXZ 0000000180008621 anticheat_detector.obj + 0002:00002628 $unwind$?show_results@anticheat_detector_t@@QEAAXXZ 0000000180008628 anticheat_detector.obj + 0002:0000264c $cppxdata$?show_results@anticheat_detector_t@@QEAAXXZ 000000018000864c anticheat_detector.obj + 0002:00002655 $stateUnwindMap$?show_results@anticheat_detector_t@@QEAAXXZ 0000000180008655 anticheat_detector.obj + 0002:00002661 $ip2state$?show_results@anticheat_detector_t@@QEAAXXZ 0000000180008661 anticheat_detector.obj + 0002:0000266c $unwind$?enter@artifact_chooser_t@@UEAA?AUcbret_t@chooser_t@@_K@Z 000000018000866c anticheat_detector.obj + 0002:0000266c $unwind$__raise_securityfailure 000000018000866c MSVCRT:gs_report.obj + 0002:0000266c $unwind$??2@YAPEAX_K@Z 000000018000866c MSVCRT:new_scalar.obj + 0002:0000266c $unwind$??_Gtype_info@@UEAAPEAXI@Z 000000018000866c MSVCRT:std_type_info_static.obj + 0002:0000266c $unwind$__scrt_initialize_crt 000000018000866c MSVCRT:utility.obj + 0002:0000266c $unwind$__scrt_initialize_onexit_tables 000000018000866c MSVCRT:utility.obj + 0002:0000266c $unwind$__scrt_release_startup_lock 000000018000866c MSVCRT:utility.obj + 0002:0000266c $unwind$__scrt_uninitialize_crt 000000018000866c MSVCRT:utility.obj + 0002:00002674 $unwind$??1artifact_chooser_t@@UEAA@XZ 0000000180008674 anticheat_detector.obj + 0002:00002684 $cppxdata$??1artifact_chooser_t@@UEAA@XZ 0000000180008684 anticheat_detector.obj + 0002:0000268d $ip2state$??1artifact_chooser_t@@UEAA@XZ 000000018000868d anticheat_detector.obj + 0002:00002690 $unwind$?run@plugin_ctx_t@@UEAA_N_K@Z 0000000180008690 anticheat_detector.obj + 0002:0000269c $chain$0$?run@plugin_ctx_t@@UEAA_N_K@Z 000000018000869c anticheat_detector.obj + 0002:000026b0 $chain$2$?run@plugin_ctx_t@@UEAA_N_K@Z 00000001800086b0 anticheat_detector.obj + 0002:000026c8 $chain$3$?run@plugin_ctx_t@@UEAA_N_K@Z 00000001800086c8 anticheat_detector.obj + 0002:000026d8 $unwind$??_Gplugin_ctx_t@@UEAAPEAXI@Z 00000001800086d8 anticheat_detector.obj + 0002:000026ec $cppxdata$??_Gplugin_ctx_t@@UEAAPEAXI@Z 00000001800086ec anticheat_detector.obj + 0002:000026f5 $ip2state$??_Gplugin_ctx_t@@UEAAPEAXI@Z 00000001800086f5 anticheat_detector.obj + 0002:000026f8 $unwind$?init@@YAPEAUplugmod_t@@XZ 00000001800086f8 anticheat_detector.obj + 0002:0000270c $cppxdata$?init@@YAPEAUplugmod_t@@XZ 000000018000870c anticheat_detector.obj + 0002:00002715 $stateUnwindMap$?init@@YAPEAUplugmod_t@@XZ 0000000180008715 anticheat_detector.obj + 0002:0000271c $ip2state$?init@@YAPEAUplugmod_t@@XZ 000000018000871c anticheat_detector.obj + 0002:00002724 $unwind$??$_Emplace_reallocate@AEBUdetected_artifact_t@@@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAAPEAUdetected_artifact_t@@QEAU2@AEBU2@@Z 0000000180008724 anticheat_detector.obj + 0002:00002740 $cppxdata$??$_Emplace_reallocate@AEBUdetected_artifact_t@@@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAAPEAUdetected_artifact_t@@QEAU2@AEBU2@@Z 0000000180008740 anticheat_detector.obj + 0002:0000274d $stateUnwindMap$??$_Emplace_reallocate@AEBUdetected_artifact_t@@@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAAPEAUdetected_artifact_t@@QEAU2@AEBU2@@Z 000000018000874d anticheat_detector.obj + 0002:0000275f $tryMap$??$_Emplace_reallocate@AEBUdetected_artifact_t@@@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAAPEAUdetected_artifact_t@@QEAU2@AEBU2@@Z 000000018000875f anticheat_detector.obj + 0002:00002767 $handlerMap$??$_Emplace_reallocate@AEBUdetected_artifact_t@@@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAAPEAUdetected_artifact_t@@QEAU2@AEBU2@@Z 0000000180008767 anticheat_detector.obj + 0002:0000276e $ip2state$??$_Emplace_reallocate@AEBUdetected_artifact_t@@@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAAPEAUdetected_artifact_t@@QEAU2@AEBU2@@Z 000000018000876e anticheat_detector.obj + 0002:00002784 $unwind$?catch$22@?0???$_Emplace_reallocate@AEBUdetected_artifact_t@@@?$vector@Udetected_artifact_t@@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAAPEAUdetected_artifact_t@@QEAU2@AEBU2@@Z@4HA 0000000180008784 anticheat_detector.obj + 0002:0000278c $unwind$??1?$_Uninitialized_backout_al@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAA@XZ 000000018000878c anticheat_detector.obj + 0002:000027a0 $cppxdata$??1?$_Uninitialized_backout_al@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAA@XZ 00000001800087a0 anticheat_detector.obj + 0002:000027a9 $ip2state$??1?$_Uninitialized_backout_al@V?$allocator@Udetected_artifact_t@@@std@@@std@@QEAA@XZ 00000001800087a9 anticheat_detector.obj + 0002:000027b0 $unwind$??_L@YAXPEAX_K1P6AX0@Z2@Z 00000001800087b0 MSVCRT:ehvecctr.obj + 0002:000027dc $unwind$?fin$0@?0??dllmain_crt_process_attach@@YAHQEAUHINSTANCE__@@QEAX@Z@4HA 00000001800087dc MSVCRT:dll_dllmain.obj + 0002:000027dc $unwind$?fin$0@?0??dllmain_crt_process_detach@@YAH_N@Z@4HA 00000001800087dc MSVCRT:dll_dllmain.obj + 0002:000027dc $unwind$?fin$1@?0??dllmain_crt_process_detach@@YAH_N@Z@4HA 00000001800087dc MSVCRT:dll_dllmain.obj + 0002:000027dc $unwind$?fin$0@?0???_L@YAXPEAX_K1P6AX0@Z2@Z@4HA 00000001800087dc MSVCRT:ehvecctr.obj + 0002:000027dc $unwind$?fin$0@?0???_M@YAXPEAX_K1P6AX0@Z@Z@4HA 00000001800087dc MSVCRT:ehvecdtr.obj + 0002:000027e4 $unwind$??_M@YAXPEAX_K1P6AX0@Z@Z 00000001800087e4 MSVCRT:ehvecdtr.obj + 0002:0000280c $unwind$?__ArrayUnwind@@YAXPEAX_K1P6AX0@Z@Z 000000018000880c MSVCRT:ehvecdtr.obj + 0002:00002838 $unwind$?filt$0@?0??__ArrayUnwind@@YAXPEAX_K1P6AX0@Z@Z@4HA 0000000180008838 MSVCRT:ehvecdtr.obj + 0002:00002844 $unwind$__GSHandlerCheck_EH4 0000000180008844 MSVCRT:gshandlereh4.obj + 0002:00002860 $xdatasym 0000000180008860 MSVCRT:amdsecgs.obj + 0002:00002864 $unwind$?dllmain_crt_process_attach@@YAHQEAUHINSTANCE__@@QEAX@Z 0000000180008864 MSVCRT:dll_dllmain.obj + 0002:000028a0 $unwind$?dllmain_crt_process_detach@@YAH_N@Z 00000001800088a0 MSVCRT:dll_dllmain.obj + 0002:000028f4 $unwind$?dllmain_dispatch@@YAHQEAUHINSTANCE__@@KQEAX@Z 00000001800088f4 MSVCRT:dll_dllmain.obj + 0002:0000291c $unwind$?filt$0@?0??dllmain_dispatch@@YAHQEAUHINSTANCE__@@KQEAX@Z@4HA 000000018000891c MSVCRT:dll_dllmain.obj + 0002:00002924 $unwind$_DllMainCRTStartup 0000000180008924 MSVCRT:dll_dllmain.obj + 0002:00002934 $unwind$__GSHandlerCheckCommon 0000000180008934 MSVCRT:gshandler.obj + 0002:0000293c $unwind$__report_gsfailure 000000018000893c MSVCRT:gs_report.obj + 0002:00002944 $unwind$capture_previous_context 0000000180008944 MSVCRT:gs_report.obj + 0002:00002950 $unwind$__security_init_cookie 0000000180008950 MSVCRT:gs_support.obj + 0002:0000295c $unwind$__scrt_is_nonwritable_in_current_image 000000018000895c MSVCRT:utility.obj + 0002:0000297c $unwind$__scrt_is_nonwritable_in_current_image$filt$0 000000018000897c MSVCRT:utility.obj + 0002:00002984 $unwind$__scrt_fastfail 0000000180008984 MSVCRT:utility_desktop.obj + 0002:00002998 $xdatasym 0000000180008998 MSVCRT:guard_dispatch.obj + 0002:000029a0 $xdatasym 00000001800089a0 MSVCRT:guard_xfg_dispatch.obj + 0002:000029a4 $unwind$__isa_available_init 00000001800089a4 MSVCRT:cpu_disp.obj + 0002:00002ab0 .edata 0000000180008ab0 anticheat_detector64.exp + 0002:00002ad8 rgpv 0000000180008ad8 anticheat_detector64.exp + 0002:00002adc rgszName 0000000180008adc anticheat_detector64.exp + 0002:00002ae0 rgwOrd 0000000180008ae0 anticheat_detector64.exp + 0002:00002ae2 szName 0000000180008ae2 anticheat_detector64.exp + 0002:00002afb $N00001 0000000180008afb anticheat_detector64.exp + 0002:00002fd0 .idata$6 0000000180008fd0 ida:ida64.dll + 0002:00002ffc .idata$6 0000000180008ffc msvcprt:MSVCP140.dll + 0002:00003170 .idata$6 0000000180009170 kernel32:KERNEL32.dll + 0002:0000328e .idata$6 000000018000928e vcruntime:VCRUNTIME140.dll + 0002:000032a0 .idata$6 00000001800092a0 vcruntime:VCRUNTIME140_1.dll + 0002:000033aa .idata$6 00000001800093aa ucrt:api-ms-win-crt-runtime-l1-1-0.dll + 0002:000033cc .idata$6 00000001800093cc ucrt:api-ms-win-crt-heap-l1-1-0.dll + 0003:00000124 ?__proc_attached@@3HA 000000018000a124 MSVCRT:dll_dllmain.obj + 0003:00000130 GS_ExceptionRecord 000000018000a130 MSVCRT:gs_report.obj + 0003:000001d0 GS_ContextRecord 000000018000a1d0 MSVCRT:gs_report.obj + 0003:000006d0 ?is_initialized_as_dll@@3_NA 000000018000a6d0 MSVCRT:utility.obj + 0003:000006d1 ?module_local_atexit_table_initialized@@3_NA 000000018000a6d1 MSVCRT:utility.obj + 0003:000006d8 ?module_local_atexit_table@@3U_onexit_table_t@@A 000000018000a6d8 MSVCRT:utility.obj + 0003:000006f0 ?module_local_at_quick_exit_table@@3U_onexit_table_t@@A 000000018000a6f0 MSVCRT:utility.obj + +``` \ No newline at end of file diff --git a/archive/stolevchristian/LUDA.txt b/archive/stolevchristian/LUDA.txt new file mode 100644 index 00000000..a382fba9 --- /dev/null +++ b/archive/stolevchristian/LUDA.txt @@ -0,0 +1,134297 @@ +Project Path: arc_stolevchristian_LUDA_o0yzb67d + +Source Tree: + +```txt +arc_stolevchristian_LUDA_o0yzb67d +├── CMakeLists.txt +├── Executor +│ ├── Executor.cpp +│ ├── Executor.h +│ └── Libraries +│ ├── assembler.hpp +│ ├── functions.hpp +│ ├── hexrays.hpp +│ ├── patching.hpp +│ ├── print.hpp +│ ├── strings.hpp +│ └── xrefs.hpp +├── IdaSDK +│ ├── allins.hpp +│ ├── allow_deprecated.hpp +│ ├── auto.hpp +│ ├── bitrange.hpp +│ ├── bytes.hpp +│ ├── compress.hpp +│ ├── config.hpp +│ ├── cvt64.hpp +│ ├── dbg.hpp +│ ├── demangle.hpp +│ ├── diff3.hpp +│ ├── dirtree.hpp +│ ├── diskio.hpp +│ ├── entry.hpp +│ ├── err.h +│ ├── exehdr.h +│ ├── expr.hpp +│ ├── fixup.hpp +│ ├── fpro.h +│ ├── frame.hpp +│ ├── funcs.hpp +│ ├── gdl.hpp +│ ├── graph.hpp +│ ├── help.h +│ ├── hexrays.hpp +│ ├── ida.hpp +│ ├── ida_highlighter.hpp +│ ├── idacfg.hpp +│ ├── idalib.hpp +│ ├── idd.hpp +│ ├── idp.hpp +│ ├── ieee.h +│ ├── intel.hpp +│ ├── jumptable.hpp +│ ├── kernwin.hpp +│ ├── lex.hpp +│ ├── libfuncs.hpp +│ ├── libs +│ │ ├── ida.lib +│ │ ├── idalib.lib +│ │ └── network.lib +│ ├── lines.hpp +│ ├── llong.hpp +│ ├── loader.hpp +│ ├── make_script_ns.hpp +│ ├── md5.h +│ ├── merge.hpp +│ ├── mergemod.hpp +│ ├── moves.hpp +│ ├── nalt.hpp +│ ├── name.hpp +│ ├── netnode.hpp +│ ├── network.hpp +│ ├── offset.hpp +│ ├── parsejson.hpp +│ ├── pro.h +│ ├── problems.hpp +│ ├── prodir.h +│ ├── pronet.h +│ ├── range.hpp +│ ├── regex.h +│ ├── regfinder.hpp +│ ├── registry.hpp +│ ├── search.hpp +│ ├── segment.hpp +│ ├── segregs.hpp +│ ├── srclang.hpp +│ ├── strlist.hpp +│ ├── tryblks.hpp +│ ├── typeinf.hpp +│ ├── ua.hpp +│ ├── undo.hpp +│ ├── workarounds.hpp +│ └── xref.hpp +├── LICENSE +├── LUDA.png +├── Lua +│ ├── lapi.c +│ ├── lapi.h +│ ├── lauxlib.c +│ ├── lauxlib.h +│ ├── lbaselib.c +│ ├── lcode.c +│ ├── lcode.h +│ ├── lcorolib.c +│ ├── lctype.c +│ ├── lctype.h +│ ├── ldblib.c +│ ├── ldebug.c +│ ├── ldebug.h +│ ├── ldo.c +│ ├── ldo.h +│ ├── ldump.c +│ ├── lfunc.c +│ ├── lfunc.h +│ ├── lgc.c +│ ├── lgc.h +│ ├── linit.c +│ ├── liolib.c +│ ├── ljumptab.h +│ ├── llex.c +│ ├── llex.h +│ ├── llimits.h +│ ├── lmathlib.c +│ ├── lmem.c +│ ├── lmem.h +│ ├── loadlib.c +│ ├── lobject.c +│ ├── lobject.h +│ ├── lopcodes.c +│ ├── lopcodes.h +│ ├── lopnames.h +│ ├── loslib.c +│ ├── lparser.c +│ ├── lparser.h +│ ├── lprefix.h +│ ├── lstate.c +│ ├── lstate.h +│ ├── lstring.c +│ ├── lstring.h +│ ├── lstrlib.c +│ ├── ltable.c +│ ├── ltable.h +│ ├── ltablib.c +│ ├── ltm.c +│ ├── ltm.h +│ ├── lua.h +│ ├── lua.hpp +│ ├── luaconf.h +│ ├── lualib.h +│ ├── lundump.c +│ ├── lundump.h +│ ├── lutf8lib.c +│ ├── lvm.c +│ ├── lvm.h +│ ├── lzio.c +│ └── lzio.h +├── LudaSocket +│ ├── ludasocket.cpp +│ └── ludasocket.h +├── Plugin.cpp +├── README.md +└── keystone + ├── arm.h + ├── arm64.h + ├── evm.h + ├── hexagon.h + ├── keystone.h + ├── mips.h + ├── ppc.h + ├── sparc.h + ├── systemz.h + └── x86.h + +``` + +`CMakeLists.txt`: + +```txt +cmake_minimum_required(VERSION 4.1) +project(LUDA) + +set(CMAKE_CXX_STANDARD 20) + +if(WIN32) + set(CMAKE_SYSTEM_VERSION 10.0) # or your Windows SDK version +endif() + +# Because obviously?? +set(VCPKG_TARGET_TRIPLET "x64-windows-static") + +set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + +# Recursively find all .cpp files in ida_sdk +file(GLOB_RECURSE IDA_SDK_SOURCES + "${CMAKE_SOURCE_DIR}/IdaSDK/*.cpp" +) +file(GLOB_RECURSE LUDASOCKET_SOURCES + "${CMAKE_SOURCE_DIR}/LudaSocket/*.cpp" +) +file(GLOB_RECURSE EXECUTOR_SOURCES + "${CMAKE_SOURCE_DIR}/Executor/*.cpp" +) +file(GLOB_RECURSE LUA_SOURCES + "${CMAKE_SOURCE_DIR}/Lua/*.cpp" + "${CMAKE_SOURCE_DIR}/Lua/*.c" +) +add_library(LUDA SHARED + Plugin.cpp + ${IDA_SDK_SOURCES} + ${LUDASOCKET_SOURCES} + ${EXECUTOR_SOURCES} + ${LUA_SOURCES} +) + +file(GLOB IDA_LIBS "${CMAKE_SOURCE_DIR}/IdaSDK/libs/*.lib") +target_link_libraries(${PROJECT_NAME} PRIVATE + ${IDA_LIBS} + ${CMAKE_SOURCE_DIR}/keystone/keystone.lib +) + +# Makes everything just look better, maybe just my opinion +target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/IdaSDK +) + +``` + +`Executor/Executor.cpp`: + +```cpp +#include "Executor.h" + +#include "Libraries/print.hpp" +#include "Libraries/hexrays.hpp" +#include "Libraries/functions.hpp" +#include "Libraries/xrefs.hpp" +#include "Libraries/strings.hpp" +#include "Libraries/patching.hpp" +#include "Libraries/assembler.hpp" + +Executor::Executor() +{ + +} + +Executor::~Executor() +{ + lua_close(this->L); +} + +#define lua_register_alias(L, g, ng) lua_getglobal(L, g); \ + lua_setglobal(L, ng) + +#define LUA_REGISTER_TABLE_FUNC(L, table, name, func) \ + lua_getglobal(L, table); \ + if (lua_isnil(L, -1)) { \ + lua_pop(L, 1); \ + lua_newtable(L); \ + lua_setglobal(L, table); \ + lua_getglobal(L, table); \ + } \ + lua_pushcfunction(L, func); \ + lua_setfield(L, -2, name); \ + lua_pop(L, 1) + +bool Executor::run_script(const std::string& script) +{ + //luda::SendOutput("Received script (" + std::to_string(script.length()) + " chars)"); + int result = luaL_loadbuffer(L, script.c_str(), script.size(), "script"); + if (result != 0) { + const char* error_msg = lua_tostring(L, -1); + msg("[ERROR] %s\n", error_msg); + luda::SendError(error_msg); + lua_pop(L, 1); // Pop the error message + return false; + } + int pres = lua_pcall(L, 0, 0, 0); + if (pres != 0) { + const char* error_msg = lua_tostring(L, -1); + msg("[RUNTIME ERROR] %s\n", error_msg); + luda::SendError(error_msg); + lua_pop(L, 1); // Pop the error message + return false; + } + luda::SendSuccess(); + //msg("Executed script successfully!\n"); + return true; +} +bool Executor::initialize() +{ + L = luaL_newstate(); // Create new Lua state + msg("[Executor] Lua state created: %p\n", L); + + if (L == nullptr) { + return false; // Failed to create Lua state + } + + luaL_openlibs(L); // Load standard Lua libraries + + /* Register custom environment */ + + // UI related / output + lua_register(this->L, "print", (lua_CFunction)LUDA::Library::c_print); + + // pseudocode/disassembly related + LUA_REGISTER_TABLE_FUNC(this->L, "hexrays", "decompile", (lua_CFunction)LUDA::Library::c_decompile); + LUA_REGISTER_TABLE_FUNC(this->L, "hexrays", "disassemble", (lua_CFunction)LUDA::Library::c_disassemble); + + // functions + lua_register(this->L, "get_function", (lua_CFunction)LUDA::Library::c_get_func); + + // xrefs + LUA_REGISTER_TABLE_FUNC(this->L, "xrefs", "get", (lua_CFunction)LUDA::Library::c_get_xrefs); + + // strings + LUA_REGISTER_TABLE_FUNC(this->L, "strings", "search", (lua_CFunction)LUDA::Library::c_search_strings); + + lua_register(this->L, "hex", (lua_CFunction)LUDA::Library::c_to_hex); + + // patching + LUA_REGISTER_TABLE_FUNC(this->L, "memory", "write", (lua_CFunction)LUDA::Library::c_patch_bytes); + LUA_REGISTER_TABLE_FUNC(this->L, "memory", "read", (lua_CFunction)LUDA::Library::c_get_bytes); + + // other shit + LUA_REGISTER_TABLE_FUNC(this->L, "image", "base", (lua_CFunction)LUDA::Library::c_get_imagebase); + LUA_REGISTER_TABLE_FUNC(this->L, "image", "first", (lua_CFunction)LUDA::Library::c_get_first_address); + LUA_REGISTER_TABLE_FUNC(this->L, "image", "last", (lua_CFunction)LUDA::Library::c_get_last_address); + + + lua_register(this->L, "assemble", (lua_CFunction)LUDA::Library::c_assemble); + + return true; +} + +``` + +`Executor/Executor.h`: + +```h +#pragma once +#define __EA64__ +#include +#include +#include +#include +#include +#include "../LudaSocket/ludasocket.h" + +extern "C" { +#include +#include +#include +} + +class Executor +{ +public: + Executor(); + ~Executor(); + bool initialize(); // Create luaState and load standard libraries + bool run_script(const std::string& script); +private: + lua_State* L; +}; + + +``` + +`Executor/Libraries/assembler.hpp`: + +```hpp +#include "../Executor.h" +#include + +namespace LUDA::Library +{ + int c_assemble(lua_State* L) { + const char* code = luaL_checkstring(L, 1); + + ks_engine* ks; + if (ks_open(KS_ARCH_X86, KS_MODE_64, &ks) != KS_ERR_OK) { + lua_pushnil(L); + lua_pushstring(L, "Failed to initialize Keystone"); + return 2; + } + + unsigned char* encoded; + size_t size; + size_t count; + + if (ks_asm(ks, code, 0, &encoded, &size, &count) != KS_ERR_OK) { + lua_pushnil(L); + lua_pushfstring(L, "Assembly failed: %s", ks_strerror(ks_errno(ks))); + ks_close(ks); + return 2; + } + + lua_newtable(L); + for (size_t i = 0; i < size; i++) { + lua_pushinteger(L, encoded[i]); + lua_rawseti(L, -2, i + 1); + } + + ks_free(encoded); + ks_close(ks); + + return 1; + } +} +``` + +`Executor/Libraries/functions.hpp`: + +```hpp +#include "../Executor.h" + +namespace LUDA::Library +{ + static int c_get_func(lua_State* L) + { + if (lua_type(L, 1) == LUA_TNUMBER) /* If address */ + { + lua_Integer addr = lua_tointeger(L, 1); // Use lua_tointeger instead + func_t* func = get_func(addr); + + if (func == NULL) { + lua_pushnil(L); + return 1; + } + + /* push the function addr to lua stack and return */ + lua_pushinteger(L, func->start_ea); + return 1; + } + else if (lua_type(L, 1) == LUA_TSTRING) /* If name */ + { + const char* func_name = lua_tostring(L, 1); + ea_t ea = get_name_ea(BADADDR, func_name); + + if (ea == BADADDR) { + lua_pushnil(L); + return 1; + } + + func_t* func = get_func(ea); + if (func == NULL) { + lua_pushnil(L); + return 1; + } + + lua_pushinteger(L, func->start_ea); + return 1; + } + lua_pushnil(L); + return 1; + } +} +``` + +`Executor/Libraries/hexrays.hpp`: + +```hpp +#include "../Executor.h" + +bool decompile_function(ea_t func_addr, std::string& out_pseudocode) +{ + if (!init_hexrays_plugin()) { + msg("Hex-Rays decompiler is not available.\n"); + return false; + } + func_t* func = get_func(func_addr); + hexrays_failure_t hf; + + msg("Func: %llx\n", func); + if (func == nullptr) + { + return false; + } + cfuncptr_t cfunc = decompile(func, &hf, DECOMP_WARNINGS); + if (cfunc == nullptr) { + msg("Failed to decompile function at address: 0x%X\n", func_addr); + return false; + } + out_pseudocode = ""; + const strvec_t& sv = cfunc->get_pseudocode(); + for (int i = 0; i < sv.size(); i++) { + qstring buf; + tag_remove(&buf, sv[i].line); + //msg("%s\n", buf.c_str()); + out_pseudocode += buf.c_str(); + out_pseudocode += "\n"; + } + //out_pseudocode = "gay"; + return true; + +} + +namespace LUDA::Library +{ + static int c_decompile(lua_State* L) + { + ea_t addr = (ea_t)lua_tointeger(L, 1); + + // Check if address is in a function + if (!get_func(addr)) { + lua_pushnil(L); + lua_pushstring(L, "Address is not in a function"); + return 2; + } + + std::string pseudocode = "no pseudo"; + if (decompile_function(addr, pseudocode)) { + lua_pushstring(L, pseudocode.c_str()); + return 1; + } + else { + lua_pushnil(L); + lua_pushstring(L, "Decompilation failed"); + return 2; + } + } + + static int c_disassemble(lua_State* L) { + ea_t func_addr = lua_tointeger(L, 1); + //msg("Looking for function at: 0x%X\n", func_addr); + func_t* func = get_func(func_addr); + if (!func) { + //msg("ERROR: get_func returned NULL\n"); + lua_pushnil(L); + return 1; + } + //msg("Function found: start=0x%X, end=0x%X\n", func->start_ea, func->end_ea); + if (func->end_ea == 0 || func->end_ea <= func->start_ea) { + //msg("ERROR: Invalid function bounds\n"); + lua_pushnil(L); + return 1; + } + lua_newtable(L); + int table_idx = 1; + for (ea_t addr = func->start_ea; addr < func->end_ea; ) { + insn_t insn; + int size = decode_insn(&insn, addr); + if (size == 0) { + msg("Failed to decode at 0x%X\n", addr); + addr++; + continue; + } + /* Create instruction table */ + lua_newtable(L); + qstring qstr; + qstring buf; + generate_disasm_line(&qstr, addr); + tag_remove(&buf, qstr); + lua_pushstring(L, buf.c_str()); + lua_setfield(L, -2, "disasm"); + char mnem[64] = { 0 }; + sscanf(buf.c_str(), "%63s", mnem); + lua_pushstring(L, mnem); + lua_setfield(L, -2, "op"); + lua_pushinteger(L, insn.ea); + lua_setfield(L, -2, "ea"); + lua_pushinteger(L, insn.size); + lua_setfield(L, -2, "size"); + + /* Operands table */ + lua_newtable(L); + int op_idx = 1; + for (int i = 0; i < UA_MAXOP; i++) { + op_t& op = insn.ops[i]; + if (op.type == o_void) break; + + lua_newtable(L); + + /* Operand type */ + lua_pushinteger(L, op.type); + lua_setfield(L, -2, "type"); + + /* Type name */ + const char* type_name = "unknown"; + switch (op.type) { + case o_reg: type_name = "reg"; break; + case o_mem: type_name = "mem"; break; + case o_phrase: type_name = "phrase"; break; + case o_displ: type_name = "displ"; break; + case o_imm: type_name = "imm"; break; + case o_far: type_name = "far"; break; + case o_near: type_name = "near"; break; + } + lua_pushstring(L, type_name); + lua_setfield(L, -2, "type_name"); + + /* Register name (if register operand) */ + if (op.type == o_reg) { + qstring reg_buf; + size_t width = op.dtype ? get_dtype_size(op.dtype) : 8; + get_reg_name(®_buf, op.reg, width); + lua_pushstring(L, reg_buf.c_str()); + lua_setfield(L, -2, "reg_name"); + } + + /* Value */ + lua_pushinteger(L, op.value); + lua_setfield(L, -2, "value"); + + /* Offset/displacement */ + lua_pushinteger(L, op.addr); + lua_setfield(L, -2, "addr"); + + lua_rawseti(L, -2, op_idx); + op_idx++; + } + lua_setfield(L, -2, "operands"); + + /* Registers used table */ + lua_newtable(L); + int reg_idx = 1; + for (int i = 0; i < UA_MAXOP; i++) { + op_t& op = insn.ops[i]; + if (op.type == o_void) break; + + if (op.type == o_reg) { + qstring reg_buf; + size_t width = op.dtype ? get_dtype_size(op.dtype) : 8; + get_reg_name(®_buf, op.reg, width); + lua_pushstring(L, reg_buf.c_str()); + lua_rawseti(L, -2, reg_idx); + reg_idx++; + } + } + lua_setfield(L, -2, "regs"); + + /* Features/flags - check mnemonic */ + lua_newtable(L); + + bool is_jump = strstr(mnem, "j") != NULL && strcmp(mnem, "mov") != 0; + bool is_call = strcmp(mnem, "call") == 0; + bool is_ret = strcmp(mnem, "ret") == 0 || strcmp(mnem, "retn") == 0; + + lua_pushboolean(L, is_jump); + lua_setfield(L, -2, "is_jump"); + lua_pushboolean(L, is_call); + lua_setfield(L, -2, "is_call"); + lua_pushboolean(L, is_ret); + lua_setfield(L, -2, "is_ret"); + + lua_setfield(L, -2, "flags"); + + lua_rawseti(L, -2, table_idx); + table_idx++; + addr += insn.size; + } + return 1; + } +} +``` + +`Executor/Libraries/patching.hpp`: + +```hpp +#include "../Executor.h" + +namespace LUDA::Library +{ + static int c_get_bytes(lua_State* L) + { + if (lua_type(L, 1) == LUA_TNUMBER && lua_type(L, 2) == LUA_TNUMBER) + { + ea_t addr = (ea_t)lua_tointeger(L, 1); + size_t len = (size_t)lua_tointeger(L, 2); + + // Create a new table to hold the bytes + lua_newtable(L); + + // Read each byte and add to table + for (size_t i = 0; i < len; i++) + { + uint8_t byte = get_byte(addr + i); // IDA's get_byte function + + lua_pushinteger(L, byte); + lua_rawseti(L, -2, i + 1); // table[i+1] = byte (Lua is 1-indexed) + } + + return 1; // Return the table + } + + lua_pushnil(L); // Return nil on invalid args + return 1; + } + + static int c_patch_bytes(lua_State* L) + { + if (lua_type(L, 1) == LUA_TNUMBER && lua_type(L, 2) == LUA_TTABLE) + { + ea_t addr = (ea_t)lua_tointeger(L, 1); + + // Get the table length + size_t len = lua_objlen(L, 2); + + // Iterate through the table and patch each byte + for (size_t i = 1; i <= len; i++) + { + lua_rawgeti(L, 2, i); // Push table[i] onto stack + + if (lua_type(L, -1) == LUA_TNUMBER) + { + uint8_t byte = (uint8_t)lua_tointeger(L, -1); + patch_byte(addr + (i - 1), byte); // IDA's patch function + } + + lua_pop(L, 1); // Pop the value from stack + } + + lua_pushboolean(L, 1); // Return true on success + return 1; + } + + lua_pushboolean(L, 0); // Return false on invalid args + return 1; + } + + // Get the image base (preferred load address) + static int c_get_imagebase(lua_State* L) + { + ea_t base = get_imagebase(); + lua_pushinteger(L, (lua_Number)base); + return 1; + } + + // Get the first address of the loaded binary (min EA) + static int c_get_first_address(lua_State* L) + { + ea_t first = inf_get_min_ea(); + lua_pushinteger(L, (lua_Integer)first); + return 1; + } + + // Get the last address of the loaded binary (max EA) + static int c_get_last_address(lua_State* L) + { + ea_t last = inf_get_max_ea(); + lua_pushinteger(L, (lua_Integer)last); + return 1; + } +} +``` + +`Executor/Libraries/print.hpp`: + +```hpp +#include "../Executor.h" + +namespace LUDA::Library +{ + lua_CFunction c_print(lua_State* L) + { + int nargs = lua_gettop(L); + std::string result; + + for (int i = 1; i <= nargs; i++) { + if (i > 1) result += "\t"; + + if (lua_isstring(L, i)) { + result += lua_tostring(L, i); + } + else if (lua_isboolean(L, i)) { + result += lua_toboolean(L, i) ? "true" : "false"; + } + else if (lua_isnil(L, i)) { + result += "nil"; + } + else if (lua_isnumber(L, i)) { + result += "0x" + std::to_string((ea_t)lua_tointeger(L, i)); + } + else if (lua_istable(L, i)) { + result += "table"; + } + else if (lua_isfunction(L, i)) { + result += "function"; + } + else if (lua_isuserdata(L, i)) { + result += "userdata"; + } + else { + result += lua_typename(L, lua_type(L, i)); + } + } + // Right now we don't send to UI through websocket + //luda::SendPrint(result); + msg("%s\n", result.c_str()); + return 0; + } +} +``` + +`Executor/Libraries/strings.hpp`: + +```hpp +#include "../Executor.h" + +namespace LUDA::Library +{ + static int c_to_hex(lua_State* L) + { + lua_Integer num = lua_tointeger(L, 1); // Use lua_tointeger + + lua_getglobal(L, "string"); + lua_getfield(L, -1, "format"); + + lua_pushstring(L, "%X"); + lua_pushinteger(L, num); + + lua_pcall(L, 2, 1, 0); + + lua_remove(L, -2); + + return 1; + } + + static int c_search_strings(lua_State* L) + { + const char* search_string = lua_tostring(L, 1); + bool exact_match = lua_toboolean(L, 2); + + lua_newtable(L); // Create result table + int result_index = 0; + + // Iterate through all strings in the database + for (ea_t addr = get_segm_base(getseg(0)); addr != BADADDR; addr = next_head(addr, BADADDR)) { + int32 str_type = get_str_type(addr); + if (str_type == STRTYPE_C) { // Only process C strings + size_t len = get_item_size(addr); + qstring str_content; + + // Get the string contents properly + ssize_t result = get_strlit_contents(&str_content, addr, len, str_type, nullptr, STRCONV_ESCAPE); + if (result == -1) continue; // Failed to get string + + bool match = false; + if (exact_match) { + match = (str_content == search_string); + } else { + match = (str_content.find(search_string) != qstring::npos); + } + + if (match) { + result_index += 1; + lua_pushinteger(L, result_index); + + // Create a sub-table with string content and address + lua_newtable(L); + lua_pushstring(L, "string"); + lua_pushstring(L, str_content.c_str()); + lua_settable(L, -3); + lua_pushstring(L, "address"); + lua_pushinteger(L, addr); + lua_settable(L, -3); + + lua_settable(L, -3); + } + } + } + + return 1; // Return the table + } +} +``` + +`Executor/Libraries/xrefs.hpp`: + +```hpp +#include "../Executor.h" + +namespace LUDA::Library +{ + static int c_get_xrefs(lua_State* L) + { + ea_t target_addr = lua_tointeger(L, 1); + + lua_newtable(L); + int i = 0; + + // Get code xrefs + ea_t xref_addr = get_first_cref_to(target_addr); + while (xref_addr != BADADDR) { + i += 1; + lua_pushinteger(L, i); + lua_pushinteger(L, xref_addr); + lua_settable(L, -3); + xref_addr = get_next_cref_to(target_addr, xref_addr); + } + + // Get data xrefs + xref_addr = get_first_dref_to(target_addr); + while (xref_addr != BADADDR) { + i += 1; + lua_pushinteger(L, i); + lua_pushinteger(L, xref_addr); + lua_settable(L, -3); + xref_addr = get_next_dref_to(target_addr, xref_addr); + } + + return 1; + } + + static int c_get_xref(lua_State* L) + { + ea_t target_addr = lua_tointeger(L, 1); + int target_index = lua_tointeger(L, 2); // Get the desired index + + ea_t xref_addr = get_first_cref_to(target_addr); + int i = 0; + + while (xref_addr != BADADDR) { + i += 1; + if (i == target_index) { + lua_pushinteger(L, xref_addr); + return 1; // Return the specific xref address + } + xref_addr = get_next_cref_to(target_addr, xref_addr); + } + + // If index not found, return nil + lua_pushnil(L); + return 1; + } +} +``` + +`IdaSDK/allins.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +NN_null = 0, // Unknown Operation +NN_aaa, // ASCII Adjust after Addition +NN_aad, // ASCII Adjust AX before Division +NN_aam, // ASCII Adjust AX after Multiply +NN_aas, // ASCII Adjust AL after Subtraction +NN_adc, // Add with Carry +NN_add, // Add +NN_and, // Logical AND +NN_arpl, // Adjust RPL Field of Selector +NN_bound, // Check Array Index Against Bounds +NN_bsf, // Bit Scan Forward +NN_bsr, // Bit Scan Reverse +NN_bt, // Bit Test +NN_btc, // Bit Test and Complement +NN_btr, // Bit Test and Reset +NN_bts, // Bit Test and Set +NN_call, // Call Procedure +NN_callfi, // Indirect Call Far Procedure +NN_callni, // Indirect Call Near Procedure +NN_cbw, // AL -> AX (with sign) +NN_cwde, // AX -> EAX (with sign) +NN_cdqe, // EAX -> RAX (with sign) +NN_clc, // Clear Carry Flag +NN_cld, // Clear Direction Flag +NN_cli, // Clear Interrupt Flag +NN_clts, // Clear Task-Switched Flag in CR0 +NN_cmc, // Complement Carry Flag +NN_cmp, // Compare Two Operands +NN_cmps, // Compare Strings +NN_cwd, // AX -> DX:AX (with sign) +NN_cdq, // EAX -> EDX:EAX (with sign) +NN_cqo, // RAX -> RDX:RAX (with sign) +NN_daa, // Decimal Adjust AL after Addition +NN_das, // Decimal Adjust AL after Subtraction +NN_dec, // Decrement by 1 +NN_div, // Unsigned Divide +NN_enterw, // Make Stack Frame for Procedure Parameters +NN_enter, // Make Stack Frame for Procedure Parameters +NN_enterd, // Make Stack Frame for Procedure Parameters +NN_enterq, // Make Stack Frame for Procedure Parameters +NN_hlt, // Halt +NN_idiv, // Signed Divide +NN_imul, // Signed Multiply +NN_in, // Input from Port +NN_inc, // Increment by 1 +NN_ins, // Input Byte(s) from Port to String +NN_int, // Call to Interrupt Procedure +NN_into, // Call to Interrupt Procedure if Overflow Flag = 1 +NN_int3, // Trap to Debugger +NN_iretw, // Interrupt Return +NN_iret, // Interrupt Return +NN_iretd, // Interrupt Return (use32) +NN_iretq, // Interrupt Return (use64) +NN_ja, // Jump if Above (CF=0 & ZF=0) +NN_jae, // Jump if Above or Equal (CF=0) +NN_jb, // Jump if Below (CF=1) +NN_jbe, // Jump if Below or Equal (CF=1 | ZF=1) +NN_jc, // Jump if Carry (CF=1) +NN_jcxz, // Jump if CX is 0 +NN_jecxz, // Jump if ECX is 0 +NN_jrcxz, // Jump if RCX is 0 +NN_je, // Jump if Equal (ZF=1) +NN_jg, // Jump if Greater (ZF=0 & SF=OF) +NN_jge, // Jump if Greater or Equal (SF=OF) +NN_jl, // Jump if Less (SF!=OF) +NN_jle, // Jump if Less or Equal (ZF=1 | SF!=OF) +NN_jna, // Jump if Not Above (CF=1 | ZF=1) +NN_jnae, // Jump if Not Above or Equal (CF=1) +NN_jnb, // Jump if Not Below (CF=0) +NN_jnbe, // Jump if Not Below or Equal (CF=0 & ZF=0) +NN_jnc, // Jump if Not Carry (CF=0) +NN_jne, // Jump if Not Equal (ZF=0) +NN_jng, // Jump if Not Greater (ZF=1 | SF!=OF) +NN_jnge, // Jump if Not Greater or Equal (ZF=1) +NN_jnl, // Jump if Not Less (SF=OF) +NN_jnle, // Jump if Not Less or Equal (ZF=0 & SF=OF) +NN_jno, // Jump if Not Overflow (OF=0) +NN_jnp, // Jump if Not Parity (PF=0) +NN_jns, // Jump if Not Sign (SF=0) +NN_jnz, // Jump if Not Zero (ZF=0) +NN_jo, // Jump if Overflow (OF=1) +NN_jp, // Jump if Parity (PF=1) +NN_jpe, // Jump if Parity Even (PF=1) +NN_jpo, // Jump if Parity Odd (PF=0) +NN_js, // Jump if Sign (SF=1) +NN_jz, // Jump if Zero (ZF=1) +NN_jmp, // Jump +NN_jmpfi, // Indirect Far Jump +NN_jmpni, // Indirect Near Jump +NN_jmpshort, // Jump Short (not used) +NN_lahf, // Load Flags into AH Register +NN_lar, // Load Access Right Byte +NN_lea, // Load Effective Address +NN_leavew, // High Level Procedure Exit +NN_leave, // High Level Procedure Exit +NN_leaved, // High Level Procedure Exit +NN_leaveq, // High Level Procedure Exit +NN_lgdt, // Load Global Descriptor Table Register +NN_lidt, // Load Interrupt Descriptor Table Register +NN_lgs, // Load Full Pointer to GS:xx +NN_lss, // Load Full Pointer to SS:xx +NN_lds, // Load Full Pointer to DS:xx +NN_les, // Load Full Pointer to ES:xx +NN_lfs, // Load Full Pointer to FS:xx +NN_lldt, // Load Local Descriptor Table Register +NN_lmsw, // Load Machine Status Word +NN_lock, // Assert LOCK# Signal Prefix +NN_lods, // Load String +NN_loopw, // Loop while ECX != 0 +NN_loop, // Loop while CX != 0 +NN_loopd, // Loop while ECX != 0 +NN_loopq, // Loop while RCX != 0 +NN_loopwe, // Loop while CX != 0 and ZF=1 +NN_loope, // Loop while rCX != 0 and ZF=1 +NN_loopde, // Loop while ECX != 0 and ZF=1 +NN_loopqe, // Loop while RCX != 0 and ZF=1 +NN_loopwne, // Loop while CX != 0 and ZF=0 +NN_loopne, // Loop while rCX != 0 and ZF=0 +NN_loopdne, // Loop while ECX != 0 and ZF=0 +NN_loopqne, // Loop while RCX != 0 and ZF=0 +NN_lsl, // Load Segment Limit +NN_ltr, // Load Task Register +NN_mov, // Move Data +NN_movsp, // Move to/from Special Registers +NN_movs, // Move Byte(s) from String to String +NN_movsx, // Move with Sign-Extend +NN_movzx, // Move with Zero-Extend +NN_mul, // Unsigned Multiplication of AL or AX +NN_neg, // Two's Complement Negation +NN_nop, // No Operation +NN_not, // One's Complement Negation +NN_or, // Logical Inclusive OR +NN_out, // Output to Port +NN_outs, // Output Byte(s) to Port +NN_pop, // Pop a word from the Stack +NN_popaw, // Pop all General Registers +NN_popa, // Pop all General Registers +NN_popad, // Pop all General Registers (use32) +NN_popaq, // Pop all General Registers (use64) +NN_popfw, // Pop Stack into Flags Register +NN_popf, // Pop Stack into Flags Register +NN_popfd, // Pop Stack into Eflags Register +NN_popfq, // Pop Stack into Rflags Register +NN_push, // Push Operand onto the Stack +NN_pushaw, // Push all General Registers +NN_pusha, // Push all General Registers +NN_pushad, // Push all General Registers (use32) +NN_pushaq, // Push all General Registers (use64) +NN_pushfw, // Push Flags Register onto the Stack +NN_pushf, // Push Flags Register onto the Stack +NN_pushfd, // Push Flags Register onto the Stack (use32) +NN_pushfq, // Push Flags Register onto the Stack (use64) +NN_rcl, // Rotate Through Carry Left +NN_rcr, // Rotate Through Carry Right +NN_rol, // Rotate Left +NN_ror, // Rotate Right +NN_rep, // Repeat String Operation +NN_repe, // Repeat String Operation while ZF=1 +NN_repne, // Repeat String Operation while ZF=0 +NN_retn, // Return Near from Procedure +NN_retf, // Return Far from Procedure +NN_sahf, // Store AH into Flags Register +NN_sal, // Shift Arithmetic Left +NN_sar, // Shift Arithmetic Right +NN_shl, // Shift Logical Left +NN_shr, // Shift Logical Right +NN_sbb, // Integer Subtraction with Borrow +NN_scas, // Compare String +NN_seta, // Set Byte if Above (CF=0 & ZF=0) +NN_setae, // Set Byte if Above or Equal (CF=0) +NN_setb, // Set Byte if Below (CF=1) +NN_setbe, // Set Byte if Below or Equal (CF=1 | ZF=1) +NN_setc, // Set Byte if Carry (CF=1) +NN_sete, // Set Byte if Equal (ZF=1) +NN_setg, // Set Byte if Greater (ZF=0 & SF=OF) +NN_setge, // Set Byte if Greater or Equal (SF=OF) +NN_setl, // Set Byte if Less (SF!=OF) +NN_setle, // Set Byte if Less or Equal (ZF=1 | SF!=OF) +NN_setna, // Set Byte if Not Above (CF=1 | ZF=1) +NN_setnae, // Set Byte if Not Above or Equal (CF=1) +NN_setnb, // Set Byte if Not Below (CF=0) +NN_setnbe, // Set Byte if Not Below or Equal (CF=0 & ZF=0) +NN_setnc, // Set Byte if Not Carry (CF=0) +NN_setne, // Set Byte if Not Equal (ZF=0) +NN_setng, // Set Byte if Not Greater (ZF=1 | SF!=OF) +NN_setnge, // Set Byte if Not Greater or Equal (ZF=1) +NN_setnl, // Set Byte if Not Less (SF=OF) +NN_setnle, // Set Byte if Not Less or Equal (ZF=0 & SF=OF) +NN_setno, // Set Byte if Not Overflow (OF=0) +NN_setnp, // Set Byte if Not Parity (PF=0) +NN_setns, // Set Byte if Not Sign (SF=0) +NN_setnz, // Set Byte if Not Zero (ZF=0) +NN_seto, // Set Byte if Overflow (OF=1) +NN_setp, // Set Byte if Parity (PF=1) +NN_setpe, // Set Byte if Parity Even (PF=1) +NN_setpo, // Set Byte if Parity Odd (PF=0) +NN_sets, // Set Byte if Sign (SF=1) +NN_setz, // Set Byte if Zero (ZF=1) +NN_sgdt, // Store Global Descriptor Table Register +NN_sidt, // Store Interrupt Descriptor Table Register +NN_shld, // Double Precision Shift Left +NN_shrd, // Double Precision Shift Right +NN_sldt, // Store Local Descriptor Table Register +NN_smsw, // Store Machine Status Word +NN_stc, // Set Carry Flag +NN_std, // Set Direction Flag +NN_sti, // Set Interrupt Flag +NN_stos, // Store String +NN_str, // Store Task Register +NN_sub, // Integer Subtraction +NN_test, // Logical Compare +NN_verr, // Verify a Segment for Reading +NN_verw, // Verify a Segment for Writing +NN_wait, // Wait until BUSY# Pin is Inactive (HIGH) +NN_xchg, // Exchange Register/Memory with Register +NN_xlat, // Table Lookup Translation +NN_xor, // Logical Exclusive OR + +// +// 486 instructions +// + +NN_cmpxchg, // Compare and Exchange +NN_bswap, // Swap bits in EAX +NN_xadd, // t<-dest; dest<-src+dest; src<-t +NN_invd, // Invalidate Data Cache +NN_wbinvd, // Invalidate Data Cache (write changes) +NN_invlpg, // Invalidate TLB entry + +// +// Pentium instructions +// + +NN_rdmsr, // Read Machine Status Register +NN_wrmsr, // Write Machine Status Register +NN_cpuid, // Get CPU ID +NN_cmpxchg8b, // Compare and Exchange Eight Bytes +NN_rdtsc, // Read Time Stamp Counter +NN_rsm, // Resume from System Management Mode + +// +// Pentium Pro instructions +// + +NN_cmova, // Move if Above (CF=0 & ZF=0) +NN_cmovb, // Move if Below (CF=1) +NN_cmovbe, // Move if Below or Equal (CF=1 | ZF=1) +NN_cmovg, // Move if Greater (ZF=0 & SF=OF) +NN_cmovge, // Move if Greater or Equal (SF=OF) +NN_cmovl, // Move if Less (SF!=OF) +NN_cmovle, // Move if Less or Equal (ZF=1 | SF!=OF) +NN_cmovnb, // Move if Not Below (CF=0) +NN_cmovno, // Move if Not Overflow (OF=0) +NN_cmovnp, // Move if Not Parity (PF=0) +NN_cmovns, // Move if Not Sign (SF=0) +NN_cmovnz, // Move if Not Zero (ZF=0) +NN_cmovo, // Move if Overflow (OF=1) +NN_cmovp, // Move if Parity (PF=1) +NN_cmovs, // Move if Sign (SF=1) +NN_cmovz, // Move if Zero (ZF=1) +NN_fcmovb, // Floating Move if Below +NN_fcmove, // Floating Move if Equal +NN_fcmovbe, // Floating Move if Below or Equal +NN_fcmovu, // Floating Move if Unordered +NN_fcmovnb, // Floating Move if Not Below +NN_fcmovne, // Floating Move if Not Equal +NN_fcmovnbe, // Floating Move if Not Below or Equal +NN_fcmovnu, // Floating Move if Not Unordered +NN_fcomi, // FP Compare, result in EFLAGS +NN_fucomi, // FP Unordered Compare, result in EFLAGS +NN_fcomip, // FP Compare, result in EFLAGS, pop stack +NN_fucomip, // FP Unordered Compare, result in EFLAGS, pop stack +NN_rdpmc, // Read Performance Monitor Counter + +// +// FPP instructuions +// + +NN_fld, // Load Real +NN_fst, // Store Real +NN_fstp, // Store Real and Pop +NN_fxch, // Exchange Registers +NN_fild, // Load Integer +NN_fist, // Store Integer +NN_fistp, // Store Integer and Pop +NN_fbld, // Load BCD +NN_fbstp, // Store BCD and Pop +NN_fadd, // Add Real +NN_faddp, // Add Real and Pop +NN_fiadd, // Add Integer +NN_fsub, // Subtract Real +NN_fsubp, // Subtract Real and Pop +NN_fisub, // Subtract Integer +NN_fsubr, // Subtract Real Reversed +NN_fsubrp, // Subtract Real Reversed and Pop +NN_fisubr, // Subtract Integer Reversed +NN_fmul, // Multiply Real +NN_fmulp, // Multiply Real and Pop +NN_fimul, // Multiply Integer +NN_fdiv, // Divide Real +NN_fdivp, // Divide Real and Pop +NN_fidiv, // Divide Integer +NN_fdivr, // Divide Real Reversed +NN_fdivrp, // Divide Real Reversed and Pop +NN_fidivr, // Divide Integer Reversed +NN_fsqrt, // Square Root +NN_fscale, // Scale: st(0) <- st(0) * 2^st(1) +NN_fprem, // Partial Remainder +NN_frndint, // Round to Integer +NN_fxtract, // Extract exponent and significand +NN_fabs, // Absolute value +NN_fchs, // Change Sign +NN_fcom, // Compare Real +NN_fcomp, // Compare Real and Pop +NN_fcompp, // Compare Real and Pop Twice +NN_ficom, // Compare Integer +NN_ficomp, // Compare Integer and Pop +NN_ftst, // Test +NN_fxam, // Examine +NN_fptan, // Partial tangent +NN_fpatan, // Partial arctangent +NN_f2xm1, // 2^x - 1 +NN_fyl2x, // Y * lg2(X) +NN_fyl2xp1, // Y * lg2(X+1) +NN_fldz, // Load +0.0 +NN_fld1, // Load +1.0 +NN_fldpi, // Load PI=3.14... +NN_fldl2t, // Load lg2(10) +NN_fldl2e, // Load lg2(e) +NN_fldlg2, // Load lg10(2) +NN_fldln2, // Load ln(2) +NN_finit, // Initialize Processor +NN_fninit, // Initialize Processor (no wait) +NN_fsetpm, // Set Protected Mode +NN_fldcw, // Load Control Word +NN_fstcw, // Store Control Word +NN_fnstcw, // Store Control Word (no wait) +NN_fstsw, // Store Status Word +NN_fnstsw, // Store Status Word (no wait) +NN_fclex, // Clear Exceptions +NN_fnclex, // Clear Exceptions (no wait) +NN_fstenv, // Store Environment +NN_fnstenv, // Store Environment (no wait) +NN_fldenv, // Load Environment +NN_fsave, // Save State +NN_fnsave, // Save State (no wait) +NN_frstor, // Restore State +NN_fincstp, // Increment Stack Pointer +NN_fdecstp, // Decrement Stack Pointer +NN_ffree, // Free Register +NN_fnop, // No Operation +NN_feni, // (8087 only) +NN_fneni, // (no wait) (8087 only) +NN_fdisi, // (8087 only) +NN_fndisi, // (no wait) (8087 only) + +// +// 80387 instructions +// + +NN_fprem1, // Partial Remainder ( < half ) +NN_fsincos, // t<-cos(st); st<-sin(st); push t +NN_fsin, // Sine +NN_fcos, // Cosine +NN_fucom, // Compare Unordered Real +NN_fucomp, // Compare Unordered Real and Pop +NN_fucompp, // Compare Unordered Real and Pop Twice + +// +// Instructions added 28.02.96 +// + +NN_setalc, // Set AL to Carry Flag +NN_svdc, // Save Register and Descriptor +NN_rsdc, // Restore Register and Descriptor +NN_svldt, // Save LDTR and Descriptor +NN_rsldt, // Restore LDTR and Descriptor +NN_svts, // Save TR and Descriptor +NN_rsts, // Restore TR and Descriptor +NN_icebp, // ICE Break Point +NN_loadall, // Load the entire CPU state from ES:EDI + +// +// MMX instructions +// + +NN_emms, // Empty MMX state +NN_movd, // Move 32 bits +NN_movq, // Move 64 bits +NN_packsswb, // Pack with Signed Saturation (Word->Byte) +NN_packssdw, // Pack with Signed Saturation (Dword->Word) +NN_packuswb, // Pack with Unsigned Saturation (Word->Byte) +NN_paddb, // Packed Add Byte +NN_paddw, // Packed Add Word +NN_paddd, // Packed Add Dword +NN_paddsb, // Packed Add with Saturation (Byte) +NN_paddsw, // Packed Add with Saturation (Word) +NN_paddusb, // Packed Add Unsigned with Saturation (Byte) +NN_paddusw, // Packed Add Unsigned with Saturation (Word) +NN_pand, // Bitwise Logical And +NN_pandn, // Bitwise Logical And Not +NN_pcmpeqb, // Packed Compare for Equal (Byte) +NN_pcmpeqw, // Packed Compare for Equal (Word) +NN_pcmpeqd, // Packed Compare for Equal (Dword) +NN_pcmpgtb, // Packed Compare for Greater Than (Byte) +NN_pcmpgtw, // Packed Compare for Greater Than (Word) +NN_pcmpgtd, // Packed Compare for Greater Than (Dword) +NN_pmaddwd, // Packed Multiply and Add +NN_pmulhw, // Packed Multiply High +NN_pmullw, // Packed Multiply Low +NN_por, // Bitwise Logical Or +NN_psllw, // Packed Shift Left Logical (Word) +NN_pslld, // Packed Shift Left Logical (Dword) +NN_psllq, // Packed Shift Left Logical (Qword) +NN_psraw, // Packed Shift Right Arithmetic (Word) +NN_psrad, // Packed Shift Right Arithmetic (Dword) +NN_psrlw, // Packed Shift Right Logical (Word) +NN_psrld, // Packed Shift Right Logical (Dword) +NN_psrlq, // Packed Shift Right Logical (Qword) +NN_psubb, // Packed Subtract Byte +NN_psubw, // Packed Subtract Word +NN_psubd, // Packed Subtract Dword +NN_psubsb, // Packed Subtract with Saturation (Byte) +NN_psubsw, // Packed Subtract with Saturation (Word) +NN_psubusb, // Packed Subtract Unsigned with Saturation (Byte) +NN_psubusw, // Packed Subtract Unsigned with Saturation (Word) +NN_punpckhbw, // Unpack High Packed Data (Byte->Word) +NN_punpckhwd, // Unpack High Packed Data (Word->Dword) +NN_punpckhdq, // Unpack High Packed Data (Dword->Qword) +NN_punpcklbw, // Unpack Low Packed Data (Byte->Word) +NN_punpcklwd, // Unpack Low Packed Data (Word->Dword) +NN_punpckldq, // Unpack Low Packed Data (Dword->Qword) +NN_pxor, // Bitwise Logical Exclusive Or + +// +// Undocumented Deschutes processor instructions +// + +NN_fxsave, // Fast save FP context +NN_fxrstor, // Fast restore FP context + +// Pentium II instructions + +NN_sysenter, // Fast Transition to System Call Entry Point +NN_sysexit, // Fast Transition from System Call Entry Point + +// 3DNow! instructions + +NN_pavgusb, // Packed 8-bit Unsigned Integer Averaging +NN_pfadd, // Packed Floating-Point Addition +NN_pfsub, // Packed Floating-Point Subtraction +NN_pfsubr, // Packed Floating-Point Reverse Subtraction +NN_pfacc, // Packed Floating-Point Accumulate +NN_pfcmpge, // Packed Floating-Point Comparison, Greater or Equal +NN_pfcmpgt, // Packed Floating-Point Comparison, Greater +NN_pfcmpeq, // Packed Floating-Point Comparison, Equal +NN_pfmin, // Packed Floating-Point Minimum +NN_pfmax, // Packed Floating-Point Maximum +NN_pi2fd, // Packed 32-bit Integer to Floating-Point +NN_pf2id, // Packed Floating-Point to 32-bit Integer +NN_pfrcp, // Packed Floating-Point Reciprocal Approximation +NN_pfrsqrt, // Packed Floating-Point Reciprocal Square Root Approximation +NN_pfmul, // Packed Floating-Point Multiplication +NN_pfrcpit1, // Packed Floating-Point Reciprocal First Iteration Step +NN_pfrsqit1, // Packed Floating-Point Reciprocal Square Root First Iteration Step +NN_pfrcpit2, // Packed Floating-Point Reciprocal Second Iteration Step +NN_pmulhrw, // Packed Floating-Point 16-bit Integer Multiply with rounding +NN_femms, // Faster entry/exit of the MMX or floating-point state +NN_prefetch, // Prefetch at least a 32-byte line into L1 data cache +NN_prefetchw, // Prefetch processor cache line into L1 data cache (mark as modified) + + +// Pentium III instructions + +NN_addps, // Packed Single-FP Add +NN_addss, // Scalar Single-FP Add +NN_andnps, // Bitwise Logical And Not for Single-FP +NN_andps, // Bitwise Logical And for Single-FP +NN_cmpps, // Packed Single-FP Compare +NN_cmpss, // Scalar Single-FP Compare +NN_comiss, // Scalar Ordered Single-FP Compare and Set EFLAGS +NN_cvtpi2ps, // Packed signed INT32 to Packed Single-FP conversion +NN_cvtps2pi, // Packed Single-FP to Packed INT32 conversion +NN_cvtsi2ss, // Scalar signed INT32 to Single-FP conversion +NN_cvtss2si, // Scalar Single-FP to signed INT32 conversion +NN_cvttps2pi, // Packed Single-FP to Packed INT32 conversion (truncate) +NN_cvttss2si, // Scalar Single-FP to signed INT32 conversion (truncate) +NN_divps, // Packed Single-FP Divide +NN_divss, // Scalar Single-FP Divide +NN_ldmxcsr, // Load Streaming SIMD Extensions Technology Control/Status Register +NN_maxps, // Packed Single-FP Maximum +NN_maxss, // Scalar Single-FP Maximum +NN_minps, // Packed Single-FP Minimum +NN_minss, // Scalar Single-FP Minimum +NN_movaps, // Move Aligned Four Packed Single-FP +NN_movhlps, // Move High to Low Packed Single-FP +NN_movhps, // Move High Packed Single-FP +NN_movlhps, // Move Low to High Packed Single-FP +NN_movlps, // Move Low Packed Single-FP +NN_movmskps, // Move Mask to Register +NN_movss, // Move Scalar Single-FP +NN_movups, // Move Unaligned Four Packed Single-FP +NN_mulps, // Packed Single-FP Multiply +NN_mulss, // Scalar Single-FP Multiply +NN_orps, // Bitwise Logical OR for Single-FP Data +NN_rcpps, // Packed Single-FP Reciprocal +NN_rcpss, // Scalar Single-FP Reciprocal +NN_rsqrtps, // Packed Single-FP Square Root Reciprocal +NN_rsqrtss, // Scalar Single-FP Square Root Reciprocal +NN_shufps, // Shuffle Single-FP +NN_sqrtps, // Packed Single-FP Square Root +NN_sqrtss, // Scalar Single-FP Square Root +NN_stmxcsr, // Store Streaming SIMD Extensions Technology Control/Status Register +NN_subps, // Packed Single-FP Subtract +NN_subss, // Scalar Single-FP Subtract +NN_ucomiss, // Scalar Unordered Single-FP Compare and Set EFLAGS +NN_unpckhps, // Unpack High Packed Single-FP Data +NN_unpcklps, // Unpack Low Packed Single-FP Data +NN_xorps, // Bitwise Logical XOR for Single-FP Data +NN_pavgb, // Packed Average (Byte) +NN_pavgw, // Packed Average (Word) +NN_pextrw, // Extract Word +NN_pinsrw, // Insert Word +NN_pmaxsw, // Packed Signed Integer Word Maximum +NN_pmaxub, // Packed Unsigned Integer Byte Maximum +NN_pminsw, // Packed Signed Integer Word Minimum +NN_pminub, // Packed Unsigned Integer Byte Minimum +NN_pmovmskb, // Move Byte Mask to Integer +NN_pmulhuw, // Packed Multiply High Unsigned +NN_psadbw, // Packed Sum of Absolute Differences +NN_pshufw, // Packed Shuffle Word +NN_maskmovq, // Byte Mask write +NN_movntps, // Move Aligned Four Packed Single-FP Non Temporal +NN_movntq, // Move 64 Bits Non Temporal +NN_prefetcht0, // Prefetch to all cache levels +NN_prefetcht1, // Prefetch to all cache levels +NN_prefetcht2, // Prefetch to L2 cache +NN_prefetchnta, // Prefetch to L1 cache +NN_sfence, // Store Fence + +// Pentium III Pseudo instructions + +NN_cmpeqps, // Packed Single-FP Compare EQ +NN_cmpltps, // Packed Single-FP Compare LT +NN_cmpleps, // Packed Single-FP Compare LE +NN_cmpunordps, // Packed Single-FP Compare UNORD +NN_cmpneqps, // Packed Single-FP Compare NOT EQ +NN_cmpnltps, // Packed Single-FP Compare NOT LT +NN_cmpnleps, // Packed Single-FP Compare NOT LE +NN_cmpordps, // Packed Single-FP Compare ORDERED +NN_cmpeqss, // Scalar Single-FP Compare EQ +NN_cmpltss, // Scalar Single-FP Compare LT +NN_cmpless, // Scalar Single-FP Compare LE +NN_cmpunordss, // Scalar Single-FP Compare UNORD +NN_cmpneqss, // Scalar Single-FP Compare NOT EQ +NN_cmpnltss, // Scalar Single-FP Compare NOT LT +NN_cmpnless, // Scalar Single-FP Compare NOT LE +NN_cmpordss, // Scalar Single-FP Compare ORDERED + +// AMD K7 instructions + +NN_pf2iw, // Packed Floating-Point to Integer with Sign Extend +NN_pfnacc, // Packed Floating-Point Negative Accumulate +NN_pfpnacc, // Packed Floating-Point Mixed Positive-Negative Accumulate +NN_pi2fw, // Packed 16-bit Integer to Floating-Point +NN_pswapd, // Packed Swap Double Word + +// Undocumented FP instructions (thanks to norbert.juffa@amd.com) + +NN_fstp1, // Alias of Store Real and Pop +NN_fcom2, // Alias of Compare Real +NN_fcomp3, // Alias of Compare Real and Pop +NN_fxch4, // Alias of Exchange Registers +NN_fcomp5, // Alias of Compare Real and Pop +NN_ffreep, // Free Register and Pop +NN_fxch7, // Alias of Exchange Registers +NN_fstp8, // Alias of Store Real and Pop +NN_fstp9, // Alias of Store Real and Pop + +// Pentium 4 instructions + +NN_addpd, // Add Packed Double-Precision Floating-Point Values +NN_addsd, // Add Scalar Double-Precision Floating-Point Values +NN_andnpd, // Bitwise Logical AND NOT of Packed Double-Precision Floating-Point Values +NN_andpd, // Bitwise Logical AND of Packed Double-Precision Floating-Point Values +NN_clflush, // Flush Cache Line +NN_cmppd, // Compare Packed Double-Precision Floating-Point Values +NN_cmpsd, // Compare Scalar Double-Precision Floating-Point Values +NN_comisd, // Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS +NN_cvtdq2pd, // Convert Packed Doubleword Integers to Packed Single-Precision Floating-Point Values +NN_cvtdq2ps, // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values +NN_cvtpd2dq, // Convert Packed Double-Precision Floating-Point Values to Packed Doubleword Integers +NN_cvtpd2pi, // Convert Packed Double-Precision Floating-Point Values to Packed Doubleword Integers +NN_cvtpd2ps, // Convert Packed Double-Precision Floating-Point Values to Packed Single-Precision Floating-Point Values +NN_cvtpi2pd, // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values +NN_cvtps2dq, // Convert Packed Single-Precision Floating-Point Values to Packed Doubleword Integers +NN_cvtps2pd, // Convert Packed Single-Precision Floating-Point Values to Packed Double-Precision Floating-Point Values +NN_cvtsd2si, // Convert Scalar Double-Precision Floating-Point Value to Doubleword Integer +NN_cvtsd2ss, // Convert Scalar Double-Precision Floating-Point Value to Scalar Single-Precision Floating-Point Value +NN_cvtsi2sd, // Convert Doubleword Integer to Scalar Double-Precision Floating-Point Value +NN_cvtss2sd, // Convert Scalar Single-Precision Floating-Point Value to Scalar Double-Precision Floating-Point Value +NN_cvttpd2dq, // Convert With Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers +NN_cvttpd2pi, // Convert with Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers +NN_cvttps2dq, // Convert With Truncation Packed Single-Precision Floating-Point Values to Packed Doubleword Integers +NN_cvttsd2si, // Convert with Truncation Scalar Double-Precision Floating-Point Value to Doubleword Integer +NN_divpd, // Divide Packed Double-Precision Floating-Point Values +NN_divsd, // Divide Scalar Double-Precision Floating-Point Values +NN_lfence, // Load Fence +NN_maskmovdqu, // Store Selected Bytes of Double Quadword +NN_maxpd, // Return Maximum Packed Double-Precision Floating-Point Values +NN_maxsd, // Return Maximum Scalar Double-Precision Floating-Point Value +NN_mfence, // Memory Fence +NN_minpd, // Return Minimum Packed Double-Precision Floating-Point Values +NN_minsd, // Return Minimum Scalar Double-Precision Floating-Point Value +NN_movapd, // Move Aligned Packed Double-Precision Floating-Point Values +NN_movdq2q, // Move Quadword from XMM to MMX Register +NN_movdqa, // Move Aligned Double Quadword +NN_movdqu, // Move Unaligned Double Quadword +NN_movhpd, // Move High Packed Double-Precision Floating-Point Values +NN_movlpd, // Move Low Packed Double-Precision Floating-Point Values +NN_movmskpd, // Extract Packed Double-Precision Floating-Point Sign Mask +NN_movntdq, // Store Double Quadword Using Non-Temporal Hint +NN_movnti, // Store Doubleword Using Non-Temporal Hint +NN_movntpd, // Store Packed Double-Precision Floating-Point Values Using Non-Temporal Hint +NN_movq2dq, // Move Quadword from MMX to XMM Register +NN_movsd, // Move Scalar Double-Precision Floating-Point Values +NN_movupd, // Move Unaligned Packed Double-Precision Floating-Point Values +NN_mulpd, // Multiply Packed Double-Precision Floating-Point Values +NN_mulsd, // Multiply Scalar Double-Precision Floating-Point Values +NN_orpd, // Bitwise Logical OR of Double-Precision Floating-Point Values +NN_paddq, // Add Packed Quadword Integers +NN_pause, // Spin Loop Hint +NN_pmuludq, // Multiply Packed Unsigned Doubleword Integers +NN_pshufd, // Shuffle Packed Doublewords +NN_pshufhw, // Shuffle Packed High Words +NN_pshuflw, // Shuffle Packed Low Words +NN_pslldq, // Shift Double Quadword Left Logical +NN_psrldq, // Shift Double Quadword Right Logical +NN_psubq, // Subtract Packed Quadword Integers +NN_punpckhqdq, // Unpack High Data +NN_punpcklqdq, // Unpack Low Data +NN_shufpd, // Shuffle Packed Double-Precision Floating-Point Values +NN_sqrtpd, // Compute Square Roots of Packed Double-Precision Floating-Point Values +NN_sqrtsd, // Compute Square Rootof Scalar Double-Precision Floating-Point Value +NN_subpd, // Subtract Packed Double-Precision Floating-Point Values +NN_subsd, // Subtract Scalar Double-Precision Floating-Point Values +NN_ucomisd, // Unordered Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS +NN_unpckhpd, // Unpack and Interleave High Packed Double-Precision Floating-Point Values +NN_unpcklpd, // Unpack and Interleave Low Packed Double-Precision Floating-Point Values +NN_xorpd, // Bitwise Logical OR of Double-Precision Floating-Point Values + +// AMD syscall/sysret instructions + +NN_syscall, // Low latency system call +NN_sysret, // Return from system call + +// AMD64 instructions + +NN_swapgs, // Exchange GS base with KernelGSBase MSR + +// New Pentium instructions (SSE3) + +NN_movddup, // Move One Double-FP and Duplicate +NN_movshdup, // Move Packed Single-FP High and Duplicate +NN_movsldup, // Move Packed Single-FP Low and Duplicate + +// Missing AMD64 instructions + +NN_movsxd, // Move with Sign-Extend Doubleword +NN_cmpxchg16b, // Compare and Exchange 16 Bytes + +// SSE3 instructions + +NN_addsubpd, // Add /Sub packed DP FP numbers +NN_addsubps, // Add /Sub packed SP FP numbers +NN_haddpd, // Add horizontally packed DP FP numbers +NN_haddps, // Add horizontally packed SP FP numbers +NN_hsubpd, // Sub horizontally packed DP FP numbers +NN_hsubps, // Sub horizontally packed SP FP numbers +NN_monitor, // Set up a linear address range to be monitored by hardware +NN_mwait, // Wait until write-back store performed within the range specified by the MONITOR instruction +NN_fisttp, // Store ST in intXX (chop) and pop +NN_lddqu, // Load unaligned integer 128-bit + +// SSSE3 instructions + +NN_psignb, // Packed SIGN Byte +NN_psignw, // Packed SIGN Word +NN_psignd, // Packed SIGN Doubleword +NN_pshufb, // Packed Shuffle Bytes +NN_pmulhrsw, // Packed Multiply High with Round and Scale +NN_pmaddubsw, // Multiply and Add Packed Signed and Unsigned Bytes +NN_phsubsw, // Packed Horizontal Subtract and Saturate +NN_phaddsw, // Packed Horizontal Add and Saturate +NN_phaddw, // Packed Horizontal Add Word +NN_phaddd, // Packed Horizontal Add Doubleword +NN_phsubw, // Packed Horizontal Subtract Word +NN_phsubd, // Packed Horizontal Subtract Doubleword +NN_palignr, // Packed Align Right +NN_pabsb, // Packed Absolute Value Byte +NN_pabsw, // Packed Absolute Value Word +NN_pabsd, // Packed Absolute Value Doubleword + +// VMX instructions + +NN_vmcall, // Call to VM Monitor +NN_vmclear, // Clear Virtual Machine Control Structure +NN_vmlaunch, // Launch Virtual Machine +NN_vmresume, // Resume Virtual Machine +NN_vmptrld, // Load Pointer to Virtual Machine Control Structure +NN_vmptrst, // Store Pointer to Virtual Machine Control Structure +NN_vmread, // Read Field from Virtual Machine Control Structure +NN_vmwrite, // Write Field from Virtual Machine Control Structure +NN_vmxoff, // Leave VMX Operation +NN_vmxon, // Enter VMX Operation + +// Undefined Instruction + +NN_ud2, // Undefined Instruction + +// Added with x86-64 + +NN_rdtscp, // Read Time-Stamp Counter and Processor ID + +// Geode LX 3DNow! extensions + +NN_pfrcpv, // Reciprocal Approximation for a Pair of 32-bit Floats +NN_pfrsqrtv, // Reciprocal Square Root Approximation for a Pair of 32-bit Floats + +// SSE2 pseudoinstructions + +NN_cmpeqpd, // Packed Double-FP Compare EQ +NN_cmpltpd, // Packed Double-FP Compare LT +NN_cmplepd, // Packed Double-FP Compare LE +NN_cmpunordpd, // Packed Double-FP Compare UNORD +NN_cmpneqpd, // Packed Double-FP Compare NOT EQ +NN_cmpnltpd, // Packed Double-FP Compare NOT LT +NN_cmpnlepd, // Packed Double-FP Compare NOT LE +NN_cmpordpd, // Packed Double-FP Compare ORDERED +NN_cmpeqsd, // Scalar Double-FP Compare EQ +NN_cmpltsd, // Scalar Double-FP Compare LT +NN_cmplesd, // Scalar Double-FP Compare LE +NN_cmpunordsd, // Scalar Double-FP Compare UNORD +NN_cmpneqsd, // Scalar Double-FP Compare NOT EQ +NN_cmpnltsd, // Scalar Double-FP Compare NOT LT +NN_cmpnlesd, // Scalar Double-FP Compare NOT LE +NN_cmpordsd, // Scalar Double-FP Compare ORDERED + +// SSSE4.1 instructions + +NN_blendpd, // Blend Packed Double Precision Floating-Point Values +NN_blendps, // Blend Packed Single Precision Floating-Point Values +NN_blendvpd, // Variable Blend Packed Double Precision Floating-Point Values +NN_blendvps, // Variable Blend Packed Single Precision Floating-Point Values +NN_dppd, // Dot Product of Packed Double Precision Floating-Point Values +NN_dpps, // Dot Product of Packed Single Precision Floating-Point Values +NN_extractps, // Extract Packed Single Precision Floating-Point Value +NN_insertps, // Insert Packed Single Precision Floating-Point Value +NN_movntdqa, // Load Double Quadword Non-Temporal Aligned Hint +NN_mpsadbw, // Compute Multiple Packed Sums of Absolute Difference +NN_packusdw, // Pack with Unsigned Saturation +NN_pblendvb, // Variable Blend Packed Bytes +NN_pblendw, // Blend Packed Words +NN_pcmpeqq, // Compare Packed Qword Data for Equal +NN_pextrb, // Extract Byte +NN_pextrd, // Extract Dword +NN_pextrq, // Extract Qword +NN_phminposuw, // Packed Horizontal Word Minimum +NN_pinsrb, // Insert Byte +NN_pinsrd, // Insert Dword +NN_pinsrq, // Insert Qword +NN_pmaxsb, // Maximum of Packed Signed Byte Integers +NN_pmaxsd, // Maximum of Packed Signed Dword Integers +NN_pmaxud, // Maximum of Packed Unsigned Dword Integers +NN_pmaxuw, // Maximum of Packed Word Integers +NN_pminsb, // Minimum of Packed Signed Byte Integers +NN_pminsd, // Minimum of Packed Signed Dword Integers +NN_pminud, // Minimum of Packed Unsigned Dword Integers +NN_pminuw, // Minimum of Packed Word Integers +NN_pmovsxbw, // Packed Move with Sign Extend +NN_pmovsxbd, // Packed Move with Sign Extend +NN_pmovsxbq, // Packed Move with Sign Extend +NN_pmovsxwd, // Packed Move with Sign Extend +NN_pmovsxwq, // Packed Move with Sign Extend +NN_pmovsxdq, // Packed Move with Sign Extend +NN_pmovzxbw, // Packed Move with Zero Extend +NN_pmovzxbd, // Packed Move with Zero Extend +NN_pmovzxbq, // Packed Move with Zero Extend +NN_pmovzxwd, // Packed Move with Zero Extend +NN_pmovzxwq, // Packed Move with Zero Extend +NN_pmovzxdq, // Packed Move with Zero Extend +NN_pmuldq, // Multiply Packed Signed Dword Integers +NN_pmulld, // Multiply Packed Signed Dword Integers and Store Low Result +NN_ptest, // Logical Compare +NN_roundpd, // Round Packed Double Precision Floating-Point Values +NN_roundps, // Round Packed Single Precision Floating-Point Values +NN_roundsd, // Round Scalar Double Precision Floating-Point Values +NN_roundss, // Round Scalar Single Precision Floating-Point Values + +// SSSE4.2 instructions + +NN_crc32, // Accumulate CRC32 Value +NN_pcmpestri, // Packed Compare Explicit Length Strings, Return Index +NN_pcmpestrm, // Packed Compare Explicit Length Strings, Return Mask +NN_pcmpistri, // Packed Compare Implicit Length Strings, Return Index +NN_pcmpistrm, // Packed Compare Implicit Length Strings, Return Mask +NN_pcmpgtq, // Compare Packed Data for Greater Than +NN_popcnt, // Return the Count of Number of Bits Set to 1 + +// AMD SSE4a instructions + +NN_extrq, // Extract Field From Register +NN_insertq, // Insert Field +NN_movntsd, // Move Non-Temporal Scalar Double-Precision Floating-Point +NN_movntss, // Move Non-Temporal Scalar Single-Precision Floating-Point +NN_lzcnt, // Leading Zero Count + +// xsave/xrstor instructions + +NN_xgetbv, // Get Value of Extended Control Register +NN_xrstor, // Restore Processor Extended States +NN_xsave, // Save Processor Extended States +NN_xsetbv, // Set Value of Extended Control Register + +// Intel Safer Mode Extensions (SMX) + +NN_getsec, // Safer Mode Extensions (SMX) Instruction + +// AMD-V Virtualization ISA Extension + +NN_clgi, // Clear Global Interrupt Flag +NN_invlpga, // Invalidate TLB Entry in a Specified ASID +NN_skinit, // Secure Init and Jump with Attestation +NN_stgi, // Set Global Interrupt Flag +NN_vmexit, // Stop Executing Guest, Begin Executing Host +NN_vmload, // Load State from VMCB +NN_vmmcall, // Call VMM +NN_vmrun, // Run Virtual Machine +NN_vmsave, // Save State to VMCB + +// VMX+ instructions + +NN_invept, // Invalidate Translations Derived from EPT +NN_invvpid, // Invalidate Translations Based on VPID + +// Intel Atom instructions + +NN_movbe, // Move Data After Swapping Bytes + +// Intel AES instructions + +NN_aesenc, // Perform One Round of an AES Encryption Flow +NN_aesenclast, // Perform the Last Round of an AES Encryption Flow +NN_aesdec, // Perform One Round of an AES Decryption Flow +NN_aesdeclast, // Perform the Last Round of an AES Decryption Flow +NN_aesimc, // Perform the AES InvMixColumn Transformation +NN_aeskeygenassist, // AES Round Key Generation Assist + +// Carryless multiplication + +NN_pclmulqdq, // Carry-Less Multiplication Quadword + +// Returns modifies by operand size prefixes + +NN_retnw, // Return Near from Procedure (use16) +NN_retnd, // Return Near from Procedure (use32) +NN_retnq, // Return Near from Procedure (use64) +NN_retfw, // Return Far from Procedure (use16) +NN_retfd, // Return Far from Procedure (use32) +NN_retfq, // Return Far from Procedure (use64) + +// RDRAND support + +NN_rdrand, // Read Random Number + +// new GPR instructions + +NN_adcx, // Unsigned Integer Addition of Two Operands with Carry Flag +NN_adox, // Unsigned Integer Addition of Two Operands with Overflow Flag +NN_andn, // Logical AND NOT +NN_bextr, // Bit Field Extract +NN_blsi, // Extract Lowest Set Isolated Bit +NN_blsmsk, // Get Mask Up to Lowest Set Bit +NN_blsr, // Reset Lowest Set Bit +NN_bzhi, // Zero High Bits Starting with Specified Bit Position +NN_clac, // Clear AC Flag in EFLAGS Register +NN_mulx, // Unsigned Multiply Without Affecting Flags +NN_pdep, // Parallel Bits Deposit +NN_pext, // Parallel Bits Extract +NN_rorx, // Rotate Right Logical Without Affecting Flags +NN_sarx, // Shift Arithmetically Right Without Affecting Flags +NN_shlx, // Shift Logically Left Without Affecting Flags +NN_shrx, // Shift Logically Right Without Affecting Flags +NN_stac, // Set AC Flag in EFLAGS Register +NN_tzcnt, // Count the Number of Trailing Zero Bits +NN_xsaveopt, // Save Processor Extended States Optimized +NN_invpcid, // Invalidate Processor Context ID +NN_rdseed, // Read Random Seed +NN_rdfsbase, // Read FS Segment Base +NN_rdgsbase, // Read GS Segment Base +NN_wrfsbase, // Write FS Segment Base +NN_wrgsbase, // Write GS Segment Base + +// new AVX instructions + +NN_vaddpd, // Add Packed Double-Precision Floating-Point Values +NN_vaddps, // Packed Single-FP Add +NN_vaddsd, // Add Scalar Double-Precision Floating-Point Values +NN_vaddss, // Scalar Single-FP Add +NN_vaddsubpd, // Add /Sub packed DP FP numbers +NN_vaddsubps, // Add /Sub packed SP FP numbers +NN_vaesdec, // Perform One Round of an AES Decryption Flow +NN_vaesdeclast, // Perform the Last Round of an AES Decryption Flow +NN_vaesenc, // Perform One Round of an AES Encryption Flow +NN_vaesenclast, // Perform the Last Round of an AES Encryption Flow +NN_vaesimc, // Perform the AES InvMixColumn Transformation +NN_vaeskeygenassist, // AES Round Key Generation Assist +NN_vandnpd, // Bitwise Logical AND NOT of Packed Double-Precision Floating-Point Values +NN_vandnps, // Bitwise Logical And Not for Single-FP +NN_vandpd, // Bitwise Logical AND of Packed Double-Precision Floating-Point Values +NN_vandps, // Bitwise Logical And for Single-FP +NN_vblendpd, // Blend Packed Double Precision Floating-Point Values +NN_vblendps, // Blend Packed Single Precision Floating-Point Values +NN_vblendvpd, // Variable Blend Packed Double Precision Floating-Point Values +NN_vblendvps, // Variable Blend Packed Single Precision Floating-Point Values +NN_vbroadcastf128, // Broadcast 128 Bits of Floating-Point Data +NN_vbroadcasti128, // Broadcast 128 Bits of Integer Data +NN_vbroadcastsd, // Broadcast Double-Precision Floating-Point Element +NN_vbroadcastss, // Broadcast Single-Precision Floating-Point Element +NN_vcmppd, // Compare Packed Double-Precision Floating-Point Values +NN_vcmpps, // Packed Single-FP Compare +NN_vcmpsd, // Compare Scalar Double-Precision Floating-Point Values +NN_vcmpss, // Scalar Single-FP Compare +NN_vcomisd, // Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS +NN_vcomiss, // Scalar Ordered Single-FP Compare and Set EFLAGS +NN_vcvtdq2pd, // Convert Packed Doubleword Integers to Packed Single-Precision Floating-Point Values +NN_vcvtdq2ps, // Convert Packed Doubleword Integers to Packed Double-Precision Floating-Point Values +NN_vcvtpd2dq, // Convert Packed Double-Precision Floating-Point Values to Packed Doubleword Integers +NN_vcvtpd2ps, // Convert Packed Double-Precision Floating-Point Values to Packed Single-Precision Floating-Point Values +NN_vcvtph2ps, // Convert 16-bit FP Values to Single-Precision FP Values +NN_vcvtps2dq, // Convert Packed Single-Precision Floating-Point Values to Packed Doubleword Integers +NN_vcvtps2pd, // Convert Packed Single-Precision Floating-Point Values to Packed Double-Precision Floating-Point Values +NN_vcvtps2ph, // Convert Single-Precision FP value to 16-bit FP value +NN_vcvtsd2si, // Convert Scalar Double-Precision Floating-Point Value to Doubleword Integer +NN_vcvtsd2ss, // Convert Scalar Double-Precision Floating-Point Value to Scalar Single-Precision Floating-Point Value +NN_vcvtsi2sd, // Convert Doubleword Integer to Scalar Double-Precision Floating-Point Value +NN_vcvtsi2ss, // Scalar signed INT32 to Single-FP conversion +NN_vcvtss2sd, // Convert Scalar Single-Precision Floating-Point Value to Scalar Double-Precision Floating-Point Value +NN_vcvtss2si, // Scalar Single-FP to signed INT32 conversion +NN_vcvttpd2dq, // Convert With Truncation Packed Double-Precision Floating-Point Values to Packed Doubleword Integers +NN_vcvttps2dq, // Convert With Truncation Packed Single-Precision Floating-Point Values to Packed Doubleword Integers +NN_vcvttsd2si, // Convert with Truncation Scalar Double-Precision Floating-Point Value to Doubleword Integer +NN_vcvttss2si, // Scalar Single-FP to signed INT32 conversion (truncate) +NN_vdivpd, // Divide Packed Double-Precision Floating-Point Values +NN_vdivps, // Packed Single-FP Divide +NN_vdivsd, // Divide Scalar Double-Precision Floating-Point Values +NN_vdivss, // Scalar Single-FP Divide +NN_vdppd, // Dot Product of Packed Double Precision Floating-Point Values +NN_vdpps, // Dot Product of Packed Single Precision Floating-Point Values +NN_vextractf128, // Extract Packed Floating-Point Values +NN_vextracti128, // Extract Packed Integer Values +NN_vextractps, // Extract Packed Floating-Point Values +NN_vfmadd132pd, // Fused Multiply-Add of Packed Double-Precision Floating-Point Values +NN_vfmadd132ps, // Fused Multiply-Add of Packed Single-Precision Floating-Point Values +NN_vfmadd132sd, // Fused Multiply-Add of Scalar Double-Precision Floating-Point Values +NN_vfmadd132ss, // Fused Multiply-Add of Scalar Single-Precision Floating-Point Values +NN_vfmadd213pd, // Fused Multiply-Add of Packed Double-Precision Floating-Point Values +NN_vfmadd213ps, // Fused Multiply-Add of Packed Single-Precision Floating-Point Values +NN_vfmadd213sd, // Fused Multiply-Add of Scalar Double-Precision Floating-Point Values +NN_vfmadd213ss, // Fused Multiply-Add of Scalar Single-Precision Floating-Point Values +NN_vfmadd231pd, // Fused Multiply-Add of Packed Double-Precision Floating-Point Values +NN_vfmadd231ps, // Fused Multiply-Add of Packed Single-Precision Floating-Point Values +NN_vfmadd231sd, // Fused Multiply-Add of Scalar Double-Precision Floating-Point Values +NN_vfmadd231ss, // Fused Multiply-Add of Scalar Single-Precision Floating-Point Values +NN_vfmaddsub132pd, // Fused Multiply-Alternating Add/Subtract of Packed Double-Precision Floating-Point Values +NN_vfmaddsub132ps, // Fused Multiply-Alternating Add/Subtract of Packed Single-Precision Floating-Point Values +NN_vfmaddsub213pd, // Fused Multiply-Alternating Add/Subtract of Packed Double-Precision Floating-Point Values +NN_vfmaddsub213ps, // Fused Multiply-Alternating Add/Subtract of Packed Single-Precision Floating-Point Values +NN_vfmaddsub231pd, // Fused Multiply-Alternating Add/Subtract of Packed Double-Precision Floating-Point Values +NN_vfmaddsub231ps, // Fused Multiply-Alternating Add/Subtract of Packed Single-Precision Floating-Point Values +NN_vfmsub132pd, // Fused Multiply-Subtract of Packed Double-Precision Floating-Point Values +NN_vfmsub132ps, // Fused Multiply-Subtract of Packed Single-Precision Floating-Point Values +NN_vfmsub132sd, // Fused Multiply-Subtract of Scalar Double-Precision Floating-Point Values +NN_vfmsub132ss, // Fused Multiply-Subtract of Scalar Single-Precision Floating-Point Values +NN_vfmsub213pd, // Fused Multiply-Subtract of Packed Double-Precision Floating-Point Values +NN_vfmsub213ps, // Fused Multiply-Subtract of Packed Single-Precision Floating-Point Values +NN_vfmsub213sd, // Fused Multiply-Subtract of Scalar Double-Precision Floating-Point Values +NN_vfmsub213ss, // Fused Multiply-Subtract of Scalar Single-Precision Floating-Point Values +NN_vfmsub231pd, // Fused Multiply-Subtract of Packed Double-Precision Floating-Point Values +NN_vfmsub231ps, // Fused Multiply-Subtract of Packed Single-Precision Floating-Point Values +NN_vfmsub231sd, // Fused Multiply-Subtract of Scalar Double-Precision Floating-Point Values +NN_vfmsub231ss, // Fused Multiply-Subtract of Scalar Single-Precision Floating-Point Values +NN_vfmsubadd132pd, // Fused Multiply-Alternating Subtract/Add of Packed Double-Precision Floating-Point Values +NN_vfmsubadd132ps, // Fused Multiply-Alternating Subtract/Add of Packed Single-Precision Floating-Point Values +NN_vfmsubadd213pd, // Fused Multiply-Alternating Subtract/Add of Packed Double-Precision Floating-Point Values +NN_vfmsubadd213ps, // Fused Multiply-Alternating Subtract/Add of Packed Single-Precision Floating-Point Values +NN_vfmsubadd231pd, // Fused Multiply-Alternating Subtract/Add of Packed Double-Precision Floating-Point Values +NN_vfmsubadd231ps, // Fused Multiply-Alternating Subtract/Add of Packed Single-Precision Floating-Point Values +NN_vfnmadd132pd, // Fused Negative Multiply-Add of Packed Double-Precision Floating-Point Values +NN_vfnmadd132ps, // Fused Negative Multiply-Add of Packed Single-Precision Floating-Point Values +NN_vfnmadd132sd, // Fused Negative Multiply-Add of Scalar Double-Precision Floating-Point Values +NN_vfnmadd132ss, // Fused Negative Multiply-Add of Scalar Single-Precision Floating-Point Values +NN_vfnmadd213pd, // Fused Negative Multiply-Add of Packed Double-Precision Floating-Point Values +NN_vfnmadd213ps, // Fused Negative Multiply-Add of Packed Single-Precision Floating-Point Values +NN_vfnmadd213sd, // Fused Negative Multiply-Add of Scalar Double-Precision Floating-Point Values +NN_vfnmadd213ss, // Fused Negative Multiply-Add of Scalar Single-Precision Floating-Point Values +NN_vfnmadd231pd, // Fused Negative Multiply-Add of Packed Double-Precision Floating-Point Values +NN_vfnmadd231ps, // Fused Negative Multiply-Add of Packed Single-Precision Floating-Point Values +NN_vfnmadd231sd, // Fused Negative Multiply-Add of Scalar Double-Precision Floating-Point Values +NN_vfnmadd231ss, // Fused Negative Multiply-Add of Scalar Single-Precision Floating-Point Values +NN_vfnmsub132pd, // Fused Negative Multiply-Subtract of Packed Double-Precision Floating-Point Values +NN_vfnmsub132ps, // Fused Negative Multiply-Subtract of Packed Single-Precision Floating-Point Values +NN_vfnmsub132sd, // Fused Negative Multiply-Subtract of Scalar Double-Precision Floating-Point Values +NN_vfnmsub132ss, // Fused Negative Multiply-Subtract of Scalar Single-Precision Floating-Point Values +NN_vfnmsub213pd, // Fused Negative Multiply-Subtract of Packed Double-Precision Floating-Point Values +NN_vfnmsub213ps, // Fused Negative Multiply-Subtract of Packed Single-Precision Floating-Point Values +NN_vfnmsub213sd, // Fused Negative Multiply-Subtract of Scalar Double-Precision Floating-Point Values +NN_vfnmsub213ss, // Fused Negative Multiply-Subtract of Scalar Single-Precision Floating-Point Values +NN_vfnmsub231pd, // Fused Negative Multiply-Subtract of Packed Double-Precision Floating-Point Values +NN_vfnmsub231ps, // Fused Negative Multiply-Subtract of Packed Single-Precision Floating-Point Values +NN_vfnmsub231sd, // Fused Negative Multiply-Subtract of Scalar Double-Precision Floating-Point Values +NN_vfnmsub231ss, // Fused Negative Multiply-Subtract of Scalar Single-Precision Floating-Point Values +NN_vgatherdps, // Gather Packed SP FP Values Using Signed Dword Indices +NN_vgatherdpd, // Gather Packed DP FP Values Using Signed Dword Indices +NN_vgatherqps, // Gather Packed SP FP Values Using Signed Qword Indices +NN_vgatherqpd, // Gather Packed DP FP Values Using Signed Qword Indices +NN_vhaddpd, // Add horizontally packed DP FP numbers +NN_vhaddps, // Add horizontally packed SP FP numbers +NN_vhsubpd, // Sub horizontally packed DP FP numbers +NN_vhsubps, // Sub horizontally packed SP FP numbers +NN_vinsertf128, // Insert Packed Floating-Point Values +NN_vinserti128, // Insert Packed Integer Values +NN_vinsertps, // Insert Packed Single Precision Floating-Point Value +NN_vlddqu, // Load Unaligned Packed Integer Values +NN_vldmxcsr, // Load Streaming SIMD Extensions Technology Control/Status Register +NN_vmaskmovdqu, // Store Selected Bytes of Double Quadword with NT Hint +NN_vmaskmovpd, // Conditionally Load Packed Double-Precision Floating-Point Values +NN_vmaskmovps, // Conditionally Load Packed Single-Precision Floating-Point Values +NN_vmaxpd, // Return Maximum Packed Double-Precision Floating-Point Values +NN_vmaxps, // Packed Single-FP Maximum +NN_vmaxsd, // Return Maximum Scalar Double-Precision Floating-Point Value +NN_vmaxss, // Scalar Single-FP Maximum +NN_vminpd, // Return Minimum Packed Double-Precision Floating-Point Values +NN_vminps, // Packed Single-FP Minimum +NN_vminsd, // Return Minimum Scalar Double-Precision Floating-Point Value +NN_vminss, // Scalar Single-FP Minimum +NN_vmovapd, // Move Aligned Packed Double-Precision Floating-Point Values +NN_vmovaps, // Move Aligned Four Packed Single-FP +NN_vmovd, // Move 32 bits +NN_vmovddup, // Move One Double-FP and Duplicate +NN_vmovdqa, // Move Aligned Double Quadword +NN_vmovdqu, // Move Unaligned Double Quadword +NN_vmovhlps, // Move High to Low Packed Single-FP +NN_vmovhpd, // Move High Packed Double-Precision Floating-Point Values +NN_vmovhps, // Move High Packed Single-FP +NN_vmovlhps, // Move Low to High Packed Single-FP +NN_vmovlpd, // Move Low Packed Double-Precision Floating-Point Values +NN_vmovlps, // Move Low Packed Single-FP +NN_vmovmskpd, // Extract Packed Double-Precision Floating-Point Sign Mask +NN_vmovmskps, // Move Mask to Register +NN_vmovntdq, // Store Double Quadword Using Non-Temporal Hint +NN_vmovntdqa, // Load Double Quadword Non-Temporal Aligned Hint +NN_vmovntpd, // Store Packed Double-Precision Floating-Point Values Using Non-Temporal Hint +NN_vmovntps, // Move Aligned Four Packed Single-FP Non Temporal +NN_vmovq, // Move 64 bits +NN_vmovsd, // Move Scalar Double-Precision Floating-Point Values +NN_vmovshdup, // Move Packed Single-FP High and Duplicate +NN_vmovsldup, // Move Packed Single-FP Low and Duplicate +NN_vmovss, // Move Scalar Single-FP +NN_vmovupd, // Move Unaligned Packed Double-Precision Floating-Point Values +NN_vmovups, // Move Unaligned Four Packed Single-FP +NN_vmpsadbw, // Compute Multiple Packed Sums of Absolute Difference +NN_vmulpd, // Multiply Packed Double-Precision Floating-Point Values +NN_vmulps, // Packed Single-FP Multiply +NN_vmulsd, // Multiply Scalar Double-Precision Floating-Point Values +NN_vmulss, // Scalar Single-FP Multiply +NN_vorpd, // Bitwise Logical OR of Double-Precision Floating-Point Values +NN_vorps, // Bitwise Logical OR for Single-FP Data +NN_vpabsb, // Packed Absolute Value Byte +NN_vpabsd, // Packed Absolute Value Doubleword +NN_vpabsw, // Packed Absolute Value Word +NN_vpackssdw, // Pack with Signed Saturation (Dword->Word) +NN_vpacksswb, // Pack with Signed Saturation (Word->Byte) +NN_vpackusdw, // Pack with Unsigned Saturation +NN_vpackuswb, // Pack with Unsigned Saturation (Word->Byte) +NN_vpaddb, // Packed Add Byte +NN_vpaddd, // Packed Add Dword +NN_vpaddq, // Add Packed Quadword Integers +NN_vpaddsb, // Packed Add with Saturation (Byte) +NN_vpaddsw, // Packed Add with Saturation (Word) +NN_vpaddusb, // Packed Add Unsigned with Saturation (Byte) +NN_vpaddusw, // Packed Add Unsigned with Saturation (Word) +NN_vpaddw, // Packed Add Word +NN_vpalignr, // Packed Align Right +NN_vpand, // Bitwise Logical And +NN_vpandn, // Bitwise Logical And Not +NN_vpavgb, // Packed Average (Byte) +NN_vpavgw, // Packed Average (Word) +NN_vpblendd, // Blend Packed Dwords +NN_vpblendvb, // Variable Blend Packed Bytes +NN_vpblendw, // Blend Packed Words +NN_vpbroadcastb, // Broadcast a Byte Integer +NN_vpbroadcastd, // Broadcast a Dword Integer +NN_vpbroadcastq, // Broadcast a Qword Integer +NN_vpbroadcastw, // Broadcast a Word Integer +NN_vpclmulqdq, // Carry-Less Multiplication Quadword +NN_vpcmpeqb, // Packed Compare for Equal (Byte) +NN_vpcmpeqd, // Packed Compare for Equal (Dword) +NN_vpcmpeqq, // Compare Packed Qword Data for Equal +NN_vpcmpeqw, // Packed Compare for Equal (Word) +NN_vpcmpestri, // Packed Compare Explicit Length Strings, Return Index +NN_vpcmpestrm, // Packed Compare Explicit Length Strings, Return Mask +NN_vpcmpgtb, // Packed Compare for Greater Than (Byte) +NN_vpcmpgtd, // Packed Compare for Greater Than (Dword) +NN_vpcmpgtq, // Compare Packed Data for Greater Than +NN_vpcmpgtw, // Packed Compare for Greater Than (Word) +NN_vpcmpistri, // Packed Compare Implicit Length Strings, Return Index +NN_vpcmpistrm, // Packed Compare Implicit Length Strings, Return Mask +NN_vperm2f128, // Permute Floating-Point Values +NN_vperm2i128, // Permute Integer Values +NN_vpermd, // Full Doublewords Element Permutation +NN_vpermilpd, // Permute Double-Precision Floating-Point Values +NN_vpermilps, // Permute Single-Precision Floating-Point Values +NN_vpermpd, // Permute Double-Precision Floating-Point Elements +NN_vpermps, // Permute Single-Precision Floating-Point Elements +NN_vpermq, // Qwords Element Permutation +NN_vpextrb, // Extract Byte +NN_vpextrd, // Extract Dword +NN_vpextrq, // Extract Qword +NN_vpextrw, // Extract Word +NN_vpgatherdd, // Gather Packed Dword Values Using Signed Dword Indices +NN_vpgatherdq, // Gather Packed Qword Values Using Signed Dword Indices +NN_vpgatherqd, // Gather Packed Dword Values Using Signed Qword Indices +NN_vpgatherqq, // Gather Packed Qword Values Using Signed Qword Indices +NN_vphaddd, // Packed Horizontal Add Doubleword +NN_vphaddsw, // Packed Horizontal Add and Saturate +NN_vphaddw, // Packed Horizontal Add Word +NN_vphminposuw, // Packed Horizontal Word Minimum +NN_vphsubd, // Packed Horizontal Subtract Doubleword +NN_vphsubsw, // Packed Horizontal Subtract and Saturate +NN_vphsubw, // Packed Horizontal Subtract Word +NN_vpinsrb, // Insert Byte +NN_vpinsrd, // Insert Dword +NN_vpinsrq, // Insert Qword +NN_vpinsrw, // Insert Word +NN_vpmaddubsw, // Multiply and Add Packed Signed and Unsigned Bytes +NN_vpmaddwd, // Packed Multiply and Add +NN_vpmaskmovd, // Conditionally Store Dword Values Using Mask +NN_vpmaskmovq, // Conditionally Store Qword Values Using Mask +NN_vpmaxsb, // Maximum of Packed Signed Byte Integers +NN_vpmaxsd, // Maximum of Packed Signed Dword Integers +NN_vpmaxsw, // Packed Signed Integer Word Maximum +NN_vpmaxub, // Packed Unsigned Integer Byte Maximum +NN_vpmaxud, // Maximum of Packed Unsigned Dword Integers +NN_vpmaxuw, // Maximum of Packed Word Integers +NN_vpminsb, // Minimum of Packed Signed Byte Integers +NN_vpminsd, // Minimum of Packed Signed Dword Integers +NN_vpminsw, // Packed Signed Integer Word Minimum +NN_vpminub, // Packed Unsigned Integer Byte Minimum +NN_vpminud, // Minimum of Packed Unsigned Dword Integers +NN_vpminuw, // Minimum of Packed Word Integers +NN_vpmovmskb, // Move Byte Mask to Integer +NN_vpmovsxbd, // Packed Move with Sign Extend +NN_vpmovsxbq, // Packed Move with Sign Extend +NN_vpmovsxbw, // Packed Move with Sign Extend +NN_vpmovsxdq, // Packed Move with Sign Extend +NN_vpmovsxwd, // Packed Move with Sign Extend +NN_vpmovsxwq, // Packed Move with Sign Extend +NN_vpmovzxbd, // Packed Move with Zero Extend +NN_vpmovzxbq, // Packed Move with Zero Extend +NN_vpmovzxbw, // Packed Move with Zero Extend +NN_vpmovzxdq, // Packed Move with Zero Extend +NN_vpmovzxwd, // Packed Move with Zero Extend +NN_vpmovzxwq, // Packed Move with Zero Extend +NN_vpmuldq, // Multiply Packed Signed Dword Integers +NN_vpmulhrsw, // Packed Multiply High with Round and Scale +NN_vpmulhuw, // Packed Multiply High Unsigned +NN_vpmulhw, // Packed Multiply High +NN_vpmulld, // Multiply Packed Signed Dword Integers and Store Low Result +NN_vpmullw, // Packed Multiply Low +NN_vpmuludq, // Multiply Packed Unsigned Doubleword Integers +NN_vpor, // Bitwise Logical Or +NN_vpsadbw, // Packed Sum of Absolute Differences +NN_vpshufb, // Packed Shuffle Bytes +NN_vpshufd, // Shuffle Packed Doublewords +NN_vpshufhw, // Shuffle Packed High Words +NN_vpshuflw, // Shuffle Packed Low Words +NN_vpsignb, // Packed SIGN Byte +NN_vpsignd, // Packed SIGN Doubleword +NN_vpsignw, // Packed SIGN Word +NN_vpslld, // Packed Shift Left Logical (Dword) +NN_vpslldq, // Shift Double Quadword Left Logical +NN_vpsllq, // Packed Shift Left Logical (Qword) +NN_vpsllvd, // Variable Bit Shift Left Logical (Dword) +NN_vpsllvq, // Variable Bit Shift Left Logical (Qword) +NN_vpsllw, // Packed Shift Left Logical (Word) +NN_vpsrad, // Packed Shift Right Arithmetic (Dword) +NN_vpsravd, // Variable Bit Shift Right Arithmetic +NN_vpsraw, // Packed Shift Right Arithmetic (Word) +NN_vpsrld, // Packed Shift Right Logical (Dword) +NN_vpsrldq, // Shift Double Quadword Right Logical (Qword) +NN_vpsrlq, // Packed Shift Right Logical (Qword) +NN_vpsrlvd, // Variable Bit Shift Right Logical (Dword) +NN_vpsrlvq, // Variable Bit Shift Right Logical (Qword) +NN_vpsrlw, // Packed Shift Right Logical (Word) +NN_vpsubb, // Packed Subtract Byte +NN_vpsubd, // Packed Subtract Dword +NN_vpsubq, // Subtract Packed Quadword Integers +NN_vpsubsb, // Packed Subtract with Saturation (Byte) +NN_vpsubsw, // Packed Subtract with Saturation (Word) +NN_vpsubusb, // Packed Subtract Unsigned with Saturation (Byte) +NN_vpsubusw, // Packed Subtract Unsigned with Saturation (Word) +NN_vpsubw, // Packed Subtract Word +NN_vptest, // Logical Compare +NN_vpunpckhbw, // Unpack High Packed Data (Byte->Word) +NN_vpunpckhdq, // Unpack High Packed Data (Dword->Qword) +NN_vpunpckhqdq, // Unpack High Packed Data (Qword->Xmmword) +NN_vpunpckhwd, // Unpack High Packed Data (Word->Dword) +NN_vpunpcklbw, // Unpack Low Packed Data (Byte->Word) +NN_vpunpckldq, // Unpack Low Packed Data (Dword->Qword) +NN_vpunpcklqdq, // Unpack Low Packed Data (Qword->Xmmword) +NN_vpunpcklwd, // Unpack Low Packed Data (Word->Dword) +NN_vpxor, // Bitwise Logical Exclusive Or +NN_vrcpps, // Packed Single-FP Reciprocal +NN_vrcpss, // Scalar Single-FP Reciprocal +NN_vroundpd, // Round Packed Double Precision Floating-Point Values +NN_vroundps, // Round Packed Single Precision Floating-Point Values +NN_vroundsd, // Round Scalar Double Precision Floating-Point Values +NN_vroundss, // Round Scalar Single Precision Floating-Point Values +NN_vrsqrtps, // Packed Single-FP Square Root Reciprocal +NN_vrsqrtss, // Scalar Single-FP Square Root Reciprocal +NN_vshufpd, // Shuffle Packed Double-Precision Floating-Point Values +NN_vshufps, // Shuffle Single-FP +NN_vsqrtpd, // Compute Square Roots of Packed Double-Precision Floating-Point Values +NN_vsqrtps, // Packed Single-FP Square Root +NN_vsqrtsd, // Compute Square Rootof Scalar Double-Precision Floating-Point Value +NN_vsqrtss, // Scalar Single-FP Square Root +NN_vstmxcsr, // Store Streaming SIMD Extensions Technology Control/Status Register +NN_vsubpd, // Subtract Packed Double-Precision Floating-Point Values +NN_vsubps, // Packed Single-FP Subtract +NN_vsubsd, // Subtract Scalar Double-Precision Floating-Point Values +NN_vsubss, // Scalar Single-FP Subtract +NN_vtestpd, // Packed Double-Precision Floating-Point Bit Test +NN_vtestps, // Packed Single-Precision Floating-Point Bit Test +NN_vucomisd, // Unordered Compare Scalar Ordered Double-Precision Floating-Point Values and Set EFLAGS +NN_vucomiss, // Scalar Unordered Single-FP Compare and Set EFLAGS +NN_vunpckhpd, // Unpack and Interleave High Packed Double-Precision Floating-Point Values +NN_vunpckhps, // Unpack High Packed Single-FP Data +NN_vunpcklpd, // Unpack and Interleave Low Packed Double-Precision Floating-Point Values +NN_vunpcklps, // Unpack Low Packed Single-FP Data +NN_vxorpd, // Bitwise Logical OR of Double-Precision Floating-Point Values +NN_vxorps, // Bitwise Logical XOR for Single-FP Data +NN_vzeroall, // Zero All YMM Registers +NN_vzeroupper, // Zero Upper Bits of YMM Registers + +// Transactional Synchronization Extensions + +NN_xabort, // Transaction Abort +NN_xbegin, // Transaction Begin +NN_xend, // Transaction End +NN_xtest, // Test If In Transactional Execution + +// Virtual PC synthetic instructions + +NN_vmgetinfo, // Virtual PC - Get VM Information +NN_vmsetinfo, // Virtual PC - Set VM Information +NN_vmdxdsbl, // Virtual PC - Disable Direct Execution +NN_vmdxenbl, // Virtual PC - Enable Direct Execution +NN_vmcpuid, // Virtual PC - Virtualized CPU Information +NN_vmhlt, // Virtual PC - Halt +NN_vmsplaf, // Virtual PC - Spin Lock Acquisition Failed +NN_vmpushfd, // Virtual PC - Push virtualized flags register +NN_vmpopfd, // Virtual PC - Pop virtualized flags register +NN_vmcli, // Virtual PC - Clear Interrupt Flag +NN_vmsti, // Virtual PC - Set Interrupt Flag +NN_vmiretd, // Virtual PC - Return From Interrupt +NN_vmsgdt, // Virtual PC - Store Global Descriptor Table +NN_vmsidt, // Virtual PC - Store Interrupt Descriptor Table +NN_vmsldt, // Virtual PC - Store Local Descriptor Table +NN_vmstr, // Virtual PC - Store Task Register +NN_vmsdte, // Virtual PC - Store to Descriptor Table Entry +NN_vpcext, // Virtual PC - ISA extension + +// AMD FMA4 + +NN_vfmaddsubps, // Multiply with Alternating Add/Subtract of Packed Single-Precision Floating-Point +NN_vfmaddsubpd, // Multiply with Alternating Add/Subtract of Packed Double-Precision Floating-Point +NN_vfmsubaddps, // Multiply with Alternating Subtract/Add of Packed Single-Precision Floating-Point +NN_vfmsubaddpd, // Multiply with Alternating Subtract/Add of Packed Double-Precision Floating-Point +NN_vfmaddps, // Multiply and Add Packed Single-Precision Floating-Point +NN_vfmaddpd, // Multiply and Add Packed Double-Precision Floating-Point +NN_vfmaddss, // Multiply and Add Scalar Single-Precision Floating-Point +NN_vfmaddsd, // Multiply and Add Scalar Double-Precision Floating-Point +NN_vfmsubps, // Multiply and Subtract Packed Single-Precision Floating-Point +NN_vfmsubpd, // Multiply and Subtract Packed Double-Precision Floating-Point +NN_vfmsubss, // Multiply and Subtract Scalar Single-Precision Floating-Point +NN_vfmsubsd, // Multiply and Subtract Scalar Double-Precision Floating-Point +NN_vfnmaddps, // Negative Multiply and Add Packed Single-Precision Floating-Point +NN_vfnmaddpd, // Negative Multiply and Add Packed Double-Precision Floating-Point +NN_vfnmaddss, // Negative Multiply and Add Scalar Single-Precision Floating-Point +NN_vfnmaddsd, // Negative Multiply and Add Double Single-Precision Floating-Point +NN_vfnmsubps, // Negative Multiply and Subtract Packed Single-Precision Floating-Point +NN_vfnmsubpd, // Negative Multiply and Subtract Packed Double-Precision Floating-Point +NN_vfnmsubss, // Negative Multiply and Subtract Scalar Single-Precision Floating-Point +NN_vfnmsubsd, // Negative Multiply and Subtract Double Single-Precision Floating-Point + +// Intel Memory Protection Extensions (MPX) + +NN_bndmk, // Make Bounds +NN_bndcl, // Check Lower Bound +NN_bndcu, // Check Upper Bound +NN_bndcn, // Check Upper Bound +NN_bndmov, // Move Bounds +NN_bndldx, // Load Extended Bounds Using Address Translation +NN_bndstx, // Store Extended Bounds Using Address Translation + +// New xstate instructions + +NN_xrstors, // Restore Processor Extended States Supervisor +NN_xsavec, // Save Processor Extended States with Compaction +NN_xsaves, // Save Processor Extended States Supervisor + +// PREFETCHWT1 support + +NN_prefetchwt1, // Prefetch Vector Data Into Caches with Intent to Write and T1 Hint + +// Memory instructions + +NN_clflushopt, // Flush a Cache Line Optimized +NN_clwb, // Cache Line Write Back +NN_pcommit, // Persistent Commit (deprecated by Intel) + +// Protection Key Rights for User Pages + +NN_rdpkru, // Read Protection Key Rights for User Pages +NN_wrpkru, // Write Data to User Page Key Register + +// AVX comparison pseudo-ops + +NN_vcmpeqpd, // Compare Packed Double-Precision Floating-Point Values - Equal (ordered, non-signaling) +NN_vcmpltpd, // Compare Packed Double-Precision Floating-Point Values - Less-than (ordered, signaling) +NN_vcmplepd, // Compare Packed Double-Precision Floating-Point Values - Less-than-or-equal (ordered, signaling) +NN_vcmpunordpd, // Compare Packed Double-Precision Floating-Point Values - Unordered (non-signaling) +NN_vcmpneqpd, // Compare Packed Double-Precision Floating-Point Values - Not-equal (unordered, non-signaling) +NN_vcmpnltpd, // Compare Packed Double-Precision Floating-Point Values - Not-less-than (unordered, signaling) +NN_vcmpnlepd, // Compare Packed Double-Precision Floating-Point Values - Not-less-than-or-equal (unordered, signaling) +NN_vcmpordpd, // Compare Packed Double-Precision Floating-Point Values - Ordered (non-signaling) +NN_vcmpeq_uqpd, // Compare Packed Double-Precision Floating-Point Values - Equal (unordered, non-signaling) +NN_vcmpngepd, // Compare Packed Double-Precision Floating-Point Values - Not-greater-than-or-equal (unordered, signaling) +NN_vcmpngtpd, // Compare Packed Double-Precision Floating-Point Values - Not-greater-than (unordered, signaling) +NN_vcmpfalsepd, // Compare Packed Double-Precision Floating-Point Values - False (ordered, non-signaling) +NN_vcmpneq_oqpd, // Compare Packed Double-Precision Floating-Point Values - Not-equal (ordered, non-signaling) +NN_vcmpgepd, // Compare Packed Double-Precision Floating-Point Values - Greater-than-or-equal (ordered, signaling) +NN_vcmpgtpd, // Compare Packed Double-Precision Floating-Point Values - Greater-than (ordered, signaling) +NN_vcmptruepd, // Compare Packed Double-Precision Floating-Point Values - True (unordered, non-signaling) +NN_vcmpeq_ospd, // Compare Packed Double-Precision Floating-Point Values - Equal (ordered, signaling) +NN_vcmplt_oqpd, // Compare Packed Double-Precision Floating-Point Values - Less-than (ordered, non-signaling) +NN_vcmple_oqpd, // Compare Packed Double-Precision Floating-Point Values - Less-than-or-equal (ordered, non-signaling) +NN_vcmpunord_spd, // Compare Packed Double-Precision Floating-Point Values - Unordered (signaling) +NN_vcmpneq_uspd, // Compare Packed Double-Precision Floating-Point Values - Not-equal (unordered, signaling) +NN_vcmpnlt_uqpd, // Compare Packed Double-Precision Floating-Point Values - Not-less-than (unordered, non-signaling) +NN_vcmpnle_uqpd, // Compare Packed Double-Precision Floating-Point Values - Not-less-than-or-equal (unordered, non-signaling) +NN_vcmpord_spd, // Compare Packed Double-Precision Floating-Point Values - Ordered (signaling) +NN_vcmpeq_uspd, // Compare Packed Double-Precision Floating-Point Values - Equal (unordered, signaling) +NN_vcmpnge_uqpd, // Compare Packed Double-Precision Floating-Point Values - Not-greater-than-or-equal (unordered, non-signaling) +NN_vcmpngt_uqpd, // Compare Packed Double-Precision Floating-Point Values - Not-greater-than (unordered, non-signaling) +NN_vcmpfalse_ospd, // Compare Packed Double-Precision Floating-Point Values - False (ordered, signaling) +NN_vcmpneq_ospd, // Compare Packed Double-Precision Floating-Point Values - Not-equal (ordered, signaling) +NN_vcmpge_oqpd, // Compare Packed Double-Precision Floating-Point Values - Greater-than-or-equal (ordered, non-signaling) +NN_vcmpgt_oqpd, // Compare Packed Double-Precision Floating-Point Values - Greater-than (ordered, non-signaling) +NN_vcmptrue_uspd, // Compare Packed Double-Precision Floating-Point Values - True (unordered, signaling) + +NN_vcmpeqps, // Packed Single-FP Compare - Equal (ordered, non-signaling) +NN_vcmpltps, // Packed Single-FP Compare - Less-than (ordered, signaling) +NN_vcmpleps, // Packed Single-FP Compare - Less-than-or-equal (ordered, signaling) +NN_vcmpunordps, // Packed Single-FP Compare - Unordered (non-signaling) +NN_vcmpneqps, // Packed Single-FP Compare - Not-equal (unordered, non-signaling) +NN_vcmpnltps, // Packed Single-FP Compare - Not-less-than (unordered, signaling) +NN_vcmpnleps, // Packed Single-FP Compare - Not-less-than-or-equal (unordered, signaling) +NN_vcmpordps, // Packed Single-FP Compare - Ordered (non-signaling) +NN_vcmpeq_uqps, // Packed Single-FP Compare - Equal (unordered, non-signaling) +NN_vcmpngeps, // Packed Single-FP Compare - Not-greater-than-or-equal (unordered, signaling) +NN_vcmpngtps, // Packed Single-FP Compare - Not-greater-than (unordered, signaling) +NN_vcmpfalseps, // Packed Single-FP Compare - False (ordered, non-signaling) +NN_vcmpneq_oqps, // Packed Single-FP Compare - Not-equal (ordered, non-signaling) +NN_vcmpgeps, // Packed Single-FP Compare - Greater-than-or-equal (ordered, signaling) +NN_vcmpgtps, // Packed Single-FP Compare - Greater-than (ordered, signaling) +NN_vcmptrueps, // Packed Single-FP Compare - True (unordered, non-signaling) +NN_vcmpeq_osps, // Packed Single-FP Compare - Equal (ordered, signaling) +NN_vcmplt_oqps, // Packed Single-FP Compare - Less-than (ordered, non-signaling) +NN_vcmple_oqps, // Packed Single-FP Compare - Less-than-or-equal (ordered, non-signaling) +NN_vcmpunord_sps, // Packed Single-FP Compare - Unordered (signaling) +NN_vcmpneq_usps, // Packed Single-FP Compare - Not-equal (unordered, signaling) +NN_vcmpnlt_uqps, // Packed Single-FP Compare - Not-less-than (unordered, non-signaling) +NN_vcmpnle_uqps, // Packed Single-FP Compare - Not-less-than-or-equal (unordered, non-signaling) +NN_vcmpord_sps, // Packed Single-FP Compare - Ordered (signaling) +NN_vcmpeq_usps, // Packed Single-FP Compare - Equal (unordered, signaling) +NN_vcmpnge_uqps, // Packed Single-FP Compare - Not-greater-than-or-equal (unordered, non-signaling) +NN_vcmpngt_uqps, // Packed Single-FP Compare - Not-greater-than (unordered, non-signaling) +NN_vcmpfalse_osps, // Packed Single-FP Compare - False (ordered, signaling) +NN_vcmpneq_osps, // Packed Single-FP Compare - Not-equal (ordered, signaling) +NN_vcmpge_oqps, // Packed Single-FP Compare - Greater-than-or-equal (ordered, non-signaling) +NN_vcmpgt_oqps, // Packed Single-FP Compare - Greater-than (ordered, non-signaling) +NN_vcmptrue_usps, // Packed Single-FP Compare - True (unordered, signaling) + +NN_vcmpeqsd, // Compare Scalar Double-Precision Floating-Point Values - Equal (ordered, non-signaling) +NN_vcmpltsd, // Compare Scalar Double-Precision Floating-Point Values - Less-than (ordered, signaling) +NN_vcmplesd, // Compare Scalar Double-Precision Floating-Point Values - Less-than-or-equal (ordered, signaling) +NN_vcmpunordsd, // Compare Scalar Double-Precision Floating-Point Values - Unordered (non-signaling) +NN_vcmpneqsd, // Compare Scalar Double-Precision Floating-Point Values - Not-equal (unordered, non-signaling) +NN_vcmpnltsd, // Compare Scalar Double-Precision Floating-Point Values - Not-less-than (unordered, signaling) +NN_vcmpnlesd, // Compare Scalar Double-Precision Floating-Point Values - Not-less-than-or-equal (unordered, signaling) +NN_vcmpordsd, // Compare Scalar Double-Precision Floating-Point Values - Ordered (non-signaling) +NN_vcmpeq_uqsd, // Compare Scalar Double-Precision Floating-Point Values - Equal (unordered, non-signaling) +NN_vcmpngesd, // Compare Scalar Double-Precision Floating-Point Values - Not-greater-than-or-equal (unordered, signaling) +NN_vcmpngtsd, // Compare Scalar Double-Precision Floating-Point Values - Not-greater-than (unordered, signaling) +NN_vcmpfalsesd, // Compare Scalar Double-Precision Floating-Point Values - False (ordered, non-signaling) +NN_vcmpneq_oqsd, // Compare Scalar Double-Precision Floating-Point Values - Not-equal (ordered, non-signaling) +NN_vcmpgesd, // Compare Scalar Double-Precision Floating-Point Values - Greater-than-or-equal (ordered, signaling) +NN_vcmpgtsd, // Compare Scalar Double-Precision Floating-Point Values - Greater-than (ordered, signaling) +NN_vcmptruesd, // Compare Scalar Double-Precision Floating-Point Values - True (unordered, non-signaling) +NN_vcmpeq_ossd, // Compare Scalar Double-Precision Floating-Point Values - Equal (ordered, signaling) +NN_vcmplt_oqsd, // Compare Scalar Double-Precision Floating-Point Values - Less-than (ordered, non-signaling) +NN_vcmple_oqsd, // Compare Scalar Double-Precision Floating-Point Values - Less-than-or-equal (ordered, non-signaling) +NN_vcmpunord_ssd, // Compare Scalar Double-Precision Floating-Point Values - Unordered (signaling) +NN_vcmpneq_ussd, // Compare Scalar Double-Precision Floating-Point Values - Not-equal (unordered, signaling) +NN_vcmpnlt_uqsd, // Compare Scalar Double-Precision Floating-Point Values - Not-less-than (unordered, non-signaling) +NN_vcmpnle_uqsd, // Compare Scalar Double-Precision Floating-Point Values - Not-less-than-or-equal (unordered, non-signaling) +NN_vcmpord_ssd, // Compare Scalar Double-Precision Floating-Point Values - Ordered (signaling) +NN_vcmpeq_ussd, // Compare Scalar Double-Precision Floating-Point Values - Equal (unordered, signaling) +NN_vcmpnge_uqsd, // Compare Scalar Double-Precision Floating-Point Values - Not-greater-than-or-equal (unordered, non-signaling) +NN_vcmpngt_uqsd, // Compare Scalar Double-Precision Floating-Point Values - Not-greater-than (unordered, non-signaling) +NN_vcmpfalse_ossd, // Compare Scalar Double-Precision Floating-Point Values - False (ordered, signaling) +NN_vcmpneq_ossd, // Compare Scalar Double-Precision Floating-Point Values - Not-equal (ordered, signaling) +NN_vcmpge_oqsd, // Compare Scalar Double-Precision Floating-Point Values - Greater-than-or-equal (ordered, non-signaling) +NN_vcmpgt_oqsd, // Compare Scalar Double-Precision Floating-Point Values - Greater-than (ordered, non-signaling) +NN_vcmptrue_ussd, // Compare Scalar Double-Precision Floating-Point Values - True (unordered, signaling) + +NN_vcmpeqss, // Scalar Single-FP Compare - Equal (ordered, non-signaling) +NN_vcmpltss, // Scalar Single-FP Compare - Less-than (ordered, signaling) +NN_vcmpless, // Scalar Single-FP Compare - Less-than-or-equal (ordered, signaling) +NN_vcmpunordss, // Scalar Single-FP Compare - Unordered (non-signaling) +NN_vcmpneqss, // Scalar Single-FP Compare - Not-equal (unordered, non-signaling) +NN_vcmpnltss, // Scalar Single-FP Compare - Not-less-than (unordered, signaling) +NN_vcmpnless, // Scalar Single-FP Compare - Not-less-than-or-equal (unordered, signaling) +NN_vcmpordss, // Scalar Single-FP Compare - Ordered (non-signaling) +NN_vcmpeq_uqss, // Scalar Single-FP Compare - Equal (unordered, non-signaling) +NN_vcmpngess, // Scalar Single-FP Compare - Not-greater-than-or-equal (unordered, signaling) +NN_vcmpngtss, // Scalar Single-FP Compare - Not-greater-than (unordered, signaling) +NN_vcmpfalsess, // Scalar Single-FP Compare - False (ordered, non-signaling) +NN_vcmpneq_oqss, // Scalar Single-FP Compare - Not-equal (ordered, non-signaling) +NN_vcmpgess, // Scalar Single-FP Compare - Greater-than-or-equal (ordered, signaling) +NN_vcmpgtss, // Scalar Single-FP Compare - Greater-than (ordered, signaling) +NN_vcmptruess, // Scalar Single-FP Compare - True (unordered, non-signaling) +NN_vcmpeq_osss, // Scalar Single-FP Compare - Equal (ordered, signaling) +NN_vcmplt_oqss, // Scalar Single-FP Compare - Less-than (ordered, non-signaling) +NN_vcmple_oqss, // Scalar Single-FP Compare - Less-than-or-equal (ordered, non-signaling) +NN_vcmpunord_sss, // Scalar Single-FP Compare - Unordered (signaling) +NN_vcmpneq_usss, // Scalar Single-FP Compare - Not-equal (unordered, signaling) +NN_vcmpnlt_uqss, // Scalar Single-FP Compare - Not-less-than (unordered, non-signaling) +NN_vcmpnle_uqss, // Scalar Single-FP Compare - Not-less-than-or-equal (unordered, non-signaling) +NN_vcmpord_sss, // Scalar Single-FP Compare - Ordered (signaling) +NN_vcmpeq_usss, // Scalar Single-FP Compare - Equal (unordered, signaling) +NN_vcmpnge_uqss, // Scalar Single-FP Compare - Not-greater-than-or-equal (unordered, non-signaling) +NN_vcmpngt_uqss, // Scalar Single-FP Compare - Not-greater-than (unordered, non-signaling) +NN_vcmpfalse_osss, // Scalar Single-FP Compare - False (ordered, signaling) +NN_vcmpneq_osss, // Scalar Single-FP Compare - Not-equal (ordered, signaling) +NN_vcmpge_oqss, // Scalar Single-FP Compare - Greater-than-or-equal (ordered, non-signaling) +NN_vcmpgt_oqss, // Scalar Single-FP Compare - Greater-than (ordered, non-signaling) +NN_vcmptrue_usss, // Scalar Single-FP Compare - True (unordered, signaling) + +// AVX-512 instructions + +NN_valignd, // Align Doubleword Vectors +NN_valignq, // Align Quadword Vectors +NN_vblendmpd, // Blend Float64 Vectors Using an OpMask Control +NN_vblendmps, // Blend Float32 Vectors Using an OpMask Control +NN_vpblendmb, // Blend Byte Vectors Using an Opmask Control +NN_vpblendmw, // Blend Word Vectors Using an Opmask Control +NN_vpblendmd, // Blend Int32 Vectors Using an OpMask Control +NN_vpblendmq, // Blend Int64 Vectors Using an OpMask Control +NN_vbroadcastf32x2, // Load with Broadcast Floating-Point Data +NN_vbroadcastf32x4, // Load with Broadcast Floating-Point Data +NN_vbroadcastf64x2, // Load with Broadcast Floating-Point Data +NN_vbroadcastf32x8, // Load with Broadcast Floating-Point Data +NN_vbroadcastf64x4, // Load with Broadcast Floating-Point Data +NN_vbroadcasti32x2, // Load Integer and Broadcast +NN_vbroadcasti32x4, // Load Integer and Broadcast +NN_vbroadcasti64x2, // Load Integer and Broadcast +NN_vbroadcasti32x8, // Load Integer and Broadcast +NN_vbroadcasti64x4, // Load Integer and Broadcast +NN_vcompresspd, // Store Sparse Packed Double-Precision Floating-Point Values into Dense Memory +NN_vcompressps, // Store Sparse Packed Single-Precision Floating-Point Values into Dense Memory +NN_vcvtpd2qq, // Convert Packed Double-Precision Floating-Point Values to Packed Quadword Integers +NN_vcvtpd2udq, // Convert Packed Double-Precision Floating-Point Values to Packed Unsigned Doubleword Integers +NN_vcvtpd2uqq, // Convert Packed Double-Precision Floating-Point Values to Packed Unsigned Quadword Integers +NN_vcvtps2udq, // Convert Packed Single-Precision Floating-Point Values to Packed Unsigned Doubleword Integer Values +NN_vcvtps2qq, // Convert Packed Single Precision Floating-Point Values to Packed Singed Quadword Integer Values +NN_vcvtps2uqq, // Convert Packed Single Precision Floating-Point Values to Packed Unsigned Quadword Integer Values +NN_vcvtqq2pd, // Convert Packed Quadword Integers to Packed Double-Precision Floating-Point Values +NN_vcvtqq2ps, // Convert Packed Quadword Integers to Packed Single-Precision Floating-Point Values +NN_vcvtsd2usi, // Convert Scalar Double-Precision Floating-Point Value to Unsigned Doubleword Integer +NN_vcvtss2usi, // Convert Scalar Single-Precision Floating-Point Value to Unsigned Doubleword Integer +NN_vcvttpd2qq, // Convert with Truncation Packed Double-Precision Floating-Point Values to Packed Quadword Integers +NN_vcvttpd2udq, // Convert with Truncation Packed Double-Precision Floating-Point Values to Packed Unsigned Doubleword Integers +NN_vcvttpd2uqq, // Convert with Truncation Packed Double-Precision Floating-Point Values to Packed Unsigned Quadword Integers +NN_vcvttps2udq, // Convert with Truncation Packed Single-Precision Floating-Point Values to Packed Unsigned Doubleword Integer Values +NN_vcvttps2qq, // Convert with Truncation Packed Single Precision Floating-Point Values to Packed Singed Quadword Integer Values +NN_vcvttps2uqq, // Convert with Truncation Packed Single Precision Floating-Point Values to Packed Unsigned Quadword Integer Values +NN_vcvttsd2usi, // Convert with Truncation Scalar Double-Precision Floating-Point Value to Unsigned Integer +NN_vcvttss2usi, // Convert with Truncation Scalar Single-Precision Floating-Point Value to Unsigned Integer +NN_vcvtudq2pd, // Convert Packed Unsigned Doubleword Integers to Packed Double-Precision Floating-Point Values +NN_vcvtudq2ps, // Convert Packed Unsigned Doubleword Integers to Packed Single-Precision Floating-Point Values +NN_vcvtuqq2pd, // Convert Packed Unsigned Quadword Integers to Packed Double-Precision Floating-Point Values +NN_vcvtuqq2ps, // Convert Packed Unsigned Quadword Integers to Packed Single-Precision Floating-Point Values +NN_vcvtusi2sd, // Convert Unsigned Integer to Scalar Double-Precision Floating-Point Value +NN_vcvtusi2ss, // Convert Unsigned Integer to Scalar Single-Precision Floating-Point Value +NN_vdbpsadbw, // Double Block Packed Sum-Absolute-Differences (SAD) on Unsigned Bytes +NN_vexpandpd, // Load Sparse Packed Double-Precision Floating-Point Values from Dense Memory +NN_vexpandps, // Load Sparse Packed Single-Precision Floating-Point Values from Dense Memory +NN_vextractf32x4, // Extract Packed Floating-Point Values +NN_vextractf64x2, // Extract Packed Floating-Point Values +NN_vextractf32x8, // Extract Packed Floating-Point Values +NN_vextractf64x4, // Extract Packed Floating-Point Values +NN_vextracti32x4, // Extract packed Integer Values +NN_vextracti64x2, // Extract packed Integer Values +NN_vextracti32x8, // Extract packed Integer Values +NN_vextracti64x4, // Extract packed Integer Values +NN_vfixupimmpd, // Fix Up Special Packed Float64 Values +NN_vfixupimmps, // Fix Up Special Packed Float32 Values +NN_vfixupimmsd, // Fix Up Special Scalar Float64 Value +NN_vfixupimmss, // Fix Up Special Scalar Float32 Value +NN_vfpclasspd, // Tests Types Of a Packed Float64 Values +NN_vfpclassps, // Tests Types Of a Packed Float32 Values +NN_vfpclasssd, // Tests Types Of a Scalar Float64 Values +NN_vfpclassss, // Tests Types Of a Scalar Float32 Values +NN_vgetexppd, // Convert Exponents of Packed DP FP Values to DP FP Values +NN_vgetexpps, // Convert Exponents of Packed SP FP Values to SP FP Values +NN_vgetexpsd, // Convert Exponents of Scalar DP FP Values to DP FP Value +NN_vgetexpss, // Convert Exponents of Scalar SP FP Values to SP FP Value +NN_vgetmantpd, // Extract Float64 Vector of Normalized Mantissas from Float64 Vector +NN_vgetmantps, // Extract Float32 Vector of Normalized Mantissas from Float32 Vector +NN_vgetmantsd, // Extract Float64 of Normalized Mantissas from Float64 Scalar +NN_vgetmantss, // Extract Float32 Vector of Normalized Mantissa from Float32 Vector +NN_vinsertf32x4, // Insert Packed Floating-Point Values +NN_vinsertf64x2, // Insert Packed Floating-Point Values +NN_vinsertf32x8, // Insert Packed Floating-Point Values +NN_vinsertf64x4, // Insert Packed Floating-Point Values +NN_vinserti32x4, // Insert Packed Integer Values +NN_vinserti64x2, // Insert Packed Integer Values +NN_vinserti32x8, // Insert Packed Integer Values +NN_vinserti64x4, // Insert Packed Integer Values +NN_vmovdqa32, // Move Aligned Packed Integer Values +NN_vmovdqa64, // Move Aligned Packed Integer Values +NN_vmovdqu8, // Move Unaligned Packed Integer Values +NN_vmovdqu16, // Move Unaligned Packed Integer Values +NN_vmovdqu32, // Move Unaligned Packed Integer Values +NN_vmovdqu64, // Move Unaligned Packed Integer Values +NN_vpabsq, // Packed Absolute Value +NN_vpandd, // Logical AND +NN_vpandq, // Logical AND +NN_vpandnd, // Logical AND NOT +NN_vpandnq, // Logical AND NOT +NN_vpbroadcastmb2q, // Broadcast Mask to Vector Register +NN_vpbroadcastmw2d, // Broadcast Mask to Vector Register +NN_vpcmpb, // Compare Packed Byte Values Into Mask +NN_vpcmpub, // Compare Packed Byte Values Into Mask +NN_vpcmpd, // Compare Packed Integer Values into Mask +NN_vpcmpud, // Compare Packed Integer Values into Mask +NN_vpcmpq, // Compare Packed Integer Values into Mask +NN_vpcmpuq, // Compare Packed Integer Values into Mask +NN_vpcmpw, // Compare Packed Word Values Into Mask +NN_vpcmpuw, // Compare Packed Word Values Into Mask +NN_vpcompressd, // Store Sparse Packed Doubleword Integer Values into Dense Memory/Register +NN_vpcompressq, // Store Sparse Packed Quadword Integer Values into Dense Memory/Register +NN_vpconflictd, // Detect Conflicts Within a Vector of Packed Dword Values into Dense Memory/Register +NN_vpconflictq, // Detect Conflicts Within a Vector of Packed Qword Values into Dense Memory/Register +NN_vpermb, // Permute Packed Bytes Elements +NN_vpermw, // Permute Packed Words Elements +NN_vpermi2b, // Full Permute of Bytes From Two Tables Overwriting the Index +NN_vpermi2w, // Full Permute From Two Tables Overwriting the Index +NN_vpermi2d, // Full Permute From Two Tables Overwriting the Index +NN_vpermi2q, // Full Permute From Two Tables Overwriting the Index +NN_vpermi2ps, // Full Permute From Two Tables Overwriting the Index +NN_vpermi2pd, // Full Permute From Two Tables Overwriting the Index +NN_vpermt2b, // Full Permute of Bytes From Two Tables Overwriting a Table +NN_vpermt2w, // Full Permute from Two Tables Overwriting one Table +NN_vpermt2d, // Full Permute from Two Tables Overwriting one Table +NN_vpermt2q, // Full Permute from Two Tables Overwriting one Table +NN_vpermt2ps, // Full Permute from Two Tables Overwriting one Table +NN_vpermt2pd, // Full Permute from Two Tables Overwriting one Table +NN_vpexpandd, // Load Sparse Packed Doubleword Integer Values from Dense Memory/Register +NN_vpexpandq, // Load Sparse Packed Quadword Integer Values from Dense Memory/Register +NN_vplzcntd, // Count the Number of Leading Zero Bits for Packed Dword Values +NN_vplzcntq, // Count the Number of Leading Zero Bits for Packed Qword Values +NN_vpmadd52luq, // Packed Multiply of Unsigned 52-bit Integers and Add the Low 52-bit Products to Qword Accumulators +NN_vpmadd52huq, // Packed Multiply of Unsigned 52-bit Unsigned Integers and Add High 52-bit Products to 64-bit Accumulators +NN_vpmaxsq, // Maximum of Packed Signed Integers +NN_vpmaxuq, // Maximum of Packed Unsigned Integers +NN_vpminsq, // Minimum of Packed Signed Integers +NN_vpminuq, // Minimum of Packed Unsigned Integers +NN_vpmovm2b, // Convert a Mask Register to a Vector Register +NN_vpmovm2w, // Convert a Mask Register to a Vector Register +NN_vpmovm2d, // Convert a Mask Register to a Vector Register +NN_vpmovm2q, // Convert a Mask Register to a Vector Register +NN_vpmovb2m, // Convert a Vector Register to a Mask +NN_vpmovw2m, // Convert a Vector Register to a Mask +NN_vpmovd2m, // Convert a Vector Register to a Mask +NN_vpmovq2m, // Convert a Vector Register to a Mask +NN_vpmovqb, // Down Convert QWord to Byte +NN_vpmovsqb, // Down Convert QWord to Byte +NN_vpmovusqb, // Down Convert QWord to Byte +NN_vpmovqw, // Down Convert QWord to Word +NN_vpmovsqw, // Down Convert QWord to Word +NN_vpmovusqw, // Down Convert QWord to Word +NN_vpmovqd, // Down Convert QWord to DWord +NN_vpmovsqd, // Down Convert QWord to DWord +NN_vpmovusqd, // Down Convert QWord to DWord +NN_vpmovdb, // Down Convert DWord to Byte +NN_vpmovsdb, // Down Convert DWord to Byte +NN_vpmovusdb, // Down Convert DWord to Byte +NN_vpmovdw, // Down Convert DWord to Word +NN_vpmovsdw, // Down Convert DWord to Word +NN_vpmovusdw, // Down Convert DWord to Word +NN_vpmovwb, // Down Convert Word to Byte +NN_vpmovswb, // Down Convert Word to Byte +NN_vpmovuswb, // Down Convert Word to Byte +NN_vpmullq, // Multiply Packed Integers and Store Low Result +NN_vpmultishiftqb, // Select Packed Unaligned Bytes from Quadword Sources +NN_vpord, // Bitwise Logical Or +NN_vporq, // Bitwise Logical Or +NN_vprold, // Bit Rotate Left +NN_vprolvd, // Bit Rotate Left +NN_vprolq, // Bit Rotate Left +NN_vprolvq, // Bit Rotate Left +NN_vprord, // Bit Rotate Right +NN_vprorvd, // Bit Rotate Right +NN_vprorq, // Bit Rotate Right +NN_vprorvq, // Bit Rotate Right +NN_vpscatterdd, // Scatter Packed Dword with Signed Dword +NN_vpscatterdq, // Scatter Packed Qword with Signed Qword Indices +NN_vpscatterqd, // Scatter Packed Dword with Signed Dword +NN_vpscatterqq, // Scatter Packed Qword with Signed Qword Indices +NN_vpsraq, // Bit Shift Arithmetic Right +NN_vpsllvw, // Variable Bit Shift Left Logical +NN_vpsrlvw, // Variable Bit Shift Right Logical +NN_vptestnmb, // Logical NAND and Set +NN_vptestnmw, // Logical NAND and Set +NN_vptestnmd, // Logical NAND and Set +NN_vptestnmq, // Logical NAND and Set +NN_vshuff32x4, // Shuffle Packed Values at 128-bit Granularity +NN_vshuff64x2, // Shuffle Packed Values at 128-bit Granularity +NN_vshufi32x4, // Shuffle Packed Values at 128-bit Granularity +NN_vshufi64x2, // Shuffle Packed Values at 128-bit Granularity +NN_vpternlogd, // Bitwise Ternary Logic +NN_vpternlogq, // Bitwise Ternary Logic +NN_vptestmb, // Logical AND and Set Mask +NN_vptestmw, // Logical AND and Set Mask +NN_vptestmd, // Logical AND and Set Mask +NN_vptestmq, // Logical AND and Set Mask +NN_vpsravw, // Variable Bit Shift Right Arithmetic +NN_vpsravq, // Variable Bit Shift Right Arithmetic +NN_vpxord, // Exclusive Or +NN_vpxorq, // Exclusive Or +NN_vrangepd, // Range Restriction Calculation For Packed Pairs of Float64 Values +NN_vrangeps, // Range Restriction Calculation For Packed Pairs of Float32 Values +NN_vrangesd, // Range Restriction Calculation From a pair of Scalar Float64 Values +NN_vrangess, // Range Restriction Calculation From a Pair of Scalar Float32 Values +NN_vrcp14pd, // Compute Approximate Reciprocals of Packed Float64 Values +NN_vrcp14sd, // Compute Approximate Reciprocal of Scalar Float64 Value +NN_vrcp14ps, // Compute Approximate Reciprocals of Packed Float32 Values +NN_vrcp14ss, // Compute Approximate Reciprocal of Scalar Float32 Value +NN_vreducepd, // Perform Reduction Transformation on Packed Float64 Values +NN_vreducesd, // Perform a Reduction Transformation on a Scalar Float64 Value +NN_vreduceps, // Perform Reduction Transformation on Packed Float32 Values +NN_vreducess, // Perform a Reduction Transformation on a Scalar Float32 Value +NN_vrndscalepd, // Round Packed Float64 Values To Include A Given Number Of Fraction Bits +NN_vrndscalesd, // Round Scalar Float64 Value To Include A Given Number Of Fraction Bits +NN_vrndscaleps, // Round Packed Float32 Values To Include A Given Number Of Fraction Bits +NN_vrndscaless, // Round Scalar Float32 Value To Include A Given Number Of Fraction Bits +NN_vrsqrt14pd, // Compute Approximate Reciprocals of Square Roots of Packed Float64 Values +NN_vrsqrt14sd, // Compute Approximate Reciprocal of Square Root of Scalar Float64 Value +NN_vrsqrt14ps, // Compute Approximate Reciprocals of Square Roots of Packed Float32 Values +NN_vrsqrt14ss, // Compute Approximate Reciprocal of Square Root of Scalar Float32 Value +NN_vscalefpd, // Scale Packed Float64 Values With Float64 Values +NN_vscalefsd, // Scale Scalar Float64 Values With Float64 Values +NN_vscalefps, // Scale Packed Float32 Values With Float32 Values +NN_vscalefss, // Scale Scalar Float32 Value With Float32 Value +NN_vscatterdps, // Scatter Packed Single, Packed Double with Signed Dword and Qword Indices +NN_vscatterdpd, // Scatter Packed Single, Packed Double with Signed Dword and Qword Indices +NN_vscatterqps, // Scatter Packed Single, Packed Double with Signed Dword and Qword Indices +NN_vscatterqpd, // Scatter Packed Single, Packed Double with Signed Dword and Qword Indices + +NN_vexp2pd, // Approximation to the Exponential 2^x of Packed Double-Precision Floating-Point Values with Less Than 2^-23 Relative Error +NN_vexp2ps, // Approximation to the Exponential 2^x of Packed Single-Precision Floating-Point Values with Less Than 2^-23 Relative Error +NN_vrcp28pd, // Approximation to the Reciprocal of Packed Double-Precision Floating-Point Values with Less Than 2^-28 Relative Error +NN_vrcp28sd, // Approximation to the Reciprocal of Scalar Double-Precision Floating-Point Value with Less Than 2^-28 Relative Error +NN_vrcp28ps, // Approximation to the Reciprocal of Packed Single-Precision Floating-Point Values with Less Than 2^-28 Relative Error +NN_vrcp28ss, // Approximation to the Reciprocal of Scalar Single-Precision Floating-Point Value with Less Than 2^-28 Relative Error +NN_vrsqrt28pd, // Approximation to the Reciprocal Square Root of Packed Double-Precision Floating-Point Values with Less Than 2^-28 Relative Error +NN_vrsqrt28sd, // Approximation to the Reciprocal Square Root of Scalar Double-Precision Floating-Point Value with Less Than 2^-28 Relative Error +NN_vrsqrt28ps, // Approximation to the Reciprocal Square Root of Packed Single-Precision Floating-Point Values with Less Than 2^-28 Relative Error +NN_vrsqrt28ss, // Approximation to the Reciprocal Square Root of Scalar Single-Precision Floating-Point Value with Less Than 2^-28 Relative Error + +NN_vgatherpf0dps, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T0 Hint +NN_vgatherpf0qps, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T0 Hint +NN_vgatherpf0dpd, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T0 Hint +NN_vgatherpf0qpd, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T0 Hint +NN_vgatherpf1dps, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T1 Hint +NN_vgatherpf1qps, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T1 Hint +NN_vgatherpf1dpd, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T1 Hint +NN_vgatherpf1qpd, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T1 Hint +NN_vscatterpf0dps, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T0 Hint with Intent to Write +NN_vscatterpf0qps, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T0 Hint with Intent to Write +NN_vscatterpf0dpd, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T0 Hint with Intent to Write +NN_vscatterpf0qpd, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T0 Hint with Intent to Write +NN_vscatterpf1dps, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T1 Hint with Intent to Write +NN_vscatterpf1qps, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T1 Hint with Intent to Write +NN_vscatterpf1dpd, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T1 Hint with Intent to Write +NN_vscatterpf1qpd, // Sparse Prefetch Packed SP/DP Data Values with Signed Dword, Signed Qword Indices Using T1 Hint with Intent to Write + +// AVX-512 comparison pseudo-ops + +NN_vpcmpltd, // Compare Packed Integer Values into Mask +NN_vpcmpled, // Compare Packed Integer Values into Mask +NN_vpcmpneqd, // Compare Packed Integer Values into Mask +NN_vpcmpnltd, // Compare Packed Integer Values into Mask +NN_vpcmpnled, // Compare Packed Integer Values into Mask + +NN_vpcmpequd, // Compare Packed Integer Values into Mask +NN_vpcmpltud, // Compare Packed Integer Values into Mask +NN_vpcmpleud, // Compare Packed Integer Values into Mask +NN_vpcmpnequd, // Compare Packed Integer Values into Mask +NN_vpcmpnltud, // Compare Packed Integer Values into Mask +NN_vpcmpnleud, // Compare Packed Integer Values into Mask + +NN_vpcmpltq, // Compare Packed Integer Values into Mask +NN_vpcmpleq, // Compare Packed Integer Values into Mask +NN_vpcmpneqq, // Compare Packed Integer Values into Mask +NN_vpcmpnltq, // Compare Packed Integer Values into Mask +NN_vpcmpnleq, // Compare Packed Integer Values into Mask + +NN_vpcmpequq, // Compare Packed Integer Values into Mask +NN_vpcmpltuq, // Compare Packed Integer Values into Mask +NN_vpcmpleuq, // Compare Packed Integer Values into Mask +NN_vpcmpnequq, // Compare Packed Integer Values into Mask +NN_vpcmpnltuq, // Compare Packed Integer Values into Mask +NN_vpcmpnleuq, // Compare Packed Integer Values into Mask + +// Opmask instructions + +NN_kaddw, // ADD Two Masks +NN_kaddb, // ADD Two Masks +NN_kaddq, // ADD Two Masks +NN_kaddd, // ADD Two Masks +NN_kandw, // Bitwise Logical AND Masks +NN_kandb, // Bitwise Logical AND Masks +NN_kandq, // Bitwise Logical AND Masks +NN_kandd, // Bitwise Logical AND Masks +NN_kandnw, // Bitwise Logical AND NOT Masks +NN_kandnb, // Bitwise Logical AND NOT Masks +NN_kandnq, // Bitwise Logical AND NOT Masks +NN_kandnd, // Bitwise Logical AND NOT Masks +NN_kmovw, // Move from and to Mask Registers +NN_kmovb, // Move from and to Mask Registers +NN_kmovq, // Move from and to Mask Registers +NN_kmovd, // Move from and to Mask Registers +NN_kunpckbw, // Unpack for Mask Registers +NN_kunpckwd, // Unpack for Mask Registers +NN_kunpckdq, // Unpack for Mask Registers +NN_knotw, // NOT Mask Register +NN_knotb, // NOT Mask Register +NN_knotq, // NOT Mask Register +NN_knotd, // NOT Mask Register +NN_korw, // Bitwise Logical OR Masks +NN_korb, // Bitwise Logical OR Masks +NN_korq, // Bitwise Logical OR Masks +NN_kord, // Bitwise Logical OR Masks +NN_kortestw, // OR Masks And Set Flags +NN_kortestb, // OR Masks And Set Flags +NN_kortestq, // OR Masks And Set Flags +NN_kortestd, // OR Masks And Set Flags +NN_kshiftlw, // Shift Left Mask Registers +NN_kshiftlb, // Shift Left Mask Registers +NN_kshiftlq, // Shift Left Mask Registers +NN_kshiftld, // Shift Left Mask Registers +NN_kshiftrw, // Shift Right Mask Registers +NN_kshiftrb, // Shift Right Mask Registers +NN_kshiftrq, // Shift Right Mask Registers +NN_kshiftrd, // Shift Right Mask Registers +NN_kxnorw, // Bitwise Logical XNOR Masks +NN_kxnorb, // Bitwise Logical XNOR Masks +NN_kxnorq, // Bitwise Logical XNOR Masks +NN_kxnord, // Bitwise Logical XNOR Masks +NN_ktestw, // Packed Bit Test Masks and Set Flags +NN_ktestb, // Packed Bit Test Masks and Set Flags +NN_ktestq, // Packed Bit Test Masks and Set Flags +NN_ktestd, // Packed Bit Test Masks and Set Flags +NN_kxorw, // Bitwise Logical XOR Masks +NN_kxorb, // Bitwise Logical XOR Masks +NN_kxorq, // Bitwise Logical XOR Masks +NN_kxord, // Bitwise Logical XOR Masks + +// SHA Extensions + +NN_sha1rnds4, // Perform Four Rounds of SHA1 Operation +NN_sha1nexte, // Calculate SHA1 State Variable E after Four Rounds +NN_sha1msg1, // Perform an Intermediate Calculation for the Next Four SHA1 Message Dwords +NN_sha1msg2, // Perform a Final Calculation for the Next Four SHA1 Message Dwords +NN_sha256rnds2, // Perform Two Rounds of SHA256 Operation +NN_sha256msg1, // Perform an Intermediate Calculation for the Next Four SHA256 Message Dwords +NN_sha256msg2, // Perform a Final Calculation for the Next Four SHA256 Message Dwords + +// Intel Software Guard Extensions + +NN_encls, // Execute an Enclave System Function of Specified Leaf Number +NN_enclu, // Execute an Enclave User Function of Specified Leaf Number + +// AMD XOP + +NN_vfrczpd, // Extract Fraction Packed Double-Precision Floating-Point +NN_vfrczps, // Extract Fraction Packed Single-Precision Floating-Point +NN_vfrczsd, // Extract Fraction Scalar Double-Precision Floating-Point +NN_vfrczss, // Extract Fraction Scalar Single-Precision Floating Point +NN_vpcmov, // Vector Conditional Moves +NN_vpcomb, // Compare Vector Signed Bytes +NN_vpcomd, // Compare Vector Signed Doublewords +NN_vpcomq, // Compare Vector Signed Quadwords +NN_vpcomub, // Compare Vector Unsigned Bytes +NN_vpcomud, // Compare Vector Unsigned Doublewords +NN_vpcomuq, // Compare Vector Unsigned Quadwords +NN_vpcomuw, // Compare Vector Unsigned Words +NN_vpcomw, // Compare Vector Signed Words +NN_vpermil2pd, // Permute Two-Source Double-Precision Floating-Point Values +NN_vpermil2ps, // Permute Two-Source Single-Precision Floating-Point Values +NN_vphaddbd, // Packed Horizontal Add Signed Byte to Signed Doubleword +NN_vphaddbq, // Packed Horizontal Add Signed Byte to Signed Quadword +NN_vphaddbw, // Packed Horizontal Add Signed Byte to Signed Word +NN_vphadddq, // Packed Horizontal Add Signed Doubleword to Signed Quadword +NN_vphaddubd, // Packed Horizontal Add Unsigned Byte to Doubleword +NN_vphaddubq, // Packed Horizontal Add Unsigned Byte to Quadword +NN_vphaddubw, // Packed Horizontal Add Unsigned Byte to Word +NN_vphaddudq, // Packed Horizontal Add Unsigned Doubleword to Quadword +NN_vphadduwd, // Packed Horizontal Add Unsigned Word to Doubleword +NN_vphadduwq, // Packed Horizontal Add Unsigned Word to Quadword +NN_vphaddwd, // Packed Horizontal Add Signed Word to Signed Doubleword +NN_vphaddwq, // Packed Horizontal Add Signed Word to Signed Quadword +NN_vphsubbw, // Packed Horizontal Subtract Signed Byte to Signed Word +NN_vphsubdq, // Packed Horizontal Subtract Signed Doubleword to Signed Quadword +NN_vphsubwd, // Packed Horizontal Subtract Signed Word to Signed Doubleword +NN_vpmacsdd, // Packed Multiply Accumulate Signed Doubleword to Signed Doubleword +NN_vpmacsdqh, // Packed Multiply Accumulate Signed High Doubleword to Signed Quadword +NN_vpmacsdql, // Packed Multiply Accumulate Signed Low Doubleword to Signed Quadword +NN_vpmacssdd, // Packed Multiply Accumulate Signed Doubleword to Signed Doubleword with Saturation +NN_vpmacssdqh, // Packed Multiply Accumulate Signed High Doubleword to Signed Quadword with Saturation +NN_vpmacssdql, // Packed Multiply Accumulate Signed Low Doubleword to Signed Quadword with Saturation +NN_vpmacsswd, // Packed Multiply Accumulate Signed Word to Signed Doubleword with Saturation +NN_vpmacssww, // Packed Multiply Accumulate Signed Word to Signed Word with Saturation +NN_vpmacswd, // Packed Multiply Accumulate Signed Word to Signed Doubleword +NN_vpmacsww, // Packed Multiply Accumulate Signed Word to Signed Word +NN_vpmadcsswd, // Packed Multiply, Add and Accumulate Signed Word to Signed Doubleword with Saturation +NN_vpmadcswd, // Packed Multiply Add and Accumulate Signed Word to Signed Doubleword +NN_vpperm, // Packed Permute Bytes +NN_vprotb, // Packed Rotate Bytes +NN_vprotd, // Packed Rotate Doublewords +NN_vprotq, // Packed Rotate Quadwords +NN_vprotw, // Packed Rotate Words +NN_vpshab, // Packed Shift Arithmetic Bytes +NN_vpshad, // Packed Shift Arithmetic Doublewords +NN_vpshaq, // Packed Shift Arithmetic Quadwords +NN_vpshaw, // Packed Shift Arithmetic Words +NN_vpshlb, // Packed Shift Logical Bytes +NN_vpshld, // Packed Shift Logical Doublewords +NN_vpshlq, // Packed Shift Logical Quadwords +NN_vpshlw, // Packed Shift Logical Words + +// AMD XOP comparison pseudo-ops + +NN_vpcomltb, // Compare Vector Signed Bytes +NN_vpcomleb, // Compare Vector Signed Bytes +NN_vpcomgtb, // Compare Vector Signed Bytes +NN_vpcomgeb, // Compare Vector Signed Bytes +NN_vpcomeqb, // Compare Vector Signed Bytes +NN_vpcomneqb, // Compare Vector Signed Bytes +NN_vpcomfalseb, // Compare Vector Signed Bytes +NN_vpcomtrueb, // Compare Vector Signed Bytes + +NN_vpcomltw, // Compare Vector Signed Words +NN_vpcomlew, // Compare Vector Signed Words +NN_vpcomgtw, // Compare Vector Signed Words +NN_vpcomgew, // Compare Vector Signed Words +NN_vpcomeqw, // Compare Vector Signed Words +NN_vpcomneqw, // Compare Vector Signed Words +NN_vpcomfalsew, // Compare Vector Signed Words +NN_vpcomtruew, // Compare Vector Signed Words + +NN_vpcomltd, // Compare Vector Signed Doublewords +NN_vpcomled, // Compare Vector Signed Doublewords +NN_vpcomgtd, // Compare Vector Signed Doublewords +NN_vpcomged, // Compare Vector Signed Doublewords +NN_vpcomeqd, // Compare Vector Signed Doublewords +NN_vpcomneqd, // Compare Vector Signed Doublewords +NN_vpcomfalsed, // Compare Vector Signed Doublewords +NN_vpcomtrued, // Compare Vector Signed Doublewords + +NN_vpcomltq, // Compare Vector Signed Quadwords +NN_vpcomleq, // Compare Vector Signed Quadwords +NN_vpcomgtq, // Compare Vector Signed Quadwords +NN_vpcomgeq, // Compare Vector Signed Quadwords +NN_vpcomeqq, // Compare Vector Signed Quadwords +NN_vpcomneqq, // Compare Vector Signed Quadwords +NN_vpcomfalseq, // Compare Vector Signed Quadwords +NN_vpcomtrueq, // Compare Vector Signed Quadwords + +NN_vpcomltub, // Compare Vector Unsigned Bytes +NN_vpcomleub, // Compare Vector Unsigned Bytes +NN_vpcomgtub, // Compare Vector Unsigned Bytes +NN_vpcomgeub, // Compare Vector Unsigned Bytes +NN_vpcomequb, // Compare Vector Unsigned Bytes +NN_vpcomnequb, // Compare Vector Unsigned Bytes +NN_vpcomfalseub, // Compare Vector Unsigned Bytes +NN_vpcomtrueub, // Compare Vector Unsigned Bytes + +NN_vpcomltuw, // Compare Vector Unsigned Words +NN_vpcomleuw, // Compare Vector Unsigned Words +NN_vpcomgtuw, // Compare Vector Unsigned Words +NN_vpcomgeuw, // Compare Vector Unsigned Words +NN_vpcomequw, // Compare Vector Unsigned Words +NN_vpcomnequw, // Compare Vector Unsigned Words +NN_vpcomfalseuw, // Compare Vector Unsigned Words +NN_vpcomtrueuw, // Compare Vector Unsigned Words + +NN_vpcomltud, // Compare Vector Unsigned Doublewords +NN_vpcomleud, // Compare Vector Unsigned Doublewords +NN_vpcomgtud, // Compare Vector Unsigned Doublewords +NN_vpcomgeud, // Compare Vector Unsigned Doublewords +NN_vpcomequd, // Compare Vector Unsigned Doublewords +NN_vpcomnequd, // Compare Vector Unsigned Doublewords +NN_vpcomfalseud, // Compare Vector Unsigned Doublewords +NN_vpcomtrueud, // Compare Vector Unsigned Doublewords + +NN_vpcomltuq, // Compare Vector Unsigned Quadwords +NN_vpcomleuq, // Compare Vector Unsigned Quadwords +NN_vpcomgtuq, // Compare Vector Unsigned Quadwords +NN_vpcomgeuq, // Compare Vector Unsigned Quadwords +NN_vpcomequq, // Compare Vector Unsigned Quadwords +NN_vpcomnequq, // Compare Vector Unsigned Quadwords +NN_vpcomfalseuq, // Compare Vector Unsigned Quadwords +NN_vpcomtrueuq, // Compare Vector Unsigned Quadwords + +// AMD Excavator + +NN_monitorx, // Setup Monitor Address +NN_mwaitx, // Monitor Wait with Timeout + +// AMD Zen + +NN_clzero, // Zero out 64 byte cache + +// Intel Processor Trace + +NN_ptwrite, // Write Data to a Processor Trace Packet + +// new Intel AVX-512 instructions (December 2016) + +NN_v4fmaddps, // Packed Single-Precision Floating-Point Fused Multiply-Add (4-iterations) +NN_v4fnmaddps, // Packed Single-Precision Floating-Point Fused Multiply-Add (4-iterations) +NN_v4fmaddss, // Scalar Single-Precision Floating-Point Fused Multiply-Add (4-iterations) +NN_v4fnmaddss, // Scalar Single-Precision Floating-Point Fused Multiply-Add (4-iterations) +NN_vp4dpwssd, // Dot Product of Signed Words with Dword Accumulation (4-iterations) +NN_vp4dpwssds, // Dot Product of Signed Words with Dword Accumulation and Saturation (4-iterations) +NN_vpopcntd, // Return the Count of Number of Bits Set to 1 in DWORD +NN_vpopcntq, // Return the Count of Number of Bits Set to 1 in QWORD + +// Read Processor ID + +NN_rdpid, // Read Processor ID + +// Invoke VM function + +NN_vmfunc, // Invoke VM function + +// Control-flow Enforcement + +NN_incsspd, // Increment Shadow Stack Pointer (by 4) +NN_incsspq, // Increment Shadow Stack Pointer (by 8) +NN_rdsspd, // Read (low 32 bits of) Shadow Stack Pointer +NN_rdsspq, // Read Shadow Stack Pointer +NN_saveprevssp, // Save Previous Shadow Stack Pointer +NN_rstorssp, // Restore saved Shadow Stack Pointer +NN_wrssd, // Write (4 bytes) to shadow stack +NN_wrssq, // Write (8 bytes) to shadow stack +NN_wrussd, // Write (4 bytes) to User Shadow Stack +NN_wrussq, // Write (8 bytes) to User Shadow Stack +NN_setssbsy, // Mark Shadow Stack Busy +NN_clrssbsy, // Clear Shadow Stack Busy Flag +NN_endbr64, // Terminate an Indirect Branch in 64-bit Mode +NN_endbr32, // Terminate an Indirect Branch in 32-bit and Compatibility Mode + +// Undefined Instruction + +NN_ud0, // Undefined Instruction +NN_ud1, // Undefined Instruction + +// Enqueue Stores + +NN_enqcmd, // Enqueue Command +NN_enqcmds, // Enqueue Command Supervisor + +// AMD Zen2 + +NN_mcommit, // Commit Stores to Memory +NN_rdpru, // Read Processor Register + +// Intel Tremont instructions + +NN_cldemote, // Cache Line Demote +NN_enclv, // Execute an Enclave VMM Function of Specified Leaf Number + +// Direct Stores + +NN_movdiri, // Move Doubleword as Direct Store +NN_movdir64b, // Move 64 Bytes as Direct Store + +// Intel WAITPKG instructions + +NN_tpause, // Timed PAUSE +NN_umonitor, // User Level Set Up Monitor Address +NN_umwait, // User Level Monitor Wait + +// Intel Sapphire Rapids instructions + +NN_serialize, // Serialize Instruction Execution + +// Intel TSX + +NN_xresldtrk, // Resume Tracking Load Addresses +NN_xsusldtrk, // Suspend Tracking Load Addresses + +// Intel Affine Transformation instructions + +NN_gf2p8mulb, // Galois Field Multiply Bytes +NN_gf2p8affineqb, // Computes Affine Transformation +NN_gf2p8affineinvqb, // Computes Inverse Affine Transformation + +// VEX versions +NN_vgf2p8mulb, // Galois Field Multiply Bytes +NN_vgf2p8affineqb, // Computes Affine Transformation +NN_vgf2p8affineinvqb, // Computes Inverse Affine Transformation + +// Intrinsics for Saving and Restoring the Extended Processor States (64-bits) + +NN_fxsave64, // Fast save FP context (64-bits) +NN_fxrstor64, // Fast restore FP context (64-bits) + +// AVX-VNNI +NN_vpdpbusd, // Multiply and Add Unsigned and Signed Bytes +NN_vpdpbusds, // Multiply and Add Unsigned and Signed Bytes With Saturation +NN_vpdpwssd, // Multiply and Add Signed Word Integers +NN_vpdpwssds, // Multiply and Add Signed Word Integers With Saturation + +// AVX512-VBMI2 +NN_vpcompressb, // Store Sparse Packed Byte Integer Values Into Dense Memory/Register +NN_vpcompressw, // Store Sparse Packed Word Integer Values Into Dense Memory/Register +NN_vpexpandb, // Expand Byte Values +NN_vpexpandw, // Expand Word Values +NN_vpshldw, // Concatenate and Shift Packed Data Left Logical +NN_vpshldd, // Concatenate and Shift Packed Data Left Logical +NN_vpshldq, // Concatenate and Shift Packed Data Left Logical +NN_vpshldvw, // Concatenate and Variable Shift Packed Data Left Logical +NN_vpshldvd, // Concatenate and Variable Shift Packed Data Left Logical +NN_vpshldvq, // Concatenate and Variable Shift Packed Data Left Logical +NN_vpshrdw, // Concatenate and Shift Packed Data Right Logical +NN_vpshrdd, // Concatenate and Shift Packed Data Right Logical +NN_vpshrdq, // Concatenate and Shift Packed Data Right Logical +NN_vpshrdvw, // Concatenate and Variable Shift Packed Data Right Logical +NN_vpshrdvd, // Concatenate and Variable Shift Packed Data Right Logical +NN_vpshrdvq, // Concatenate and Variable Shift Packed Data Right Logical + +// AVX512-BITALG +NN_vpopcntb, // Return the Count of Number of Bits Set to 1 in BYTE +NN_vpopcntw, // Return the Count of Number of Bits Set to 1 in WORD +NN_vpshufbitqmb, // Shuffle Bits From Quadword Elements Using Byte Indexes Into Mask + +// AVX512-VP2INTERSECT +NN_vp2intersectd, // Compute Intersection Between DWORDS to aPair of Mask Registers +NN_vp2intersectq, // Compute Intersection Between QUADWORDS to aPair of Mask Registers + +// AVX512-BF16 +NN_vcvtne2ps2bf16, // Convert Two Packed Single Data to One Packed BF16 Data +NN_vcvtneps2bf16, // Convert Packed Single Data to Packed BF16 Data +NN_vdpbf16ps, // Dot Product of BF16 Pairs Accumulated Into Packed Single Precision + +// AVX512-FP16 +NN_vaddph, // Add Packed FP16 Values +NN_vcmpph, // Compare Packed FP16 Values +NN_vcvtdq2ph, // Convert Packed Signed Doubleword Integers to Packed FP16 Values +NN_vcvtpd2ph, // Convert Packed Double Precision FP Values to Packed FP16 Values +NN_vcvtph2dq, // Convert Packed FP16 Values to Signed Doubleword Integers +NN_vcvtph2pd, // Convert Packed FP16 Values to FP64 Values +NN_vcvtph2psx, // Convert Packed FP16 Values to Single Precision Floating-Point Values +NN_vcvtph2qq, // Convert Packed FP16 Values to Signed Quadword Integer Values +NN_vcvtph2udq, // Convert Packed FP16 Values to Unsigned Doubleword Integers +NN_vcvtph2uqq, // Convert Packed FP16 Values to Unsigned Quadword Integers +NN_vcvtph2uw, // Convert Packed FP16 Values to Unsigned Word Integers +NN_vcvtph2w, // Convert Packed FP16 Values to Signed Word Integers +NN_vcvtps2phx, // Convert Packed Single Precision Floating-Point Values to Packed FP16 Values +NN_vcvtqq2ph, // Convert Packed Signed Quadword Integers to Packed FP16 Values +NN_vcvttph2dq, // Convert with Truncation Packed FP16 Values to Signed Doubleword Integers +NN_vcvttph2qq, // Convert with Truncation Packed FP16 Values to Signed Quadword Integers +NN_vcvttph2udq, // Convert with Truncation Packed FP16 Values to Unsigned Doubleword Integers +NN_vcvttph2uqq, // Convert with Truncation Packed FP16 Values to Unsigned Quadword Integers +NN_vcvttph2uw, // Convert Packed FP16 Values to Unsigned Word Integers +NN_vcvttph2w, // Convert Packed FP16 Values to Signed Word Integers +NN_vcvtudq2ph, // Convert Packed Unsigned Doubleword Integers to Packed FP16 Values +NN_vcvtuqq2ph, // Convert Packed Unsigned Quadword Integers to Packed FP16 Values +NN_vcvtuw2ph, // Convert Packed Unsigned Word Integers to FP16 Values +NN_vcvtw2ph, // Convert Packed Signed Word Integers to FP16 Values +NN_vdivph, // Divide Packed FP16 Values +NN_vfcmaddcph, // Complex Multiply and Accumulate FP16 Values +NN_vfcmulcph, // Complex Multiply FP16 Values +NN_vfmadd132ph, // Fused Multiply-Add of Packed FP16 Values +NN_vfmadd213ph, // Fused Multiply-Add of Packed FP16 Values +NN_vfmadd231ph, // Fused Multiply-Add of Packed FP16 Values +NN_vfmaddcph, // Complex Multiply and Accumulate FP16 Values +NN_vfmaddsub132ph, // Fused Multiply-Alternating Add/Subtract of Packed FP16 Values +NN_vfmaddsub213ph, // Fused Multiply-Alternating Add/Subtract of Packed FP16 Values +NN_vfmaddsub231ph, // Fused Multiply-Alternating Add/Subtract of Packed FP16 Values +NN_vfmsub132ph, // Fused Multiply-Subtract of Packed FP16 Values +NN_vfmsub213ph, // Fused Multiply-Subtract of Packed FP16 Values +NN_vfmsub231ph, // Fused Multiply-Subtract of Packed FP16 Values +NN_vfmsubadd132ph, // Fused Multiply-Alternating Subtract/Add of Packed FP16 Values +NN_vfmsubadd213ph, // Fused Multiply-Alternating Subtract/Add of Packed FP16 Values +NN_vfmsubadd231ph, // Fused Multiply-Alternating Subtract/Add of Packed FP16 Values +NN_vfmulcph, // Complex Multiply FP16 Values +NN_vfnmadd132ph, // Fused Multiply-Add of Packed FP16 Values +NN_vfnmadd213ph, // Fused Multiply-Add of Packed FP16 Values +NN_vfnmadd231ph, // Fused Multiply-Add of Packed FP16 Values +NN_vfnmsub132ph, // Fused Multiply-Subtract of Packed FP16 Values +NN_vfnmsub213ph, // Fused Multiply-Subtract of Packed FP16 Values +NN_vfnmsub231ph, // Fused Multiply-Subtract of Packed FP16 Values +NN_vfpclassph, // Test Types of Packed FP16 Values +NN_vgetexpph, // Fused Multiply-Subtract of Packed FP16 Values +NN_vgetmantph, // Extract FP16 Vector of Normalized Mantissas from FP16 Vector +NN_vmaxph, // Return Maximum of Packed FP16 Values +NN_vminph, // Return Minimum of Packed FP16 Values +NN_vmulph, // Multiply Packed FP16 Values +NN_vrcpph, // Compute Reciprocals of Packed FP16 Values +NN_vreduceph, // Perform Reduction Transformation on Packed FP16 Values +NN_vrndscaleph, // Round Packed FP16 Values to Include a Given Number of Fraction Bits +NN_vrsqrtph, // Compute Reciprocals of Square Roots of Packed FP16 Values +NN_vscalefph, // Scale Packed FP16 Values with FP16 Values +NN_vsqrtph, // Compute Square Root of Packed FP16 Values +NN_vsubph, // Subtract Packed FP16 Values + +// AVX512-FP16-128N +NN_vmovw, // Move Word + +// AVX512-FP16-SCALAR +NN_vaddsh, // Add Scalar FP16 Values +NN_vcmpsh, // Compare Scalar FP16 Values +NN_vcomish, // Compare Scalar Ordered FP16 Values and Set EFLAGS +NN_vcvtsd2sh, // Convert Low FP64 Value to an FP16 Value +NN_vcvtsh2sd, // Convert Low FP16 Value to an FP64 Value +NN_vcvtsh2si, // Convert Low FP16 Value to Signed Integer +NN_vcvtsh2ss, // Convert Low FP16 Value to FP32 Value +NN_vcvtsh2usi, // Convert Low FP16 Value to Unsigned Integer +NN_vcvtsi2sh, // Convert a Signed Doubleword/Quadword Integer to an FP16 Value +NN_vcvtss2sh, // Convert Low FP32 Value to an FP16 Value +NN_vcvttsh2si, // Convert with Truncation Low FP16 Value to a Signed Integer +NN_vcvttsh2usi, // Convert with Truncation Low FP16 Value to an Unsigned Integer +NN_vcvtusi2sh, // Convert Unsigned Doubleword Integer to an FP16 Value +NN_vdivsh, // Divide Scalar FP16 Values +NN_vfcmaddcsh, // Complex Multiply and Accumulate Scalar FP16 Values +NN_vfcmulcsh, // Complex Multiply Scalar FP16 Values +NN_vfmadd132sh, // Fused Multiply-Add of Scalar FP16 Values +NN_vfmadd213sh, // Fused Multiply-Add of Scalar FP16 Values +NN_vfmadd231sh, // Fused Multiply-Add of Scalar FP16 Values +NN_vfmaddcsh, // Complex Multiply and Accumulate Scalar FP16 Values +NN_vfmsub132sh, // Fused Multiply-Subtract of Scalar FP16 Values +NN_vfmsub213sh, // Fused Multiply-Subtract of Scalar FP16 Values +NN_vfmsub231sh, // Fused Multiply-Subtract of Scalar FP16 Values +NN_vfmulcsh, // Complex Multiply Scalar FP16 Values +NN_vfnmadd132sh, // Fused Multiply-Add of Scalar FP16 Values +NN_vfnmadd213sh, // Fused Multiply-Add of Scalar FP16 Values +NN_vfnmadd231sh, // Fused Multiply-Add of Scalar FP16 Values +NN_vfnmsub132sh, // Fused Multiply-Subtract of Scalar FP16 Values +NN_vfnmsub213sh, // Fused Multiply-Subtract of Scalar FP16 Values +NN_vfnmsub231sh, // Fused Multiply-Subtract of Scalar FP16 Values +NN_vfpclasssh, // Test Types of Packed FP16 Values +NN_vgetexpsh, // Convert Exponents of Packed FP16 Values to FP16 Values +NN_vgetmantsh, // Extract FP16 Vector of Normalized Mantissas from FP16 Vector +NN_vmaxsh, // Return Maximum of Scalar FP16 Values +NN_vminsh, // Return Minimum Scalar FP16 Value +NN_vmovsh, // Move Scalar FP16 Value +NN_vmulsh, // Multiply Scalar FP16 Values +NN_vrcpsh, // Compute Reciprocals of Packed FP16 Values +NN_vreducesh, // Perform Reduction Transformation on Packed FP16 Values +NN_vrndscalesh, // Round Packed FP16 Values to Include a Given Number of Fraction Bits +NN_vrsqrtsh, // Compute Reciprocals of Square Roots of Packed FP16 Values +NN_vscalefsh, // Scale Packed FP16 Values with FP16 Values +NN_vsqrtsh, // Compute Square Root of Scalar FP16 Value +NN_vsubsh, // Subtract Scalar FP16 Value +NN_vucomish, // Unordered Compare Scalar FP16 Values and Set EFLAGS + +// User Interrupt (UINTR) + +NN_clui, // Clear User Interrupt Flag +NN_stui, // Set User Interrupt Flag +NN_testui, // Determine User Interrupt Flag +NN_uiret, // User-Interrupt Return +NN_senduipi, // Send User Interprocessor Interrupts + +// Prediction history reset (HRESET) +NN_hreset, // History Reset + + +NN_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +I5_null = 0, // Unknown Operation + +// +// Intel 8080-8085 instructions +// + +I5_aci, +I5_adc, Z80_adc = I5_adc, +I5_add, Z80_add = I5_add, +I5_adi, +I5_ana, +I5_ani, +I5_call, +I5_cnz, +I5_cz, +I5_cnc, +I5_cc, +I5_cpo, +I5_cpe, +I5_cp, +I5_cm, +I5_cmc, +I5_cmp, +I5_cpi, +I5_cma, +I5_daa, +I5_dad, +I5_dcr, +I5_dcx, +I5_di, Z80_di = I5_di, +I5_ei, Z80_ei = I5_ei, +I5_halt, +I5_in, Z80_in = I5_in, +I5_inr, +I5_inx, +I5_jmp, +I5_jnz, +I5_jz, +I5_jnc, +I5_jc, +I5_jpo, +I5_jpe, +I5_jp, +I5_jm, +I5_lda, +I5_ldax, +I5_lhld, +I5_lxi, +I5_mov, +I5_mvi, +I5_nop, +I5_ora, +I5_ori, +I5_out, Z80_out = I5_out, +I5_pchl, +I5_pop, Z80_pop = I5_pop, +I5_push, Z80_push = I5_push, +I5_ret, +I5_rnz, +I5_rz, +I5_rnc, +I5_rc, +I5_rpo, +I5_rpe, +I5_rp, +I5_rm, +I5_ral, +I5_rlc, +I5_rar, +I5_rrc, +I5_rst, +I5_sbb, +I5_sbi, +I5_stc, +I5_sphl, +I5_sta, +I5_stax, +I5_shld, +I5_sui, +I5_sub, Z80_sub = I5_sub, +I5_xra, +I5_xri, +I5_xchg, +I5_xthl, + +I5_rim, +I5_sim, + +// +// Z80 extensions +// + +Z80_and, +Z80_bit, +Z80_call, +Z80_ccf, +Z80_cp, +Z80_cpd, +Z80_cpdr, +Z80_cpi, +Z80_cpir, +Z80_cpl, +Z80_dec, +Z80_djnz, +Z80_ex, +Z80_exx, +Z80_halt, +Z80_im, +Z80_inc, +Z80_ind, +Z80_indr, +Z80_ini, +Z80_inir, +Z80_jp, +Z80_jr, +Z80_ld, +Z80_ldd, +Z80_lddr, +Z80_ldi, +Z80_ldir, +Z80_neg, +Z80_or, +Z80_otdr, +Z80_otir, +Z80_outd, +Z80_outi, +Z80_res, +Z80_ret, +Z80_reti, +Z80_retn, +Z80_rl, +Z80_rla, +Z80_rlc, +Z80_rlca, +Z80_rld, +Z80_rr, +Z80_rra, +Z80_rrc, +Z80_rrca, +Z80_rrd, +Z80_scf, +Z80_sbc, +Z80_set, +Z80_sla, +Z80_sra, +Z80_srl, +Z80_xor, +Z80_inp, // undocumented +Z80_outp, // undocumented +Z80_srr, // undocumented + +// +// HD64180 extensions +// + +HD_in0, Z80_in0 = HD_in0, +HD_mlt, Z80_mlt = HD_mlt, +HD_otim, Z80_otim = HD_otim, +HD_otimr, Z80_otimr = HD_otimr, +HD_otdm, Z80_otdm = HD_otdm, +HD_otdmr, Z80_otdmr = HD_otdmr, +HD_out0, Z80_out0 = HD_out0, +HD_slp, Z80_slp = HD_slp, +HD_tst, Z80_tst = HD_tst, +HD_tstio, Z80_tstio = HD_tstio, + +// +// A80 special instructions +// + +A80_lbcd, +A80_lded, +A80_lspd, +A80_lixd, +A80_liyd, +A80_sbcd, +A80_sded, +A80_sspd, +A80_sixd, +A80_siyd, +A80_xtix, +A80_xtiy, +A80_spix, +A80_spiy, +A80_pcix, +A80_pciy, +A80_mvra, +A80_mvia, +A80_mvar, +A80_mvai, +A80_addix, +A80_addiy, +A80_addc, +A80_addcix, +A80_addciy, +A80_subc, +A80_subcix, +A80_subciy, +A80_jrc, +A80_jrnc, +A80_jrz, +A80_jrnz, +A80_cmpi, +A80_cmpd, +A80_im0, +A80_im1, +A80_im2, +A80_otd, +A80_oti, + +// Intel 8085 undocumented instructions +// (info from http://oak.oakland.edu/pub/cpm/maclib/i8085.lib) + +I5_dsub, // (HL) <- (HL)-(BC), affects all flags +I5_arhl, // SHIFT HL RIGHT ONE BIT, (H7 IS DUPLICATED, L0 IS SHIFTED INTO CY) +I5_rdel, // ROTATE DE LEFT ONE BIT THRU CY, (E0 RECEIVES CY, CY RECEIVES D7) +I5_ldhi, // (DE) <- (HL)+arg +I5_ldsi, // (DE) <- (SP)+arg +I5_shlx, // ((DE)) <- (HL) +I5_lhlx, // (HL) <- ((DE)) +I5_rstv, // RESTART 40H ON V (OVERFLOW) +I5_jx5, // JUMP IF X5 SET +I5_jnx5, // JUMP IF NOT X5 SET + +// Z380 instructions + +Z80_cplw, // Complement HL register +Z80_swap, // Swap upper register word with lower register word +Z80_inw, // Input word +Z80_outw, // Output word +Z80_ldw, // Load word +Z80_addw, // Add word +Z80_subw, // Subtract word +Z80_adcw, // Add with carry word +Z80_sbcw, // Subtract with borrow word +Z80_andw, // AND logical word +Z80_xorw, // XOR logical word +Z80_orw, // OR logical word +Z80_cpw, // Compare word +Z80_ddir, // Decoder directive +Z80_calr, // Call relative +Z80_ldctl, // Load control register +Z80_mtest, // Mode test +Z80_exxx, // Exchange Index Register with Alternate Bank +Z80_exxy, // Exchange Index Register with Alternate Bank +Z80_exall, // Exchange all registers with Alternate Bank +Z80_setc, // Set control bit +Z80_resc, // Reset control bit +Z80_rlcw, // Rotate Left Circular Word +Z80_rrcw, // Rotate Right Circular Word +Z80_rlw, // Rotate Left Word +Z80_rrw, // Rotate Right Word +Z80_slaw, // Shift Left Arithmetic Word +Z80_sraw, // Shift Right Arithmetic Word +Z80_srlw, // Shift Right Logical Word +Z80_multw, // Multiply Word +Z80_multuw, // Multiply Word Unsigned +Z80_divuw, // Divide unsigned +Z80_outaw, // Output word direct to port address +Z80_inaw, // Input word direct from port address +Z80_outa, // Output byte direct to port address +Z80_ina, // Input byte direct from port address +Z80_negw, // Negate word +Z80_exts, // Extend byte sign +Z80_extsw, // Extend word sign +Z80_btest, // Bank test +Z80_ldiw, // Load and increment (word) +Z80_ldirw, // Load and increment, repeat (word) +Z80_lddw, // Load and decrement (word) +Z80_lddrw, // Load and decrement, repeat (word) +Z80_iniw, // Input and increment (word) +Z80_inirw, // Input and increment, repeat (word) +Z80_indw, // Input and decrement (word) +Z80_indrw, // Input and decrement, repeat (word) +Z80_outiw, // Output and increment (word) +Z80_otirw, // Output and increment, repeat (word) +Z80_outdw, // Output and decrement (word) +Z80_otdrw, // Output and decrement, repeat (word) + +// Gameboy instructions + +GB_ldh, +GB_stop, + +I5_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + +I860_null = 0, // Unknown Operation + +// +// Intel 860 XP instructions +// + +I860_adds, +I860_addu, +I860_and, +I860_andh, +I860_andnot, +I860_andnoth, +I860_bc, +I860_bc_t, +I860_bla, +I860_bnc, +I860_bnc_t, +I860_br, +I860_bri, +I860_bte, +I860_btne, +I860_call, +I860_calli, +I860_fadd, +I860_faddp, +I860_faddz, +I860_famov, +I860_fiadd, +I860_fisub, +I860_fix, +I860_fld, +I860_flush, +I860_fmlow_dd, +I860_fmul, +I860_form, +I860_frcp, +I860_frsqr, +I860_fst, +I860_fsub, +I860_ftrunc, +I860_fxfr, +I860_fzchkl, +I860_fzchks, +I860_introvr, +I860_ixfr, +I860_ld_c, +I860_ld, +I860_ldint, +I860_ldio, +I860_lock, +I860_or, +I860_orh, +I860_pfadd, +I860_pfaddp, +I860_pfaddz, +I860_pfamov, +I860_pfeq, +I860_pfgt, +I860_pfiadd, +I860_pfisub, +I860_pfix, +I860_pfld, +I860_pfle, +I860_pfmul, +I860_pfmul3_dd, +I860_pform, +I860_pfsub, +I860_pftrunc, +I860_pfzchkl, +I860_pfzchks, +I860_pst_d, +I860_scyc, +I860_shl, +I860_shr, +I860_shra, +I860_shrd, +I860_st_c, +I860_st, +I860_stio, +I860_subs, +I860_subu, +I860_trap, +I860_unlock, +I860_xor, +I860_xorh, +// +// iNTEL 860 XP Pipelined F-P instructions +// +I860_r2p1, +I860_r2pt, +I860_r2ap1, +I860_r2apt, +I860_i2p1, +I860_i2pt, +I860_i2ap1, +I860_i2apt, +I860_rat1p2, +I860_m12apm, +I860_ra1p2, +I860_m12ttpa, +I860_iat1p2, +I860_m12tpm, +I860_ia1p2, +I860_m12tpa, +I860_r2s1, +I860_r2st, +I860_r2as1, +I860_r2ast, +I860_i2s1, +I860_i2st, +I860_i2as1, +I860_i2ast, +I860_rat1s2, +I860_m12asm, +I860_ra1s2, +I860_m12ttsa, +I860_iat1s2, +I860_m12tsm, +I860_ia1s2, +I860_m12tsa, +I860_mr2p1, +I860_mr2pt, +I860_mr2mp1, +I860_mr2mpt, +I860_mi2p1, +I860_mi2pt, +I860_mi2mp1, +I860_mi2mpt, +I860_mrmt1p2, +I860_mm12mpm, +I860_mrm1p2, +I860_mm12ttpm, +I860_mimt1p2, +I860_mm12tpm, +I860_mim1p2, +I860_mr2s1, +I860_mr2st, +I860_mr2ms1, +I860_mr2mst, +I860_mi2s1, +I860_mi2st, +I860_mi2ms1, +I860_mi2mst, +I860_mrmt1s2, +I860_mm12msm, +I860_mrm1s2, +I860_mm12ttsm, +I860_mimt1s2, +I860_mm12tsm, +I860_mim1s2, + +I860_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +I51_null = 0, // Unknown Operation + +I51_acall, // Absolute Call +I51_add, // Add Second Operand to Acc +I51_addc, // Add Second Operand to Acc with carry +I51_ajmp, // Absolute Jump +I51_anl, // Logical AND (op1 &= op2) +I51_cjne, // Compare Operands and JNE +I51_clr, // Clear Operand (0) +I51_cpl, // Complement Operand +I51_da, // Decimal Adjust Accumulator +I51_dec, // Decrement Operand +I51_div, // Divide Acc by B +I51_djnz, // Decrement Operand and JNZ +I51_inc, // Increment Operand +I51_jb, // Jump if Bit is set +I51_jbc, // Jump if Bit is set & clear Bit +I51_jc, // Jump if Carry is set +I51_jmp, // Jump indirect relative to Data Pointer +I51_jnb, // Jump if Bit is clear +I51_jnc, // Jump if Carry is clear +I51_jnz, // Jump if Acc is not zero +I51_jz, // Jump if Acc is zero +I51_lcall, // Long Subroutine Call +I51_ljmp, // Long Jump +I51_mov, // Move (Op1 <- Op2) +I51_movc, // Move code byte relative to second op to Acc +I51_movx, // Move from/to external RAM +I51_mul, // Multiply Acc by B +I51_nop, // No operation +I51_orl, // Logical OR (op1 |= op2) +I51_pop, // Pop from Stack and put in Direct RAM +I51_push, // Push from Direct RAM to Stack +I51_ret, // Return from subroutine +I51_reti, // Return from Interrupt +I51_rl, // Rotate Acc left +I51_rlc, // Rotate Acc left through Carry +I51_rr, // Rotate Acc right +I51_rrc, // Rotate Acc right through Carry +I51_setb, // Set Direct Bit +I51_sjmp, // Short jump +I51_subb, // Subtract Second Operand from Acc with Borrow +I51_swap, // Swap nibbles of Acc +I51_xch, // Exchange Operands +I51_xchd, // Exchange Digit in Acc with Indirect RAM +I51_xrl, // Exclusive OR (op1 ^= op2) + +// 80251 instructions + +I51_jsle, // Jump if less than or equal (signed) +I51_jsg, // Jump if greater than (signed) +I51_jle, // Jump if less than or equal +I51_jg, // Jump if greater than +I51_jsl, // Jump if less than (signed) +I51_jsge, // Jump if greater than or equal (signed) +I51_je, // Jump if equal +I51_jne, // Jump if not equal +I51_trap, // Trap +I51_ejmp, // Extended jump +I51_ecall, // Extended call +I51_eret, // Extended return +I51_movh, // Move immediate 16-bit data to the high word of a dword (double-word) register +I51_movz, // Move 8-bit register to 16-bit register with zero extension +I51_movs, // Move 8-bit register to 16-bit register with sign extension +I51_srl, // Shift logical right by 1 bit +I51_sra, // Shift arithmetic right by 1 bit +I51_sll, // Shift logical left by 1 bit +I51_sub, // Subtract +I51_cmp, // Compare + +// 51mx instructions +I51_emov, // Move (A <- @PRi+disp) or (@PRi+disp <- A) + +I51_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +TMS_null = 0, // Unknown Operation +TMS_abs, // Absolute value of Acc +TMS_adcb, // Add ACCB to Acc With Carry +TMS_add, // Add to Acc +TMS_addb, // Add ACCB to Acc +TMS_addc, // Add to Acc With Carry +TMS_adds, // Add to Acc With Sign-Extension Suppressed +TMS_addt, // Add to Acc With Shift Specified by TREG1 +TMS_adrk, // Add to Aux Register With Short Immediate +TMS_and, // AND With Acc +TMS_andb, // AND ACCD With Acc +TMS_apac, // Add P Register to Acc +TMS_apl, // AND Data Memory Value With DBMR +TMS_apl2, // AND Data Memory Value With Long Constant +TMS_b, // Branch Unconditionally +TMS_bacc, // Branch to Location (Acc) +TMS_baccd, // Branch to Location (Acc) Delayed +TMS_banz, // Branch on Aux Register Not Zero +TMS_banzd, // Branch on Aux Register Not Zero Delayed +TMS_bcnd, // Branch Conditionally +TMS_bcndd, // Branch Conditionally Delayed +TMS_bd, // Branch Unconditionally Delayed +TMS_bit, // Test Bit +TMS_bitt, // Test Bit Specified by TREG2 +TMS_bldd, // Block Move From Data Memory to Data Memory +TMS_bldp, // Block Move From Data Memory to Program Memory +TMS_blpd, // Block Move From Program Memory to Data Memory +TMS_bsar, // Barrel Shift +TMS_cala, // Call Subroutine at (Acc) +TMS_calad, // Call Subroutine at (Acc) Delayed +TMS_call, // Call Unconditionally +TMS_calld, // Call Unconditionally Delayed +TMS_cc, // Call Conditionally +TMS_ccd, // Call Conditionally Delayed +TMS_clrc, // Clear Control Bit +TMS_cmpl, // Complement Acc +TMS_cmpr, // Compare Aux Register With ARCR +TMS_cpl, // Compare DBMR With Data Value +TMS_cpl2, // Compare Long Immediate With Data Value +TMS_crgt, // Test for Acc Greater Than ACCB +TMS_crlt, // Test for Acc Smaller Than ACCB +TMS_dmov, // Data Move in Data Memory +TMS_estop, // Emulator Stop +TMS_exar, // Exchange ACCB With Acc +TMS_idle, // Idle Until Interrupt +TMS_idle2, // Idle Until Interrupt - Low Power Mode +TMS_in, // Input Data From Port +TMS_intr, // Soft Interrupt +TMS_lacb, // Load Acc With ACCB +TMS_lacc, // Load Acc With Shift +TMS_lacl, // Load Low Acc and Clear High Acc +TMS_lact, // Load Acc With Shift Specified by TREG1 +TMS_lamm, // Load Acc With Memory-Mapped Register +TMS_lar, // Load Aux Register +TMS_ldp, // Load Data Memory Pointer +TMS_lmmr, // Load Memory-Mapped Register +TMS_lph, // Load Product High Register +TMS_lst, // Load Status Register +TMS_lt, // Load TREG0 +TMS_lta, // Load TREG0 and Accumulate Previous Product +TMS_ltd, // Load TREG0,Accumulate Previous Product and Move Data +TMS_ltp, // Load TREG0 and Store P -> Acc +TMS_lts, // Load TREG0 and Subtract Previous Product +TMS_mac, // Multiply and Accumulate +TMS_macd, // Multiply and Accumulate With Data Move +TMS_madd, // Multiply and Accumulate With Data Move and Dynamic Addressing +TMS_mads, // Multiply and Accumulate With Dynamic Addressing +TMS_mar, // Modify Aux Register +TMS_mpy, // Multiply +TMS_mpya, // Multiply and Accumulate Previous Product +TMS_mpys, // Multiply and Subtract Previous Product +TMS_mpyu, // Multiply Unsigned +TMS_neg, // Negate Acc +TMS_nmi, // Nonmaskable Interrupt +TMS_nop, // No Operation +TMS_norm, // Normalize Contents of Acc +TMS_opl, // OR With DBMS +TMS_opl2, // OR With Long Immediate +TMS_or, // OR With Acc +TMS_orb, // OR ACCB With Accumulator +TMS_out, // Out Data to Port +TMS_pac, // Load Acc <- P +TMS_pop, // Pop Top of Stack to Low Acc +TMS_popd, // Pop Top of Stack to Data Memory +TMS_pshd, // Push Data Memory Value Onto Stack +TMS_push, // Push Low Acc Onto Stack +TMS_ret, // Return From Subroutine +TMS_retc, // Return Conditionally +TMS_retcd, // Return Conditionally Delayed +TMS_retd, // Return From Subroutine Delayed +TMS_rete, // Enable Interrupts and Return From Interrupt +TMS_reti, // Return From Interrupt +TMS_rol, // Rotate Acc Left +TMS_rolb, // Rotate ACCB and Acc Left +TMS_ror, // Rotate Acc Right +TMS_rorb, // Rotate ACCB and Acc Right +TMS_rpt, // Repeat Next Instruction +TMS_rptb, // Repeat Block +TMS_rptz, // Repeat Preceded by Clearing Acc and P +TMS_sacb, // Store Acc in ACCB +TMS_sach, // Store High Acc With Shift +TMS_sacl, // Store Low Acc With Shift +TMS_samm, // Store Acc in Memory-Mapped Register +TMS_sar, // Store Aux Register +TMS_sath, // Barrel Shift Acc as Specified by TREG1(4) +TMS_satl, // Barrel Shift Acc as Specified by TREG1(3-0) +TMS_sbb, // Subtract ACCB From Acc +TMS_sbbb, // Subtract ACCB From Acc With Borrow +TMS_sbrk, // Subtract From Aux Register Short Immediate +TMS_setc, // Set Control Bit +TMS_sfl, // Shift Acc Left +TMS_sflb, // Shift ACCB and Acc Left +TMS_sfr, // Shift Acc Right +TMS_sfrb, // Shift ACCB and Acc Right +TMS_smmr, // Store Memory-Mapped Register +TMS_spac, // Subtract P From Acc +TMS_sph, // Store High P Register +TMS_spl, // Store Low P Register +TMS_splk, // Store Parallel Long Immediate +TMS_spm, // Store ACCB and Acc Right +TMS_sqra, // Square and Accumulate Previous Product +TMS_sqrs, // Square and Subtract Previous Product +TMS_sst, // Store Status Register +TMS_sub, // Subtract From Acc +TMS_subb, // Subtract From Acc With Borrow +TMS_subc, // Conditional Subtract +TMS_subs, // Subtract From Acc With Sign-Extension Suppressed +TMS_subt, // Subtract From Acc With Shift Specified by TREG1 +TMS_tblr, // Table Read +TMS_tblw, // Table Write +TMS_trap, // Software Interrupt +TMS_xc, // Execute Conditionally +TMS_xor, // Exclusive-OR With Acc +TMS_xorb, // Exclusive-OR of ACCB With Acc +TMS_xpl, // Exclusive-OR Data Memory Value +TMS_xpl2, // Exclusive-OR Data Memory Value +TMS_zalr, // Zero Low Acc Load High Acc With Rounding +TMS_zap, // Zero Acc and P +TMS_zpr, // Zero P Register + +// +// TMS320C2x instructions +// + +TMS2_abs, // Absolute value of accumulator +TMS2_add, // Add to accumulator with shift +TMS2_addc, // Add to accumulator with carry +TMS2_addh, // Add to high accumulator +TMS2_addk, // Add to accumulator short immediate +TMS2_adds, // Add to low accumulator with sign extension suppressed +TMS2_addt, // Add to accumulator with shift specified by T register +TMS2_adlk, // Add to accumulator long immediate with shift +TMS2_adrk, // Add to auxiliary register short immediate +TMS2_and, // And with accumulator +TMS2_andk, // And immediate with accumulator with shift +TMS2_apac, // App P register to accumulator +TMS2_b, // Branch unconditionally +TMS2_bacc, // Branch to address specified by accumulator +TMS2_banz, // Bnrach on auxiliary register not zero +TMS2_bbnz, // Branch if tc bit != 0 +TMS2_bbz, // Branch if tc bit = 0 +TMS2_bc, // Branch on carry +TMS2_bgez, // Branch if accumulator >= 0 +TMS2_bgz, // Branch if accumulator > 0 +TMS2_bioz, // Branch on i/o status = 0 +TMS2_bit, // Test bit +TMS2_bitt, // Test bit specifed by T register +TMS2_blez, // Branch if accumulator <= 0 +TMS2_blkd, // Block move from data memory to data memory +TMS2_blkp, // Block move from program memory to data memory +TMS2_blz, // Branch if accumulator < 0 +TMS2_bnc, // Branch on no carry +TMS2_bnv, // Branch if no overflow +TMS2_bnz, // Branch if accumulator != 0 +TMS2_bv, // Branch on overflow +TMS2_bz, // Branch if accumulator = 0 +TMS2_cala, // Call subroutine indirect +TMS2_call, // Call subroutine +TMS2_cmpl, // Complement accumulator +TMS2_cmpr, // Compare auxiliary register with auxiliary register ar0 +TMS2_cnfd, // Configure block as data memory +TMS2_cnfp, // Configure block as program memory +TMS2_conf, // Configure block as data/program memory +TMS2_dint, // Disable interrupt +TMS2_dmov, // Data move in data memory +TMS2_eint, // Enable interrupt +TMS2_fort, // Format serial port registers +TMS2_idle, // Idle until interrupt +TMS2_in, // Input data from port +TMS2_lac, // Load accumulator with shift +TMS2_lack, // Load accumulator short immediate +TMS2_lact, // Load accumulator with shift specified by T register +TMS2_lalk, // Load accumulator long immediate with shift +TMS2_lar, // Load auxiliary register +TMS2_lark, // Load auxiliary register short immediate +TMS2_larp, // Load auxiliary register pointer +TMS2_ldp, // Load data memory page pointer +TMS2_ldpk, // Load data memory page pointer immediate +TMS2_lph, // Load high P register +TMS2_lrlk, // Load auxiliary register long immediate +TMS2_lst, // Load status register ST0 +TMS2_lst1, // Load status register ST1 +TMS2_lt, // Load T register +TMS2_lta, // Load T register and accumulate previous product +TMS2_ltd, // Load T register, accumulate previous product and move data +TMS2_ltp, // Load T register and store P register in accumulator +TMS2_lts, // Load T register and subtract previous product +TMS2_mac, // Multiply and accumulate +TMS2_macd, // Multiply and accumulate with data move +TMS2_mar, // Modify auxiliary register +TMS2_mpy, // Multiply (with T register, store product in P register) +TMS2_mpya, // Multiply and accumulate previous product +TMS2_mpyk, // Multiply immediate +TMS2_mpys, // Multiply and subtract previous product +TMS2_mpyu, // Multiply unsigned +TMS2_neg, // Negate accumulator +TMS2_nop, // No operation +TMS2_norm, // Normalize contents of accumulator +TMS2_or, // Or with accumulator +TMS2_ork, // Or immediate with accumulator with shift +TMS2_out, // Output data to port +TMS2_pac, // Load accumulator with P register +TMS2_pop, // Pop top of stack to low accumulator +TMS2_popd, // Pop top of stack to data memory +TMS2_pshd, // Push data memory value onto stack +TMS2_push, // Push low accumulator onto stack +TMS2_rc, // Reset carry bit +TMS2_ret, // Return from subroutine +TMS2_rfsm, // Reset serial port frame synchronization mode +TMS2_rhm, // Reset hold mode +TMS2_rol, // Rotate accumulator left +TMS2_ror, // Rotate acuumulator right +TMS2_rovm, // Reset overflow mode +TMS2_rpt, // Repeat instruction as specified by data memory value +TMS2_rptk, // Repeat instruction as specified by immediate value +TMS2_rsxm, // Reset sign extension mode +TMS2_rtc, // Reset test/control flag +TMS2_rtxm, // Reset serial port transmit mode +TMS2_rxf, // Reset external flag +TMS2_sach, // Store high accumulator with shift +TMS2_sacl, // Store low accumulator with shift +TMS2_sar, // Store auxiliary register +TMS2_sblk, // Subtract from accumulator long immediate with shift +TMS2_sbrk, // Subtract from auxiliary register short immediate +TMS2_sc, // Set carry bit +TMS2_sfl, // Shift accumulator left +TMS2_sfr, // Shift accumulator right +TMS2_sfsm, // Set serial port frame synchronization mode +TMS2_shm, // Set hold mode +TMS2_sovm, // Set overflow mode +TMS2_spac, // Subtract P register from accumulator +TMS2_sph, // Store high P register +TMS2_spl, // Store low P register +TMS2_spm, // Set P register output shift mode +TMS2_sqra, // Square and accumulate +TMS2_sqrs, // Square and subtract previous product +TMS2_sst, // Store status register ST0 +TMS2_sst1, // Store status register ST1 +TMS2_ssxm, // Set sign extension mode +TMS2_stc, // Set test/control flag +TMS2_stxm, // Set serial port transmit mode +TMS2_sub, // Subtract from accumulator with shift +TMS2_subb, // Subtract from accumulator with borrow +TMS2_subc, // Conditional subtract +TMS2_subh, // Subtract from high accumulator +TMS2_subk, // Subtract from accumulator shoft immediate +TMS2_subs, // Subtract from low accumulator with sign extension suppressed +TMS2_subt, // Subtract from accumulator with shift specified by T register +TMS2_sxf, // Set external flag +TMS2_tblr, // Table read +TMS2_tblw, // Table write +TMS2_trap, // Software interrupt +TMS2_xor, // Exclusive or with accumulator +TMS2_xork, // Exclusive or immediate with accumulator with shift +TMS2_zac, // Zero accumulator +TMS2_zalh, // Zero low accumulator and load high accumulator +TMS2_zalr, // Zero low accumulator and load high accumulator with rounding +TMS2_zals, // Zero low accumulator and load high accumulator with sign extension suppressed + +TMS_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + M65_null = 0, // Unknown Operation + + // NMOS instructions + + M65_adc, // A <- (A) + M + C + M65_anc, // A <- A /\ M, C <- ~A7 + M65_and, // A <- (A) /\ M + M65_ane, // M <-[(A)\/$EE] /\ (X)/\(M) + M65_arr, // A <- [(A /\ M) >> 1] + M65_asl, // C <- A7, A <- (A) << 1 + M65_asr, // A <- [(A /\ M) >> 1] + M65_bcc, // if C=0, PC = PC + offset + M65_bcs, // if C=1, PC = PC + offset + M65_beq, // if Z=1, PC = PC + offset + M65_bit, // Z <- ~(A /\ M) N<-M7 V<-M6 + M65_bmi, // if N=1, PC = PC + offset + M65_bne, // if Z=0, PC = PC + offset + M65_bpl, // if N=0, PC = PC + offset + M65_brk, // Stack <- PC, PC <- ($fffe) + M65_bvc, // if V=0, PC = PC + offset + M65_bvs, // if V=1, PC = PC + offset + M65_clc, // C <- 0 + M65_cld, // D <- 0 + M65_cli, // I <- 0 + M65_clv, // V <- 0 + M65_cmp, // (A - M) -> NZC + M65_cpx, // (X - M) -> NZC + M65_cpy, // (Y - M) -> NZC + M65_dcp, // M <- (M)-1, (A-M) -> NZC + M65_dec, // M <- (M) - 1 + M65_dex, // X <- (X) - 1 + M65_dey, // Y <- (Y) - 1 + M65_eor, // A <- (A) \-/ M + M65_inc, // M <- (M) + 1 + M65_inx, // X <- (X) +1 + M65_iny, // Y <- (Y) + 1 + M65_isb, // M <- (M) - 1,A <- (A)-M-~C + M65_jmp, // PC <- Address + M65_jmpi, // (PC <- Address) + M65_jsr, // Stack <- PC, PC <- Address + M65_lae, // X,S,A <- (S /\ M) + M65_lax, // A <- M, X <- M + M65_lda, // A <- M + M65_ldx, // X <- M + M65_ldy, // Y <- M + M65_lsr, // C <- A0, A <- (A) >> 1 + M65_lxa, // X04 <- (X04) /\ M04, A04 <- (A04) /\ M04 + M65_nop, // [no operation] + M65_ora, // A <- (A) V M + M65_pha, // Stack <- (A) + M65_php, // Stack <- (P) + M65_pla, // A <- (Stack) + M65_plp, // A <- (Stack) + M65_rla, // M <- (M << 1) /\ (A) + M65_rol, // C <- A7 & A <- A << 1 + C + M65_ror, // C<-A0 & A<- (A7=C + A>>1) + M65_rra, // M <- (M >> 1) + (A) + C + M65_rti, // P <- (Stack), PC <-(Stack) + M65_rts, // PC <- (Stack) + M65_sax, // M <- (A) /\ (X) + M65_sbc, // A <- (A) - M - ~C + M65_sbx, // X <- (X)/\(A) - M + M65_sec, // C <- 1 + M65_sed, // D <- 1 + M65_sei, // I <- 1 + M65_sha, // M <- (A) /\ (X) /\ (PCH+1) + M65_shs, // X <- (A) /\ (X), S <- (X), M <- (X) /\ (PCH+1) + M65_shx, // M <- (X) /\ (PCH+1) + M65_shy, // M <- (Y) /\ (PCH+1) + M65_slo, // M <- (M >> 1) + A + C + M65_sre, // M <- (M >> 1) \-/ A + M65_sta, // M <- (A) + M65_stx, // M <- (X) + M65_sty, // M <- (Y) + M65_tax, // X <- (A) + M65_tay, // Y <- (A) + M65_tsx, // X <- (S) + M65_txa, // A <- (X) + M65_txs, // S <- (X) + M65_tya, // A <- (Y) + + // CMOS instructions + + M65_bbr0, // Branch if bit 0 reset + M65_bbr1, // Branch if bit 1 reset + M65_bbr2, // Branch if bit 2 reset + M65_bbr3, // Branch if bit 3 reset + M65_bbr4, // Branch if bit 4 reset + M65_bbr5, // Branch if bit 5 reset + M65_bbr6, // Branch if bit 6 reset + M65_bbr7, // Branch if bit 7 reset + M65_bbs0, // Branch if bit 0 set + M65_bbs1, // Branch if bit 1 set + M65_bbs2, // Branch if bit 2 set + M65_bbs3, // Branch if bit 3 set + M65_bbs4, // Branch if bit 4 set + M65_bbs5, // Branch if bit 5 set + M65_bbs6, // Branch if bit 6 set + M65_bbs7, // Branch if bit 7 set + M65_rmb0, // Reset memory bit 0 + M65_rmb1, // Reset memory bit 1 + M65_rmb2, // Reset memory bit 2 + M65_rmb3, // Reset memory bit 3 + M65_rmb4, // Reset memory bit 4 + M65_rmb5, // Reset memory bit 5 + M65_rmb6, // Reset memory bit 6 + M65_rmb7, // Reset memory bit 7 + M65_smb0, // Set memory bit 0 + M65_smb1, // Set memory bit 1 + M65_smb2, // Set memory bit 2 + M65_smb3, // Set memory bit 3 + M65_smb4, // Set memory bit 4 + M65_smb5, // Set memory bit 5 + M65_smb6, // Set memory bit 6 + M65_smb7, // Set memory bit 7 + M65_stz, // Store zero + M65_tsb, // Test and set bits + M65_trb, // Test and reset bits + M65_phy, // Push Y register + M65_ply, // Pull Y register + M65_phx, // Push X register + M65_plx, // Pull X register + M65_bra, // Branch always + M65_wai, // Wait for interrupt + M65_stp, // Stop processor + + M65_last, +}; + + + + +// The instruction types (``itype''s) +// m65* CPUs implements. + +enum m65_itype_t +{ + // http://www.westerndesigncenter.com/wdc/datasheets/Programmanual.pdf + M65816_null=0, // Unknown Operation + M65816_adc, // Add with carry + M65816_and, // AND A with memory + M65816_asl, // Shift memory or A left + M65816_bcc, // Branch if carry clear + M65816_bcs, // Branch if carry set + M65816_beq, // Branch if equal + M65816_bit, // Test memory bits against A + M65816_bmi, // Branch if minus + M65816_bne, // Branch if not equal + M65816_bpl, // Branch if plus + M65816_bra, // Branch always + M65816_brk, // Software break + M65816_brl, // Branch always long + M65816_bvc, // Branch if overflow clear + M65816_bvs, // Branch if overflow set + M65816_clc, // Clear carry flag + M65816_cld, // Clear decimal mode flag + M65816_cli, // Clear interrupt disable flag + M65816_clv, // Clear overflow flag + M65816_cmp, // Compare A with memory + M65816_cop, // Co-processor enable + M65816_cpx, // Compare X with memory + M65816_cpy, // Compare Y with memory + M65816_dec, // Decrement + M65816_dex, // Decrement X + M65816_dey, // Decrement Y + M65816_eor, // XOR A with M + M65816_inc, // Increment + M65816_inx, // Increment X + M65816_iny, // Increment Y + M65816_jml, // Jump long (inter-bank) + M65816_jmp, // Jump + M65816_jsl, // Jump to subroutine long (inter-bank) + M65816_jsr, // Jump to subroutine + M65816_lda, // Load A from memory + M65816_ldx, // Load X from memory + M65816_ldy, // Load Y from memory + M65816_lsr, // Logical shift memory or A right + M65816_mvn, // Block move next + M65816_mvp, // Block move prev + M65816_nop, // Nop + M65816_ora, // Or A with memory + M65816_pea, // Push effective absolute address + M65816_pei, // Push effective indirect address + M65816_per, // Push effective PC-relative indirect address + M65816_pha, // Push A + M65816_phb, // Push B (data bank register) + M65816_phd, // Push D (direct page register) + M65816_phk, // Push K (program bank register) + M65816_php, // Push processor status + M65816_phx, // Push X + M65816_phy, // Push Y + M65816_pla, // Pull A + M65816_plb, // Pull B + M65816_pld, // Pull D + M65816_plp, // Pull processor status + M65816_plx, // Pull X + M65816_ply, // Pull Y + M65816_rep, // Reset status bits + M65816_rol, // Rotate memory or A left + M65816_ror, // Rotate memory or A right + M65816_rti, // Return from interrupt + M65816_rtl, // Return from subroutine long + M65816_rts, // Return from subroutine + M65816_sbc, // Subtract with borrow from A + M65816_sec, // Set carry flag + M65816_sed, // Set decimal mode flag + M65816_sei, // Set interrupt disable flag + M65816_sep, // Set status bits + M65816_sta, // Store A to memory + M65816_stp, // Stop processor + M65816_stx, // Store X to memory + M65816_sty, // Store Y to memory + M65816_stz, // Store zero to memory + M65816_tax, // Transfer A to X + M65816_tay, // Transfer A to Y + M65816_tcd, // Transfer 16-bit A to D (direct page register) + M65816_tcs, // Transfer A to S + M65816_tdc, // Transfer 16-bit D to A + M65816_trb, // Test and reset memory bits against A + M65816_tsb, // Test and set memory bits against A + M65816_tsc, // Transfer S to A + M65816_tsx, // Transfer S to X + M65816_txa, // Transfer X to A + M65816_txs, // Transfer X to S + M65816_txy, // Transfer X to Y + M65816_tya, // Transfer Y to A + M65816_tyx, // Transfer Y to X + M65816_wai, // Wait for interrupt + M65816_wdm, // Reserved + M65816_xba, // Exchange bytes in A + M65816_xce, // Exchange carry and emulation bits + M65816_last +}; + +/* + * Interactive disassembler (IDA) + * Copyright (c) 1990-2025 Hex-Rays + * PDP11 module. + * Copyright (c) 1995-2006 by Iouri Kharon. + * E-mail: yjh@styx.cabel.net + * + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +pdp_null = 0, // Unknown Operation + +pdp_halt, // Stop CPU +pdp_wait, // Wait interrupt +pdp_rti, // Interrupt return +pdp_bpt, // Trap to Debbuger +pdp_iot, // Trap to 20 (i/o) +pdp_reset, // Reset CPU and device +pdp_rtt, // Interrupt return and ignore dbg-flag +pdp_mfpt, // Load Processor Type (* hi model) +pdp_jmp, // Absolute jmp +pdp_rts, // Return into subroutine +pdp_spl, // Set Prior. +pdp_nop, // Not operation +pdp_clc, // Clear C bit in PSW +pdp_clv, // Clear V bit in PSW +pdp_clz, // Clear Z bit in PSW +pdp_cln, // Clear N bit in PSW +pdp_ccc, // Clear All Condition bits in PSW +pdp_sec, // Set C bit in PSW +pdp_sev, // Set V bit in PSW +pdp_sez, // Set Z bit in PSW +pdp_sen, // Set N bit in PSW +pdp_scc, // Set All Condition bits bit in PSW +pdp_swab, // Exchange byte in word +pdp_br, // Relative jmp +pdp_bne, // Jump if Z=0 +pdp_beq, // Jump if Z=1 +pdp_bge, // Jump if N^V=0 +pdp_blt, // Jump if N^V=1 +pdp_bgt, // Jump if Z|(N^V)=0 +pdp_ble, // Jump if Z|(N^V)=1 +pdp_jsr, // Call procedure +pdp_clr, // Clear operand +pdp_com, // Inverse operand +pdp_inc, // Increment operand +pdp_dec, // Decrement operand +pdp_neg, // op = -op +pdp_adc, // Add with Carry +pdp_sbc, // Substract with Carry +pdp_tst, // Test operand +pdp_ror, // Cyclic shift rignt +pdp_rol, // Cyclic shift left +pdp_asr, // Arifmetic shift rignt +pdp_asl, // Arifmetic shift left +pdp_mark, // Return and empty stack +pdp_mfpi, // Load from previous instruction space (*hi model) +pdp_mtpi, // Store to previous instruction space (*hi model) +pdp_sxt, // N=>op +pdp_mov, // Move operand +pdp_cmp, // Compare operands +pdp_bit, // Check Bit's +pdp_bic, // Clear Bit's +pdp_bis, // Set Bit's +pdp_add, // Add operands +pdp_sub, // Substract operands +pdp_mul, // Multiple Reg (*eis) +pdp_div, // Divide Reg (*eis) +pdp_ash, // Multistep shift (*eis) +pdp_ashc, // Multistep shift 2 reg (*eis) +pdp_xor, // Exclusive or (*eis) +pdp_fadd, // Floating Add (*fis) +pdp_fsub, // Floating Subtract (*fis) +pdp_fmul, // Floating Multiple (*fis) +pdp_fdiv, // Floating Divide (*fis) +pdp_sob, // +pdp_bpl, // Jump if N=0 +pdp_bmi, // Jump if N=1 +pdp_bhi, // Jump if (!C)&(!Z)=0 +pdp_blos, // Jump if C|Z=1 +pdp_bvc, // Jump if V=0 +pdp_bvs, // Jump if V=1 +pdp_bcc, // Jump if C=0 +pdp_bcs, // Jump if C=1 +pdp_emt, // Trap to System +pdp_trap, // Trap to user/compiler +pdp_mtps, // Store PSW (*lsi11 only) +pdp_mfpd, // Load from previous data space (*hi model) +pdp_mtpd, // Store to previous data space (*hi model) +pdp_mfps, // Load PSW (*lsi11 only) + // FPU instruction +pdp_cfcc, // Copy cond.codes into FPS to PSW +pdp_setf, // Set Float +pdp_seti, // Set Integer +pdp_setd, // Set Double +pdp_setl, // Set Long Integer +pdp_ldfps, // Load FPS +pdp_stfps, // Store FPS +pdp_stst, // Load interrupt status +pdp_clrd, // Clear +pdp_tstd, // Test +pdp_absd, // op = mod(op) +pdp_negd, // op = -op +pdp_muld, // Multiple +pdp_modd, // Get int. part +pdp_addd, // Add +pdp_ldd, // Load in Acc +pdp_subd, // Substract +pdp_cmpd, // Compare +pdp_std, // Store into Acc +pdp_divd, // Divide +pdp_stexp, // Store exponent +pdp_stcdi, // Store and convert double/float to integer/long +pdp_stcdf, // Store and convert double/float to float/double +pdp_ldexp, // Load exponent +pdp_ldcif, // Load and convert integer/long to double/float +pdp_ldcfd, // Load and convert float/double to double/float +pdp_call, // Jsr PC, +pdp_return, // RTS PC +pdp_compcc, // Complex Condition Codes + +pdp_last + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + mc_null = 0, // Unknown Operation + + mc_abcd, // summa with BCD correction + mc_add, // summa with Dn + mc_adda, // summa with An + mc_addi, // summa with immediate + mc_addq, // -"- + mc_addx, // summa 2 reg and const + mc_and, // logical and + mc_andi, // logical and with immediate + mc_asl, // shift by count + mc_asr, // -"- + mc_b, // branch if cond code + mc_bchg, // bit chang + mc_bclr, // bit clear + mc_bftst, // bit field test + mc_bfchg, // bit field change + mc_bfclr, // bit field set in 0 + mc_bfset, // bit field set in 1 + mc_bfextu, // bit field extract (unsigned) + mc_bfexts, // bit field extract (signed) + mc_bfffo, // bit field offset + mc_bfins, // bit field include + mc_bgnd, // CPU32- enter backgound mode + mc_bkpt, // disable TRAP at any cycles + mc_bra, // branch + mc_bset, // bit set + mc_bsr, // relative call + mc_btst, // bit test + mc_callm, // call module [descrtiptor] (020) + mc_cas, // compare and swap + mc_cas2, // -"- + mc_chk, // compare and trap + mc_chk2, // -"- + mc_cinv, // invalidate cache + mc_clr, // clear data + mc_cmp, // compare data + mc_cmp2, // -"- + mc_cmpa, // compare address + mc_cmpi, // compare immediate + mc_cmpm, // compare memory + mc_cpush, // invalidate cache + mc_db, // (djnz) if cond code + mc_divs, // sign divide + mc_divsl, // -"- + mc_divu, // unsign didide + mc_divul, // -"- + mc_eor, // logical xor + mc_eori, // logical xor with immediate + mc_exg, // exchange registers + mc_ext, // analog cwd + mc_extb, // analog cwq + mc_fabs, // FP abs + mc_facos, // FP acos + mc_fadd, // FP add + mc_fasin, // FP asin + mc_fatan, // FP atan + mc_fatanh, // FP atanh + mc_fb, // fBcc + mc_fcmp, // FP cmp + mc_fcos, // FP cos + mc_fcosh, // FP cosh + mc_fdabs, // FP abs double rounded + mc_fdadd, // FP add + mc_fdb, // fDBcc + mc_fddiv, // FP div double rounded + mc_fdiv, // FP div + mc_fdmove, // FP mov double rounded + mc_fdmul, // FP mul double rounded + mc_fdneg, // FP neg double rounded + mc_fdsqrt, // FP sqrt double rounded + mc_fdsub, // FP sub double rounded + mc_fetox, // FP e**x + mc_fetoxm1, // FP e**x-1 + mc_fgetexp, // FP extract exponent + mc_fgetman, // FP extract mantissa + mc_fint, // FP get integer part + mc_fintrz, // FP get integer part by round + mc_flog2, // FP log[2] + mc_flog10, // FP log[10] + mc_flogn, // FP log[e] + mc_flognp1, // FP log[e] x+1 + mc_fmod, // FP mod + mc_fmove, // move float + mc_fmovecr, // move floating const + mc_fmovem, // move float register list + mc_fmul, // FM mul + mc_fneg, // FP neg + mc_fnop, // float nop + mc_frem, // FP rem + mc_frestore, // restore status + mc_fs, // fScc + mc_fsabs, // FP abs single rounded + mc_fsadd, // FP add single rounded + mc_fsave, // Fsave status + mc_fscale, // FP *2**x + mc_fsdiv, // FP div single rounded + mc_fsgldiv, // FP single div + mc_fsglmul, // FP single mul + mc_fsin, // FP sin + mc_fsincos, // FP sin & cos + mc_fsinh, // FP sinh + mc_fsmove, // FP mov single rounded + mc_fsmul, // FP mul single rounded + mc_fsneg, // FP neg single rounded + mc_fsqrt, // FP sqrt + mc_fssqrt, // FP sqrt single rounded + mc_fssub, // FP sub single rounded + mc_fsub, // FP sub + mc_ftan, // FP tan + mc_ftanh, // FP tanh + mc_ftentox, // FP 10**x + mc_ftrap, // fTRAPcc + mc_ftst, // FP tst + mc_ftwotox, // FP 2**x + mc_halt, // (coldfire) Halt the processor + mc_illegal, // trap to vector + mc_jmp, // absolute Jmp + mc_jsr, // absolute jsr + mc_lea, // load effective address + mc_link, // change sp context + mc_lpstop, // cpu32 - stop + mc_lsl, // logical shift + mc_lsr, // -"- + mc_mac, // multiple and accumulate + mc_macl, // multiple and accumulate with register load + mc_move, // move memory + mc_move16, // move 16 byte + mc_movea, // move move with address reg + mc_movec, // move from/to control register + mc_movem, // move register list + mc_movep, // move with data register + mc_moveq, // move with immediate + mc_moves, // mtpd/mfpd + mc_msac, // multiple and subtract + mc_msacl, // multiple and subtract with register load + mc_muls, // sign multiplication + mc_mulu, // unsign multiplication + mc_nbcd, // negative with BCD correct + mc_neg, // negative + mc_negx, // negative with immediate + mc_nop, // null operation + mc_not, // logical not + mc_or, // logical or + mc_ori, // logical or with immediate + mc_pack, // UNP(bcd)+adj -> PACK(bcd) + mc_pea, // lea into stack + mc_pb, // 68851 pbcc + mc_pdb, // 68851 pdbcc + mc_pflush, // invalidate cache + mc_pflushr, // 68851 load root entry + mc_ploadr, // 68851 + mc_ploadw, // 68851 + mc_pmove, // move MMU register + mc_prestore, // 68851 + mc_psave, // 68851 + mc_ps, // 68851 pscc + mc_ptestr, // test a logical address + mc_ptestw, // -"- + mc_ptrap, // 68851 ptrapcc + mc_pulse, // (coldfire) generate unique PST value + mc_pvalid, // 68851 move AL bit + mc_rol, // cycl. shift + mc_ror, // -"- + mc_roxl, // two op cycl shift + mc_roxr, // -"- + mc_reset, // reset + mc_rtd, // mark + mc_rte, // return with SR + mc_rtm, // reload save module state + mc_rtr, // return with CCR + mc_rts, // return + mc_sbcd, // substract with BCD correction + mc_s, // 1 if cond code else 0 + mc_stop, // halt + mc_sub, // substract + mc_suba, // substract address + mc_subi, // substract immediate + mc_subq, // -"- + mc_subx, // substract memory + mc_swap, // hi and low word in register + mc_tas, // test byte sign + mc_tbl, // CPU32-tblu/tblun/tbls/tblsn + mc_trap, // analog int(86) (or trap if cond code) + mc_trapv, // if V then trap + mc_tst, // test destination + mc_unlk, // destroy address ? + mc_unpk, // PACK(bcd)+adj -> UNPACK(bcd) + mc_wddata, // (coldfire) load WDDATA port (pins) + mc_wdebug, // (coldfire) load debug register + mc_atrap, // MacOS atrap + + // new ColdFire instructions + mc_bitrev, // bit reverse + mc_byterev, // byte reverse + mc_ff1, // find first one + mc_intouch, // instruction fetch touch + mc_mov3q, // move 3-bit data quick + mc_mvs, // move with sign extend + mc_mvz, // move with zero-fill + mc_sats, // signed saturate + mc_movclr, // move from accumulator and clear + mc_maaac, // Multiply and Add to 1st Accumulator, Add to 2nd Accumulator + mc_masac, // Multiply and Add to 1st Accumulator, Subtract from 2nd Accumulator + mc_msaac, // Multiply and Subtract to 1st Accumulator, Add to 2nd Accumulator + mc_mssac, // Multiply and Subtract to 1st Accumulator, Subtract to 2nd Accumulator + + mc_remsl, // Signed Divide Remainder + mc_remul, // Unsigned Divide Remainder + + mc_last +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +mc8_null = 0, // Unknown Operation + +mc8_aba, // Add b to a (11) +mc8_ab, // Add b to index register +mc8_adc, // Add with carry +mc8_add, // Add without carry +mc8_addd, // Add 16-bit to D (11) +mc8_ais, // Add immediate value to stack pointer (8) +mc8_aix, // Add immediate value to index register (H:X) (8) +mc8_and, // Logical AND +mc8_andcc, // Logical AND immdiate with CCR (9) +mc8_asr, // Ariphmetic shift right +mc8_bcc, // Branch if carry bit clear +mc8_bclr, // Clear bit in memory +mc8_bcs, // Branch if carry bit set +mc8_beq, // Branch if equal +mc8_bge, // Branch if >= (signed) +mc8_bgt, // Branch if > (signed) +mc8_bhcc, // Branch if half carry bit clear (8) +mc8_bhcs, // Branch if half carry bit set (8) +mc8_bhi, // Branch if > (unsigned) +mc8_bhs, // Branch if >= (unsigned) (11) +mc8_bih, // Branch if IRQ pin high (8) +mc8_bil, // Branch if IRQ pin low (8) +mc8_bit, // Bit test (8) +mc8_ble, // Branch if <= (signed) (8) +mc8_blo, // Branch if < (unsigned) (11) +mc8_bls, // Branch if <= (unsigned) (8) +mc8_blt, // Branch if < (signed) (8) +mc8_bmc, // Branch if interrupt mask clear (8) +mc8_bmi, // Branch if minus +mc8_bms, // Branch if interrupt mask set (8) +mc8_bne, // Branch if not equal +mc8_bpl, // Branch if not plus +mc8_bra, // Branch always +mc8_brclr, // Branch if bit (n) in memory clear +mc8_brn, // Branch never +mc8_brset, // Branch if bit (n) in memory set +mc8_bset, // Set bit in memory +mc8_bsr, // Branch to subroutine +mc8_bvc, // Branch if overflow cleared (11) +mc8_bvs, // Branch if overflow set (11) +mc8_cba, // Compare A to B (11) +mc8_cbeq, // Compare and branch if equal (8) +mc8_clc, // Clear carry bit +mc8_cli, // Clear interrupt mask +mc8_clr, // Clear +mc8_clv, // Clear overflow flag (11) +mc8_cmp, // Compare accumulator with memory +mc8_com, // Complement +mc8_cp, // Compare index register 16 bit (11) +mc8_cpd, // Compare D 16 bit (11) +mc8_cphx, // Compare index register with memory (8) +mc8_cpx, // Compare X (index register low) with memory (8) +mc8_cwai, // Clear CC bits & wait for interrupt (9) +mc8_daa, // Decimal adjust accumulator +mc8_dbnz, // Decrement and branch if not equal (8) +mc8_de, // Decrement index register (11) +mc8_dec, // Decrement +mc8_des, // Decrement stack pointer (11) +mc8_div, // Divide (8) +mc8_eor, // Excusive OR memory with accumulator +mc8_exg, // Exchange register (9) +mc8_fdiv, // Fractional divide 16 by 16 (11) +mc8_idiv, // Integer divide 16 by 16 (11) +mc8_in, // Increment index register (11) +mc8_inc, // Increment +mc8_ins, // Increment stack pointer (11) +mc8_jmp, // Jump +mc8_jsr, // Jump to subroutine +mc8_ld, // Load index register (11) +mc8_lda, // Load accumulator from memory +mc8_ldd, // Load double accumulator (11) +mc8_ldhx, // Load index register from memory (8) +mc8_lds, // Load stack pointer (11) +mc8_ldx, // Load X (index register low) from memory (8) +mc8_lea, // Load register from memory (9) +mc8_lsl, // Logical (or Ariphmetic) shift left +mc8_lsld, // Logical (or Ariphmetic) shift left double (11) +mc8_lsr, // Logical shift right +mc8_lsrd, // Logical shift right double (11) +mc8_mov, // Move (8) +mc8_mul, // Unsigned multiply +mc8_neg, // Negate +mc8_nop, // No Operation +mc8_nsa, // Nibble swap accumulator (8) +mc8_ora, // Inclusive OR memory with accumulator +mc8_orcc, // Inclusive OR immediate with CCR (9) +mc8_psh, // Push index register into stack (low first) (11) +mc8_psha, // Push accumulator A into stack +mc8_pshb, // Push accumulator B into stack (11) +mc8_pshh, // Push H (index register high) into stack (8) +mc8_pshx, // Push X (index register low) into stack (8) +mc8_pul, // Pull index register from stack (hi first) (11) +mc8_pula, // Pull accumulator A from stack +mc8_pulb, // Pull accumulator B from stack (11) +mc8_pulh, // Pull H (index register high) from stack (8) +mc8_pulx, // Pull X (index register low) from stack (8) +mc8_rol, // Rotate left trough carry +mc8_ror, // Rotate right trough carry +mc8_rsp, // Reset stack pointer (8) +mc8_rti, // Reset from interrupt +mc8_rts, // Reset from subroutine +mc8_sba, // Subtract B from A (11) +mc8_sbc, // Subtract with carry +mc8_sec, // Set carry bit +mc8_sei, // Set interrupt mask bit +mc8_sev, // Set Overflow Flag (11) +mc8_sex, // Sign extended (9) +mc8_slp, // Sleep (HD63701 extention) (11) +mc8_st, // Store index register in memory (11) +mc8_sta, // Store accumulator in memory +mc8_std, // Store accumulators in memory (11) +mc8_sthx, // Store H:X (index reg) (8) +mc8_stop, // Enable IRQ pin. Stop oscillator +mc8_sts, // Store stack pointer (11) +mc8_stx, // Store X (index register low) in memory (8) +mc8_sub, // Subtract +mc8_subd, // Subtract 16-bit (11) +mc8_swi, // Software interrupt +mc8_sync, // Syncronisation with interrupt (9) +mc8_tab, // Transfer A to B (11) +mc8_tap, // Transfer A to CCR +mc8_tax, // Transfer accumulator to X (index register low) (8) +mc8_tba, // Transfer B to A (11) +mc8_test, // Address Bus Counts (in test mode) (11) +mc8_tfr, // Transfer register to register (9) +mc8_tpa, // Transfer CCR to A +mc8_ts, // Transfer SP to index reg (11) +mc8_tst, // Test for negative or zero +mc8_tsx, // Transfer SP to index reg (8) +mc8_txa, // Transfer X (index reg low) to accumulator (8) +mc8_txs, // Transfer index reg X to SP +mc8_tys, // Transfer index reg Y to SP (11) +mc8_wai, // Wait for interrupt (11) +mc8_wait, // Enable interrupt. Stop processor (8) +mc8_xgd, // Exchange D with index register (11) +mc8_1, // Special testing command (11) +mc8_2, // Special testing command (11) +mc8_os9, // OS9 system call (9) +// these instructions must be continuous (HD63701 extention) +mc8_aim, +mc8_oim, +mc8_eim, +mc8_tim, + +// new S08 instructions +mc8_bgnd, // Background (8) +mc8_call, // Call Subroutine (8) +mc8_rtc, // Return fom Call (8) + +// Pseudoinstructions +mc8_skip1, // Skip one byte +mc8_skip2, // Skip two bytes + +mc8_last + + }; + +/* + * Interactive disassembler (IDA) + * Copyright (c) 1990-2025 Hex-Rays + * JVM module. + * Copyright (c) 1995-2006 by Iouri Kharon. + * E-mail: yjh@styx.cabel.net + * + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + j_nop = 0, // 0 Do nothing + j_aconst_null, // 1 Push null object reference + j_iconst_m1, // 2 Push integer constant -1 + j_iconst_0, // 3 Push the integer 0 + j_iconst_1, // 4 Push the integer 1 + j_iconst_2, // 5 Push the integer 2 + j_iconst_3, // 6 Push the integer 3 + j_iconst_4, // 7 Push the integer 4 + j_iconst_5, // 8 Push the integer 5 + j_lconst_0, // 9 Push the long integer 0 + j_lconst_1, // 10 Push the long integer 1 + j_fconst_0, // 11 Push the single-precision foating point 0.0 + j_fconst_1, // 12 Push the single-precision foating point 1.0 + j_fconst_2, // 13 Push the single-precision foating point 2.0 + j_dconst_0, // 14 Push the double-precision foating point 2.0 + j_dconst_1, // 15 Push the double-precision foating point 2.0 + j_bipush, // 16 Push one byte signed integer + j_sipush, // 17 Push two-byte signed integer + j_ldc, // 18 Push item from constant pool (i1) + j_ldcw, // 19 Push item from constant pool (i2) + j_ldc2w, // 20 Push long or double from constant pool + j_iload, // 21 Push integer value of the local variable + j_lload, // 22 Push long value of the local variable + j_fload, // 23 Push single float value of the local variable + j_dload, // 24 Push double float value of the local variable + j_aload, // 25 Push object reference from the local variable + j_iload_0, // 26 Push integer value of the local variable #0 + j_iload_1, // 27 Push integer value of the local variable #1 + j_iload_2, // 28 Push integer value of the local variable #2 + j_iload_3, // 29 Push integer value of the local variable #3 + j_lload_0, // 30 Push long value of the local variable #0 + j_lload_1, // 31 Push long value of the local variable #1 + j_lload_2, // 32 Push long value of the local variable #2 + j_lload_3, // 33 Push long value of the local variable #3 + j_fload_0, // 34 Push single float value of the local variable #0 + j_fload_1, // 35 Push single float value of the local variable #1 + j_fload_2, // 36 Push single float value of the local variable #2 + j_fload_3, // 37 Push single float value of the local variable #3 + j_dload_0, // 38 Push double float value of the local variable #0 + j_dload_1, // 39 Push double float value of the local variable #1 + j_dload_2, // 40 Push double float value of the local variable #2 + j_dload_3, // 41 Push double float value of the local variable #3 + j_aload_0, // 42 Push object reference from the local variable #0 + j_aload_1, // 43 Push object reference from the local variable #1 + j_aload_2, // 44 Push object reference from the local variable #2 + j_aload_3, // 45 Push object reference from the local variable #3 + j_iaload, // 46 Push integer from array + j_laload, // 47 Push long from array + j_faload, // 48 Push single float from array + j_daload, // 49 Push double float from array + j_aaload, // 50 Push object refernce from array + j_baload, // 51 Push signed byte from array + j_caload, // 52 Push character from array + j_saload, // 53 Push short from array + j_istore, // 54 Pop integer value into local variable + j_lstore, // 55 Pop long value into local variable + j_fstore, // 56 Pop single float value into local variable + j_dstore, // 57 Pop double float value into local variable + j_astore, // 58 Pop object refernce into local variable + j_istore_0, // 59 Pop integer value into local variable #0 + j_istore_1, // 60 Pop integer value into local variable #1 + j_istore_2, // 61 Pop integer value into local variable #2 + j_istore_3, // 62 Pop integer value into local variable #3 + j_lstore_0, // 63 Pop long value into local variable #0 + j_lstore_1, // 64 Pop long value into local variable #1 + j_lstore_2, // 65 Pop long value into local variable #2 + j_lstore_3, // 66 Pop long value into local variable #3 + j_fstore_0, // 67 Pop single float value into local variable #0 + j_fstore_1, // 68 Pop single float value into local variable #1 + j_fstore_2, // 69 Pop single float value into local variable #2 + j_fstore_3, // 70 Pop single float value into local variable #3 + j_dstore_0, // 71 Pop double float value into local variable + j_dstore_1, // 72 Pop double float value into local variable #0 + j_dstore_2, // 73 Pop double float value into local variable #1 + j_dstore_3, // 74 Pop double float value into local variable #2 + j_astore_0, // 75 Pop object refernce into local variable #0 + j_astore_1, // 76 Pop object refernce into local variable #1 + j_astore_2, // 77 Pop object refernce into local variable #2 + j_astore_3, // 78 Pop object refernce into local variable #3 + j_iastore, // 79 Pop integer from array + j_lastore, // 80 Pop long from array + j_fastore, // 81 Pop single float from array + j_dastore, // 82 Pop double float from array + j_aastore, // 83 Pop object refernce from array + j_bastore, // 84 Pop signed byte from array + j_castore, // 85 Pop character from array + j_sastore, // 86 Pop short from array + j_pop, // 87 Pop top stack word + j_pop2, // 88 Pop top two stack word + j_dup, // 89 Duplicate top stack word + j_dup_x1, // 90 Duplicate top stack word and put two down + j_dup_x2, // 91 Duplicate top stack word and put three down + j_dup2, // 92 Duplicate top two stack word + j_dup2_x1, // 93 Duplicate top two stack words and put two down + j_dup2_x2, // 94 Duplicate top two stack words and put three down + j_swap, // 95 Swap two top stack words + j_iadd, // 96 Integer add + j_ladd, // 97 Long add + j_fadd, // 98 Single float add + j_dadd, // 99 Double float add + j_isub, // 100 Integer subtract + j_lsub, // 101 Long subtract + j_fsub, // 102 Single float subtract + j_dsub, // 103 Double Float subtract + j_imul, // 104 Integer multiply + j_lmul, // 105 Long multiply + j_fmul, // 106 Single float multiply + j_dmul, // 107 Double Float multiply + j_idiv, // 108 Integer divide + j_ldiv, // 109 Long divide + j_fdiv, // 110 Single float divide + j_ddiv, // 111 Double Float divide + j_irem, // 112 Integer reminder + j_lrem, // 113 Long reminder + j_frem, // 114 Single float reminder + j_drem, // 115 Double Float reminder + j_ineg, // 116 Integer negate + j_lneg, // 117 Long negate + j_fneg, // 118 Single float negate + j_dneg, // 119 Double Float negate + j_ishl, // 120 Integer shift left + j_lshl, // 121 Long shift left + j_ishr, // 122 Integer logical shift right + j_lshr, // 123 Long logical shift right + j_iushr, // 124 Integer arithmetic shift right + j_lushr, // 125 Long arithmeticshift right + j_iand, // 126 Integer boolean AND + j_land, // 127 Long boolean AND + j_ior, // 128 Integer boolean OR + j_lor, // 129 Long boolean OR + j_ixor, // 130 Integer boolean XOR + j_lxor, // 131 Long boolean XOR + j_iinc, // 132 Add 8-bit signed const to local variable + j_i2l, // 133 Integer to Long conversion + j_i2f, // 134 Integer to single float conversion + j_i2d, // 135 Integer to double float conversion + j_l2i, // 136 Long to Integer conversion + j_l2f, // 137 Long to single float conversion + j_l2d, // 138 Long to double float conversion + j_f2i, // 139 Single float to Integer conversion + j_f2l, // 140 Single float to Long conversion + j_f2d, // 141 Single float to double float conversion + j_d2i, // 142 Double float to Integer conversion + j_d2l, // 143 Double float to Long conversion + j_d2f, // 144 Double float to double float conversion + j_i2b, // 145 Integer to signed byte conversion + j_i2c, // 146 Integer to unsigned short conversion + j_i2s, // 147 Integer to signed short conversion + j_lcmp, // 148 Long compare + j_fcmpl, // 149 Single float compare (-1 on NaN) + j_fcmpg, // 150 Single float compare (1 on NaN) + j_dcmpl, // 151 Double float compare (-1 on NaN) + j_dcmpg, // 152 Double float compare (1 on NaN) + j_ifeq, // 153 Branch if equal to 0 + j_ifne, // 154 Branch if not equal to 0 + j_iflt, // 155 Branch if less then 0 + j_ifge, // 156 Branch if greater than or equal to 0 + j_ifgt, // 157 Branch if greater than 0 + j_ifle, // 158 Branch if less than or equal to 0 + j_if_icmpeq, // 159 Branch if integers equal + j_if_icmpne, // 160 Branch if integers not equal + j_if_icmplt, // 161 Branch if integers less than + j_if_icmpge, // 162 Branch if integers grater than or equal to + j_if_icmpgt, // 163 Branch if integers grater than + j_if_icmple, // 164 Branch if integers less than or equal to + j_if_acmpeq, // 165 Branch if object references are equal + j_if_acmpne, // 166 Branch if object references not equal + j_goto, // 167 Branch always + j_jsr, // 168 Jump subroutine + j_ret, // 169 Return from subroutine + j_tableswitch, // 170 Access jump table by index and jump + j_lookupswitch, // 171 Access jump table by key match and jump + j_ireturn, // 172 Return integer from function + j_lreturn, // 173 Return long from function + j_freturn, // 174 Return single floatr from function + j_dreturn, // 175 Return double float from function + j_areturn, // 176 Return object reference from function + j_return, // 177 Return (void) from procedure + j_getstatic, // 178 Set static field from class + j_putstatic, // 179 Set static field in class + j_getfield, // 180 Fetch field from object + j_putfield, // 181 Set field in object + j_invokevirtual, // 182 invoke instance method + j_invokespecial, // 183 invoke instance method (superclass/init/...) + j_invokestatic, // 184 invoke a class (static) method + j_invokeinterface,// 185 invoke interface method + j_invokedynamic, // 186 invoke instance method (select by paraneter) + j_new, // 187 Create new object + j_newarray, // 188 Allocate new array + j_anewarray, // 189 Allocate new array of refernces to object + j_arraylength, // 190 Get length of array + j_athrow, // 191 Throw exception or error + j_checkcast, // 192 Make sure object is of given type + j_instanceof, // 193 Determine if an object is of given type + j_monitorenter, // 194 Enter monitored region of code + j_monitorexit, // 195 Exit monitored region of code + j_wide, // 196 wide (prefix of command) + j_multianewarray, // 197 Allocate new multi-dimensional array + j_ifnull, // 198 Branch if NULL-ptr + j_ifnonnull, // 199 Branch if not NULL-ptr + j_goto_w, // 200 Branch always (wide index) + j_jsr_w, // 201 Jump subroutine (wide index) + j_breakpoint, // 202 Stop and pass control to breakpoint handler + // + j_lastnorm, + j_a_invokesuper = j_lastnorm, + j_a_invokevirtualobject, + j_a_invokeignored, + // bottom of table ! (emu) + j_a_software, + j_a_hardware, + // + j_last +}; + +enum name_quick +{ + j_ldc_quick = j_lastnorm, // 203 (18) + j_ldcw_quick, // 204 (19) + j_ldc2w_quick, // 205 (20) + j_getfield_quick, // 206 (180) + j_putfield_quick, // 207 (181) + j_getfield2_quick, // 208 + j_putfield2_quick, // 209 + j_getstatic_quick, // 210 (178) + j_putstatic_quick, // 211 (179) + j_getstatic2_quick, // 212 + j_putstatic2_quick, // 213 + j_invokevirtual_quick, // 214 (182) + j_invokenonvirtual_quick, // 215 (183) + j_invokesuper_quick, // 216 + j_invokestatic_quick, // 217 (184) + j_invokeinterface_quick, // 218 (185) + j_invokevirtualobject_quick, // 219 + j_invokeignored_quick, // 220 + j_new_quick, // 221 (187) + j_anewarray_quick, // 222 (189) + j_multianewarray_quick, // 223 (197) + j_checkcast_quick, // 224 (192) + j_instanceof_quick, // 225 (193) + j_invokevirtual_quick_w, // 226 + j_getfield_quick_w, // 227 + j_putfield_quick_w, // 228 + j_quick_last +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +ARM_null = 0, // Unknown Operation +ARM_ret, // Return from Subroutine +ARM_nop, // No Operation +ARM_b, // Branch +ARM_bl, // Branch with Link +ARM_asr, // Arithmetic Shift Right +ARM_lsl, // Logical Shift Left +ARM_lsr, // Logical Shift Right +ARM_ror, // Rotate Right +ARM_neg, // Negate +ARM_and, // 0 Rd = Op1 & Op2 +ARM_eor, // 1 Rd = Op1 ^ Op2 +ARM_sub, // 2 Rd = Op1 - Op2 +ARM_rsb, // 3 Rd = Op2 - Op1 +ARM_add, // 4 Rd = Op1 + Op2 +ARM_adc, // 5 Rd = Op1 + Op2 + C +ARM_sbc, // 6 Rd = Op1 - Op2 + C - 1 +ARM_rsc, // 7 Rd = Op2 - Op1 + C - 1 +ARM_tst, // 8 Set cond. codes on Op1 & Op2 +ARM_teq, // 9 Set cond. codes on Op1 ^ Op2 +ARM_cmp, // A Set cond. codes on Op1 - Op2 +ARM_cmn, // B Set cond. codes on Op1 + Op2 +ARM_orr, // C Rd = Op1 | Op2 +ARM_mov, // D Rd = Op2 +ARM_bic, // E Rd = Op1 & ~Op2 +ARM_mvn, // F Rd = ~Op2 +ARM_mrs, // Transfer PSR to Register +ARM_msr, // Transfer Register to PSR +ARM_mul, // Multiply +ARM_mla, // Multiply-Accumulate +ARM_ldr, // Load from Memory +ARM_ldrpc, // Indirect Jump +ARM_str, // Store to Memory +ARM_ldm, // Load Block from Memory +ARM_stm, // Store Block to Memory +ARM_swp, // Single Data Swap +ARM_svc, // Supervisor call + +// Version 4 + +ARM_smull, // Signed Multiply long +ARM_smlal, // Signed Multiply-Accumulate long +ARM_umull, // Unsigned Multiply long +ARM_umlal, // Unsigned Multiply-Accumulate long +ARM_bx, // Branch to/from Thumb mode +ARM_pop, // Pop registers +ARM_push, // Push registers +ARM_adr, // Load address + +// Version 5 + +ARM_bkpt, // Breakpoint +ARM_blx1, // Branch with Link and Exchange (immediate address) +ARM_blx2, // Branch with Link and Exchange (register indirect) +ARM_clz, // Count Leading Zeros + +// Version 5E + +ARM_ldrd, // Load pair of registers +ARM_pld, // Prepare to load data +ARM_qadd, // Saturated addition +ARM_qdadd, // Saturated addition with doubling +ARM_qdsub, // Saturated subtraction with doubling +ARM_qsub, // Saturated subtraction +ARM_smlabb, // Signed multiply-accumulate (bottom*bottom) +ARM_smlatb, // Signed multiply-accumulate (top*bottom) +ARM_smlabt, // Signed multiply-accumulate (bottom*top) +ARM_smlatt, // Signed multiply-accumulate (top*top) +ARM_smlalbb, // Long signed multiply-accumulate (bottom*bottom) +ARM_smlaltb, // Long signed multiply-accumulate (top*bottom) +ARM_smlalbt, // Long signed multiply-accumulate (bottom*top) +ARM_smlaltt, // Long signed multiply-accumulate (top*top) +ARM_smlawb, // Wide signed multiply-accumulate (bottom) +ARM_smulwb, // Wide signed multiply (bottom) +ARM_smlawt, // Wide signed multiply-accumulate (top) +ARM_smulwt, // Wide signed multiply (top) +ARM_smulbb, // Signed multiply (bottom*bottom) +ARM_smultb, // Signed multiply (top*bottom) +ARM_smulbt, // Signed multiply (bottom*top) +ARM_smultt, // Signed multiply (top*top) +ARM_strd, // Store pair of registers + +// Intel xScale coprocessor instructions + +xScale_mia, // Multiply-Internal Accumulate +xScale_miaph, // Multiply-Internal Accumulate Packed HalfWords +xScale_miabb, // Multiply-Internal Accumulate Bottom-Bottom Halves +xScale_miabt, // Multiply-Internal Accumulate Bottom-Top Halves +xScale_miatb, // Multiply-Internal Accumulate Top-Bottom Halves +xScale_miatt, // Multiply-Internal Accumulate Top-Top Halves +xScale_mar, // Move To Internal Accumulator +xScale_mra, // Move From Internal Accumulator + +// Macro instructions + +ARM_movl, // Move immediate to register +ARM_adrl, // Load address +ARM_swbkpt, // WinCE Debugger break + +// Coprocessor instructions + +ARM_cdp, // Coprocessor Data Processing +ARM_cdp2, // Coprocessor Data Processing +ARM_ldc, // Load Coprocessor Register +ARM_ldc2, // Load Coprocessor Register +ARM_stc, // Store Coprocessor Register +ARM_stc2, // Store Coprocessor Register +ARM_mrc, // Move from Coprocessor to ARM Register +ARM_mrc2, // Move from Coprocessor to ARM Register +ARM_mcr, // Move from ARM to Coprocessor Register +ARM_mcr2, // Move from ARM to Coprocessor Register +ARM_mcrr, // Copy pair of registers to coprocessor (5E) +ARM_mrrc, // Copy pair of registers from coprocessor (5E) + +// VFP instructions + +ARM_fabsd, // Floating point Absolute Value, Double precision +ARM_fabss, // Floating point Absolute Value, Single precision +ARM_faddd, // Floating point Addition, Double precision +ARM_fadds, // Floating point Addition, Single precision +ARM_fcmpd, // Floating point Compare, Double precision +ARM_fcmps, // Floating point Compare, Single precision +ARM_fcmped, // Floating point Compare (NaN Exceptions), Double precision +ARM_fcmpes, // Floating point Compare (NaN Exceptions), Single precision +ARM_fcmpezd, // Floating point Compare (NaN Exceptions) with Zero, Double precision +ARM_fcmpezs, // Floating point Compare (NaN Exceptions) with Zero, Single precision +ARM_fcmpzd, // Floating point Compare with Zero, Double precision +ARM_fcmpzs, // Floating point Compare with Zero, Single precision +ARM_fcpyd, // Floating point Copy, Double precision +ARM_fcpys, // Floating point Copy, Single precision +ARM_fcvtsd, // Floating point Convert to Single precision from Double precision +ARM_fcvtds, // Floating point Convert to Double precision from Single precision +ARM_fdivd, // Floating point Divide, Double precision +ARM_fdivs, // Floating point Divide, Single precision +ARM_fldd, // Floating point Load, Double precision +ARM_flds, // Floating point Load, Single precision +ARM_fldmd, // Floating point Load Multiple, Double precision +ARM_fldms, // Floating point Load Multiple, Single precision +ARM_fldmx, // Floating point Load Multiple, Unknown precision +ARM_fmacd, // Floating point Multiply and Accumulate, Double precision +ARM_fmacs, // Floating point Multiply and Accumulate, Single precision +ARM_fmscd, // Floating point Multiply and Subtract, Double precision +ARM_fmscs, // Floating point Multiply and Subtract, Single precision +ARM_fmstat, // Floating point Move Status +ARM_fmuld, // Floating point Multiply, Double precision +ARM_fmuls, // Floating point Multiply, Single precision +ARM_fnegd, // Floating point Negate, Double precision +ARM_fnegs, // Floating point Negate, Single precision +ARM_fnmacd, // Floating point Negated Multiply and Accumulate, Double precision +ARM_fnmacs, // Floating point Negated Multiply and Accumulate, Single precision +ARM_fnmscd, // Floating point Negated Multiply and Subtract, Double precision +ARM_fnmscs, // Floating point Negated Multiply and Subtract, Single precision +ARM_fnmuld, // Floating point Negated Multiply, Double precision +ARM_fnmuls, // Floating point Negated Multiply, Single precision +ARM_fsitod, // Floating point Convert Signed Integer to Double precision +ARM_fsitos, // Floating point Convert Signed Integer to Single precision +ARM_fsqrtd, // Floating point Square Root, Double precision +ARM_fsqrts, // Floating point Square Root, Single precision +ARM_fstd, // Floating point Store, Double precision +ARM_fsts, // Floating point Store, Single precision +ARM_fstmd, // Floating point Store Multiple, Double precision +ARM_fstms, // Floating point Store Multiple, Single precision +ARM_fstmx, // Floating point Store Multiple, Unknown precision +ARM_fsubd, // Floating point Subtract, Double precision +ARM_fsubs, // Floating point Subtract, Single precision +ARM_ftosid, // Floating point Convert to Signed Integer from Double precision +ARM_ftosis, // Floating point Convert to Signed Integer from Single precision +ARM_ftosizd, // Floating point Convert to Signed Integer from Double precision, RZ mode +ARM_ftosizs, // Floating point Convert to Signed Integer from Single precision, RZ mode +ARM_ftouid, // Floating point Convert to Unsigned Integer from Double precision +ARM_ftouis, // Floating point Convert to Unsigned Integer from Single precision +ARM_ftouizd, // Floating point Convert to Unsigned Integer from Double precision, RZ mode +ARM_ftouizs, // Floating point Convert to Unsigned Integer from Single precision, RZ mode +ARM_fuitod, // Floating point Convert Unsigned Integer to Double precision +ARM_fuitos, // Floating point Convert Unsigned Integer to Single precision +ARM_fmdhr, // Floating point Move to Double precision High from Register +ARM_fmrdh, // Floating point Move to Register from Double precision High +ARM_fmdlr, // Floating point Move to Double precision Low from Register +ARM_fmrdl, // Floating point Move to Register from Double precision Low +ARM_fmxr, // Floating point Move to System Register from Register +ARM_fmrx, // Floating point Move to Register from System Register +ARM_fmsr, // Floating point Move to Single precision from Register +ARM_fmrs, // Floating point Move to Register from Single precision + +// VFP ARMv5TE extensions + +ARM_fmdrr, // Floating point Move to Double precision from two Registers +ARM_fmrrd, // Floating point Move to two Registers from Double precision +ARM_fmsrr, // Floating point Move to two Single precision from two Registers +ARM_fmrrs, // Floating point Move to two Registers from two Single precision + +// ARM v5J instructions + +ARM_bxj, // Branch to Jazelle + +// ARM v6 instructions + +ARM_mcrr2, // Move to Coprocessor from two ARM Registers +ARM_mrrc2, // Move to two ARM Registers from Coprocessor +ARM_cps, // Change Processor State +ARM_cpsid, // Disable Interrupts +ARM_cpsie, // Enable Interrupts +ARM_ldrex, // Load Register Exclusive +ARM_pkhbt, // Pack halfword bottom + top +ARM_pkhtb, // Pack halfword top + bottom +ARM_qadd16, // Signed saturating arithmetic hafword-wise addition +ARM_qadd8, // Signed saturating arithmetic byte-wise addition +ARM_qasx, // Saturating Add and Subtract with Exchange +ARM_qaddsubx=ARM_qasx, // Signed saturating arithmetic exchange, add, substract (old name) +ARM_qsub16, // Signed saturating arithmetic hafword-wise substraction +ARM_qsub8, // Signed saturating arithmetic byte-wise substraction +ARM_qsax, // Saturating Subtract and Add with Exchange +ARM_qsubaddx=ARM_qsax, // Signed saturating arithmetic exchange, substract, add (old name) +ARM_rev, // Byte Reverse Word +ARM_rev16, // Byte Reverse Packed Halfword +ARM_revsh, // Byte Reverse Signed Halfword +ARM_rfe, // Return from exception +ARM_sadd16, // Signed arithmetic modulo hafword-wise addition +ARM_sadd8, // Signed arithmetic modulo byte-wise addition +ARM_sasx, // Signed Add and Subtract with Exchange +ARM_saddsubx=ARM_sasx, // Signed arithmetic modulo exchange, add, substract (old name) +ARM_sel, // Select bytes +ARM_setend, // Set Byte Endianness +ARM_shadd16, // Signed arithmetic hafword-wise addition, halving results +ARM_shadd8, // Signed arithmetic byte-wise addition, halving results +ARM_shasx, // Signed Halving Add and Subtract with Exchange +ARM_shaddsubx=ARM_shasx,// Signed arithmetic exchange, add, substract, halving results (old name) +ARM_shsub16, // Signed arithmetic hafword-wise substraction, halving results +ARM_shsub8, // Signed arithmetic byte-wise substraction, halving results +ARM_shsax, // Signed Halving Subtract and Add with Exchange +ARM_shsubaddx=ARM_shsax,// Signed arithmetic exchange, substract, add, halving results (old name) +ARM_smlad, // Dual signed multiply, add and accumulate +ARM_smladx, // Dual signed multiply, add and accumulate crossed +ARM_smuad, // Dual signed multiply and add +ARM_smuadx, // Dual signed multiply and add crossed +ARM_smlald, // Dual signed multiply, add and accumulate long +ARM_smlaldx, // Dual signed multiply, add and accumulate long crossed +ARM_smlsd, // Dual signed multiply, substract and accumulate +ARM_smlsdx, // Dual signed multiply, substract and accumulate crossed +ARM_smusd, // Dual signed multiply and substract +ARM_smusdx, // Dual signed multiply and substract crossed +ARM_smlsld, // Dual signed multiply, substract and accumulate long +ARM_smlsldx, // Dual signed multiply, substract and accumulate long crossed +ARM_smmla, // Signed most significant word multiply and accumulate truncated +ARM_smmlar, // Signed most significant word multiply and accumulate rounded +ARM_smmul, // Signed most significant word multiply truncated +ARM_smmulr, // Signed most significant word multiply rounded +ARM_smmls, // Signed most significant word multiply and substract truncated +ARM_smmlsr, // Signed most significant word multiply and substract rounded +ARM_srs, // Store Return State +ARM_ssat, // Signed Saturate +ARM_ssat16, // Signed saturate two halfwords +ARM_ssub16, // Signed arithmetic hafword-wise substraction +ARM_ssub8, // Signed arithmetic byte-wise substraction +ARM_ssax, // Signed Subtract and Add with Exchange +ARM_ssubaddx=ARM_ssax, // Signed arithmetic exchange, substract, add (old name) +ARM_strex, // Store Register Exclusive +ARM_sxtab, // Signed extend byte to word, add +ARM_sxtb, // Signed extend byte to word +ARM_sxtab16, // Signed extend two bytes to halfwords, add +ARM_sxtb16, // Signed extend two bytes to halfwords +ARM_sxtah, // Signed extend halfword to word, add +ARM_sxth, // Signed extend halfword to word +ARM_uadd16, // Unsigned arithmetic modulo hafword-wise addition +ARM_uadd8, // Unsigned arithmetic modulo byte-wise addition +ARM_uasx, // Unsigned Add and Subtract with Exchange +ARM_uaddsubx=ARM_uasx, // Unsigned arithmetic modulo exchange, add, substract (old name) +ARM_uhadd16, // Unsigned arithmetic hafword-wise addition, halving results +ARM_uhadd8, // Unsigned arithmetic byte-wise addition, halving results +ARM_uhasx, // Unsigned Halving Add and Subtract with Exchange +ARM_uhaddsubx=ARM_uhasx,// Unsigned arithmetic exchange, add, substract, halving results (old name) +ARM_uhsub16, // Unsigned arithmetic hafword-wise substraction, halving results +ARM_uhsub8, // Unsigned arithmetic byte-wise substraction, halving results +ARM_uhsax, // Unsigned Halving Subtract and Add with Exchange +ARM_uhsubaddx=ARM_uhsax,// Unsigned arithmetic exchange, substract, add, halving results (old name) +ARM_umaal, // Multiply unsigned double accumulate long +ARM_uqadd16, // Unsigned saturating arithmetic hafword-wise addition +ARM_uqadd8, // Unsigned saturating arithmetic byte-wise addition +ARM_uqasx, // Unsigned Saturating Add and Subtract with Exchange +ARM_uqaddsubx=ARM_uqasx,// Unsigned saturating arithmetic exchange, add, substract (old name) +ARM_uqsub16, // Unsigned saturating arithmetic hafword-wise substraction +ARM_uqsub8, // Unsigned saturating arithmetic byte-wise substraction +ARM_uqsax, // Unsigned Saturating Subtract and Add with Exchange +ARM_uqsubaddx=ARM_uqsax,// Unsigned saturating arithmetic exchange, substract, add (old name) +ARM_usada8, // Unsigned sum of absolute differences and accumulate +ARM_usad8, // Unsigned sum of absolute differences +ARM_usat, // Unsigned saturate word +ARM_usat16, // Unsigned saturate two halfwords +ARM_usub16, // Unsigned arithmetic hafword-wise substraction +ARM_usub8, // Unsigned arithmetic byte-wise substraction +ARM_usax, // Unsigned Subtract and Add with Exchange +ARM_usubaddx=ARM_usax, // Unsigned arithmetic exchange, substract, add (old name) +ARM_uxtab, // Unsigned extend byte to word, add +ARM_uxtb, // Unsigned extend byte to word +ARM_uxtab16, // Unsigned extend two bytes to halfwords, add +ARM_uxtb16, // Unsigned extend two bytes to halfwords +ARM_uxtah, // Unsigned extend halfword to word, add +ARM_uxth, // Unsigned extend halfword to word + +// ARM v6zk instructions + +ARM_clrex, // Clear Exclusive +ARM_ldrexb, // Load Byte Exclusive +ARM_ldrexd, // Load DoubleWord Exclusive +ARM_ldrexh, // Load Halfword Exclusive +ARM_strexb, // Store Byte Exclusive +ARM_strexd, // Store DoubleWord Exclusive +ARM_strexh, // Store Halfword Exclusive +ARM_yield, // Yield (hint) +ARM_sev, // Send Event (hint) +ARM_wfe, // Wait For Event (hint) +ARM_wfi, // Wait For Interrupt (hint) +ARM_smc, // Secure Monitor Call + +// ARM Thumb32 instructions + +ARM_orn, // Rd = Op1 | ~Op2 +ARM_movt, // Move Top +ARM_sbfx, // Signed Bit Field Extract +ARM_ubfx, // Unsigned Bit Field Extract +ARM_bfi, // Bit Field Insert +ARM_bfc, // Bit Field Clear +ARM_tbb, // Table Branch Byte +ARM_tbh, // Table Branch Halfword +ARM_pli, // Prepare to load code +ARM_rbit, // Reverse Bits +ARM_it, // If Then +ARM_mls, // Multiply and Subtract +ARM_sdiv, // Signed Divide +ARM_udiv, // Unsigned Divide +ARM_cbz, // Compare and Branch on Zero +ARM_cbnz, // Compare and Branch on Non-Zero +ARM_dsb, // Data Synchronization Barrier +ARM_dmb, // Data Memory Barrier +ARM_isb, // Instruction Synchronization Barrier +ARM_dbg, // Debug Hint + +ARM_und, // Architecturally undefined instruction + +// missing instructions (not yet decoded) + +ARM_rrx, // Rotate Right with Extend +ARM_enterx, // Enter ThumbEE state +ARM_leavex, // Leave ThumbEE state +ARM_chka, // Check Array +ARM_hb, // Handler Branch +ARM_hbl, // Handler Branch with Link +ARM_hblp, // Handler Branch with Link and Parameter +ARM_hbp, // Handler Branch with Parameter + +// NEON (Advanced SIMD) and extra VFP instructions + +ARM_vaba, // Vector Absolute Difference and Accumulate +ARM_vabal, // Vector Absolute Difference and Accumulate Long +ARM_vabd, // Vector Absolute Difference +ARM_vabdl, // Vector Absolute Difference Long +ARM_vabs, // Vector Absolute +ARM_vacge, // Vector Absolute Compare Greater Than or Equal +ARM_vacgt, // Vector Absolute Compare Greater Than +ARM_vacle, // Vector Absolute Compare Less Than or Equal +ARM_vaclt, // Vector Absolute Compare Less Than +ARM_vadd, // Vector Add +ARM_vaddhn, // Vector Add and Narrow, returning High Half +ARM_vaddl, // Vector Add Long +ARM_vaddw, // Vector Add Wide +ARM_vand, // Vector Bitwise AND +ARM_vbic, // Vector Bitwise Bit Clear +ARM_vbif, // Vector Bitwise Insert if False +ARM_vbit, // Vector Bitwise Insert if True +ARM_vbsl, // Vector Bitwise Select +ARM_vceq, // Vector Compare Equal +ARM_vcge, // Vector Compare Greater Than or Equal +ARM_vcgt, // Vector Compare Greater Than +ARM_vcle, // Vector Compare Less Than or Equal +ARM_vcls, // Vector Count Leading Sign Bits +ARM_vclt, // Vector Compare Less Than +ARM_vclz, // Vector Count Leading Zeros +ARM_vcmp, // Vector Compare +ARM_vcmpe, // Vector Compare (Quiet NaNs trigger Exception) +ARM_vcnt, // Vector Count Number of Bits +ARM_vcvt, // Vector Convert +ARM_vcvtr, // Vector Convert Rounding +ARM_vcvtb, // Vector Convert Half-Precision Bottom +ARM_vcvtt, // Vector Convert Half-Precision Top +ARM_vdiv, // Vector Divide +ARM_vdup, // Vector Duplicate +ARM_veor, // Vector Bitwise Exclusive OR +ARM_vext, // Vector Extract +ARM_vfma, // Vector Fused Multiply Accumulate +ARM_vfms, // Vector Fused Multiply Substract +ARM_vfnma, // Vector Fused Negated Multiply Accumulate +ARM_vfnms, // Vector Fused Negated Multiply Substract +ARM_vhadd, // Vector Halving Add +ARM_vhsub, // Vector Halving Sub +ARM_vld1, // Vector Load Single Element +ARM_vld2, // Vector Load Two-Element Structures +ARM_vld3, // Vector Load Three-Element Structures +ARM_vld4, // Vector Load Four-Element Structures +ARM_vldm, // Vector Load Multiple +ARM_vldr, // Vector Load Register +ARM_vmax, // Vector Maximum +ARM_vmin, // Vector Minimum +ARM_vmla, // Vector Multiply Accumulate +ARM_vmlal, // Vector Multiply Accumulate Long +ARM_vmls, // Vector Multiply Subtract +ARM_vmlsl, // Vector Multiply Subtract Long +ARM_vmov, // Vector Move +ARM_vmovl, // Vector Move Long +ARM_vmovn, // Vector Move and Narrow +ARM_vmrs, // Move FPSCR to ARM Register +ARM_vmsr, // Move to FPSCR from ARM Register +ARM_vmul, // Vector Multiply +ARM_vmull, // Vector Multiply Long +ARM_vmvn, // Vector Bitwise NOT +ARM_vneg, // Vector Negate +ARM_vnmla, // Vector Multiply Add Negated +ARM_vnmls, // Vector Multiply Substract Negated +ARM_vnmul, // Vector Multiply Negated +ARM_vorn, // Vector Bitwise OR NOT +ARM_vorr, // Vector Bitwise OR +ARM_vpadal, // Vector Pairwise Add and Accumulate Long +ARM_vpadd, // Vector Pairwise Add +ARM_vpaddl, // Vector Pairwise Add Long +ARM_vpmax, // Vector Pairwise Maximum +ARM_vpmin, // Vector Pairwise Minimum +ARM_vpop, // Vector Pop +ARM_vpush, // Vector Push +ARM_vqabs, // Vector Saturating Absolute +ARM_vqadd, // Vector Saturating Add +ARM_vqdmlal, // Vector Saturating Doubling Multiply Accumulate Long +ARM_vqdmlsl, // Vector Saturating Doubling Multiply Subtract Long +ARM_vqdmulh, // Vector Saturating Doubling Multiply Returning High Half +ARM_vqdmull, // Vector Saturating Doubling Multiply Long +ARM_vqmovn, // Vector Saturating Move and Narrow +ARM_vqmovun, // Vector Saturating Move and Narrow, Unsigned result +ARM_vqneg, // Vector Saturating Negate +ARM_vqrdmulh, // Vector Saturating Rounding Doubling Multiply Returning High Half +ARM_vqrshl, // Vector Saturating Rounding Shift Left +ARM_vqrshrn, // Vector Saturating Rounding Shift Right, Narrow +ARM_vqrshrun, // Vector Saturating Rounding Shift Right, Narrow, Unsigned result +ARM_vqshl, // Vector Saturating Shift Left +ARM_vqshlu, // Vector Saturating Shift Left, Unsigned result +ARM_vqshrn, // Vector Saturating Shift Right, Narrow +ARM_vqshrun, // Vector Saturating Shift Right, Narrow, Unsigned result +ARM_vqsub, // Vector Saturating Subtract +ARM_vraddhn, // Vector Rounding Add and Narrow, returning High Half +ARM_vrecpe, // Vector Reciprocal Estimate +ARM_vrecps, // Vector Reciprocal Step +ARM_vrev16, // Vector Reverse in halfwords +ARM_vrev32, // Vector Reverse in words +ARM_vrev64, // Vector Reverse in doublewords +ARM_vrhadd, // Vector Rounding Halving Add +ARM_vrshl, // Vector Rounding Shift Left +ARM_vrshr, // Vector Rounding Shift Right +ARM_vrshrn, // Vector Rounding Shift Right and Narrow +ARM_vrsqrte, // Vector Reciprocal Square Root Estimate +ARM_vrsqrts, // Vector Reciprocal Square Root Step +ARM_vrsra, // Vector Rounding Shift Right and Accumulate +ARM_vrsubhn, // Vector Rounding Subtract and Narrow, returning High Half +ARM_vshl, // Vector Shift Left +ARM_vshll, // Vector Shift Left Long +ARM_vshr, // Vector Shift Right +ARM_vshrn, // Vector Shift Right Narrow +ARM_vsli, // Vector Shift Left and Insert +ARM_vsqrt, // Vector Square Root +ARM_vsra, // Vector Shift Right and Accumulate +ARM_vsri, // Vector Shift Right and Insert +ARM_vst1, // Vector Store Single Element +ARM_vst2, // Vector Store Two-Element Structures +ARM_vst3, // Vector Store Three-Element Structures +ARM_vst4, // Vector Store Four-Element Structures +ARM_vstm, // Vector Store Multiple +ARM_vstr, // Vector Store Register +ARM_vsub, // Vector Subtract +ARM_vsubhn, // Vector Subtract and Narrow, returning High Half +ARM_vsubl, // Vector Subtract Long +ARM_vsubw, // Vector Subtract Wide +ARM_vswp, // Vector Swap +ARM_vtbl, // Vector Table Lookup +ARM_vtbx, // Vector Table Extension +ARM_vtrn, // Vector Transpose +ARM_vtst, // Vector Test Bits +ARM_vuzp, // Vector Unzip +ARM_vzip, // Vector Zip + +// ARMv7 Hypervisor extensions + +ARM_eret, // Exception Return +ARM_hvc, // Hypervisor Call + +// new ARMv8 instructions for A32 and T32 mode + +ARM_lda, // Load-Acquire Word/Byte/Halfword +ARM_stl, // Store-Release Word/Byte/Halfword +ARM_ldaex, // Load-Acquire Exclusive Word/Byte/Halfword +ARM_stlex, // Store-Release Exclusive Word/Byte/Halfword +ARM_vsel, // Floating-point Conditional Select + +ARM_vmaxnm, // Vector Maximum Numeric +ARM_vminnm, // Vector Minimum Numeric +ARM_vcvta, // Vector Convert Round to Nearest with Ties to Away +ARM_vcvtn, // Vector Convert Round to Nearest with Ties to Even +ARM_vcvtp, // Vector Convert Round towards +Infinity +ARM_vcvtm, // Vector Convert Round towards -Infinity +ARM_vrintx, // Vector Round to Integral, FPSCR rounding mode and signaling inexactness +ARM_vrintr, // Vector Round to Integral, FPSCR rounding mode +ARM_vrintz, // Vector Round to Integral, Round toward Zero +ARM_vrinta, // Vector Round to Integral, Round to Nearest with Ties to Away +ARM_vrintn, // Vector Round to Integral, Round to Nearest with Ties to Even +ARM_vrintp, // Vector Round to Integral, Round towards +Infinity +ARM_vrintm, // Vector Round to Integral, Round towards -Infinity + +ARM_aesd, // AES single round decryption +ARM_aese, // AES single round encryption +ARM_aesimc, // AES inverse mix columns +ARM_aesmc, // AES mix columns +ARM_sha1c, // SHA1 hash update accelerator, choose +ARM_sha1m, // SHA1 hash update accelerator, majority +ARM_sha1p, // SHA1 hash update accelerator, parity +ARM_sha1h, // SHA1 hash update accelerator, rotate left by 30 +ARM_sha1su0, // SHA1 schedule update accelerator, first part +ARM_sha1su1, // SHA1 schedule update accelerator, second part +ARM_sha256h, // SHA256 hash update accelerator +ARM_sha256h2, // SHA256 hash update accelerator upper part +ARM_sha256su0, // SHA256 schedule update accelerator, first part +ARM_sha256su1, // SHA256 schedule update accelerator, second part + +ARM_dcps1, // Debug Change Processor State to EL1 +ARM_dcps2, // Debug Change Processor State to EL2 +ARM_dcps3, // Debug Change Processor State to EL3 +ARM_hlt, // Halting mode software breakpoint +ARM_sevl, // Send Event Locally + +// ARMv8 AArch64 new instructions + +ARM_tbz, // Test and Branch Zero +ARM_tbnz, // Test and Branch Non-Zero +ARM_br, // Branch To Register +ARM_blr, // Branch and Link Register +ARM_ldur, // Load Single Register (unscaled offset) +ARM_stur, // Store Single Register (unscaled offset) +ARM_ldp, // Load Pair +ARM_stp, // Store Pair +ARM_ldnp, // Load Non-temporal Pair +ARM_stnp, // Store Non-temporal Pair +ARM_ldtr, // Load Unprivileged Register +ARM_sttr, // Store Unprivileged Register +ARM_ldxr, // Load Exclusive Register +ARM_stxr, // Store Exclusive Register +ARM_ldxp, // Load Exclusive Pair +ARM_stxp, // Store Exclusive Pair +ARM_ldar, // Load-Acquire Register +ARM_stlr, // Store-Release Register +ARM_ldaxr, // Load-Acquire Exclusive Register +ARM_stlxr, // Store-Release Exclusive Register +ARM_ldaxp, // Load-Acquire Exclusive Pair +ARM_stlxp, // Store Exclusive Pair +ARM_prfm, // Prefetch Memory +ARM_prfum, // Prefetch Memory (unscaled offset) +ARM_movi, // Move Immediate +ARM_mvni, // Move Inverted Immediate +ARM_movz, // Move with Zero +ARM_movn, // Move with Not +ARM_movk, // Move with Keep +ARM_adrp, // Address of Page +ARM_bfm, // Bitfield Move +ARM_sbfm, // Signed Bitfield Move +ARM_ubfm, // Unsigned Bitfield Move +// ARM_bfi, // Bitfield Insert +ARM_bfxil, // Bitfield Extract and Insert Low +ARM_sbfiz, // Signed Bitfield Insert in Zero +// ARM_sbfx, // Signed Bitfield Extract +ARM_ubfiz, // Signed Bitfield Insert in Zero +// ARM_ubfx, // Unsigned Bitfield Extract +ARM_extr, // Extract +ARM_sxtw, // Signed Extend Word +ARM_uxtw, // Unsigned Extend Word (non-existing insn) +ARM_eon, // Bitwise exclusive OR NOT +ARM_not, // Bitwise NOT +ARM_cls, // Count Leading Sign Bits +ARM_rev32, // Reverse Bytes in Words +ARM_csel, // Conditional Select +ARM_csinc, // Conditional Select Increment +ARM_csinv, // Conditional Select Invert +ARM_csneg, // Conditional Select Negate +ARM_cset, // Conditional Set +ARM_csetm, // Conditional Set Mask +ARM_cinc, // Conditional Increment +ARM_cinv, // Conditional Invert +ARM_cneg, // Conditional Negate +ARM_ngc, // Negate with Carry +ARM_ccmn, // Conditional Compare Negative +ARM_ccmp, // Conditional Compare +ARM_madd, // Multiply-Add +ARM_msub, // Multiply-Subtract +ARM_mneg, // Multiply-Negate +ARM_smaddl, // Signed Multiply-Add Long +ARM_smsubl, // Signed Multiply-Subtract Long +ARM_smnegl, // Signed Multiply-Negate Long +ARM_smulh, // Signed Multiply High +ARM_umaddl, // Unsigned Multiply-Add Long +ARM_umsubl, // Unsigned Multiply-Subtract Long +ARM_umnegl, // Unsigned Multiply-Negate Long +ARM_umulh, // Unsigned Multiply High + +ARM_drps, // Debug Restore Processor State +ARM_sys, // System Maintenance Operation +ARM_sysl, // System Maintenance Operation With Result +ARM_ic, // Instruction Cache Maintenance +ARM_dc, // Data Cache Maintenance +ARM_at, // Address Translation +ARM_tlbi, // TLB Invalidation +ARM_hint, // Hint instruction +ARM_brk, // Monitor debug-mode breakpoint + +// AArch64 Advanced SIMD +ARM_uaba, // Unsigned Integer Absolute Difference and Accumulate +ARM_saba, // Signed Integer Absolute Difference and Accumulate +ARM_uabal, // Unsigned Integer Absolute Difference and Accumulate Long +ARM_uabal2, // Unsigned Integer Absolute Difference and Accumulate Long (Second Part) +ARM_sabal, // Signed Integer Absolute Difference and Accumulate Long +ARM_sabal2, // Signed Integer Absolute Difference and Accumulate Long (Second Part) +ARM_uabd, // Unsigned Integer Absolute Difference +ARM_sabd, // Signed Integer Absolute Difference +ARM_fabd, // Floating-point Absolute Difference +ARM_uabdl, // Unsigned Integer Absolute Difference Long +ARM_uabdl2, // Unsigned Integer Absolute Difference Long (Second Part) +ARM_sabdl, // Signed Integer Absolute Difference Long +ARM_sabdl2, // Signed Integer Absolute Difference Long (Second Part) +ARM_abs, // Integer Absolute Value +ARM_fabs, // Floating-point Absolute Value +ARM_facge, // Floating-point Absolute Compare Greater Than or Equal +ARM_facgt, // Floating-point Absolute Compare Greater Than +ARM_facle, // Floating-point Absolute Compare Less Than or Equal +ARM_faclt, // Floating-point Absolute Compare Less Than +ARM_fadd, // Floating-point Add +ARM_addhn, // Integer Vector Add and Narrow High Half +ARM_addhn2, // Integer Vector Add and Narrow High Half (second part) +ARM_uaddl, // Unsigned Integer Add Long +ARM_uaddl2, // Unsigned Integer Add Long (second part) +ARM_saddl, // Signed Integer Add Long +ARM_saddl2, // Signed Integer Add Long (second part) +ARM_uaddw, // Unsigned Integer Add Wide +ARM_uaddw2, // Unsigned Integer Add Wide (second part) +ARM_saddw, // Signed Integer Add Wide +ARM_saddw2, // Signed Integer Add Wide (second part) +ARM_bif, // Bitwise Vector Insert if False +ARM_bit, // Bitwise Vector Insert if True +ARM_bsl, // Bitwise Vector Select +ARM_cmeq, // Ingeger Vector Compare Equal +ARM_fcmeq, // Floating-point Vector Compare Equal +ARM_cmhs, // Unsigned Compare Greater Than or Equal +ARM_cmge, // Signed Compare Greater Than or Equal +ARM_fcmge, // Floating-point Compare Greater Than or Equal +ARM_cmhi, // Unsigned Compare Greater Than +ARM_cmgt, // Signed Compare Greater Than +ARM_fcmgt, // Floating-point Compare Greater Than +ARM_cmls, // Unsigned Compare Less Than or Equal +ARM_cmle, // Signed Compare Less Than or Equal +ARM_fcmle, // Floating-point Compare Less Than or Equal +ARM_cmlo, // Unsigned Compare Less Than +ARM_cmlt, // Signed Compare Less Than +ARM_fcmlt, // Floating-point Compare Less Than +ARM_fcmp, // Floating-point Compare +ARM_fcmpe, // Floating-point Signaling Compare +ARM_fccmp, // Floating-point Conditional Quiet Compare +ARM_fccmpe, // Floating-point Conditional Signaling Compare +ARM_fcsel, // Floating-point Conditional Select +ARM_cnt, // Vector Count Non-zero Bits +ARM_fcvt, // Floating-point convert precision (scalar) +ARM_fcvtzs, // Convert Floating-point to Signed Integer (Round to Zero) +ARM_fcvtas, // Convert Floating-point to Signed Integer (Round to Nearest, Ties to Away) +ARM_fcvtns, // Convert Floating-point to Signed Integer (Round to Nearest, Ties to Even) +ARM_fcvtps, // Convert Floating-point to Signed Integer (Round towards +Infinity) +ARM_fcvtms, // Convert Floating-point to Signed Integer (Round towards -Infinity) +ARM_fcvtzu, // Convert Floating-point to Unsigned Integer (Round to Zero) +ARM_fcvtau, // Convert Floating-point to Unsigned Integer (Round to Nearest, Ties to Away) +ARM_fcvtnu, // Convert Floating-point to Unsigned Integer (Round to Nearest, Ties to Even) +ARM_fcvtpu, // Convert Floating-point to Unsigned Integer (Round towards +Infinity) +ARM_fcvtmu, // Convert Floating-point to Unsigned Integer (Round towards -Infinity) +ARM_ucvtf, // Convert Unsigned Integer to Floating-point +ARM_scvtf, // Convert Signed Integer to Floating-point +ARM_fcvtn, // Floating-point Convert Precision, Narrow +ARM_fcvtn2, // Floating-point Convert Precision, Narrow (second part) +ARM_fcvtl, // Floating-point Convert Precision, Long +ARM_fcvtl2, // Floating-point Convert Precision, Long (second part) +ARM_fcvtxn, // Floating-point Convert Precision, Narrow (Round to Odd) +ARM_fcvtxn2, // Floating-point Convert Precision, Narrow (Round to Odd) (second part) +ARM_frinta, // Floating-point Round to Integral (Round to Nearest, Ties to Away) +ARM_frinti, // Floating-point Round to Integral (using FPCR rounding mode) +ARM_frintm, // Floating-point Round to Integral (Round towards -Infinity) +ARM_frintn, // Floating-point Round to Integral (Round to Nearest, Ties to Even) +ARM_frintp, // Floating-point Round to Integral (Round towards +Infinity) +ARM_frintx, // Floating-point Round to Integral (using FPCR rounding mode, with exactness test) +ARM_frintz, // Floating-point Round to Integral (Round towards Zero) +ARM_fmadd, // Floating-point scalar fused multiply-add +ARM_fmsub, // Floating-point scalar fused multiply-subtract +ARM_fnmadd, // Floating-point scalar negated fused multiply-add +ARM_fnmsub, // Floating-point scalar negated fused multiply-subtract +ARM_fdiv, // Floating-point Divide +ARM_dup, // Duplicate Single Element to All Elements +ARM_ins, // Insert Single Element in Another Element +ARM_ext, // Bitwise Extract +ARM_uhadd, // Unsigned Integer Halving Add +ARM_shadd, // Signed Integer Halving Add +ARM_uhsub, // Unsigned Integer Halving Subtract +ARM_shsub, // Signed Integer Halving Subtract +ARM_ld1, // Vector Load Single Element +ARM_ld2, // Vector Load Two-Element Structures +ARM_ld3, // Vector Load Three-Element Structures +ARM_ld4, // Vector Load Four-Element Structures +ARM_ld1r, // Vector Load Single Element and Replicate +ARM_ld2r, // Vector Load Two-Element Structures and Replicate +ARM_ld3r, // Vector Load Three-Element Structures and Replicate +ARM_ld4r, // Vector Load Four-Element Structures and Replicate +ARM_umax, // Unsigned Integer Maximum +ARM_smax, // Signed Integer Maximum +ARM_fmax, // Floating-point Maximum +ARM_fmaxnm, // Floating-point maxNum() +ARM_umin, // Unsigned Integer Minimum +ARM_smin, // Signed Integer Minimum +ARM_fmin, // Floating-point Minimum +ARM_fminnm, // Floating-point minNum() +ARM_fmla, // Fused Multiply-Add +ARM_umlal2, // Unsigned Integer Multiply-Add Long (second part) +ARM_smlal2, // Signed Integer Multiply-Add Long (second part) +ARM_fmls, // Fused Multiply-Subtract +ARM_umlsl, // Unsigned Integer Multiply-Subtract Long +ARM_umlsl2, // Unsigned Integer Multiply-Subtract Long (second part) +ARM_smlsl, // Signed Integer Multiply-Subtract Long +ARM_smlsl2, // Signed Integer Multiply-Subtract Long (second part) +ARM_umov, // Unsigned Integer Move +ARM_smov, // Signed Integer Move +ARM_fmov, // Floating-point Move +ARM_uxtl, // Unsigned Integer Vector Lengthen +ARM_uxtl2, // Unsigned Integer Vector Lengthen (second part) +ARM_sxtl, // Signed Integer Vector Lengthen +ARM_sxtl2, // Signed Integer Vector Lengthen (second part) +ARM_xtn, // Integer Vector Narrow +ARM_xtn2, // Integer Vector Narrow (second part) +ARM_fmul, // Floating-point Vector Multiply +ARM_pmul, // Polynomial Vector Multiply +ARM_fmulx, // Floating-point Multiply Extended +ARM_fnmul, // Floating-point Multiply-Negate +ARM_umull2, // Unsigned Integer Vector Multiply Long (second part) +ARM_smull2, // Signed Integer Vector Multiply Long (second part) +ARM_pmull, // Polynomial Vector Multiply Long +ARM_pmull2, // Polynomial Vector Multiply Long (second part) +ARM_fneg, // Floating-point Negate +ARM_uadalp, // Unsigned Integer Vector Add and Accumulate Long Pair +ARM_sadalp, // Signed Integer Vector Add and Accumulate Long Pair +ARM_addp, // Integer Vector Add Pair +ARM_faddp, // Floating-point Vector Add Pair +ARM_uaddlp, // Unsigned Integer Vector Add Long Pair +ARM_saddlp, // Signed Integer Vector Add Long Pair +ARM_umaxp, // Unsigned Integer Maximum Pair +ARM_smaxp, // Signed Integer Maximum Pair +ARM_fmaxp, // Floating-point Maximum Pair +ARM_fmaxnmp, // Floating-point maxNum Pair +ARM_uminp, // Unsigned Integer Minimum Pair +ARM_sminp, // Signed Integer Minimum Pair +ARM_fminp, // Floating-point Minimum Pair +ARM_fminnmp, // Floating-point minNum Pair +ARM_sqabs, // Signed Integer Saturating Absolute +ARM_uqadd, // Unsigned Integer Saturating Add +ARM_sqadd, // Signed Integer Saturating Add +ARM_suqadd, // Signed Integer Saturating Accumulate of Unsigned Values +ARM_usqadd, // Unsigned Integer Saturating Accumulate of Signed Values +ARM_sqdmlal, // Signed Integer Saturating Doubling Multiply Add Long +ARM_sqdmlal2, // Signed Integer Saturating Doubling Multiply Add Long (second part) +ARM_sqdmlsl, // Signed Integer Saturating Doubling Multiply Subtract Long +ARM_sqdmlsl2, // Signed Integer Saturating Doubling Multiply Subtract Long (second part) +ARM_sqdmulh, // Signed Integer Saturating Doubling Multiply Returning High Half +ARM_sqdmull, // Signed Integer Saturating Doubling Multiply Long +ARM_sqdmull2, // Signed Integer Saturating Doubling Multiply Long (second part) +ARM_uqxtn, // Unsigned Integer Saturating Vector Narrow +ARM_uqxtn2, // Unsigned Integer Saturating Vector Narrow (second part) +ARM_sqxtn, // Signed Integer Saturating Vector Narrow +ARM_sqxtn2, // Signed Integer Saturating Vector Narrow (second part) +ARM_sqxtun, // Signed Integer Saturating Vector and Unsigned Narrow +ARM_sqxtun2, // Signed Integer Saturating Vector and Unsigned Narrow (second part) +ARM_sqneg, // Signed Integer Saturating Negate +ARM_sqrdmulh, // Signed Integer Saturating Rounding Doubling Multiply Returning High Half +ARM_uqrshl, // Unsigned Integer Saturating Rounding Shift Left +ARM_sqrshl, // Signed Integer Saturating Rounding Shift Left +ARM_uqrshrn, // Unsigned Integer Saturating Rounding Shift Right, Narrow +ARM_uqrshrn2, // Unsigned Integer Saturating Rounding Shift Right, Narrow (second part) +ARM_sqrshrn, // Signed Integer Saturating Rounding Shift Right, Narrow +ARM_sqrshrn2, // Signed Integer Saturating Rounding Shift Right, Narrow (second part) +ARM_sqrshrun, // Signed Integer Saturating Rounding Shift Right, Narrow, Unsigned result +ARM_sqrshrun2, // Signed Integer Saturating Rounding Shift Right, Narrow, Unsigned result (second part) +ARM_uqshl, // Unsigned Integer Saturating Shift Left +ARM_sqshl, // Signed Integer Saturating Shift Left +ARM_sqshlu, // Signed Integer Saturating Shift Left, Unsigned result +ARM_uqshrn, // Unsigned Integer Saturating Shift Right, Narrow +ARM_uqshrn2, // Unsigned Integer Saturating Shift Right, Narrow (second part) +ARM_sqshrn, // Signed Integer Saturating Shift Right, Narrow +ARM_sqshrn2, // Signed Integer Saturating Shift Right, Narrow (second part) +ARM_sqshrun, // Signed Integer Saturating Shift Right, Narrow, Unsigned result +ARM_sqshrun2, // Signed Integer Saturating Shift Right, Narrow, Unsigned result (second part) +ARM_uqsub, // Unsigned Integer Saturating Subtract +ARM_sqsub, // Signed Integer Saturating Subtract +ARM_raddhn, // Integer Rounding Add and Narrow, returning High Half +ARM_raddhn2, // Integer Rounding Add and Narrow, returning High Half (second part) +ARM_urecpe, // Unsigned Integer Reciprocal Estimate +ARM_frecpe, // Floating-point Reciprocal Estimate +ARM_frecps, // Floating-point Reciprocal Step +ARM_frecpx, // Floating-point Reciprocal Exponent +ARM_rev64, // Reverse doublewords +ARM_urhadd, // Unsigned Integer Rounding Halving Add +ARM_srhadd, // Signed Integer Rounding Halving Add +ARM_urshl, // Unsigned Integer Rounding Shift Left +ARM_srshl, // Signed Integer Rounding Shift Left +ARM_urshr, // Unsigned Integer Rounding Shift Right +ARM_srshr, // Signed Integer Rounding Shift Right +ARM_rshrn, // Integer Rounding Shift Right and Narrow +ARM_rshrn2, // Integer Rounding Shift Right and Narrow (second part) +ARM_ursqrte, // Unsigned Integer Reciprocal Square Root Estimate +ARM_frsqrte, // Floating-point Reciprocal Square Root Estimate +ARM_frsqrts, // Floating-point Reciprocal Square Root Step +ARM_ursra, // Unsigned Integer Rounding Shift Right and Accumulate +ARM_srsra, // Signed Integer Rounding Shift Right and Accumulate +ARM_rsubhn, // Integer Subtract and Narrow, returning High Half +ARM_rsubhn2, // Integer Subtract and Narrow, returning High Half (second part) +ARM_ushl, // Unsigned Integer Shift Left +ARM_sshl, // Signed Integer Shift Left +ARM_ushll, // Unsigned Integer Shift Left Long +ARM_ushll2, // Unsigned Integer Shift Left Long (second part) +ARM_sshll, // Signed Integer Shift Left Long +ARM_sshll2, // Signed Integer Shift Left Long (second part) +ARM_ushr, // Unsigned Integer Shift Right +ARM_sshr, // Signed Integer Shift Right +ARM_shrn, // Integer Shift Right Narrow +ARM_shrn2, // Integer Shift Right Narrow (second part) +ARM_shl, // Shift Left +ARM_shll, // Shift Left Long +ARM_shll2, // Shift Left Long (second part) +ARM_sli, // Integer Vector Shift Left and Insert +ARM_fsqrt, // Floating-point Square Root +ARM_usra, // Unsigned Integer Shift Right and Accumulate +ARM_ssra, // Signed Integer Shift Right and Accumulate +ARM_sri, // Integer Shift Right and Insert +ARM_st1, // Vector Store Single Element +ARM_st2, // Vector Store Two-Element Structures +ARM_st3, // Vector Store Three-Element Structures +ARM_st4, // Vector Store Four-Element Structures +ARM_fsub, // Floating-point Subtract +ARM_subhn, // Integer Subtract and Narrow, returning High Half +ARM_subhn2, // Integer Subtract and Narrow, returning High Half (second part) +ARM_usubl, // Unsigned Integer Subtract Long +ARM_usubl2, // Unsigned Integer Subtract Long (second part) +ARM_ssubl, // Signed Integer Subtract Long +ARM_ssubl2, // Signed Integer Subtract Long (second part) +ARM_usubw, // Unsigned Integer Subtract Wide +ARM_usubw2, // Unsigned Integer Subtract Wide (second part) +ARM_ssubw, // Signed Integer Subtract Wide +ARM_ssubw2, // Signed Integer Subtract Wide (second part) +ARM_tbl, // Vector Table Lookup +ARM_tbx, // Vector Table Extension +ARM_trn1, // Vector Element Transpose (primary) +ARM_trn2, // Vector Element Transpose (secondary) +ARM_cmtst, // Integer Compare Mask Bitwise Test +ARM_uzp1, // Vector Element Unzip (primary) +ARM_uzp2, // Vector Element Unzip (secondary) +ARM_zip1, // Vector Element Zip (primary) +ARM_zip2, // Vector Element Zip (secondary) +ARM_addv, // Integer Sum Elements in Vector +ARM_uaddlv, // Unsigned Integer Sum Elements in Vector Long +ARM_saddlv, // Signed Integer Sum Elements in Vector Long +ARM_umaxv, // Unsigned Integer Max Element in Vector +ARM_smaxv, // Signed Integer Max Element in Vector +ARM_fmaxv, // Floating-point Max Element in Vector +ARM_fmaxnmv, // Floating-point maxNum Element in Vector +ARM_uminv, // Unsigned Integer Min Element in Vector +ARM_sminv, // Signed Integer Min Element in Vector +ARM_fminv, // Floating-point Min Element in Vector +ARM_fminnmv, // Floating-point minNum Element in Vector + +// atomic operations (ARMv8.1/ARMv8.3) +// ARM_swp, // Swap value with memory, No memory ordering +ARM_swpl, // Swap value with memory, Release +ARM_swpa, // Swap value with memory, Acquire +ARM_swpal, // Swap value with memory, Acquire and release + +ARM_ldapr, // Load-Acquire RCpc Register + +ARM_ldadd, // Atomic add on value in memory (return initial value), No memory ordering +ARM_ldaddl, // Atomic add on value in memory (return initial value), Release +ARM_ldadda, // Atomic add on value in memory (return initial value), Acquire +ARM_ldaddal, // Atomic add on value in memory (return initial value), Acquire and release +ARM_stadd, // Atomic add on value in memory (no return), No memory ordering +ARM_staddl, // Atomic add on value in memory (no return), Release + +ARM_ldclr, // Atomic bit clear on value in memory (return initial value), No memory ordering +ARM_ldclrl, // Atomic bit clear on value in memory (return initial value), Release +ARM_ldclra, // Atomic bit clear on value in memory (return initial value), Acquire +ARM_ldclral, // Atomic bit clear on value in memory (return initial value), Acquire and release +ARM_stclr, // Atomic bit clear on value in memory (no return), No memory ordering +ARM_stclrl, // Atomic bit clear on value in memory (no return), Release + +ARM_ldeor, // Atomic exclusive OR on value in memory (return initial value), No memory ordering +ARM_ldeorl, // Atomic exclusive OR on value in memory (return initial value), Release +ARM_ldeora, // Atomic exclusive OR on value in memory (return initial value), Acquire +ARM_ldeoral, // Atomic exclusive OR on value in memory (return initial value), Acquire and release +ARM_steor, // Atomic exclusive OR on value in memory (no return), No memory ordering +ARM_steorl, // Atomic exclusive OR on value in memory (no return), Release + +ARM_ldset, // Atomic bit set on value in memory (return initial value), No memory ordering +ARM_ldsetl, // Atomic bit set on value in memory (return initial value), Release +ARM_ldseta, // Atomic bit set on value in memory (return initial value), Acquire +ARM_ldsetal, // Atomic bit set on value in memory (return initial value), Acquire and release +ARM_stset, // Atomic bit set on value in memory (no return), No memory ordering +ARM_stsetl, // Atomic bit set on value in memory (no return), Release + +ARM_ldsmax, // Atomic signed maximum on value in memory (return initial value), No memory ordering +ARM_ldsmaxl, // Atomic signed maximum on value in memory (return initial value), Release +ARM_ldsmaxa, // Atomic signed maximum on value in memory (return initial value), Acquire +ARM_ldsmaxal, // Atomic signed maximum on value in memory (return initial value), Acquire and release +ARM_stsmax, // Atomic signed maximum on value in memory (no return), No memory ordering +ARM_stsmaxl, // Atomic signed maximum on value in memory (no return), Release + + +ARM_ldsmin, // Atomic signed minimum on value in memory (return initial value), No memory ordering +ARM_ldsminl, // Atomic signed minimum on value in memory (return initial value), Release +ARM_ldsmina, // Atomic signed minimum on value in memory (return initial value), Acquire +ARM_ldsminal, // Atomic signed minimum on value in memory (return initial value), Acquire and release +ARM_stsmin, // Atomic signed minimum on value in memory (no return), No memory ordering +ARM_stsminl, // Atomic signed minimum on value in memory (no return), Release + +ARM_ldumax, // Atomic unsigned maximum on value in memory (return initial value), No memory ordering +ARM_ldumaxl, // Atomic unsigned maximum on value in memory (return initial value), Release +ARM_ldumaxa, // Atomic unsigned maximum on value in memory (return initial value), Acquire +ARM_ldumaxal, // Atomic unsigned maximum on value in memory (return initial value), Acquire and release +ARM_stumax, // Atomic unsigned maximum on value in memory (no return), No memory ordering +ARM_stumaxl, // Atomic unsigned maximum on value in memory (no return), Release + + +ARM_ldumin, // Atomic unsigned minimum on value in memory (return initial value), No memory ordering +ARM_lduminl, // Atomic unsigned minimum on value in memory (return initial value), Release +ARM_ldumina, // Atomic unsigned minimum on value in memory (return initial value), Acquire +ARM_lduminal, // Atomic unsigned minimum on value in memory (return initial value), Acquire and release +ARM_stumin, // Atomic unsigned minimum on value in memory (no return), No memory ordering +ARM_stuminl, // Atomic unsigned minimum on value in memory (no return), Release + +// Compare and Swap (ARMv8.1) + +ARM_cas, // Compare and Swap , No memory ordering +ARM_casl, // Compare and Swap, Release +ARM_casa, // Compare and Swap, Acquire +ARM_casal, // Compare and Swap, Acquire and release + +ARM_casp, // Compare and Swap Pair, No memory ordering +ARM_caspl, // Compare and Swap Pair, Release +ARM_caspa, // Compare and Swap Pair, Acquire +ARM_caspal, // Compare and Swap Pair, Acquire and release + +ARM_ldlar, // Load LOAcquire Register +ARM_stllr, // Store LORelease Register + +ARM_sqrdmlah, // Signed Saturating Rounding Doubling Multiply Accumulate +ARM_sqrdmlsh, // Signed Saturating Rounding Doubling Multiply Subtract + +// Pointer authentication (ARMv8.3) +// only completely new instructions are included; +// authenticated variations of old ones are represented by instruction flags (see arm.hpp) + +ARM_pac, // Pointer Authentication Code for Data/Instruction address/generic +ARM_aut, // Authenticate Data/Instruction address +ARM_xpac, // Strip Pointer Authentication Code + +// ARM insns for the legacy assembler (2 operands) +ARM_ldrd2, // Load pair of registers +ARM_strd2, // Store pair of registers + +// CRC32 (optional in 8.0, mandatory since 8.1) +ARM_crc32, // CRC32 using polynomial 0x04C11DB7 +ARM_crc32c, // CRC32 using polynomial 0x1EDC6F41 + +// Armv8-M Security Extension (TrustZone for Armv8-M) +ARM_tt, // Test Target (query the Security state and access permissions of a memory location) +ARM_tta, // Test Target Alternate Domain (query the Security state and access permissions of a memory location for a Non-secure access) +ARM_sg, // Secure Gateway +ARM_vlldm, // Floating-point Lazy Load Multiple +ARM_vlstm, // Floating-point Lazy Store Multiple + +// MP Extension instructions +ARM_pldw, // Preload Data for write + +// ARMv8.1-RDMA (AArh32) +ARM_vqrdmlah, // Vector Saturating Rounding Doubling Multiply Accumulate Returning High Half. + // Vector Saturating Rounding Doubling Multiply Accumulate +ARM_vqrdmlsh, // Vector Saturating Rounding Doubling Multiply Subtract Returning High Half + +// ARMv8.2-FP16 (AArch32) + +ARM_vmovx, // Vector Move extraction +ARM_vins, // Vector move Insertion. + +// ARMv8.3-JSConv (AArch32) +ARM_vjcvt, // Javascript Convert to signed fixed-point, rounding toward Zero + +// ARMv8.3-JSConv (A64) +ARM_fjcvtzs, // Javascript Convert to signed fixed-point, rounding toward Zero + + +/* +// ARMv8.3-CompNum, SIMD complex number support +ARM_fcadd, // Floating-point complex add +ARM_fcmla, // Floating-point complex multiply accumulate +*/ + +// ARMv8.4-RCPC + +ARM_ldapur, // Load-Acquire RCpc Register (unscaled offset) +ARM_stlur, // Store-Release Register (unscaled offset) + +// ARMv8.4 + +ARM_cfinv, // Invert Carry Flag +ARM_rmif, // Rotate value and mask into flags +ARM_setf8, // set flags from value (8-bit) +ARM_setf16, // set flags from value (16-bit) +ARM_xaflag, // Convert floating-point condition flags to ARM format +ARM_axflag, // Convert floating-point condition flags from ARM to external format. + +// ARMv8.5 +ARM_addg, // Add with Tag +ARM_subg, // Subtract with Tag +ARM_subp, // Subtract Pointer +ARM_cmpp, // Compare with Tag +ARM_irg, // Insert Random Tag +ARM_gmi, // Tag Mask Insert +ARM_stg, // Store Allocation Tag +ARM_stzg, // Store Allocation Tag, Zeroing +ARM_stzgm, // Store Tag and Zero Multiple +ARM_st2g, // Store Allocation Tags +ARM_stz2g, // Store Allocation Tags, Zeroing +ARM_stgm, // Store Tag Multiple +ARM_stgp, // Store Allocation Tag and Pair of registers +ARM_ldg, // Load Allocation Tag +ARM_ldgm, // Load Tag Multiple +ARM_bti, // Branch Target Identification +ARM_sb, // Speculation Barrier +ARM_ssbb, // Speculative Store Bypass Barrier +ARM_pssbb, // Physical Speculative Store Bypass Barrier + + +// + +ARM_frint32x, // Floating-point Round to 32-bit Integer, using current rounding mode +ARM_frint32z, // Floating-point Round to 32-bit Integer toward Zero +ARM_frint64x, // Floating-point Round to 32-bit Integer, using current rounding mode +ARM_frint64z, // Floating-point Round to 32-bit Integer toward Zero + +// ARMv8.2-SHA3 (AArch64) +ARM_bcax, // Bit Clear and exclusive-OR +ARM_eor3, // Three-way Exclusive-OR +ARM_rax1, // Rotate and Exclusive-OR +ARM_xar, // Exclusive-OR and Rotate + +// ARMv8.2-SHA512 (AArch64) +ARM_sha512h, // SHA512 Hash update part 1 +ARM_sha512h2, // SHA512 Hash update part 2 +ARM_sha512su0, // SHA512 Schedule Update 0 +ARM_sha512su1, // SHA512 Schedule Update 1 + +// ARMv8.2-SM3 (AArch64) +ARM_sm3partw1, +ARM_sm3partw2, +ARM_sm3ss1, +ARM_sm3tt1a, +ARM_sm3tt1b, +ARM_sm3tt2a, +ARM_sm3tt2b, + +// ARMv8.2-SM4 (AArch64) +ARM_sm4e, +ARM_sm4ekey, + +// ARMv8.6-I8MM (AArch64) +ARM_sudot, +ARM_ummla, +ARM_usdot, +ARM_usmmla, + +// ARMv8.6-DotProd (AArch64) +ARM_sdot, +ARM_udot, + +// ARMv8.2-FHM (AArch64) +ARM_fmlal, +ARM_fmlal2, +ARM_fmlsl, +ARM_fmlsl2, + +// ARMv8.6-BF16 (AArch64) +ARM_bfmmla, +ARM_bfmlalb, +ARM_bfmlalt, +ARM_bfdot, +ARM_bfcvtn, +ARM_bfcvtn2, +ARM_bfcvt, + + + +// Apple custom instructions + +ARM_trap, // Undefined opcode used for debugging +ARM_trap2, // Undefined opcode used for debugging + +ARM_genter, // Enter guarded execution mode +ARM_gexit, // Exit guarded execution mode +ARM_wkdmc, // Compress memory page +ARM_wkdmd, // Decompress memory page +ARM_sdsb, // Strong data synchronization barrier +ARM_amx, // AMX instruction +ARM_mul53lo, // Multiply 2 53-bit pairs and store low 53 bits +ARM_mul53hi, // Multiply 2 53-bit pairs and store high 53 bits + +// ARMv8.1-M MVE +ARM_vbrsr, // Vector Bit Reverse and Shift Right +ARM_vcmul, // Vector Complex Multiply +ARM_vfmas, // Vector Fused Multiply Accumulate Scalar +ARM_vmaxa, // Vector Maximum Absolute +ARM_vmaxnma, // Vector Maximum Absolute (floating-point) +ARM_vmina, // Vector Minimum Absolute +ARM_vminnma, // Vector Minimum Absolute (floating-point) +ARM_vmlas, // Vector Multiply Accumulate Scalar +ARM_vmovnb, // Vector Move and Narrow Bottom +ARM_vmovnt, // Vector Move and Narrow Top +ARM_vmulh, // Vector Multiply Returning High Half +ARM_vrmulh, // Vector Rounding Multiply Returning High Half +ARM_vmullb, // Vector Multiply Long Bottom +ARM_vmullt, // Vector Multiply Long Top +ARM_vqdmladh, // Vector Saturating Doubling Multiply Add Dual Returning High Half +ARM_vqdmladhX, // Vector Saturating Doubling Multiply Add Dual Returning High Half (exchange) +ARM_vqrdmladh, // Vector Saturating Rounding Doubling Multiply Add Dual Returning High Half +ARM_vqrdmladhX, // Vector Saturating Rounding Doubling Multiply Add Dual Returning High Half (exchange) +ARM_vqdmlah, // Vector Saturating Doubling Multiply Accumulate +ARM_vqdmlash, // Vector Saturating Doubling Multiply Accumulate Scalar High Half +ARM_vqrdmlash, // Vector Saturating Rounding Doubling Multiply Accumulate Scalar High Half +ARM_vqdmlsdh, // Vector Saturating Doubling Multiply Subtract Dual Returning High Half +ARM_vqdmlsdhX, // Vector Saturating Doubling Multiply Subtract Dual Returning High Half (exchange) +ARM_vqrdmlsdh, // Vector Saturating Rounding Doubling Multiply Subtract Dual Returning High Half +ARM_vqrdmlsdhX, // Vector Saturating Rounding Doubling Multiply Subtract Dual Returning High Half (exchange) +ARM_vqmovnb, // Vector Saturating Move and Narrow Bottom +ARM_vqmovnt, // Vector Saturating Move and Narrow Top +ARM_vqmovunb, // Vector Saturating Move Unsigned and Narrow Bottom +ARM_vqmovunt, // Vector Saturating Move Unsigned and Narrow Top +ARM_vshllb, // Vector Shift Left Long Bottom +ARM_vshllt, // Vector Shift Left Long Top +ARM_vmladav, // Vector Multiply Add Dual Accumulate Across Vector +ARM_vmladavX, // Vector Multiply Add Dual Accumulate Across Vector (exchange) +ARM_vmladavA, // Vector Multiply Add Dual Accumulate Across Vector (accumulate) +ARM_vmladavAX, // Vector Multiply Add Dual Accumulate Across Vector (accumulate, exchange) +ARM_vmlsdav, // Vector Multiply Subtract Dual Accumulate Across Vector +ARM_vmlsdavX, // Vector Multiply Subtract Dual Accumulate Across Vector (exchange) +ARM_vmlsdavA, // Vector Multiply Subtract Dual Accumulate Across Vector (accumulate) +ARM_vmlsdavAX, // Vector Multiply Subtract Dual Accumulate Across Vector (accumulate, exchange) +ARM_vmlaldav, // Vector Multiply Add Long Dual Accumulate Across Vector +ARM_vmlaldavX, // Vector Multiply Add Long Dual Accumulate Across Vector (exchange) +ARM_vmlaldavA, // Vector Multiply Add Long Dual Accumulate Across Vector (accumulate) +ARM_vmlaldavAX, // Vector Multiply Add Long Dual Accumulate Across Vector (accumulate, exchange) +ARM_vmlsldav, // Vector Multiply Subtract Long Dual Accumulate Across Vector +ARM_vmlsldavX, // Vector Multiply Subtract Long Dual Accumulate Across Vector (exchange) +ARM_vmlsldavA, // Vector Multiply Subtract Long Dual Accumulate Across Vector (accumulate) +ARM_vmlsldavAX, // Vector Multiply Subtract Long Dual Accumulate Across Vector (accumulate, exchange) +ARM_vrmlsldavh, // Vector Rounding Multiply Subtract Long Dual Accumulate Across Vector Returning High 64 bits +ARM_vrmlsldavhX, // Vector Rounding Multiply Subtract Long Dual Accumulate Across Vector Returning High 64 bits (exchange) +ARM_vrmlsldavhA, // Vector Rounding Multiply Subtract Long Dual Accumulate Across Vector Returning High 64 bits (accumulate) +ARM_vrmlsldavhAX, // Vector Rounding Multiply Subtract Long Dual Accumulate Across Vector Returning High 64 bits (accumulate, exchange) +ARM_vidup, // Vector Increment and Duplicate +ARM_vddup, // Vector Decrement and Duplicate +ARM_viwdup, // Vector Increment with Wrap and Duplicate +ARM_vdwdup, // Vector Decrement with Wrap and Duplicate +ARM_vhcadd, // Vector Halving Complex Add with Rotate +ARM_vcadd, // Vector Complex Add with Rotate +ARM_vadc, // Whole Vector Add With Carry +ARM_vadcI, // Whole Vector Add With Carry (initial carry is 1) +ARM_sbdc, // Whole Vector Subtract With Carry +ARM_vsbcI, // Whole Vector Subtract With Carry (initial carry is 1) +ARM_vqdmullb, // Vector Multiply Long Bottom +ARM_vqdmullt, // Vector Multiply Long Top +ARM_vpsel, // Vector Predicated Select +ARM_vcmpv, // Vector Compare (vector) +ARM_vpt, // Vector Predicate Then +ARM_vpnot, // Vector Predicate NOT +ARM_vpst, // Vector Predicate Set Then +ARM_vmaxv, // Vector Maximum Across Vector +ARM_vmaxav, // Vector Maximum Absolute Across Vector +ARM_vminv, // Vector Minimum Across Vector +ARM_vminav, // Vector Minimum Absolute Across Vector +ARM_vmaxnmv, // Vector Maximum Across Vector (floating-point) +ARM_vmaxnmav, // Vector Maximum Absolute Across Vector (floating-point) +ARM_vminnmv, // Vector Minimum Across Vector (floating-point) +ARM_vminnmav, // Vector Minimum Absolute Across Vector (floating-point) +ARM_vqshrnb, // Vector Saturating Shift Right and Narrow Bottom +ARM_vqshrnt, // Vector Saturating Shift Right and Narrow Top +ARM_vqrshrnb, // Vector Saturating Rounding Shift Right and Narrow Bottom +ARM_vqrshrnt, // Vector Saturating Rounding Shift Right and Narrow Top +ARM_vqshrunb, // Vector Saturating Shift Right Unsigned and Narrow Bottom +ARM_vqshrunt, // Vector Saturating Shift Right Unsigned and Narrow Top +ARM_vqrshrunb, // Vector Saturating Rounding Shift Right Unsigned and Narrow Bottom +ARM_vqrshrunt, // Vector Saturating Rounding Shift Right Unsigned and Narrow Top +ARM_vshrnb, // Vector Shift Right and Narrow Bottom +ARM_vshrnt, // Vector Shift Right and Narrow Top +ARM_vrshrnb, // Vector Rounding Shift Right and Narrow Bottom +ARM_vrshrnt, // Vector Rounding Shift Right and Narrow Top +ARM_vrmlaldavh, // Vector Rounding Multiply Add Long Dual Accumulate Across Vector Returning High 64 bits +ARM_vrmlaldavhX, // Vector Rounding Multiply Add Long Dual Accumulate Across Vector Returning High 64 bits (exchange) +ARM_vrmlaldavhA, // Vector Rounding Multiply Add Long Dual Accumulate Across Vector Returning High 64 bits (accumulate) +ARM_vrmlaldavhAX, // Vector Rounding Multiply Add Long Dual Accumulate Across Vector Returning High 64 bits (accumulate, exchange) +ARM_vabav, // Vector Absolute Difference and Accumulate Across Vector +ARM_vaddlv, // Vector Add Long Across Vector +ARM_vaddlvA, // Vector Add Long Across Vector (accumulate) +ARM_vaddv, // Vector Add Across Vector +ARM_vaddvA, // Vector Add Across Vector (accumulate) +ARM_vshlc, // Whole Vector Left Shift with Carry +ARM_vmovlb, // Vector Move Long Bottom +ARM_vmovlt, // Vector Move Long Top +ARM_vcmla, // Vector Complex Multiply Accumulate +ARM_vscclrm, // Floating-point Secure Context Clear Multiple +ARM_vld20, // Vector Deinterleaving Load - Stride 2 (pattern=0) +ARM_vld21, // Vector Deinterleaving Load - Stride 2 (pattern=1) +ARM_vld40, // Vector Deinterleaving Load - Stride 4 (pattern=0) +ARM_vld41, // Vector Deinterleaving Load - Stride 4 (pattern=1) +ARM_vld42, // Vector Deinterleaving Load - Stride 4 (pattern=2) +ARM_vld43, // Vector Deinterleaving Load - Stride 4 (pattern=3) +ARM_vst20, // Vector Interleaving Store - Stride 2 (pattern=0) +ARM_vst21, // Vector Interleaving Store - Stride 2 (pattern=1) +ARM_vst40, // Vector Interleaving Store - Stride 4 (pattern=0) +ARM_vst41, // Vector Interleaving Store - Stride 4 (pattern=1) +ARM_vst42, // Vector Interleaving Store - Stride 4 (pattern=2) +ARM_vst43, // Vector Interleaving Store - Stride 4 (pattern=3) +ARM_vldrb, // Vector Load Register/Vector Gather Load (byte) +ARM_vldrh, // Vector Load Register/Vector Gather Load (halfword) +ARM_vldrw, // Vector Load Register/Vector Gather Load (word) +ARM_vldrd, // Vector Load Register/Vector Gather Load (doubleword) +ARM_vstrb, // Vector Store Register/Vector Scatter Store (byte) +ARM_vstrh, // Vector Store Register/Vector Scatter Store (halfword) +ARM_vstrw, // Vector Store Register/Vector Scatter Store (word) +ARM_vstrd, // Vector Store Register/Vector Scatter Store (doubleword) +ARM_vmov2, // Vector Move (two general-purpose registers to two 32-bit vector lanes) +ARM_lsll, // Logical Shift Left Long +ARM_lsrl, // Logical Shift Right Long +ARM_asrl, // Arithmetic Shift Right Long +ARM_sqshll, // Signed Saturating Shift Left Long +ARM_uqshll, // Unsigned Saturating Shift Left Long +ARM_urshrl, // Unsigned Rounding Shift Right Long +ARM_srshrl, // Signed Rounding Shift Right Long +ARM_uqrshll, // Unsigned Saturating Rounding Shift Left Long +ARM_sqrshrl, // Signed Saturating Rounding Shift Right Long +ARM_sqrshr, // Signed Saturating Rounding Shift Right +ARM_lctp, // Loop Clear with Tail Predication +ARM_vctp, // Create Vector Tail Predicate +// ARMv8.1-M PACBTI +ARM_pacbti, // Pointer Authentication Code for the link register with BTI clearing, using key +ARM_autg, // Authenticate general value, using key +ARM_bxaut, // Branch Exchange after Authenticating the address, using key +ARM_pacg, // Pointer Authentication Code for a general value, using key +// ARMv8.1-M RAS +ARM_esb, // Error Synchronization Barrier +// ARMv8.1-M Low Overhead Branch Extension +ARM_wls, // While Loop Start +ARM_dls, // Do Loop Start +ARM_wlstp, // While Loop Start with Tail Predication +ARM_dlstp, // Do Loop Start with Tail Predication +ARM_le1, // Loop End (count) +ARM_le2, // Loop End (forever) +ARM_letp, // Loop End with Tail Prediction +ARM_bf, // Branch Future +ARM_bfx, // Branch Future and Exchange +ARM_bfl, // Branch Future with Link +ARM_bflx, // Branch Future with Link and Exchange +ARM_bfcsel, // Branch Future Conditional Select + +// ARMv8-M Custom Datapath Extension +ARM_cx1, // Custom Instruction Class 1 +ARM_cx1A, // Custom Instruction Class 1 (accumulator) +ARM_cx1d, // Custom Instruction Class 1 (pair) +ARM_cx1dA, // Custom Instruction Class 1 (pair, accumulator) +ARM_cx2, // Custom Instruction Class 2 +ARM_cx2A, // Custom Instruction Class 2 (accumulator) +ARM_cx2d, // Custom Instruction Class 2 (pair) +ARM_cx2dA, // Custom Instruction Class 2 (pair, accumulator) +ARM_cx3, // Custom Instruction Class 3 +ARM_cx3A, // Custom Instruction Class 3 (accumulator) +ARM_cx3d, // Custom Instruction Class 3 (pair) +ARM_cx3dA, // Custom Instruction Class 3 (pair, accumulator) +ARM_vcx1, // Custom Extension Instruction Class 1 +ARM_vcx1A, // Custom Extension Instruction Class 1 (accumulator) +ARM_vcx2, // Custom Extension Instruction Class 2 +ARM_vcx2A, // Custom Extension Instruction Class 2 (accumulator) +ARM_vcx3, // Custom Extension Instruction Class 3 +ARM_vcx3A, // Custom Extension Instruction Class 3 (accumulator) + +// FEAT_WFxT +ARM_wfet, // Wait For Event with Timeout +ARM_wfit, // Wait For Interrupt with Timeout + +ARM_last + +}; + + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + * TMS320C6xx - VLIW (very long instruction word) architecture + * + */ + + + +enum +{ +// Original TMS320C62x instructions + +TMS6_null = 0, // Unknown Operation +TMS6_abs, // Absolute value +TMS6_add, // Integer addition without saturation (signed) +TMS6_addu, // Integer addition without saturation (unsigned) +TMS6_addab, // Integer addition using addressing mode (byte) +TMS6_addah, // Integer addition using addressing mode (halfword) +TMS6_addaw, // Integer addition using addressing mode (word) +TMS6_addk, // Integer addition 16bit signed constant +TMS6_add2, // Two 16bit Integer adds on register halves +TMS6_and, // Logical AND +TMS6_b, // Branch +TMS6_clr, // Clear a bit field +TMS6_cmpeq, // Compare for equality +TMS6_cmpgt, // Compare for greater than (signed) +TMS6_cmpgtu, // Compare for greater than (unsigned) +TMS6_cmplt, // Compare for less than (signed) +TMS6_cmpltu, // Compare for less than (unsigned) +TMS6_ext, // Extract and sign-extend a bit filed +TMS6_extu, // Extract an unsigned bit field +TMS6_idle, // Multicycle NOP with no termination until interrupt +TMS6_ldb, // Load from memory (signed 8bit) +TMS6_ldbu, // Load from memory (unsigned 8bit) +TMS6_ldh, // Load from memory (signed 16bit) +TMS6_ldhu, // Load from memory (unsigned 16bit) +TMS6_ldw, // Load from memory (32bit) +TMS6_lmbd, // Leftmost bit detection +TMS6_mpy, // Signed Integer Multiply (LSB16 x LSB16) +TMS6_mpyu, // Unsigned Integer Multiply (LSB16 x LSB16) +TMS6_mpyus, // Integer Multiply Signed*Unsigned (LSB16 x LSB16) +TMS6_mpysu, // Integer Multiply Unsigned*Signed (LSB16 x LSB16) +TMS6_mpyh, // Signed Integer Multiply (MSB16 x MSB16) +TMS6_mpyhu, // Unsigned Integer Multiply (MSB16 x MSB16) +TMS6_mpyhus, // Integer Multiply Unsigned*Signed (MSB16 x MSB16) +TMS6_mpyhsu, // Integer Multiply Signed*Unsigned (MSB16 x MSB16) +TMS6_mpyhl, // Signed Integer Multiply (MSB16 x LSB16) +TMS6_mpyhlu, // Unsigned Integer Multiply (MSB16 x LSB16) +TMS6_mpyhuls, // Integer Multiply Signed*Unsigned (MSB16 x LSB16) +TMS6_mpyhslu, // Integer Multiply Unsigned*Signed (MSB16 x LSB16) +TMS6_mpylh, // Signed Integer Multiply (LSB16 x MB16) +TMS6_mpylhu, // Unsigned Integer Multiply (LSB16 x MSB16) +TMS6_mpyluhs, // Integer Multiply Signed*Unsigned (LSB16 x MSB16) +TMS6_mpylshu, // Integer Multiply Unsigned*Signed (LSB16 x MSB16) +TMS6_mv, // Move from register to register +TMS6_mvc, // Move between the control file & register file +TMS6_mvk, // Move a 16bit signed constant into register +TMS6_mvkh, // Move a 16bit constant into the upper bits of a register +TMS6_mvklh, // Move a 16bit constant into the upper bits of a register +TMS6_neg, // Negate +TMS6_nop, // No operation +TMS6_norm, // Normalize +TMS6_not, // Bitwise NOT +TMS6_or, // Logical or +TMS6_sadd, // Integer addition with saturation +TMS6_sat, // Saturate 40bit value to 32bits +TMS6_set, // Set a bit field +TMS6_shl, // Arithmetic shift left +TMS6_shr, // Arithmetic shift right +TMS6_shru, // Logical shift left +TMS6_smpy, // Integer multiply with left shift & saturation (LSB16*LSB16) +TMS6_smpyhl, // Integer multiply with left shift & saturation (MSB16*LSB16) +TMS6_smpylh, // Integer multiply with left shift & saturation (LSB16*MSB16) +TMS6_smpyh, // Integer multiply with left shift & saturation (MSB16*MSB16) +TMS6_sshl, // Shift left with saturation +TMS6_ssub, // Integer substraction with saturation +TMS6_stb, // Store to memory (signed 8bit) +TMS6_stbu, // Store to memory (unsigned 8bit) +TMS6_sth, // Store to memory (signed 16bit) +TMS6_sthu, // Store to memory (unsigned 16bit) +TMS6_stw, // Store to memory (32bit) +TMS6_sub, // Integer substaraction without saturation (signed) +TMS6_subu, // Integer substaraction without saturation (unsigned) +TMS6_subab, // Integer subtraction using addressing mode (byte) +TMS6_subah, // Integer subtraction using addressing mode (halfword) +TMS6_subaw, // Integer subtraction using addressing mode (word) +TMS6_subc, // Conditional subtract & shift (for division) +TMS6_sub2, // Two 16bit integer subtractions on register halves +TMS6_xor, // Exclusive OR +TMS6_zero, // Zero a register + +// New TMS320C674x instructions + +TMS6_abs2, // Absolute Value With Saturation, Signed, Packed 16-bit +TMS6_absdp, // Absolute Value, Double-Precision Floating-Point +TMS6_abssp, // Absolute Value, Single-Precision Floating-Point +TMS6_add4, // Add Without Saturation, Four 8-Bit Pairs for Four 8-Bit Results +TMS6_addad, // Add Using Doubleword Addressing Mode +TMS6_adddp, // Add Two Double-Precision Floating-Point Values +TMS6_addkpc, // Add Signed 7-bit Constant to Program Counter +TMS6_addsp, // Add Two Single-Precision Floating-Point Values +TMS6_addsub, // Parallel ADD and SUB Operations On Common Inputs +TMS6_addsub2, // Parallel ADD2 and SUB2 Operations On Common Inputs +TMS6_andn, // Bitwise AND Invert +TMS6_avg2, // Average, Signed, Packed 16-bit +TMS6_avgu4, // Average, Unsigned, Packed 16-bit +TMS6_bdec, // Branch and Decrement +TMS6_bitc4, // Bit Count, Packed 8-bit +TMS6_bitr, // Bit Reverse +TMS6_bnop, // Branch With NOP +TMS6_bpos, // Branch Positive +TMS6_callp, // Call Using a Displacement +TMS6_cmpeq2, // Compare for Equality, Packed 16-bit +TMS6_cmpeq4, // Compare for Equality, Packed 8-bit +TMS6_cmpeqdp, // Compare for Equality, Double-Precision Floating-Point Values +TMS6_cmpeqsp, // Compare for Equality, Single-Precision Floating-Point Values +TMS6_cmpgt2, // Compare for Greater Than, Packed 16-bit +TMS6_cmpgtdp, // Compare for Greater Than, Double-Precision Floating-Point Values +TMS6_cmpgtsp, // Compare for Greater Than, Single-Precision Floating-Point Values +TMS6_cmpgtu4, // Compare for Greater Than, Unsigned, Packed 8-bit +TMS6_cmplt2, // Compare for Less Than, Packed 16-bit +TMS6_cmpltdp, // Compare for Less Than, Double-Precision Floating-Point Values +TMS6_cmpltsp, // Compare for Less Than, Single-Precision Floating-Point Values +TMS6_cmpltu4, // Compare for Less Than, Unsigned, Packed 8-bit +TMS6_cmpy, // Complex Multiply Two Pairs, Signed, Packed 16-bit +TMS6_cmpyr, // Complex Multiply Two Pairs, Signed, Packed 16-bit With Rounding +TMS6_cmpyr1, // Complex Multiply Two Pairs, Signed, Packed 16-bit With Rounding +TMS6_ddotp4, // Double Dot Product, Signed, Packed 16-Bit and Signed, Packed 8-Bit +TMS6_ddotph2, // Double Dot Product, Two Pairs, Signed, Packed 16-Bit +TMS6_ddotph2r, // Double Dot Product With Rounding, Two Pairs, Signed, Packed 16-Bit +TMS6_ddotpl2, // Double Dot Product, Two Pairs, Signed, Packed 16-Bit +TMS6_ddotpl2r, // Double Dot Product With Rounding, Two Pairs, Signed Packed 16-Bit +TMS6_deal, // Deinterleave and Pack +TMS6_dint, // Disable Interrupts and Save Previous Enable State +TMS6_dmv, // Move Two Independent Registers to Register Pair +TMS6_dotp2, // Dot Product, Signed, Packed 16-Bit +TMS6_dotpn2, // Dot Product With Negate, Signed, Packed 16-Bit +TMS6_dotpnrsu2, // Dot Product With Negate, Shift and Round, Signed by Unsigned, Packed 16-Bit +TMS6_dotpnrus2, // Dot Product With Negate, Shift and Round, Unsigned by Signed, Packed 16-Bit +TMS6_dotprsu2, // Dot Product With Shift and Round, Signed by Unsigned, Packed 16-Bit +TMS6_dotprus2, // Dot Product With Shift and Round, Unsigned by Signed, Packed 16-Bit +TMS6_dotpsu4, // Dot Product, Signed by Unsigned, Packed 8-Bit +TMS6_dotpu4, // Dot Product, Unsigned, Packed 8-Bit +TMS6_dotpus4, // Dot Product, Unsigned by Signed, Packed 8-Bit +TMS6_dpack2, // Parallel PACK2 and PACKH2 Operations +TMS6_dpackx2, // Parallel PACKLH2 Operations +TMS6_dpint, // Convert Double-Precision Floating-Point Value to Integer +TMS6_dpsp, // Convert Double-Precision Floating-Point Value to Single-Precision Floating-Point Value +TMS6_dptrunc, // Convert Double-Precision Floating-Point Value to Integer With Truncation +TMS6_gmpy, // Galois Field Multiply +TMS6_gmpy4, // Galois Field Multiply, Packed 8-Bit +TMS6_intdp, // Convert Signed Integer to Double-Precision Floating-Point Value +TMS6_intdpu, // Convert Unsigned Integer to Double-Precision Floating-Point Value +TMS6_intsp, // Convert Signed Integer to Single-Precision Floating-Point Value +TMS6_intspu, // Convert Unsigned Integer to Single-Precision Floating-Point Value +TMS6_lddw, // Load Doubleword From Memory With a 5-Bit Unsigned Constant Offset or Register Offset +TMS6_ldndw, // Load Nonaligned Doubleword From Memory With Constant or Register Offset +TMS6_ldnw, // Load Nonaligned Word From Memory With Constant or Register Offset +TMS6_max2, // Maximum, Signed, Packed 16-Bit +TMS6_maxu4, // Maximum, Unsigned, Packed 8-Bit +TMS6_min2, // Minimum, Signed, Packed 16-Bit +TMS6_minu4, // Minimum, Unsigned, Packed 8-Bit +TMS6_mpy2, // Multiply Signed by Signed, 16 LSB x 16 LSB and 16 MSB x 16 MSB +TMS6_mpy2ir, // Multiply Two 16-Bit x 32-Bit, Shifted by 15 to Produce a Rounded 32-Bit Result +TMS6_mpy32, // Multiply Signed 32-Bit x Signed 32-Bit Into 32-Bit Result +TMS6_mpy32su, // Multiply Signed 32-Bit x Unsigned 32-Bit Into Signed 64-Bit Result +TMS6_mpy32u, // Multiply Unsigned 32-Bit x Unsigned 32-Bit Into Unsigned 64-Bit Result +TMS6_mpy32us, // Multiply Unsigned 32-Bit x Signed 32-Bit Into Signed 64-Bit Result +TMS6_mpydp, // Multiply Two Double-Precision Floating-Point Values +TMS6_mpyhi, // Multiply 16 MSB x 32-Bit Into 64-Bit Result +TMS6_mpyhir, // Multiply 16 MSB x 32-Bit, Shifted by 15 to Produce a Rounded 32-Bit Result +TMS6_mpyi, // Multiply 32-Bit x 32-Bit Into 32-Bit Result +TMS6_mpyid, // Multiply 32-Bit x 32-Bit Into 64-Bit Result +TMS6_mpyih, // Multiply 32-Bit x 16-MSB Into 64-Bit Result +TMS6_mpyihr, // Multiply 32-Bit x 16 MSB, Shifted by 15 to Produce a Rounded 32-Bit Result +TMS6_mpyil, // Multiply 32-Bit x 16 LSB Into 64-Bit Result +TMS6_mpyilr, // Multiply 32-Bit x 16 LSB, Shifted by 15 to Produce a Rounded 32-Bit Result +TMS6_mpyli, // Multiply 16 LSB x 32-Bit Into 64-Bit Result +TMS6_mpylir, // Multiply 16 LSB x 32-Bit, Shifted by 15 to Produce a Rounded 32-Bit Result +TMS6_mpysp, // Multiply Two Single-Precision Floating-Point Values +TMS6_mpysp2dp, // Multiply Two Single-Precision Floating-Point Values for Double-Precision Result +TMS6_mpyspdp, // Multiply Single-Precision Floating-Point Value x Double-Precision Floating-Point Value +TMS6_mpysu4, // Multiply Signed x Unsigned, Four 8-Bit Pairs for Four 8-Bit Results +TMS6_mpyu4, // Multiply Unsigned x Unsigned, Four 8-Bit Pairs for Four 8-Bit Results +TMS6_mpyus4, // Multiply Unsigned x Signed, Four 8-Bit Pairs for Four 8-Bit Results +TMS6_mvd, // Move From Register to Register, Delayed +TMS6_mvkl, // Move Signed Constant Into Register and Sign Extend +TMS6_pack2, // Pack Two 16 LSBs Into Upper and Lower Register Halves +TMS6_packh2, // Pack Two 16 MSBs Into Upper and Lower Register Halves +TMS6_packh4, // Pack Four High Bytes Into Four 8-Bit Halfwords +TMS6_packhl2, // Pack 16 MSB Into Upper and 16 LSB Into Lower Register Halves +TMS6_packl4, // Pack Four Low Bytes Into Four 8-Bit Halfwords +TMS6_packlh2, // Pack 16 LSB Into Upper and 16 MSB Into Lower Register Halves +TMS6_rcpdp, // Double-Precision Floating-Point Reciprocal Approximation +TMS6_rcpsp, // Single-Precision Floating-Point Reciprocal Approximation +TMS6_rint, // Restore Previous Enable State +TMS6_rotl, // Rotate Left +TMS6_rpack2, // Shift With Saturation and Pack Two 16 MSBs Into Upper and Lower Register Halves +TMS6_rsqrdp, // Double-Precision Floating-Point Square-Root Reciprocal Approximation +TMS6_rsqrsp, // Single-Precision Floating-Point Square-Root Reciprocal Approximation +TMS6_sadd2, // Add Two Signed 16-Bit Integers on Upper and Lower Register Halves With Saturation +TMS6_saddsu2, // Add Two Signed and Unsigned 16-Bit Integers on Register Halves With Saturation +TMS6_saddsub, // Parallel SADD and SSUB Operations On Common Inputs +TMS6_saddsub2, // Parallel SADD2 and SSUB2 Operations On Common Inputs +TMS6_saddu4, // Add With Saturation, Four Unsigned 8-Bit Pairs for Four 8-Bit Results +TMS6_saddus2, // Add Two Unsigned and Signed 16-Bit Integers on Register Halves With Saturation +TMS6_shfl, // Shuffle +TMS6_shfl3, // 3-Way Bit Interleave On Three 16-Bit Values Into a 48-Bit Result +TMS6_shlmb, // Shift Left and Merge Byte +TMS6_shr2, // Arithmetic Shift Right, Signed, Packed 16-Bit +TMS6_shrmb, // Shift Right and Merge Byte +TMS6_shru2, // Arithmetic Shift Right, Unsigned, Packed 16-Bit +TMS6_smpy2, // Multiply Signed by Signed, 16 LSB x 16 LSB and 16 MSB x 16 MSB With Left Shift and Saturation +TMS6_smpy32, // Multiply Signed 32-Bit x Signed 32-Bit Into 64-Bit Result With Left Shift and Saturation +TMS6_spack2, // Saturate and Pack Two 16 LSBs Into Upper and Lower Register Halves +TMS6_spacku4, // Saturate and Pack Four Signed 16-Bit Integers Into Four Unsigned 8-Bit Halfwords +TMS6_spdp, // Convert Single-Precision Floating-Point Value to Double-Precision Floating-Point Value +TMS6_spint, // Convert Single-Precision Floating-Point Value to Integer +TMS6_spkernel, // Software Pipelined Loop (SPLOOP) Buffer Operation Code Boundary +TMS6_spkernelr, // Software Pipelined Loop (SPLOOP) Buffer Operation Code Boundary +TMS6_sploop, // Software Pipelined Loop (SPLOOP) Buffer Operation +TMS6_sploopd, // Software Pipelined Loop (SPLOOP) Buffer Operation With Delayed Testing +TMS6_sploopw, // Software Pipelined Loop (SPLOOP) Buffer Operation With Delayed Testing and No Epilog +TMS6_spmask, // Software Pipelined Loop (SPLOOP) Buffer Operation Load/Execution Control +TMS6_spmaskr, // Software Pipelined Loop (SPLOOP) Buffer Operation Load/Execution Control +TMS6_sptrunc, // Convert Single-Precision Floating-Point Value to Integer With Truncation +TMS6_sshvl, // Variable Shift Left +TMS6_sshvr, // Variable Shift Right +TMS6_ssub2, // Subtract Two Signed 16-Bit Integers on Upper and Lower Register Halves With Saturation +TMS6_stdw, // Store Doubleword to Memory With a 5-Bit Unsigned Constant Offset or Register Offset +TMS6_stndw, // Store Nonaligned Doubleword to Memory With a 5-Bit Unsigned Constant Offset or Register Offset +TMS6_stnw, // Store Nonaligned Word to Memory With a 5-Bit Unsigned Constant Offset or Register Offset +TMS6_sub4, // Subtract Without Saturation, Four 8-Bit Pairs for Four 8-Bit Results +TMS6_subabs4, // Subtract With Absolute Value, Four 8-Bit Pairs for Four 8-Bit Results +TMS6_subdp, // Subtract Two Double-Precision Floating-Point Values +TMS6_subsp, // Subtract Two Single-Precision Floating-Point Values +TMS6_swap2, // Swap Bytes in Upper and Lower Register Halves +TMS6_swap4, // Swap Byte Pairs in Upper and Lower Register Halves +TMS6_swe, // Software Exception +TMS6_swenr, // Software Exception, no Return +TMS6_unpkhu4, // Unpack 16 MSB Into Two Lower 8-Bit Halfwords of Upper and Lower Register Halves +TMS6_unpklu4, // Unpack 16 LSB Into Two Lower 8-Bit Halfwords of Upper and Lower Register Halves +TMS6_xormpy, // Galois Field Multiply With Zero Polynomial +TMS6_xpnd2, // Expand Bits to Packed 16-Bit Masks +TMS6_xpnd4, // Expand Bits to Packed 8-Bit Masks + +// New TMS320C66x Instructions + +TMS6_cmatmpy, // Complex Matrix Multiply, Signed Complex 16-bit (16-bit real/16-bit Imaginary) +TMS6_ccmatmpyr1, // Complex Conjugate Matrix Multiply With Rounding, Signed Complex 16-bit (16-bit Real/16-bit Imaginary) +TMS6_ccmpy32r1, // Complex Multiply With Rounding and Conjugate, Signed Complex 16-bit (16-bit Real/16-bit Imaginary) +TMS6_ccmatmpy, // Complex Conjugate Matrix Multiply, Signed Complex 16-bit (16-bit real/16-bit Imaginary) +TMS6_cmatmpyr1, // Complex Matrix Multiply With Rounding, Signed Complex 16-bit (16-bit Real/16-bit Imaginary) +TMS6_cmpysp, // Single Precision Complex Floating Point Multiply +TMS6_crot90, // Complex Rotate By 90 Degrees, Signed Complex 16-bit (16-bit Real/16-bit Imaginary) +TMS6_crot270, // Complex Rotate By 270 Degrees, Signed Complex 16-bit (16-bit Real/16-bit Imaginary) +TMS6_dadd, // 2-Way SIMD Addition, Packed Signed 32-bit +TMS6_dadd2, // 4-Way SIMD Addition, Packed Signed 16-bit +TMS6_daddsp, // 2-Way SIMD Single Precision Floating Point Addition +TMS6_dapys2, // 4-Way SIMD Apply Sign Bits to Operand +TMS6_davg2, // 4-Way SIMD Average, Signed, Packed 16-bit +TMS6_davgnr2, // 4-Way SIMD Average Without Rounding, Signed Packed 16-bit +TMS6_davgnru4, // 8-Way SIMD Average Without Rounding, Unsigned Packed 8-bit +TMS6_davgu4, // 8-Way SIMD Average, Unsigned Packed 8-bit +TMS6_dccmpy, // 2-Way SIMD Complex Multiply With Conjugate, Packed Complex Signed 16-bit (16-bit Real/16-bit Imaginary) +TMS6_dccmpyr1, // 2-Way SIMD Complex Multiply With Conjugate and Rounding, Packed Complex 16-bit (16-bit Real/16-bit Imaginary) +TMS6_dcmpeq2, // 2-Way SIMD Compare If Equal, Packed 16-bit +TMS6_dcmpeq4, // 4-Way SIMD Compare If Equal, Packed 8-bit +TMS6_dcmpgt2, // 2-Way SIMD Compare If Greater-Than, Packed 16-bit +TMS6_dcmpgtu4, // 4-Way SIMD Compare If Greater-Than, Unsigned Packed 8-bit +TMS6_dcmpy, // 2-Way SIMD Complex Multiply, Packed Complex 16-bit (16-bit Real/16-bit Imaginary) +TMS6_dcmpyr1, // 2-Way SIMD Complex Multiply With Rounding, Packed Complex 16-bit (16-bit Real/16-bit Imaginary) +TMS6_dcrot270, // 2-Way SIMD Rotate Complex Number By 270 Degrees, Packed Complex 16-bit (16-bit Real/16-bit Imaginary) +TMS6_dcrot90, // 2-Way SIMD Rotate Complex Number By 90 Degrees, Packed Complex 16-bit (16-bit Real/16-bit Imaginary) +TMS6_dinthspu, // 2-Way SIMD Convert 16-bit Unsigned Integer to Single Precision Floating Point +TMS6_dintspu, // 2-Way SIMD Convert 32-bit Unsigned Integer to Single Precision Floating Point, Packed Unsigned 32-bit +TMS6_dmax2, // 2-Way SIMD Maximum, Packed Signed 16-bit +TMS6_dmaxu4, // 4-Way SIMD Maximum, Packed Unsigned 8-bit +TMS6_dmin2, // 2-Way SIMD Minimum, Packed Signed 16-bit +TMS6_dminu4, // 4-Way SIMD Minimum, Packed Unsigned 8-bit +TMS6_dmpy2, // 4-Way SIMD Multiply, Packed Signed 16-bit +TMS6_dmpysp, // 2-Way SIMD Multiply, Packed Single Precision Floating Point +TMS6_dmpysu4, // 4-Way SIMD Multiply Signed By Unsigned, Packed 8-bit +TMS6_dmpyu2, // 4-Way SIMD Multiply Unisgned by Unsigned, Packed 16-bit +TMS6_dmpyu4, // 4-Way SIMD Multiply Unsigned By Unsigned, Packed 8-bit +TMS6_dmvd, // Move Two Independent Registers to a Register Pair, Delayed +TMS6_dpackh2, // 2-Way SIMD Pack 16 MSBs Into Upper and Lower Register Halves +TMS6_dpackh4, // 2-Way SIMD Pack Four High Bytes Into Four 8-Bit Halfwords +TMS6_dpackhl2, // 2-Way SIMD Pack 16 MSB Into Upper and 16 LSB Into Lower Register Halves +TMS6_dpackl2, // 2-Way SIMD Pack 16 LSBSs Into Upper and Lower Register Halves +TMS6_dpackl4, // 2-Way SIMD Pack Four Low Bytes Into Four 8-bit Halfwords +TMS6_dpacklh2, // 2-Way SIMD Pack 16 LSB Into Upper and 16 MSB Into Lower Register Halves +TMS6_dsadd, // 2-Way SIMD Addition With Saturation, Packed Signed 32-bit +TMS6_dsadd2, // 4-Way SIMD Addition with Saturation, Packed Signed 16-bit +TMS6_dshl, // 2-Way SIMD Shift Left, Packed Signed 32-bit +TMS6_dshl2, // 4-Way SIMD Shift Left, Packed Signed 16-bit +TMS6_dshr, // 2-Way SIMD Shift Left, Packed Signed 32-bit +TMS6_dshr2, // 4-Way SIMD Shift Left, Packed Signed 16-bit +TMS6_dshru, // 2-Way SIMD Shift Right, Packed Unsigned 32-bit +TMS6_dshru2, // 4-Way SIMD Shift Right, Packed Unsigned 16-bit +TMS6_dsmpy2, // 4-Way SIMD Multiply Signed by Signed With Left Shift and Saturation, Packed Signed 16-bit +TMS6_dspacku4, // 2-Way SIMD Saturate and Pack Into Unisgned Packed 8-bit +TMS6_dspint, // 2-Way SIMD Convert Single Precision Floating Point to Signed 32-bit Integer +TMS6_dspinth, // 2-Way SIMD Convert Single Precision Floating Point to Signed 16-bit Integer +TMS6_dssub, // 2-Way SIMD Saturating Subtract, Packed Signed 32-bit +TMS6_dssub2, // 4-Way SIMD Saturating Subtract, Packed Signed 16-bit +TMS6_dsub, // 2-Way SIMD Subtract, Packed Signed 32-bit +TMS6_dsub2, // 4-Way SIMD Subtract, Packed Signed 16-bit +TMS6_dsubsp, // 2-Way SIMD Subtract, Packed Single Precision Floating Point +TMS6_dxpnd2, // Expand Bits to Packed 16-bit Masks +TMS6_dxpnd4, // Expand Bits to Packed 8-bit Masks +TMS6_fadddp, // Fast Double-Precision Floating Point Add +TMS6_faddsp, // Fast Single-Precision Floating Point Add +TMS6_fmpydp, // Fast Double-Precision Floating Point Multiply +TMS6_fsubdp, // Fast Double-Precision Floating Point Subtract +TMS6_fsubsp, // Fast Single-Precision Floating Point Subtract +TMS6_land, // Logical AND +TMS6_landn, // Logical AND, One Operand Negated +TMS6_lor, // Logical OR +TMS6_mfence, // Memory Fence +TMS6_mpyu2, // Multiply Unsigned by Unsigned, Packed 16-bit +TMS6_qmpy32, // 4-Way SIMD Multiply, Packed Signed 32-bit +TMS6_qmpysp, // 4-Way SIMD Floating Point Multiply, Packed Single-Precision Floating Point +TMS6_qsmpy32r1, // 4-Way SIMD Multiply with Saturation and Rounding, Packed Signed 32-bit +TMS6_shl2, // 2-Way SIMD Shift Left, Packed Signed 16-bit +TMS6_unpkbu4, // Unpack All Unsigned Packed 8-bit to Unsigned Packed 16-bit +TMS6_unpkh2, // Unpack High Signed Packed 16-bit to Packed 32-bit +TMS6_unpkhu2, // Unpack High Unsigned Packed 16-bit to Packed 32-bit + +TMS6_fphead, // Special fake instruction to show the fetch packet header +TMS6_last, + + }; + +/* + * Interactive disassembler (IDA). + * Intel 80196 module + * + */ + + + +enum +{ + I196_null = 0, // Unknown Operation + + I196_add2, // Add words (2 operands) + I196_add3, // Add words (3 operands) + I196_addb2, // Add bytes (2 operands) + I196_addb3, // Add bytes (3 operands) + + I196_addc, // Add words with carry + I196_addcb, // Add bytes with carry + + I196_and2, // Logical AND words (2 operands) + I196_and3, // Logical AND words (3 operands) + I196_andb2, // Logical AND bytes (2 operands) + I196_andb3, // Logical AND bytes (3 operands) + + I196_bmov, // Block move + I196_bmovi, // Interruptable block move + + I196_br, // Branch indirect + + I196_clr, // Clear word + I196_clrb, // Clear byte + I196_clrc, // Clear carry flag + I196_clrvt, // Clear overflow-trap flag + + I196_cmp, // Compare words + I196_cmpb, // Compare bytes + I196_cmpl, // Compare long + + I196_dec, // Decrement word + I196_decb, // Decrement byte + + I196_di, // Disable interrupts + + I196_div, // Divide integers + I196_divb, // Divide short-integers + I196_divu, // Divide words, unsigned + I196_divub, // Divide bytes, unsigned + + I196_djnz, // Decrement and jump if not zero + I196_djnzw, // Decrement and jump if not zero word + + I196_dpts, // Disable peripheral transaction server + + I196_ei, // Enable interrupts + + I196_epts, // Enable peripheral transaction server + + I196_ext, // Sign-extend integer into long-integer + I196_extb, // Sign-extend short-integer into integer + + I196_idlpd, // Idle/powerdown + + I196_inc, // Increment word + I196_incb, // Increment byte + + I196_jbc, // Jump if bit is clear + I196_jbs, // Jump if bit is set + I196_jc, // Jump if carry flag is set + I196_je, // Jump if equal + I196_jge, // Jump if signed greater than or equal + I196_jgt, // Jump if signed greater than + I196_jh, // Jump if higher (unsigned) + I196_jle, // Jump if signed less than or equal + I196_jlt, // Jump if signed less than + I196_jnc, // Jump if carry flag is clear + I196_jne, // Jump if not equal + I196_jnh, // Jump if not higher (unsigned) + I196_jnst, // Jump if sticky bit flag is clear + I196_jnv, // Jump if overflow flag is clear + I196_jnvt, // Jump if overflow-trap flag is clear + I196_jst, // Jump if sticky bit flag is set + I196_jv, // Jump if overflow flag is set + I196_jvt, // Jump if overflow-trap flag is set + + I196_lcall, // Long call + + I196_ld, // Load word + I196_ldb, // Load byte + I196_ldbse, // Load byte sign-extended + I196_ldbze, // Load byte zero-extended + + I196_ljmp, // Long jump + + I196_mul2, // Multiply integers (2 operands) + I196_mul3, // Multiply integers (3 operands) + I196_mulb2, // Multiply short-integers (2 operands) + I196_mulb3, // Multiply short-integers (3 operands) + I196_mulu2, // Multiply words, unsigned (2 operands) + I196_mulu3, // Multiply words, unsigned (3 operands) + I196_mulub2, // Multiply bytes, unsigned (2 operands) + I196_mulub3, // Multiply bytes, unsigned (3 operands) + + I196_neg, // Negate integer + I196_negb, // Negate short-integer + + I196_nop, // No operation + + I196_norml, // Normalize long-integer + + I196_not, // Complement word + I196_notb, // Complement byte + + I196_or, // Logical OR words + I196_orb, // Logical OR bytes + + I196_pop, // Pop word + I196_popa, // Pop all + I196_popf, // Pop flags + I196_push, // Push word + I196_pusha, // Push all + I196_pushf, // Push flags + + I196_ret, // Return from subroutine + + I196_rst, // Reset system + + I196_scall, // Short call + + I196_setc, // Set carry flag + + I196_shl, // Shift word left + I196_shlb, // Shift byte left + I196_shll, // Shift double-word left + I196_shr, // Logical right shift word + I196_shra, // Arithmetic right shift word + I196_shrab, // Arithmetic right shift byte + I196_shral, // Arithmetic right shift double-word + I196_shrb, // Logical right shift byte + I196_shrl, // Logical right shift double-word + + I196_sjmp, // Short jump + + I196_skip, // Two byte no-operation + + I196_st, // Store word + I196_stb, // Store byte + + I196_sub2, // Subtract words (2 operands) + I196_sub3, // Subtract words (3 operands) + I196_subb2, // Subtract bytes (2 operands) + I196_subb3, // subtract bytes (3 operands) + + I196_subc, // Subtract words with borrow + I196_subcb, // Subtract bytes with borrow + + I196_tijmp, // Table indirect jump + + I196_trap, // Software trap + + I196_xch, // Exchange word + I196_xchb, // Exchange byte + + I196_xor, // Logical exclusive-or words + I196_xorb, // Logical exclusive-or bytes + +// 8x196NU, NP instructions + + I196_ebmovi, // Extended interruptable block move + I196_ebr, // Extended branch indirect + I196_ecall, // Extended call + I196_ejmp, // Extended jump + I196_eld, // Extended load word + I196_eldb, // Extended load byte + I196_est, // Extended store word + I196_estb, // Extended store byte + + I196_last +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + * Renesas SuperH - RISC with 16bit instructions + * + */ + + + + +enum +{ +SH3_null = 0, // Unknown Operation + +SH3_add, // Add binary +SH3_addc, // Add with Carry +SH3_addv, // Add with V Flag Overflow Check +SH3_and, // AND Logical +SH3_and_b, // AND Byte Logical +SH3_bf, // Branch if False +SH3_bf_s, // Branch of False with Delay Slot +SH3_bra, // Branch +SH3_braf, // Branch Far +SH3_bsr, // Branch to Subroutine +SH3_bsrf, // Branch to Subroutine Far +SH3_bt, // Branch if True +SH3_bt_s, // Branch if True with Delay Slot +SH3_clrmac, // Clear MAC register +SH3_clrs, // Clear S Bit +SH3_clrt, // Clear T Bit +SH3_cmp_eq, // Compare: Equal +SH3_cmp_ge, // Compare: Signed Greater or Equal +SH3_cmp_gt, // Compare: Signed Greater +SH3_cmp_hi, // Compare: Unsigned Greater +SH3_cmp_hs, // Compare: Unsigned Greater or Equal +SH3_cmp_pl, // Compare: Positive +SH3_cmp_pz, // Compare: Positive or Zero +SH3_cmp_str, // Compare: Equal Bytes +SH3_div0s, // Divide Step 0 as Signed +SH3_div0u, // Divide Step 0 as Unsigned +SH3_div1, // Divide Step 1 +SH3_dmuls_l, // Double-Length Multiply as Signed +SH3_dmulu_l, // Double-Length Multiply as Unsigned +SH3_dt, // Decrement and Test +SH3_exts_b, // Extend as Signed (Byte) +SH3_exts_w, // Extend as Signed (Word) +SH3_extu_b, // Extend as Unsigned (Byte) +SH3_extu_w, // Extend as Unsigned (Word) +SH3_jmp, // Jump +SH3_jsr, // Jump to Subroutine +SH3_ldc, // Load to Control Register +SH3_ldc_l, // Load to Control Register Long +SH3_lds, // Load to System Register +SH3_lds_l, // Load to System Register Long +SH3_ldtlb, // Load PTEH/PTEL/PTEA to TLB +SH3_mac_w, // Multiply and Accumulate Word +SH3_mac_l, // Multiply and Accumulate Long +SH3_mov, // Move Data +SH3_mov_b, // Move Byte Data +SH3_mov_w, // Move Word Data +SH3_mov_l, // Move Long Data +SH3_movi, // Move Immediate Byte Data +SH3_movi_w, // Move Immediate Word Data +SH3_movi_l, // Move Immediate Long Data +SH3_movp_b, // Move Peripherial Byte Data +SH3_movp_w, // Move Peripherial Word Data +SH3_movp_l, // Move Peripherial Long Data +SH3_movs_b, // Move Structure Byte Data +SH3_movs_w, // Move Structure Word Data +SH3_movs_l, // Move Structure Long Data +SH3_mova, // Move Effective Address +SH3_movt, // Move T Bit +SH3_mul, // Multiply Long +SH3_muls, // Multiply as Signed Word +SH3_mulu, // Multiply as Unsigned Word +SH3_neg, // Negate +SH3_negc, // Negate with Carry +SH3_nop, // No Operation +SH3_not, // NOT - Logical Complement +SH3_or, // OR Logical +SH3_or_b, // OR Byte Logical +SH3_pref, // Prefetch Data to the Cache +SH3_rotcl, // Rotate with Carry Left +SH3_rotcr, // Rotate with Carry Right +SH3_rotl, // Rotate Left +SH3_rotr, // Rotate Right +SH3_rte, // Return from Exception +SH3_rts, // Return from Subroutine +SH3_sets, // Set S Bit +SH3_sett, // Set T Bit +SH3_shad, // Shift Arithmetic Dynamically +SH3_shal, // Shift Arithmetic Left +SH3_shar, // Shift Arithmetic Right +SH3_shld, // Shift Logical Dynamically +SH3_shll, // Shift Logical Left +SH3_shll2, // Shift Logical Left 2 +SH3_shll8, // Shift Logical Left 8 +SH3_shll16, // Shift Logical Left 16 +SH3_shlr, // Shift Logical Right +SH3_shlr2, // Shift Logical Right 2 +SH3_shlr8, // Shift Logical Right 8 +SH3_shlr16, // Shift Logical Right 16 +SH3_sleep, // Sleep +SH3_stc, // Store Control Register +SH3_stc_l, // Store Control Register Long +SH3_sts, // Store System Register +SH3_sts_l, // Store System Register Long +SH3_sub, // Subtract Binary +SH3_subc, // Subtract with Carry +SH3_subv, // Subtract with V Flag Underflow Check +SH3_swap_b, // Swap Register Halves (Byte) +SH3_swap_w, // Swap Register Halves (Word) +SH3_tas_b, // Test and Set +SH3_trapa, // Trap Always +SH3_tst, // Test Logical +SH3_tst_b, // Test Byte Logical +SH3_xor, // Exclusive OR Logical +SH3_xor_b, // Exclusive OR Byte Logical +SH3_xtrct, // Extract + +SH4_fabs, // Floating-point absolute value +SH4_fadd, // Floating-point add +SH4_fcmp_eq, // Floating-point compare eqaul +SH4_fcmp_gt, // Floating-point compare greater than +SH4_fcnvds, // Floating-point convert double to single precision +SH4_fcnvsd, // Floating-point convert single to double precision +SH4_fdiv, // Floating-point divide +SH4_fipr, // Floating-point inner product +SH4_fldi0, // Floating-point Load Immediate 0.0 +SH4_fldi1, // Floating-point Load Immediate 1.0 +SH4_flds, // Floating-point Load to system register +SH4_float, // Floating-point convert from integer +SH4_fmac, // Floating-point multiply and accumulate +SH4_fmov, // Floating-point move +SH4_fmov_s, // Floating-point move single precision +SH4_fmovex, // Floating-point move extension +SH4_fmul, // Floating-point multiply +SH4_fneg, // Floating-Point sign inversion +SH4_frchg, // FR-bit change +SH4_fschg, // SZ-bit change +SH4_fsqrt, // Floating-point square root +SH4_fsts, // Floating-point store system register +SH4_fsub, // Floating-point subtract +SH4_ftrc, // Floating-point truncate and convert to integer +SH4_ftrv, // Floating-point transform vector +SH4_ftstn, // Floating point square root reciprocal approximate +SH4_movca_l, // Move with cache block allocation +SH4_ocbi, // Operand Cache Block Invalidate +SH4_ocbp, // Operand Cache Block Purge +SH4_ocbwb, // Operand Cache Block Write Back +SH4_fsca, // Floating point sine and cosine approximate + +// SH-2a new instructions + +SH2a_band_b, // Bit And Byte Data +SH2a_bandnot_b, // Bit And Not Byte Data +SH2a_bclr, // Bit Clear +SH2a_bclr_b, // Bit Clear Byte Data +SH2a_bld, // Bit Load +SH2a_bld_b, // Bit Load Byte Data +SH2a_bldnot_b, // Bit Load Not Byte Data +SH2a_bor_b, // Bit Or Byte Data +SH2a_bornot_b, // Bit Or Not Byte Data +SH2a_bset, // Bit Set +SH2a_bset_b, // Bit Set Byte Data +SH2a_bst, // Bit Store +SH2a_bst_b, // Bit Store Byte Data +SH2a_bxor_b, // Bit Exclusive Or Byte Data +SH2a_clips_b, // Clip as Signed Byte +SH2a_clips_w, // Clip as Signed Word +SH2a_clipu_b, // Clip as Unsigned Byte +SH2a_clipu_w, // Clip as Unsigned Word +SH2a_divs, // Divide as Signed +SH2a_divu, // Divide as Unsigned +SH2a_jsr_n, // Jump to Subroutine with No delay slot +SH2a_ldbank, // Load Register Bank +SH2a_movi20, // 20-bit immediate data transfer +SH2a_movi20s, // 20-bit immediate data transfer, 8-bit left-shift +SH2a_movml_l, // Move Multi-register Lower part +SH2a_movmu_l, // Move Multi-register Upper part +SH2a_movrt, // Move Reverse T bit +SH2a_movu_b, // Move Structure Byte Data as Unsigned +SH2a_movu_w, // Move Structure Word Data as Unsigned +SH2a_mulr, // Multiply to Register +SH2a_nott, // Not T bit +SH2a_resbank, // Restore From Register Bank +SH2a_rts_n, // Return from Subroutine with No delay slot +SH2a_rtv_n, // Return to Value and from Subroutine with No delay slot +SH2a_stbank, // Store Register Bank + +// SH-4a new instructions + +SH4a_movco_l, // Move Conditional +SH4a_movli_l, // Move Linked +SH4a_movua_l, // Move Unaligned +SH4a_icbi, // Instruction Cache Block Invalidate +SH4a_prefi, // Prefetch Instruction Cache Block +SH4a_synco, // Synchronize Data Operation +SH4a_fsrra, // Floating Point Square Reciprocal Approximate +SH4a_fpchg, // PR-bit Change + +SH4_last, + + }; + +/* + * Interactive disassembler (IDA). + * Zilog Z8 module + * + */ + + + +enum +{ + Z8_null = 0, // Unknown Operation + + Z8_adc, // Add with carry + Z8_add, // Add + Z8_and, // Logical AND + Z8_call, // Call procedure + Z8_ccf, // Complement carry flag + Z8_clr, // Clear + Z8_com, // Complement + Z8_cp, // Compare + Z8_da, // Decimal adjust + Z8_dec, // Decrement + Z8_decw, // Decrement word + Z8_di, // Disable interrupts + Z8_djnz, // Decrement and jump if non-zero + Z8_ei, // Enable interrupts + Z8_halt, // Enter HALT mode + Z8_inc, // Increment + Z8_incw, // Increment word + Z8_iret, // Return from interrupt + Z8_jp, // Unconditional jump + Z8_jpcond, // Conditional jump + Z8_jr, // Relative jump + Z8_jrcond, // Conditional relative jump + Z8_ld, // Load data + Z8_ldc, // Load constant + Z8_ldci, // Load constant with auto-increment + Z8_lde, // Load external data + Z8_ldei, // Load external data with auto-increment + Z8_nop, // NOP + Z8_or, // Logical OR + Z8_pop, // Pop + Z8_push, // Push + Z8_rcf, // Reset carry flag + Z8_ret, // Return + Z8_rl, // Rotate left + Z8_rlc, // Rotate left through carry + Z8_rr, // Rotate right + Z8_rrc, // Rotate right through carry + Z8_sbc, // Subtract with carry + Z8_scf, // Set carry flag + Z8_sra, // Shift right arithmetic + Z8_srp, // Set register pointer + Z8_stop, // Enter STOP mode + Z8_sub, // Subtract + Z8_swap, // Swap nibbles + Z8_tm, // Test under mask + Z8_tcm, // Test complement under mask + Z8_xor, // Logical EXCLUSIVE OR + Z8_wdh, // Enable WATCH-DOG in HALT mode + Z8_wdt, // Clear WATCH-DOG timer + + Z8_last +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + * Atmel AVR - 8-bit RISC processor + * + */ + + + +enum +{ + +AVR_null = 0, // Unknown Operation + +// ARITHMETIC AND LOGIC INSTRUCTIONS +AVR_add, // Add without Carry +AVR_adc, // Add with Carry +AVR_adiw, // Add Immediate to Word +AVR_sub, // Subtract without Carry +AVR_subi, // Subtract Immediate +AVR_sbc, // Subtract with Carry +AVR_sbci, // Subtract Immediate with Carry +AVR_sbiw, // Subtract Immediate from Word +AVR_and, // Logical AND +AVR_andi, // Logical AND with Immediate +AVR_or, // Logical OR +AVR_ori, // Logical OR with Immediate +AVR_eor, // Exclusive OR +AVR_com, // One's Complement +AVR_neg, // Two's Complement +AVR_sbr, // Set Bit(s) in Register +AVR_cbr, // Clear Bit(s) in Register +AVR_inc, // Increment +AVR_dec, // Decrement +AVR_tst, // Test for Zero or Minus +AVR_clr, // Clear Register +AVR_ser, // Set Register +AVR_cp, // Compare +AVR_cpc, // Compare with Carry +AVR_cpi, // Compare with Immediate +AVR_mul, // Multiply + +// BRANCH INSTRUCTIONS +AVR_rjmp, // Relative Jump +AVR_ijmp, // Indirect Jump to (Z) +AVR_jmp, // Jump +AVR_rcall, // Relative Call Subroutine +AVR_icall, // Indirect Call to (Z) +AVR_call, // Call Subroutine +AVR_ret, // Subroutine Return +AVR_reti, // Interrupt Return +AVR_cpse, // Compare, Skip if Equal +AVR_sbrc, // Skip if Bit in Register Cleared +AVR_sbrs, // Skip if Bit in Register Set +AVR_sbic, // Skip if Bit in I/O Register Cleared +AVR_sbis, // Skip if Bit in I/O Register Set +AVR_brbs, // Branch if Status Flag Set +AVR_brbc, // Branch if Status Flag Cleared +AVR_breq, // Branch if Equal +AVR_brne, // Branch if Not Equal +AVR_brcs, // Branch if Carry Set +AVR_brcc, // Branch if Carry Cleared +AVR_brsh, // Branch if Same or Higher +AVR_brlo, // Branch if Lower +AVR_brmi, // Branch if Minus +AVR_brpl, // Branch if Plus +AVR_brge, // Branch if Greater or Equal +AVR_brlt, // Branch if Less Than +AVR_brhs, // Branch if Half Carry Flag Set +AVR_brhc, // Branch if Half Carry Flag Cleared +AVR_brts, // Branch if T Flag Set +AVR_brtc, // Branch if T Flag Cleared +AVR_brvs, // Branch if Overflow Flag is Set +AVR_brvc, // Branch if Overflow Flag is Cleared +AVR_brie, // Branch if Interrupt Enabled +AVR_brid, // Branch if Interrupt Disabled + +// DATA TRANSFER INSTRUCTIONS +AVR_mov, // Copy Register +AVR_ldi, // Load Immediate +AVR_lds, // Load Direct +AVR_ld, // Load Indirect +AVR_ldd, // Load Indirect with Displacement +AVR_sts, // Store Direct to SRAM +AVR_st, // Store Indirect +AVR_std, // Store Indirect with Displacement +AVR_lpm, // Load Program Memory +AVR_in, // In Port +AVR_out, // Out Port +AVR_push, // Push Register on Stack +AVR_pop, // Pop Register from Stack + +// BIT AND BIT-TEST INSTRUCTIONS +AVR_lsl, // Logical Shift Left +AVR_lsr, // Logical Shift Right +AVR_rol, // Rotate Left Through Carry +AVR_ror, // Rotate Right Through Carry +AVR_asr, // Arithmetic Shift Right +AVR_swap, // Swap Nibbles +AVR_bset, // Flag Set +AVR_bclr, // Flag Clear +AVR_sbi, // Set Bit in I/O Register +AVR_cbi, // Clear Bit in I/O Register +AVR_bst, // Bit Store from Register to T +AVR_bld, // Bit load from T to Register +AVR_sec, // Set Carry +AVR_clc, // Clear Carry +AVR_sen, // Set Negative Flag +AVR_cln, // Clear Negative Flag +AVR_sez, // Set Zero Flag +AVR_clz, // Clear Zero Flag +AVR_sei, // Global Interrupt Enable +AVR_cli, // Global Interrupt Disable +AVR_ses, // Set Signed Test Flag +AVR_cls, // Clear Signed Test Flag +AVR_sev, // Set Two's Complement Overflow +AVR_clv, // Clear Two's Complement Overflow +AVR_set, // Set T in SREG +AVR_clt, // Clear T in SREG +AVR_seh, // Set Half Carry Flag in SREG +AVR_clh, // Clear Half Carry Flag in SREG +AVR_nop, // No Operation +AVR_sleep, // Sleep +AVR_wdr, // Watchdog Reset + +// New MegaAVR instructions + +AVR_elpm, // Extended Load Program Memory +AVR_espm, // Extended Store Program Memory +AVR_fmul, // Fractional Multiply Unsigned +AVR_fmuls, // Fractional Multiply Signed +AVR_fmulsu, // Fractional Multiply Signed with Unsigned +AVR_movw, // Copy Register Word +AVR_muls, // Multiply Signed +AVR_mulsu, // Multiply Signed with Unsigned +AVR_spm, // Store Program Memory +AVR_eicall, // Extended Indirect Call to Subroutine +AVR_eijmp, // Extended Indirect Jump + +// New XMega instructions + +AVR_des, // Data Encryption Standard +AVR_lac, // Load And Clear +AVR_las, // Load And Set +AVR_lat, // Load And Toggle +AVR_xch, // Exchange + +AVR_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + + +enum +{ +MIPS_null = 0, // Unknown Operation + +// The core processor instructions + +MIPS_add, // Add +MIPS_addu, // Add Unsigned +MIPS_and, // AND +MIPS_dadd, // Doubleword Add +MIPS_daddu, // Doubleword Add Unsigned +MIPS_dsub, // Doubleword Subtract +MIPS_dsubu, // Doubleword Subtract Unsigned +MIPS_nor, // NOR +MIPS_or, // OR +MIPS_slt, // Set on Less Than +MIPS_sltu, // Set on Less Than Unsigned +MIPS_sub, // Subtract +MIPS_subu, // Subtract Unsigned +MIPS_xor, // Exclusive OR +MIPS_dsll, // Doubleword Shift Left Logical +MIPS_dsll32, // Doubleword Shift Left Logical + 32 +MIPS_dsra, // Doubleword Shift Right Arithmetic +MIPS_dsra32, // Doubleword Shift Right Arithmetic + 32 +MIPS_dsrl, // Doubleword Shift Right Logical +MIPS_dsrl32, // Doubleword Shift Right Logical + 32 +MIPS_sll, // Shift Left Logical +MIPS_sra, // Shift Right Arithmetic +MIPS_srl, // Shift Right Logical +MIPS_dsllv, // Doubleword Shift Left Logical Variable +MIPS_dsrav, // Doubleword Shift Right Arithmetic Variable +MIPS_dsrlv, // Doubleword Shift Right Logical Variable +MIPS_sllv, // Shift Left Logical Variable +MIPS_srav, // Shift Right Arithmetic Variable +MIPS_srlv, // Shift Right Logical Variable +MIPS_addi, // Add Immediate +MIPS_addiu, // Add Immediate Unsigned +MIPS_daddi, // Doubleword Add Immediate +MIPS_daddiu, // Doubleword Add Immediate Unsigned +MIPS_slti, // Set on Less Than Immediate +MIPS_sltiu, // Set on Less Than Immediate Unsigned +MIPS_andi, // AND Immediate +MIPS_ori, // OR Immediate +MIPS_xori, // Exclusive OR Immediate +MIPS_teq, // Trap if Equal +MIPS_tge, // Trap if Greater Than or Equal +MIPS_tgeu, // Trap if Greater Than or Equal Unsigned +MIPS_tlt, // Trap if Less Than +MIPS_tltu, // Trap if Less Than Unsigned +MIPS_tne, // Trap if Not Equal +MIPS_cfc1, // Move Control From FPU +MIPS_cfc2, // Move Control From Coprocessor 2 +MIPS_ctc1, // Move Control to FPU +MIPS_ctc2, // Move Control to Coprocessor 2 +MIPS_dmfc0, // Doubleword Move From CP0 +MIPS_qmfc2, // Quadword Move From CP2 +MIPS_dmtc0, // Doubleword Move To CP0 +MIPS_qmtc2, // Quadword Move To CP2 +MIPS_mfc0, // Move from CP0 +MIPS_mfc1, // Move from FPU +MIPS_mfc2, // Move from CP2 +MIPS_mtc0, // Move to CP0 +MIPS_mtc1, // Move to FPU +MIPS_mtc2, // Move to CP2 +MIPS_teqi, // Trap if Equal Immediate +MIPS_tgei, // Trap if Greater Than or Equal Immediate +MIPS_tgeiu, // Trap if Greater Than or Equal Immediate Unsigned +MIPS_tlti, // Trap if Less Than Immediate +MIPS_tltiu, // Trap if Less Than Immediate Unsigned +MIPS_tnei, // Trap if Not Equal Immediate +MIPS_ddiv, // Doubleword Divide +MIPS_ddivu, // Doubleword Divide Unsigned +MIPS_div, // Divide +MIPS_divu, // Divide Unsigned +MIPS_dmult, // Doubleword Multiply +MIPS_dmultu, // Doubleword Multiply Unsigned +MIPS_mult, // Multiply +MIPS_multu, // Multiply Unsigned +MIPS_mthi, // Move To HI +MIPS_mtlo, // Move To LO +MIPS_mfhi, // Move From HI +MIPS_mflo, // Move From LO +MIPS_cop0, // Coprocessor 0 Operation +MIPS_cop1, // FPU Operation +MIPS_cop2, // Coprocessor 2 Operation +MIPS_break, // Break +MIPS_syscall, // System Call +MIPS_bc0f, // Branch on Coprocessor 0 False +MIPS_bc1f, // Branch on FPU False +MIPS_bc2f, // Branch on Coprocessor 2 False +MIPS_bc3f, // Branch on Coprocessor 3 False +MIPS_bc0fl, // Branch on Coprocessor 0 False Likely +MIPS_bc1fl, // Branch on FPU False Likely +MIPS_bc2fl, // Branch on Coprocessor 2 False Likely +MIPS_bc3fl, // Branch on Coprocessor 3 False Likely +MIPS_bc0t, // Branch on Coprocessor 0 True +MIPS_bc1t, // Branch on FPU True +MIPS_bc2t, // Branch on Coprocessor 2 True +MIPS_bc3t, // Branch on Coprocessor 3 True +MIPS_bc0tl, // Branch on Coprocessor 0 True Likely +MIPS_bc1tl, // Branch on FPU True Likely +MIPS_bc2tl, // Branch on Coprocessor 2 True Likely +MIPS_bc3tl, // Branch on Coprocessor 3 True Likely +MIPS_bgez, // Branch on Greater Than or Equal to Zero +MIPS_bgezal, // Branch on Greater Than or Equal to Zero And Link +MIPS_bgezall, // Branch on Greater Than or Equal to Zero And Link Likely +MIPS_bgezl, // Branch on Greater Than or Equal to Zero Likely +MIPS_bgtz, // Branch on Greater Than Zero +MIPS_bgtzl, // Branch on Greater Than Zero Likely +MIPS_blez, // Branch on Less Than or Equal to Zero +MIPS_blezl, // Branch on Less Than or Equal to Zero Likely +MIPS_bltz, // Branch on Less Than Zero +MIPS_bltzal, // Branch on Less Than Zero And Link +MIPS_bltzall, // Branch on Less Than Zero And Link Likely +MIPS_bltzl, // Branch on Less Than Zero Likely +MIPS_beq, // Branch on Equal +MIPS_beql, // Branch on Equal Likely +MIPS_bne, // Branch on Not Equal +MIPS_bnel, // Branch on Not Equal Likely +MIPS_jalr, // Jump And Link Register +MIPS_j, // Jump +MIPS_jr, // Jump Register +MIPS_jal, // Jump And Link +MIPS_jalx, // Jump And Link And Exchange +MIPS_cache, // Cache Operation +MIPS_lb, // Load Byte +MIPS_lbu, // Load Byte Unsigned +MIPS_ldl, // Load Doubleword Left +MIPS_ldr, // Load Doubleword Right +MIPS_lwl, // Load Word Left +MIPS_lwr, // Load Word Right +MIPS_ld, // Load Doubleword +MIPS_lld, // Load Linked Doubleword +MIPS_ldc1, // Load Double FPU +MIPS_ldc2, // Load Double Coprocessor 2 +MIPS_ll, // Load Linked +MIPS_lw, // Load Word +MIPS_lwu, // Load Word Unsigned +MIPS_lh, // Load Halfword +MIPS_lhu, // Load Halfword Unsigned +MIPS_lui, // Load Upper Immediate +MIPS_lwc1, // Load Word to FPU +MIPS_lwc2, // Load Word to Coprocessor 2 +MIPS_sb, // Store Byte +MIPS_sdl, // Store Doubleword Left +MIPS_sdr, // Store Doubleword Right +MIPS_swl, // Store Word Left +MIPS_swr, // Store Word Right +MIPS_scd, // Store Conditional Doubleword +MIPS_sd, // Store Doubleword +MIPS_sdc1, // Store Double FPU +MIPS_sdc2, // Store Double Coprocessor 2 +MIPS_sc, // Store Conditional +MIPS_sw, // Store Word +MIPS_sh, // Store Halfword +MIPS_swc1, // Store Word from FPU +MIPS_swc2, // Store Word from Coprocessor 2 +MIPS_sync, // Sync + +// Coprocessor 0 instructions + +MIPS_eret, // Exception Return +MIPS_tlbp, // Probe TLB for Matching Entry +MIPS_tlbr, // Read Indexed TLB Entry +MIPS_tlbwi, // Write Indexed TLB Entry +MIPS_tlbwr, // Write Random TLB Entry + + +// Coprocessor 1 (FPU) instructions + +MIPS_fadd, // Floating-point Add +MIPS_fsub, // Floating-point Subtract +MIPS_fmul, // Floating-point Multiply +MIPS_fdiv, // Floating-point Divide +MIPS_fabs, // Floating-point Absolute Value +MIPS_fcvt_s, // Floating-point Convert to Single Fixed-Point Format +MIPS_fcvt_d, // Floating-point Convert to Double Floating-Point Format +MIPS_fcvt_w, // Floating-point Convert to Fixed-Point Format +MIPS_fcvt_l, // Floating-point Convert to Long Fixed-Point Format +MIPS_fround_l, // Floating-point Round to Long Fixed-Point Format +MIPS_ftrunc_l, // Floating-point Truncate to Long Fixed-Point Format +MIPS_fceil_l, // Floating-point Ceiling to Long Fixed-Point Format +MIPS_ffloor_l, // Floating-point Floor to Long Fixed-Point Format +MIPS_fround_w, // Floating-point Round to Single Fixed-Point Format +MIPS_ftrunc_w, // Floating-point Truncate to Single Fixed-Point Format +MIPS_fceil_w, // Floating-point Ceiling to Single Fixed-Point Format +MIPS_ffloor_w, // Floating-point Floor to Single Fixed-Point Format +MIPS_fmov, // Floating-point Move +MIPS_fneg, // Floating-point Negate +MIPS_fsqrt, // Floating-point Square Root +MIPS_fc_f, // Floating-point Compare +MIPS_fc_un, // Floating-point Compare +MIPS_fc_eq, // Floating-point Compare +MIPS_fc_ueq, // Floating-point Compare +MIPS_fc_olt, // Floating-point Compare +MIPS_fc_ult, // Floating-point Compare +MIPS_fc_ole, // Floating-point Compare +MIPS_fc_ule, // Floating-point Compare +MIPS_fc_sf, // Floating-point Compare +MIPS_fc_ngle, // Floating-point Compare +MIPS_fc_seq, // Floating-point Compare +MIPS_fc_ngl, // Floating-point Compare +MIPS_fc_lt, // Floating-point Compare +MIPS_fc_nge, // Floating-point Compare +MIPS_fc_le, // Floating-point Compare +MIPS_fc_ngt, // Floating-point Compare + +// Pseudo instructions + +MIPS_nop, // No operation +MIPS_mov, // Move register +MIPS_neg, // Negate +MIPS_negu, // Negate Unsigned +MIPS_bnez, // Branch on Not Zero +MIPS_bnezl, // Branch on Not Zero Likely +MIPS_beqz, // Branch on Zero +MIPS_beqzl, // Branch on Zero Likely +MIPS_b, // Branch Always +MIPS_bal, // Branch Always and Link +MIPS_li, // Load Immediate +MIPS_la, // Load Address + +// MIPS IV instructions + +MIPS_pref, // Prefetch +MIPS_ldxc1, // Load Doubleword Indexed to Floating Point +MIPS_lwxc1, // Load Word Indexed to Floating Point +MIPS_sdxc1, // Store Doubleword Indexed from Floating Point +MIPS_swxc1, // Store Word Indexed from Floating Point +MIPS_madd_s, // Floating-Point Multiply Add +MIPS_madd_d, // Floating-Point Multiply Add +MIPS_msub_s, // Floating-Point Multiply Subtract +MIPS_msub_d, // Floating-Point Multiply Subtract +MIPS_movf, // Move Conditional on FP False +MIPS_movt, // Move Conditional on FP True +MIPS_movn, // Move Conditional on Not Zero +MIPS_movz, // Move Conditional on Zero +MIPS_fmovf, // Floating-Point Move Conditional on FP False +MIPS_fmovt, // Floating-Point Move Conditional on FP True +MIPS_fmovn, // Floating-Point Move Conditional on Not Zero +MIPS_fmovz, // Floating-Point Move Conditional on Zero +MIPS_nmadd_s, // Floating-Pont Negative Multiply Add +MIPS_nmadd_d, // Floating-Pont Negative Multiply Add +MIPS_nmsub_s, // Floating-Pont Negative Multiply Subtract +MIPS_nmsub_d, // Floating-Pont Negative Multiply Subtract +MIPS_prefx, // Prefetch Indexed +MIPS_frecip, // Reciprocal Approximation +MIPS_frsqrt, // Reciprocal Suare Root Approximation + +// RSP instructions + +MIPS_lbv, // Load Byte into Vector +MIPS_lsv, // Load Short into Vector +MIPS_llv, // Load Word into Vector +MIPS_ldv, // Load Doubleword into Vector +MIPS_lqv, // Load Quadword into Vector +MIPS_lrv, // Load Rest Vector +MIPS_lpv, // Load Packed Vector +MIPS_luv, // Load Unpack Vector +MIPS_lhv, // Load Half Vector +MIPS_lfv, // Load Fourth Vector +MIPS_lwv, // Load Wrap Vector +MIPS_ltv, // Load Transpose Vector +MIPS_sbv, // Store Byte from Vector +MIPS_ssv, // Store Short from Vector +MIPS_slv, // Store Word from Vector +MIPS_sdv, // Store Doubleword from Vector +MIPS_sqv, // Store Quadword from Vector +MIPS_srv, // Store Rest Vector +MIPS_spv, // Store Packed Vector +MIPS_suv, // Store Unpack Vector +MIPS_shv, // Store Half Vector +MIPS_sfv, // Store Fourth Vector +MIPS_swv, // Store Wrap Vector +MIPS_stv, // Store Transpose Vector +MIPS_vmulf, // Vector (Frac) Multiply +MIPS_vmacf, // Vector (Frac) Multiply Accumulate +MIPS_vmulu, // Vector (Unsigned Frac) Multiply +MIPS_vmacu, // Vector (Unsigned Frac) Multiply Accumulate +MIPS_vrndp, // Vector DCT Round (+) +MIPS_vrndn, // Vector DCT Round (-) +MIPS_vmulq, // Vector (Integer) Multiply +MIPS_vmacq, // Vector (Integer) Multiply Accumulate +MIPS_vmudh, // Vector (High) Multiply +MIPS_vmadh, // Vector (High) Multiply Accumulate +MIPS_vmudm, // Vector (Mid-M) Multiply +MIPS_vmadm, // Vector (Mid-M) Multiply Accumulate +MIPS_vmudn, // Vector (Mid-N) Multiply +MIPS_vmadn, // Vector (Mid-N) Multiply Accumulate +MIPS_vmudl, // Vector (Low) Multiply +MIPS_vmadl, // Vector (Low) Multiply Accumulate +MIPS_vadd, // Vector Add +MIPS_vsub, // Vector Subtract +MIPS_vsut, // Vector SUT (vt - vs) +MIPS_vabs, // Vector Absolute Value +MIPS_vaddc, // Vector ADDC +MIPS_vsubc, // Vector SUBC +MIPS_vaddb, // Vector Add Byte +MIPS_vsubb, // Vector Subtract Byte +MIPS_vaccb, // Vector Add Byte/Add Accumulator +MIPS_vsucb, // Vector Subtract Byte/Add Accumulator +MIPS_vsad, // Vector SAD +MIPS_vsac, // Vector SAC +MIPS_vsum, // Vector SUM +MIPS_vsaw, // Vector SAW +MIPS_vlt, // Vector Less Than +MIPS_veq, // Vector Equal To +MIPS_vne, // Vector Not Equal To +MIPS_vge, // Vector Greater Than or Equal To +MIPS_vcl, // Vector Clip Low +MIPS_vch, // Vector Clip High +MIPS_vcr, // Vector 1's Complement Clip +MIPS_vmrg, // Vector Merge +MIPS_vand, // Vector Logical AND +MIPS_vnand, // Vector Logical NAND +MIPS_vor, // Vector Logical OR +MIPS_vnor, // Vector Logical NOR +MIPS_vxor, // Vector Logical Exclusive OR +MIPS_vnxor, // Vector Logical NOT Exclusive OR +MIPS_vnoop, // Vector No-Operation +MIPS_vmov, // Vector Scalar-Element Move +MIPS_vrcp, // Single Precision, Lookup Source, Write Result +MIPS_vrsq, // Single Precision, Lookup Source, Write Result +MIPS_vrcph, // Set Source, Write Previous Result +MIPS_vrsqh, // Set Source, Write Previous Result +MIPS_vrcpl, // Lookup Source and Previous, Write Result +MIPS_vrsql, // Lookup Source and Previous, Write Result +MIPS_vinst, // Vector Insert Triple (5/5/5/1) +MIPS_vextt, // Vector Extract Triple (5/5/5/1) +MIPS_vinsq, // Vector Insert Quad (4/4/4/4) +MIPS_vextq, // Vector Extract Quad (4/4/4/4) +MIPS_vinsn, // Vector Insert Nibble (4/4/4/4) Sign-Extended +MIPS_vextn, // Vector Insert Nibble (4/4/4/4) Sign-Extended +MIPS_cfc0, // Move Control From Coprocessor 0 +MIPS_ctc0, // Move Control to Coprocessor 0 + +// R5900 (PSX2 or PlayStation2) processor additional commands + +MIPS_mtsa, // Move To Shift Amount Register +MIPS_R5900_first = MIPS_mtsa, +MIPS_mfsa, // Move From Shift Amount Register +MIPS_mtsab, // Move Byte Count To Shift Amount Register +MIPS_mtsah, // Move Halfword Count To Shift Amount Register +MIPS_fadda, // Floating-point add to accumulator +MIPS_fsuba, // Floating-point subtract to accumulator +MIPS_fmula, // Floating-point multiply to accumulator +MIPS_fmadda, // Floating-point multiply and add to accumulator +MIPS_fmsuba, // Floating-point multiply and subtract from accumulator +MIPS_fmadd, // Floating-point multiply and add +MIPS_fmsub, // Floating-point multiply and subtract +MIPS_fmax, // Floating-point maximum +MIPS_fmin, // Floating-point minimum +MIPS_plzcw, // Parallel Leading Zero or One Count Word +MIPS_mthi1, // Move To HI1 Register +MIPS_mtlo1, // Move To LO1 Register +MIPS_pmthl_lw, // Parallel Move From HI/LO Register +MIPS_pmthi, // Parallel Move To HI Register +MIPS_pmtlo, // Parallel Move To LO Register +MIPS_div1, // Divide Pipeline 1 +MIPS_divu1, // Divide Unsigned Pipeline 1 +MIPS_pdivw, // Parallel Divide Word +MIPS_pdivuw, // Parallel Divide Unsigned Word +MIPS_pdivbw, // Parallel Divide Broadcast Word +MIPS_paddw, // Parallel Add Word +MIPS_pmaddw, // Parallel Multiply-Add Word +MIPS_mult1, // Multiply Pipeline 1 +MIPS_multu1, // Multiply Unsigned Pipeline 1 +MIPS_madd1, // Multiply-Add Pipeline 1 +MIPS_maddu1, // Multiply-Add Unsigned Pipeline 1 +MIPS_pmadduw, // Parallel Multiply-Add Unsigned Word +MIPS_psubw, // Parallel Subtract HalfWord +MIPS_pcgtw, // Parallel Compare for Greater Than Word +MIPS_psllvw, // Parallel Shift Left Logical Variable Word +MIPS_pceqw, // Parallel Compare for Equal Word +MIPS_pmaxw, // Parallel Maximize Word +MIPS_psrlvw, // Parallel Shift Right Logical Variable Word +MIPS_pminw, // Parallel Minimize Word +MIPS_psravw, // Parallel Shift Right Arithmetic Variable Word +MIPS_paddh, // Parallel Add Halfword +MIPS_pmsubw, // Parallel Multiply-Subtract Word +MIPS_padsbh, // Parallel Add/Subtract Halfword +MIPS_psubh, // Parallel Subtract Halfword +MIPS_pcgth, // Parallel Compare for Greater Than Halfword +MIPS_pceqh, // Parallel Compare for Equal Halfword +MIPS_pmaxh, // Parallel Maximize Halfword +MIPS_pminh, // Parallel Minimize Halfword +MIPS_paddb, // Parallel Add Byte +MIPS_psubb, // Parallel Subtract Byte +MIPS_pcgtb, // Parallel Compare for Greater Than Byte +MIPS_pinth, // Parallel Interleave Halfword +MIPS_pceqb, // Parallel Compare for Equal Byte +MIPS_pintoh, // Parallel Interleave Odd Halfword +MIPS_pmultw, // Parallel Multiply Word +MIPS_pmultuw, // Parallel Multiply Unsigned Word +MIPS_pcpyld, // Parallel Copy Lower Doubleword +MIPS_pcpyud, // Parallel Copy Upper Doubleword +MIPS_paddsw, // Parallel Add with Signed Saturation Word +MIPS_pmaddh, // Parallel Multiply-Add Halfword +MIPS_padduw, // Parallel Add with Unsigned Saturation Word +MIPS_psubsw, // Parallel Subtract with Signed Saturation Word +MIPS_phmadh, // Parallel Horizontal Multiply-Add Halfword +MIPS_psubuw, // Parallel Subtract with Unsigned Saturation Word +MIPS_pextlw, // Parallel Extend Lower from Word +MIPS_pand, // Parallel AND +MIPS_pextuw, // Parallel Extend Upper from Word +MIPS_por, // Parallel OR +MIPS_ppacw, // Paralle Pack to Word +MIPS_pxor, // Parallel XOR +MIPS_pnor, // Parallel NOR +MIPS_paddsh, // Parallel Add with Signed Saturation Halfword +MIPS_pmsubh, // Parallel Multiply-Subtract Halfword +MIPS_padduh, // Parallel Add with Unsigned Saturation Halfword +MIPS_psubsh, // Parallel Subtract with Signed Saturation Halfword +MIPS_phmsbh, // Parallel Horizontal Multiply-Subtract Halfword +MIPS_psubuh, // Parallel Subtract with Unsigned Saturation Halfword +MIPS_pextlh, // Parallel Extend Lower from Halfword +MIPS_pextuh, // Parallel Extend Upper from Halfword +MIPS_ppach, // Paralle Pack to Halfword +MIPS_paddsb, // Parallel Add with Signed Saturation Byte +MIPS_paddub, // Parallel Add with Unsigned Saturation Byte +MIPS_psubsb, // Parallel Subtract with Signed Saturation Byte +MIPS_psubub, // Parallel Subtract with Unsigned Saturation Byte +MIPS_pextlb, // Parallel Extend Lower from Byte +MIPS_pextub, // Parallel Extend Upper from Byte +MIPS_ppacb, // Paralle Pack to Byte +MIPS_qfsrv, // Quadword Funnel Shift Right Variable +MIPS_pmulth, // Parallel Multiply Halfword +MIPS_pabsw, // Parallel Absolute Word +MIPS_pabsh, // Parallel Absolute Halfword +MIPS_pexoh, // Parallel Exchange Odd Halfword +MIPS_pexch, // Parallel Exchange Center Halfword +MIPS_prevh, // Parallel Reverse Halfword +MIPS_pcpyh, // Parallel Copy Halfword +MIPS_pext5, // Parallel Extend Upper from 5 bits +MIPS_pexow, // Parallel Exchange Odd Word +MIPS_pexcw, // Parallel Exchange Center Word +MIPS_ppac5, // Parallel Pack to 5 bits +MIPS_prot3w, // Parallel Rotate 3 Words +MIPS_psllh, // Parallel Shift Left Logical Halfword +MIPS_psrlh, // Parallel Shift Right Logical Halfword +MIPS_psrah, // Parallel Shift Right Arithmetic Halfword +MIPS_psllw, // Parallel Shift Left Logical Word +MIPS_psrlw, // Parallel Shift Right Logical Word +MIPS_psraw, // Parallel Shift Right Arithmetic Word +MIPS_mfhi1, // Move From HI1 Register +MIPS_mflo1, // Move From LO1 Register +MIPS_pmfhi, // Parallel Move From HI Register +MIPS_pmflo, // Parallel Move From LO Register +MIPS_pmfhl, // Parallel Move From HI/LO Register +MIPS_lq, // Load Quadword +MIPS_sq, // Store Quadword +MIPS_lqc2, // Load Quadword Coprocessor 2 +MIPS_sqc2, // Store Quadword Coprocessor 2 +MIPS_madd_r5900, // Multiply/Add +MIPS_maddu_r5900, // Multiply/Add Unsigned +MIPS_R5900_last = MIPS_maddu_r5900, +MIPS_mult3, // Multiply (3-operand) +MIPS_multu3, // Multiply Unsigned (3-operand) + +// 16-bit instructions +// NOTE: in previous version of IDA, all mips16 instructions +// had separate numbers, even for the instructions with the +// same mnemonics. Now same mnemonics have the same numbers, +// regardless of the encoding. + +MIPS_bteqz=439, // Branch on T Equal to Zero +MIPS_btnez, // Branch on T Not Equal to Zero +MIPS_cmp, // Compare +MIPS_cmpi, // Compare Immediate +MIPS_extend=458, // Extend +MIPS_move=473, // Move +MIPS_not=477, // Not +MIPS_dla=495, // Load 64-bit address + +// Instructions which belong to MIPS32 but which were not decoded by IDA + +MIPS_clo=498, // Count Leading Ones in Word +MIPS_clz, // Count Leading Zeros in Word +MIPS_madd, // Multiply/Add +MIPS_maddu, // Multiply/Add Unsigned +MIPS_msub, // Multiply and Subtract Word to Hi,Lo +MIPS_msubu, // Multiply and Subtract Word to Hi,Lo +MIPS_mul, // Multiply Word to GPR +MIPS_sdbbp, // Software Debug Breakpoint +MIPS_wait, // Enter Standby Mode + +// New instructions for MIPS32 Release 2.0 + +MIPS_alnv_ps, // Floating Point Align Variable +MIPS_deret, // Debug Exception Return +MIPS_di, // Disable interrupts +MIPS_ehb, // Execution Hazard Barrier +MIPS_ei, // Enable interrupts +MIPS_ext, // Extract Bit Field +MIPS_fcvt_ps, // Floating Point Convert Pair to Paired Single +MIPS_fcvt_s_pl, // Floating Point Convert Pair Lower to Single Floating Point +MIPS_fcvt_s_pu, // Floating Point Convert Pair Upper to Single Floating Point +MIPS_ins, // Insert Bit Field +MIPS_jalr_hb, // Jump and Link Register with Hazard Barrier +MIPS_jr_hb, // Jump Register with Hazard Barrier +MIPS_luxc1, // Load Doubleword Indexed Unaligned to Floating Point +MIPS_madd_ps, // Floating Point Multiply Add +MIPS_mfhc1, // Move Word from High Half of Floating Point Register +MIPS_mfhc2, // Move Word from High Half of Coprocessor 2 Register +MIPS_msub_ps, // Floating Point Multiply Subtract +MIPS_mthc1, // Move Word to High Half of Floating Point Register +MIPS_mthc2, // Move Word to High Half of Coprocessor 2 Register +MIPS_nmadd_ps, // Floating Point Negative Multiply Add +MIPS_nmsub_ps, // Floating Point Negative Multiply Subtract +MIPS_pll, // Pair Lower Lower +MIPS_plu, // Pair Lower Upper +MIPS_pul, // Pair Upper Lower +MIPS_puu, // Pair Upper Upper +MIPS_rdhwr, // Read Hardware Register +MIPS_rdpgpr, // Read GPR from Previous Shadow Set +MIPS_rotr, // Rotate Word Right +MIPS_rotrv, // Rotate Word Right Variable +MIPS_seb, // Sign-Extend Byte +MIPS_seh, // Sign-Extend Halfword +MIPS_suxc1, // Store Doubleword Indexed Unaligned from Floating Point +MIPS_synci, // Synchronize Caches to Make Instruction Writes Effective +MIPS_wrpgpr, // Write GPR to Previous Shadow Set +MIPS_wsbh, // Word Swap Bytes Within Halfwords + +// Missing instructions - for some reason they were not supported before + +MIPS_dmfc1, // Doubleword Move From CP1 +MIPS_dmtc1, // Doubleword Move To CP1 + +// additional MIPS16e instructions + +MIPS_save, // Save Registers and Set Up Stack Frame +MIPS_restore, // Restore Registers and Deallocate Stack Frame +MIPS_jalrc, // Jump and Link Register, Compact +MIPS_jrc, // Jump Register, Compact +MIPS_sew, // Sign-Extend Word +MIPS_zeb, // Zero-Extend Byte +MIPS_zeh, // Zero-Extend Halfword +MIPS_zew, // Zero-Extend Word + +// extra pseudoinstructions +MIPS_ssnop, // Superscalar No operation +MIPS_li_s, // Load floating-point immediate +MIPS_li_d, // Load floating-point immediate +MIPS_dneg, // Negate +MIPS_dnegu, // Negate Unsigned +MIPS_pause, // Wait for the LLBit to clear + +// Missing instructions from MIPS64 Release 2 + +MIPS_dclo, // Count Leading Ones in Doubleword +MIPS_dclz, // Count Leading Zeros in Doubleword +MIPS_dext, // Doubleword Extract Bit Field +MIPS_dextm, // Doubleword Extract Bit Field Middle +MIPS_dextu, // Doubleword Extract Bit Field Upper +MIPS_dins, // Doubleword Insert Bit Field +MIPS_dinsm, // Doubleword Insert Bit Field Middle +MIPS_dinsu, // Doubleword Insert Bit Field Upper +MIPS_dmfc2, // Doubleword Move From CP2 +MIPS_dmtc2, // Doubleword Move To CP2 +MIPS_drotr, // Doubleword Rotate Right +MIPS_drotr32, // Doubleword Rotate Right Plus 32 +MIPS_drotrv, // Doubleword Rotate Right Variable +MIPS_dsbh, // Doubleword Swap Bytes Within Halfwords +MIPS_dshd, // Doubleword Swap Halfwords Within Doublewords + +// cnMIPS (Cavium Networks Octeon) instructions + +MIPS_baddu, // Unsigned Byte Add +MIPS_bbit0, // Branch on Bit Clear +MIPS_bbit032, // Branch on Bit Clear Plus 32 +MIPS_bbit1, // Branch on Bit Set +MIPS_bbit132, // Branch on Bit Set Plus 32 +MIPS_cins, // Clear and Insert a Bit Field +MIPS_cins32, // Clear and Insert a Bit Field Plus 32 +MIPS_dmul, // Multiply Doubleword to GPR +MIPS_dpop, // Count Ones in a Doubleword +MIPS_exts, // Extract a Signed Bit Field +MIPS_exts32, // Extract a Signed Bit Field Plus 32 +MIPS_mtm0, // Load Multiplier Register MPL0 +MIPS_mtm1, // Load Multiplier Register MPL1 +MIPS_mtm2, // Load Multiplier Register MPL2 +MIPS_mtp0, // Load Multiplier Register P0 +MIPS_mtp1, // Load Multiplier Register P1 +MIPS_mtp2, // Load Multiplier Register P2 +MIPS_pop, // Count Ones in a Word +MIPS_saa, // Store Atomic Add Word +MIPS_saad, // Store Atomic Add Double Word +MIPS_seq, // Set on Equal +MIPS_seqi, // Set on Equal Immediate +MIPS_sne, // Set on Not Equal +MIPS_snei, // Set on Not Equal Immediate +MIPS_synciobdma, // Synchronize IOBDMAs +MIPS_syncs, // Synchronize Special +MIPS_syncw, // Synchronize Stores +MIPS_syncws, // Synchronize Stores Special +MIPS_uld, // Unaligned Load Doubleword +MIPS_ulw, // Unaligned Load Word +MIPS_usd, // Unaligned Store Doubleword +MIPS_usw, // Unaligned Store Word +MIPS_v3mulu, // 192-bit x 64-bit Unsigned Multiply and Add +MIPS_vmm0, // 64-bit Unsigned Multiply and Add Move +MIPS_vmulu_cn, // 64-bit Unsigned Multiply and Add + +// NEC VR5432 and PSP instructions + +MIPS_dbreak, // Debug Break +MIPS_dret, // Debug Return +MIPS_mfdr, // Move from Debug Register +MIPS_mtdr, // Move to Debug Register + +// Allegrex (Sony PSP) instructions + +PSP_bitrev, // Bit reverse +PSP_max, // Maximum +PSP_min, // Minimum +PSP_mfic, // Move from interrupt controller +PSP_mtic, // Move to interrupt controller +PSP_wsbw, // Word Swap Bytes Within Word +PSP_sleep, // Sleep + +// Allegrex VFPU instructions + +PSP_lv, // Load Vector +PSP_lvl, // Load Vector Left +PSP_lvr, // Load Vector Right +PSP_sv, // Store Vector +PSP_svl, // Store Vector Left +PSP_svr, // Store Vector Right +PSP_mfv, // Move from VFPU +PSP_mtv, // Move to VFPU +PSP_mfvc, // Move Control from VFPU +PSP_mtvc, // Move Control to VFPU +PSP_bvf, // Branch on VFPU False +PSP_bvt, // Branch on VFPU True +PSP_bvfl, // Branch on VFPU False Likely +PSP_bvtl, // Branch on VFPU True Likely +PSP_vnop, // VFPU no-op +PSP_vflush, // VFPU flush +PSP_vsync, // VFPU sync +PSP_vabs, // Vector absolute value +PSP_vadd, // Vector add +PSP_vasin, // Vector arcsine +PSP_vavg, // Vector average +PSP_vbfy1, // IDCT butterfly 1 +PSP_vbfy2, // IDCT butterfly 2 +PSP_vc2i, // Vector convert signed char to integer +PSP_vcmovf, // Vector move if condition field is true +PSP_vcmovt, // Vector move if condition field is false +PSP_vcmp, // Vector compare and set condition fields +PSP_vcos, // Vector cosine +PSP_vcrs, // Vector cross multiplication: vd = [y1*z2, z1*x2, x1*y2] +PSP_vcrsp, // Vector cross product +PSP_vcst, // Set constant +PSP_vdet, // Determinant +PSP_vdiv, // Vector divide +PSP_vdot, // Vector dot product +PSP_vexp2, // Vector exponent of 2 (2^x) +PSP_vf2h, // Vector convert float single to half precision +PSP_vf2id, // Vector convert float to integer, round down +PSP_vf2in, // Vector convert float to integer, round to nearest +PSP_vf2iu, // Vector convert float to integer, round up +PSP_vf2iz, // Vector convert float to integer, round toward zero +PSP_vfad, // Vector funnel add (sum components) +PSP_vfim, // Set floating-point immediate +PSP_vh2f, // Vector convert float half to single precision +PSP_vhdp, // Vector homogenous dot product +PSP_vhtfm2, // Homogenous transform vector by matrix +PSP_vhtfm3, // Homogenous transform vector by matrix +PSP_vhtfm4, // Homogenous transform vector by matrix +PSP_vi2c, // Vector convert integer to signed char +PSP_vi2f, // Vector convert integer to float +PSP_vi2s, // Vector convert integer to signed short +PSP_vi2uc, // Vector convert integer to unsigned char +PSP_vi2us, // Vector convert integer to unsigned short +PSP_vidt, // Set vector to identity +PSP_viim, // Set integer immediate +PSP_vlgb, // Vector log binary (extract exponent) +PSP_vlog2, // Vector logarithm base 2 +PSP_vmax, // Vector maximum values +PSP_vmfvc, // Vector move from control register +PSP_vmidt, // Set matrix to identity +PSP_vmin, // Vector minimum values +PSP_vmmov, // Move matrix +PSP_vmmul, // Matrix multiply +PSP_vmone, // Set matrix to ones +PSP_vmov, // Move vector +PSP_vmscl, // Scale matrix by +PSP_vmtvc, // Vector move to control register +PSP_vmul, // Vector multiply +PSP_vmzero, // Set matrix to zeroes +PSP_vneg, // Vector negate +PSP_vnrcp, // Vector negative reciprocal (-1/x) +PSP_vnsin, // Vector negative sine +PSP_vocp, // Vector one complement (1-x) +PSP_vone, // Set vector to ones +PSP_vpfxd, // Set prefix operation for vd +PSP_vpfxs, // Set prefix operation for vs +PSP_vpfxt, // Set prefix operation for vt +PSP_vqmul, // Quaternion multiply +PSP_vrcp, // Vector reciprocal (1/x) +PSP_vrexp2, // Vector reciprocal exponent of 2 (1/(2^x)) +PSP_vrndf1, // Vector generate pseudorandom float (1.0 ~ 2.0) +PSP_vrndf2, // Vector generate pseudorandom float (2.0 ~ 4.0) +PSP_vrndi, // Vector generate pseudorandom integer +PSP_vrnds, // Vector set pseudorandom seed +PSP_vrot, // Rotate vector +PSP_vrsq, // Vector reciprocal square root (1/sqrt(x)) +PSP_vs2i, // Vector convert signed short to integer +PSP_vsat0, // Vector saturate to range 0..1 +PSP_vsat1, // Vector saturate to range -1..1 +PSP_vsbn, // Vector scale by 2^x, round to nearest +PSP_vsbz, // Vector scale by 2^x, round towards zero +PSP_vscl, // Vector scale by +PSP_vscmp, // Vector set signed compare +PSP_vsge, // Vector set results for greater than or equal +PSP_vsgn, // Vector get sign +PSP_vsin, // Vector sine +PSP_vslt, // Vector set results for less +PSP_vsocp, // Vector split and one complement +PSP_vsqrt, // Vector square root +PSP_vsrt1, // Vector sort 1: vd = min(x,y), max(x,y), min(z,w), max(z,w) +PSP_vsrt2, // Vector sort 2: vd = min(x,w), max(y,z), min(y,z), max(x,w) +PSP_vsrt3, // Vector sort 3: vd = max(x,y), min(x,y), max(z,w), min(z,w) +PSP_vsrt4, // Vector sort 4: vd = max(x,w), max(y,z), min(y,z), max(x,w) +PSP_vsub, // Vector subtract +PSP_vt4444, // Transform color RGBA8888 to RGBA4444 +PSP_vt5551, // Transform color RGBA8888 to RGBA5551 +PSP_vt5650, // Transform color RGB888 to RGB565 +PSP_vtfm2, // Transform vector by matrix +PSP_vtfm3, // Transform vector by matrix +PSP_vtfm4, // Transform vector by matrix +PSP_vuc2i, // Vector convert unsigned char to integer +PSP_vus2i, // Vector convert unsigned short to integer +PSP_vwbn, // Wrap BigNum +PSP_vzero, // VFPU set vector to zeroes + +// PSP Media Engine instructions +PSP_mfvme, // move from VME +PSP_mtvme, // move to VME + +// Toshiba TX19a instructions +MIPS_ac0iu, // Add Coprocessor 0 Immediate Unsigned +MIPS_bs1f, // Bit Search One Forward +MIPS_bfins, // Bit field insert +MIPS_addmiu, // Add Immediate to Memory Word +MIPS_sadd, // Saturated Add +MIPS_ssub, // Saturated Subtract +MIPS_btst, // Bit Test +MIPS_bclr, // Bit Clear +MIPS_bset, // Bit Set +MIPS_bins, // Bit Insert +MIPS_bext, // Bit Extract +MIPS_dive, // Divide, with Overflow Exception +MIPS_diveu, // Divide unsigned, with Overflow Exception +MIPS_min, // Minimum signed +MIPS_max, // Maximum signed + +MIPS_madd3, // Multiply/Add (3-operand) +MIPS_maddu3, // Multiply/Add Unsigned (3-operand) +MIPS_msub3, // Multiply and Subtract Word to Hi,Lo (3-operand) +MIPS_msubu3, // Multiply and Subtract Word to Hi,Lo (3-operand) + +// MIPS-MT +MIPS_dvpe, // Disable Virtual Processor Execution +MIPS_evpe, // Enable Virtual Processor Execution +MIPS_dmt, // Disable Multi-Threaded Execution +MIPS_emt, // Enable Multi-Threaded Execution +MIPS_fork, // Allocate and Schedule a New Thread +MIPS_yield, // Conditionally Deschedule or Deallocate the Current Thread +MIPS_mftr, // Move From Thread Context +MIPS_mftc0, // Move From Thread Context (CP0 register, low-order half) +MIPS_mfthc0, // Move From Thread Context (CP0 register, high-order half) +MIPS_mftlo, // Move From Thread Context (LO register) +MIPS_mfthi, // Move From Thread Context (HI register) +MIPS_mftacx, // Move From Thread Context (ACX register) +MIPS_mftdsp, // Move From Thread Context (DSPControl register) +MIPS_mfthc1, // Move From Thread Context (CP1 register, high-order half) +MIPS_mftc1, // Move From Thread Context (CP1 register, low-order half) +MIPS_cftc1, // Move From Thread Context (CP1 control register) +MIPS_mfthc2, // Move From Thread Context (CP2 register, high-order half) +MIPS_mftc2, // Move From Thread Context (CP2 register, low-order half) +MIPS_cftc2, // Move From Thread Context (CP2 control register) +MIPS_mftgpr, // Move From Thread Context (general-purpose register) +MIPS_mttr, // Move To Thread Context +MIPS_mttc0, // Move To Thread Context (CP0 register, low-order half) +MIPS_mtthc0, // Move To Thread Context (CP0 register, high-order half) +MIPS_mttlo, // Move To Thread Context (LO register) +MIPS_mtthi, // Move To Thread Context (HI register) +MIPS_mttacx, // Move To Thread Context (ACX register) +MIPS_mttdsp, // Move To Thread Context (DSPControl register) +MIPS_mtthc1, // Move To Thread Context (CP1 register, high-order half) +MIPS_mttc1, // Move To Thread Context (CP1 register, low-order half) +MIPS_cttc1, // Move To Thread Context (CP1 control register) +MIPS_mtthc2, // Move To Thread Context (CP2 register, high-order half) +MIPS_mttc2, // Move To Thread Context (CP2 register, low-order half) +MIPS_cttc2, // Move To Thread Context (CP2 control register) +MIPS_mttgpr, // Move To Thread Context (general-purpose register) + +// MIPS-3D +MIPS_faddr, // Floating-point Reduction Addition +MIPS_bc1any2f, // Branch on Any of Two Floating Point Condition Codes False +MIPS_bc1any2t, // Branch on Any of Two Floating Point Condition Codes True +MIPS_bc1any4f, // Branch on Any of Four Floating Point Condition Codes False +MIPS_bc1any4t, // Branch on Any of Four Floating Point Condition Codes True +MIPS_fcabs_f, // Floating-point Absolute Compare +MIPS_fcabs_un, // Floating-point Absolute Compare +MIPS_fcabs_eq, // Floating-point Absolute Compare +MIPS_fcabs_ueq, // Floating-point Absolute Compare +MIPS_fcabs_olt, // Floating-point Absolute Compare +MIPS_fcabs_ult, // Floating-point Absolute Compare +MIPS_fcabs_ole, // Floating-point Absolute Compare +MIPS_fcabs_ule, // Floating-point Absolute Compare +MIPS_fcabs_sf, // Floating-point Absolute Compare +MIPS_fcabs_ngle, // Floating-point Absolute Compare +MIPS_fcabs_seq, // Floating-point Absolute Compare +MIPS_fcabs_ngl, // Floating-point Absolute Compare +MIPS_fcabs_lt, // Floating-point Absolute Compare +MIPS_fcabs_nge, // Floating-point Absolute Compare +MIPS_fcabs_le, // Floating-point Absolute Compare +MIPS_fcabs_ngt, // Floating-point Absolute Compare +MIPS_fcvt_pw_ps, // Floating-point Convert Paired Single to Paired Word +MIPS_fcvt_ps_pw, // Floating-point Convert Paired Word to Paired Single +MIPS_fmulr, // Floating-point Reduction Multiply +MIPS_frecip1, // Floating-point Reduced Precision Reciprocal (Step 1) +MIPS_frecip2, // Floating-point Reduced Precision Reciprocal (Step 2) +MIPS_frsqrt1, // Floating-point Reduced Precision Reciprocal Square Root (Step 1) +MIPS_frsqrt2, // Floating-point Reduced Precision Reciprocal Square Root (Step 2) + +// smartMIPS +MIPS_lwxs, // Load Word Indexed, Scaled +MIPS_maddp, // Multiply and Add Polynomial Basis Word to Hi,Lo +MIPS_mflhxu, // Move from Extended Carry, Hi and Lo (Unsigned) +MIPS_mtlhx, // Move to Lo, Hi, and Extended Carry +MIPS_multp, // Multiply Binary Polynomial Basis Word +MIPS_pperm, // Partial Permutation of Word Data into ACX-Hi-Lo Accumulator + +// microMIPS +MIPS_jals, // Jump and Link, Short Delay Slot +MIPS_lwp, // Load Word Pair +MIPS_ldp, // Load Doubleword Pair +MIPS_lwm, // Load Word Multiple +MIPS_ldm, // Load Doubleword Multiple +MIPS_swp, // Store Word Pair +MIPS_sdp, // Store Doubleword Pair +MIPS_swm, // Store Word Multiple +MIPS_sdm, // Store Doubleword Multiple +MIPS_bnezc, // Branch on Not Equal to Zero, Compact +MIPS_bltzals, // Branch on Less Than Zero and Link, Short Delay-Slot +MIPS_beqzc, // Branch on Equal to Zero, Compact +MIPS_bgezals, // Branch on Greater Than or Equal to Zero and Link, Short Delay-Slot +MIPS_jraddiusp, // Jump Register, Adjust Stack Pointer +MIPS_jalrs, // Jump and Link Register, Short Delay Slot +MIPS_jalrs_hb, // Jump and Link Register with Hazard Barrier, Short Delay-Slot +MIPS_movep, // Move a Pair of Registers + +// had been missed; 64-bit MIPS pseudoinstruction +MIPS_dli, // Doubleword Load Immediate + +// DSP ASE instructions +MIPS_insv, // Insert Bit Field Variable +MIPS_dinsv, // Doubleword Insert Variable Bit Field +MIPS_bposge32, // Branch on Greater Than or Equal To Value 32 in DSPControl Pos Field +MIPS_bposge64, // Branch on Greater Than or Equal To Value 64 in DSPControl Pos Field +MIPS_addu_qb, // Unsigned Add Quad Byte Vectors +MIPS_addu_ph, // Unsigned Add Integer Halfwords +MIPS_addsc, // Add Signed Word and Set Carry Bit +MIPS_subu_qb, // Subtract Unsigned Quad Byte Vector +MIPS_subu_ph, // Subtract Unsigned Integer Halfwords +MIPS_addwc, // Add Word with Carry Bit +MIPS_addq_ph, // Add Fractional Halfword Vectors +MIPS_modsub, // Modular Subtraction on an Index Value +MIPS_subq_ph, // Subtract Fractional Halfword Vector +MIPS_addu_s_qb, // Unsigned Add Quad Byte Vectors +MIPS_addu_s_ph, // Unsigned Add Integer Halfwords +MIPS_raddu_w_qb, // Unsigned Reduction Add Vector Quad Bytes +MIPS_muleq_s_w_phl, // Multiply Vector Fractional Left Halfwords to Expanded Width Products +MIPS_subu_s_qb, // Subtract Unsigned Quad Byte Vector +MIPS_subu_s_ph, // Subtract Unsigned Integer Halfwords +MIPS_muleq_s_w_phr, // Multiply Vector Fractional Right Halfwords to Expanded Width Products +MIPS_muleu_s_ph_qbl, // Multiply Unsigned Vector Left Bytes by Halfwords to Halfword Products +MIPS_addq_s_ph, // Add Fractional Halfword Vectors +MIPS_addq_s_w, // Add Fractional Words +MIPS_mulq_s_ph, // Multiply Vector Fractional Half-Words to Same Size Products +MIPS_muleu_s_ph_qbr, // Multiply Unsigned Vector Right Bytes with halfwords to Half Word Products +MIPS_subq_s_ph, // Subtract Fractional Halfword Vector +MIPS_subq_s_w, // Subtract Fractional Word +MIPS_mulq_rs_ph, // Multiply Vector Fractional Halfwords to Fractional Halfword Products +MIPS_addu_ob, // Unsigned Add Octal Byte Vectors +MIPS_subu_ob, // Subtract Unsigned Octal Byte Vector +MIPS_addq_qh, // Add Fractional Halfword Vectors +MIPS_addq_pw, // Add Fractional Word Vectors +MIPS_subq_qh, // Subtract Fractional Halfword Vector +MIPS_subq_pw, // Subtract Fractional Word Vector +MIPS_addu_s_ob, // Unsigned Add Octal Byte Vectors +MIPS_raddu_l_ob, // Unsigned Reduction Add Vector Octal Bytes +MIPS_muleq_s_pw_qhl, // Multiply Vector Fractional Left Halfwords to Expanded Width Products +MIPS_subu_s_ob, // Subtract Unsigned Octal Byte Vector +MIPS_muleq_s_pw_qhr, // Multiply Vector Fractional Right Halfwords to Expanded Width Products +MIPS_muleu_s_qh_obl, // Multiply Unsigned Vector Left Bytes by Halfwords to Halfword Products +MIPS_addq_s_qh, // Add Fractional Halfword Vectors +MIPS_addq_s_pw, // Add Fractional Word Vectors +MIPS_muleu_s_qh_obr, // Multiply Unsigned Vector Right Bytes by Halfwords to Halfword Products +MIPS_subq_s_qh, // Subtract Fractional Halfword Vector +MIPS_subq_s_pw, // Subtract Fractional Word Vector +MIPS_mulq_rs_qh, // Multiply Vector Fractional Halfwords to Fractional Halfword Products +MIPS_cmpu_eq_qb, // Compare Vectors of Unsigned Byte Values +MIPS_cmp_eq_ph, // Compare Vectors of Signed Integer Halfword Values +MIPS_cmpgdu_eq_qb, // Compare Unsigned Vector of Four Bytes and Write Result to GPR and DSPControl +MIPS_cmpu_lt_qb, // Compare Vectors of Unsigned Byte Values +MIPS_cmp_lt_ph, // Compare Vectors of Signed Integer Halfword Values +MIPS_cmpgdu_lt_qb, // Compare Unsigned Vector of Four Bytes and Write Result to GPR and DSPControl +MIPS_cmpu_le_qb, // Compare Vectors of Unsigned Byte Values +MIPS_cmp_le_ph, // Compare Vectors of Signed Integer Halfword Values +MIPS_cmpgdu_le_qb, // Compare Unsigned Vector of Four Bytes and Write Result to GPR and DSPControl +MIPS_pick_qb, // Pick a Vector of Byte Values Based on Condition Code Bits +MIPS_pick_ph, // Pick a Vector of Halfword Values Based on Condition Code Bits +MIPS_cmpgu_eq_qb, // Compare Vectors of Unsigned Byte Values and Write Results to a GPR +MIPS_precrq_qb_ph, // Precision Reduce Four Fractional Halfwords to Four Bytes +MIPS_precrq_ph_w, // Precision Reduce Fractional Words to Fractional Halfwords +MIPS_cmpgu_lt_qb, // Compare Vectors of Unsigned Byte Values and Write Results to a GPR +MIPS_precr_qb_ph, // Precision Reduce Four Integer Halfwords to Four Bytes +MIPS_precrq_rs_ph_w, // Precision Reduce Fractional Words to Halfwords With Rounding and Saturation +MIPS_cmpgu_le_qb, // Compare Vectors of Unsigned Byte Values and Write Results to a GPR +MIPS_packrl_ph, // Pack a Vector of Halfwords from Vector Halfword Sources +MIPS_precr_sra_ph_w, // Precision Reduce Two Integer Words to Halfwords after a Right Shift +MIPS_precrqu_s_qb_ph, // Precision Reduce Fractional Halfwords to Unsigned Bytes With Saturation +MIPS_precr_sra_r_ph_w, // Precision Reduce Two Integer Words to Halfwords after a Right Shift +MIPS_cmpu_eq_ob, // Compare Vectors of Unsigned Byte Values +MIPS_cmp_eq_qh, // Compare Vectors of Signed Integer Halfword Values +MIPS_cmp_eq_pw, // Compare Vectors of Signed Integer Word Values +MIPS_cmpu_lt_ob, // Compare Vectors of Unsigned Byte Values +MIPS_cmp_lt_qh, // Compare Vectors of Signed Integer Halfword Values +MIPS_cmp_lt_pw, // Compare Vectors of Signed Integer Word Values +MIPS_cmpu_le_ob, // Compare Vectors of Unsigned Byte Values +MIPS_cmp_le_qh, // Compare Vectors of Signed Integer Halfword Values +MIPS_cmp_le_pw, // Compare Vectors of Signed Integer Word Values +MIPS_pick_ob, // Pick a Vector of Byte Values Based on Condition Code Bits +MIPS_pick_qh, // Pick a Vector of Halfword Values Based on Condition Code Bits +MIPS_pick_pw, // Pick a Vector of Word Values Based on Condition Code Bits +MIPS_cmpgu_eq_ob, // Compare Vectors of Unsigned Byte Values and Write Results to a GPR +MIPS_precrq_ob_qh, // Precision Reduce Fractional Halfwords to Fractional Bytes +MIPS_precrq_qh_pw, // Precision Reduce Fractional Words to Fractional Halfwords +MIPS_precrq_pw_l, // Precision Reduce Fractional Doublewords to Fractional Words +MIPS_cmpgu_lt_ob, // Compare Vectors of Unsigned Byte Values and Write Results to a GPR +MIPS_precrq_rs_qh_pw, // Precision Reduce Fractional Words to Halfwords With Rounding and Saturation +MIPS_cmpgu_le_ob, // Compare Vectors of Unsigned Byte Values and Write Results to a GPR +MIPS_packrl_pw, // Pack a Vector of Words from Vector Word Sources +MIPS_precrqu_s_ob_qh, // Precision Reduce Fractional Halfwords to Unsigned Bytes With Saturation +MIPS_absq_s_qb, // Find Absolute Value of Four Fractional Byte Values +MIPS_absq_s_ph, // Find Absolute Value of Two Fractional Halfwords +MIPS_absq_s_w, // Find Absolute Value of Fractional Word +MIPS_repl_qb, // Replicate Immediate Integer into all Vector Element Positions +MIPS_repl_ph, // Replicate Immediate Integer into all Vector Element Positions +MIPS_replv_qb, // Replicate Byte into all Vector Element Positions +MIPS_replv_ph, // Replicate a Halfword into all Vector Element Positions +MIPS_bitrev, // Bit-Reverse Halfword +MIPS_precequ_ph_qbl, // Precision Expand two Unsigned Bytes to Fractional Halfword Values +MIPS_preceq_w_phl, // Precision Expand Fractional Halfword to Fractional Word Value +MIPS_preceu_ph_qbl, // Precision Expand Two Unsigned Bytes to Unsigned Halfword Values +MIPS_precequ_ph_qbr, // Precision Expand two Unsigned Bytes to Fractional Halfword Values +MIPS_preceq_w_phr, // Precision Expand Fractional Halfword to Fractional Word Value +MIPS_preceu_ph_qbr, // Precision Expand two Unsigned Bytes to Unsigned Halfword Values +MIPS_precequ_ph_qbla, // Precision Expand two Unsigned Bytes to Fractional Halfword Values +MIPS_preceu_ph_qbla, // Precision Expand Two Unsigned Bytes to Unsigned Halfword Values +MIPS_precequ_ph_qbra, // Precision Expand two Unsigned Bytes to Fractional Halfword Values +MIPS_preceu_ph_qbra, // Precision Expand Two Unsigned Bytes to Unsigned Halfword Values +MIPS_absq_s_qh, // Find Absolute Value of Four Fractional Halfwords +MIPS_absq_s_pw, // Find Absolute Value of Two Fractional Words +MIPS_repl_ob, // Replicate Immediate Integer into all Vector Element Positions +MIPS_repl_qh, // Replicate Immediate Integer into all Vector Element Positions +MIPS_repl_pw, // Replicate Immediate Integer into all Vector Element Positions +MIPS_replv_ob, // Replicate Byte into all Vector Element Positions +MIPS_replv_qh, // Replicate a Halfword into all Vector Element Positions +MIPS_replv_pw, // Replicate Word into all Vector Element Positions +MIPS_precequ_pw_qhl, // Precision Expand two Unsigned Bytes to Fractional Halfword Values +MIPS_preceq_pw_qhl, // Precision Expand Two Fractional Halfwords to Fractional Word Values +MIPS_preceq_s_l_pwl, // Precision Expand Fractional Word to Fractional Doubleword Value +MIPS_preceu_qh_obl, // Precision Expand Four Unsigned Bytes to Unsigned Halfword Values +MIPS_precequ_pw_qhr, // Precision Expand two Unsigned Bytes to Fractional Halfword Values +MIPS_preceq_pw_qhr, // Precision Expand Two Fractional Halfwords to Fractional Word Values +MIPS_preceq_s_l_pwr, // Precision Expand Fractional Word to Fractional Doubleword Value +MIPS_preceu_qh_obr, // Precision Expand Four Unsigned Bytes to Unsigned Halfword Values +MIPS_precequ_pw_qhla, // Precision Expand two Unsigned Bytes to Fractional Halfword Values +MIPS_preceq_pw_qhla, // Precision Expand Two Fractional Halfwords to Fractional Word Values +MIPS_preceu_qh_obla, // Precision Expand Four Unsigned Bytes to Unsigned Halfword Values +MIPS_precequ_pw_qhra, // Precision Expand two Unsigned Bytes to Fractional Halfword Values +MIPS_preceq_pw_qhra, // Precision Expand Two Fractional Halfwords to Fractional Word Values +MIPS_preceu_qh_obra, // Precision Expand Four Unsigned Bytes to Unsigned Halfword Values +MIPS_shll_qb, // Shift Left Logical Vector Quad Bytes +MIPS_shll_ph, // Shift Left Logical Vector Pair Halfwords +MIPS_shrl_qb, // Shift Right Logical Vector Quad Bytes +MIPS_shra_ph, // Shift Right Arithmetic Vector Pair Halfwords +MIPS_shrl_ph, // Shift Right Logical Two Halfwords +MIPS_shllv_qb, // Shift Left Logical Variable Vector Quad Bytes +MIPS_shllv_ph, // Shift Left Logical Variable Vector Pair Halfwords +MIPS_shrlv_qb, // Shift Right Logical Variable Vector Quad Bytes +MIPS_shrav_ph, // Shift Right Arithmetic Variable Vector Pair Halfwords +MIPS_shrlv_ph, // Shift Variable Right Logical Pair of Halfwords +MIPS_shra_qb, // Shift Right Arithmetic Vector of Four Bytes +MIPS_shll_s_ph, // Shift Left Logical Vector Pair Halfwords +MIPS_shll_s_w, // Shift Left Logical Word with Saturation +MIPS_shra_r_qb, // Shift Right Arithmetic Vector of Four Bytes +MIPS_shra_r_ph, // Shift Right Arithmetic Vector Pair Halfwords +MIPS_shra_r_w, // Shift Right Arithmetic Word with Rounding +MIPS_shrav_qb, // Shift Right Arithmetic Variable Vector of Four Bytes +MIPS_shllv_s_ph, // Shift Left Logical Variable Vector Pair Halfwords +MIPS_shllv_s_w, // Shift Left Logical Variable Vector Word +MIPS_shrav_r_qb, // Shift Right Arithmetic Variable Vector of Four Bytes +MIPS_shrav_r_ph, // Shift Right Arithmetic Variable Vector Pair Halfwords +MIPS_shrav_r_w, // Shift Right Arithmetic Variable Word with Rounding +MIPS_shll_ob, // Shift Left Logical Vector Octal Bytes +MIPS_shll_qh, // Shift Left Logical Vector Quad Halfwords +MIPS_shll_pw, // Shift Left Logical Vector Pair Words +MIPS_shrl_ob, // Shift Right Logical Vector Octal Bytes +MIPS_shra_qh, // Shift Right Arithmetic Vector Quad Halfwords +MIPS_shra_pw, // Shift Right Arithmetic Vector Pair Words +MIPS_shllv_ob, // Shift Left Logical Variable Vector Octal Bytes +MIPS_shllv_qh, // Shift Left Logical Variable Vector Quad Halfwords +MIPS_shllv_pw, // Shift Left Logical Variable Vector Pair Words +MIPS_shrlv_ob, // Shift Right Logical Variable Vector Octal Bytes +MIPS_shrav_qh, // Shift Right Arithmetic Variable Vector Quad Halfwords +MIPS_shrav_pw, // Shift Right Arithmetic Variable Vector Pair Words +MIPS_shll_s_qh, // Shift Left Logical Vector Quad Halfwords +MIPS_shll_s_pw, // Shift Left Logical Vector Pair Words +MIPS_shra_r_qh, // Shift Right Arithmetic Vector Quad Halfwords +MIPS_shra_r_pw, // Shift Right Arithmetic Vector Pair Words +MIPS_shllv_s_qh, // Shift Left Logical Variable Vector Quad Halfwords +MIPS_shllv_s_pw, // Shift Left Logical Variable Vector Pair Words +MIPS_shrav_r_qh, // Shift Right Arithmetic Variable Vector Quad Halfwords +MIPS_shrav_r_pw, // Shift Right Arithmetic Variable Vector Pair Words +MIPS_lwx, // Load Word Indexed +MIPS_ldx, // Load Doubleword Indexed +MIPS_lhx, // Load Halfword Indexed +MIPS_lbux, // Load Unsigned Byte Indexed +MIPS_dpa_w_ph, // Dot Product with Accumulate on Vector Integer Halfword Elements +MIPS_dpax_w_ph, // Cross Dot Product with Accumulate on Vector Integer Halfword Elements +MIPS_maq_sa_w_phl, // Multiply with Accumulate Single Vector Fractional Halfword Element +MIPS_dpaqx_s_w_ph, // Cross Dot Product with Accumulation on Fractional Halfword Elements +MIPS_dps_w_ph, // Dot Product with Subtract on Vector Integer Half-Word Elements +MIPS_dpsx_w_ph, // Cross Dot Product with Subtract on Vector Integer Halfword Elements +MIPS_dpsqx_s_w_ph, // Cross Dot Product with Subtraction on Fractional Halfword Elements +MIPS_mulsa_w_ph, // Multiply and Subtract Vector Integer Halfword Elements and Accumulate +MIPS_maq_sa_w_phr, // Multiply with Accumulate Single Vector Fractional Halfword Element +MIPS_dpaqx_sa_w_ph, // Cross Dot Product with Accumulation on Fractional Halfword Elements +MIPS_dpau_h_qbl, // Dot Product with Accumulate on Vector Unsigned Byte Elements +MIPS_dpsu_h_qbl, // Dot Product with Subtraction on Vector Unsigned Byte Elements +MIPS_dpsqx_sa_w_ph, // Cross Dot Product with Subtraction on Fractional Halfword Elements +MIPS_dpaq_s_w_ph, // Dot Product with Accumulation on Fractional Halfword Elements +MIPS_dpaq_sa_l_w, // Dot Product with Accumulate on Fractional Word Element +MIPS_maq_s_w_phl, // Multiply with Accumulate Single Vector Fractional Halfword Element +MIPS_dpsq_s_w_ph, // Dot Product with Subtraction on Fractional Halfword Elements +MIPS_dpsq_sa_l_w, // Dot Product with Subtraction on Fractional Word Element +MIPS_mulsaq_s_w_ph, // Multiply And Subtract Vector Fractional Halfwords And Accumulate +MIPS_maq_s_w_phr, // Multiply with Accumulate Single Vector Fractional Halfword Element +MIPS_dpau_h_qbr, // Dot Product with Accumulate on Vector Unsigned Byte Elements +MIPS_dpsu_h_qbr, // Dot Product with Subtraction on Vector Unsigned Byte Elements +MIPS_maq_sa_w_qhll, // Multiply with Accumulate Single Vector Fractional Halfword Element +MIPS_maq_sa_w_qhlr, // Multiply with Accumulate Single Vector Fractional Halfword Element +MIPS_dmadd, // Multiply Vector Words And Accumulate +MIPS_dmsub, // Multiply Vector Words And Subtract From Accumulator +MIPS_maq_sa_w_qhrl, // Multiply with Accumulate Single Vector Fractional Halfword Element +MIPS_dpau_h_obl, // Dot Product with Accumulate on Vector Unsigned Byte Elements +MIPS_dpsu_h_obl, // Dot Product with Subtract on Vector Unsigned Byte Elements +MIPS_maq_sa_w_qhrr, // Multiply with Accumulate Single Vector Fractional Halfword Element +MIPS_dpaq_s_w_qh, // Dot Product with Accumulation on Fractional Halfword Elements +MIPS_dpaq_sa_l_pw, // Dot Product with Accumulate on Fractional Word Elements +MIPS_maq_s_w_qhll, // Multiply with Accumulate Single Vector Fractional Halfword Element +MIPS_maq_s_l_pwl, // Multiply with Accumulate Single Vector Fractional Word Element +MIPS_dpsq_s_w_qh, // Dot Product with Subtraction on Fractional Halfword Elements +MIPS_dpsq_sa_l_pw, // Dot Product with Subtraction on Fractional Word Elements +MIPS_maq_s_w_qhlr, // Multiply with Accumulate Single Vector Fractional Halfword Element +MIPS_dmaddu, // Multiply Vector Unsigned Words And Accumulate +MIPS_mulsaq_s_w_qh, // Multiply And Subtract Vector Fractional Halfwords And Accumulate +MIPS_mulsaq_s_l_pw, // Multiply And Subtract Vector Fractional Words And Accumulate +MIPS_maq_s_w_qhrl, // Multiply with Accumulate Single Vector Fractional Halfword Element +MIPS_maq_s_l_pwr, // Multiply with Accumulate Single Vector Fractional Word Element +MIPS_dpau_h_obr, // Dot Product with Accumulate on Vector Unsigned Byte Elements +MIPS_dpsu_h_obr, // Dot Product with Subtract on Vector Unsigned Byte Elements +MIPS_maq_s_w_qhrr, // Multiply with Accumulate Single Vector Fractional Halfword Element +MIPS_dmsubu, // Multiply Vector Unsigned Words And Subtract From Accumulator +MIPS_extr_w, // Extract Word Value With Right Shift From Accumulator to GPR +MIPS_extrv_w, // Extract Word Value With Variable Right Shift From Accumulator to GPR +MIPS_extp, // Extract Fixed Bitfield From Arbitrary Position in Accumulator to GPR +MIPS_extpdp, // Extract Fixed Bitfield From Arbitrary Position in Accumulator to GPR and Decrement Pos +MIPS_rddsp, // Read DSPControl Register Fields to a GPR +MIPS_shilo, // Shift an Accumulator Value Leaving the Result in the Same Accumulator +MIPS_extpv, // Extract Variable Bitfield From Arbitrary Position in Accumulator to GPR +MIPS_extpdpv, // Extract Variable Bitfield From Arbitrary Position in Accumulator to GPR and Decrement Pos +MIPS_wrdsp, // Write Fields to DSPControl Register from a GPR +MIPS_shilov, // Variable Shift of Accumulator Value Leaving the Result in the Same Accumulator +MIPS_extr_r_w, // Extract Word Value With Right Shift And Rounding From Accumulator to GPR +MIPS_extrv_r_w, // Extract Word Value With Variable Right Shift And Rounding From Accumulator to GPR +MIPS_extr_rs_w, // Extract Word Value With Right Shift From Accumulator to GPR +MIPS_extr_s_h, // Extract Halfword Value From Accumulator to GPR With Right Shift and Saturate +MIPS_extrv_rs_w, // Extract Word Value With Variable Right Shift From Accumulator to GPR +MIPS_extrv_s_h, // Extract Halfword Value Variable From Accumulator to GPR With Right Shift and Saturate +MIPS_mthlip, // Copy LO to HI and a GPR to LO and Increment Pos by 32 +MIPS_dextr_w, // Extract Word Value With Right Shift From Accumulator to GPR +MIPS_dextr_l, // Extract Doubleword Value With Right Shift From Accumulator to GPR +MIPS_dextrv_w, // Extract Word Value With Variable Right Shift From Accumulator to GPR +MIPS_dextrv_l, // Extract Doubleword Value With Variable Right Shift From Accumulator to GPR +MIPS_dextp, // Extract Fixed Bitfield From Arbitrary Position in Accumulator to GPR +MIPS_dextpdp, // Extract Fixed Bitfield From Arbitrary Position in Accumulator to GPR and Decrement Pos +MIPS_dshilo, // Shift an Accumulator Value Leaving the Result in the Same Accumulator +MIPS_dextpv, // Extract Variable Bitfield From Arbitrary Position in Accumulator to GPR +MIPS_dextpdpv, // Extract Variable Bitfield From Arbitrary Position in Accumulator to GPR and Decrement Pos +MIPS_dshilov, // Variable Shift of Accumulator Value Leaving the Result in the Same Accumulator +MIPS_dextr_r_w, // Extract Word Value With Right Shift And Rounding From Accumulator to GPR +MIPS_dextr_r_l, // Extract Doubleword Value With Right Shift And Rounding From Accumulator to GPR +MIPS_dextrv_r_w, // Extract Word Value With Variable Right Shift And Rounding From Accumulator to GPR +MIPS_dextrv_r_l, // Extract Doubleword Value With Variable Right Shift And Rounding From Accumulator to GPR +MIPS_dextr_rs_w, // Extract Word Value With Right Shift From Accumulator to GPR +MIPS_dextr_s_h, // Extract Halfword Value From Accumulator to GPR With Right Shift and Saturate +MIPS_dextr_rs_l, // Extract Doubleword Value With Right Shift From Accumulator to GPR +MIPS_dextrv_rs_w, // Extract Word Value With Variable Right Shift From Accumulator to GPR +MIPS_dextrv_s_h, // Extract Halfword Value Variable From Accumulator to GPR With Right Shift and Saturate +MIPS_dextrv_rs_l, // Extract Doubleword Value With Variable Right Shift From Accumulator to GPR +MIPS_dmthlip, // Copy LO to HI and a GPR to LO and Increment Pos by 64 +MIPS_adduh_qb, // Unsigned Add Vector Quad-Bytes And Right Shift to Halve Results +MIPS_addqh_ph, // Add Fractional Halfword Vectors And Shift Right to Halve Results +MIPS_addqh_w, // Add Fractional Words And Shift Right to Halve Results +MIPS_subuh_qb, // Subtract Unsigned Bytes And Right Shift to Halve Results +MIPS_subqh_ph, // Subtract Fractional Halfword Vectors And Shift Right to Halve Results +MIPS_subqh_w, // Subtract Fractional Words And Shift Right to Halve Results +MIPS_adduh_r_qb, // Unsigned Add Vector Quad-Bytes And Right Shift to Halve Results +MIPS_addqh_r_ph, // Add Fractional Halfword Vectors And Shift Right to Halve Results +MIPS_addqh_r_w, // Add Fractional Words And Shift Right to Halve Results +MIPS_subuh_r_qb, // Subtract Unsigned Bytes And Right Shift to Halve Results +MIPS_subqh_r_ph, // Subtract Fractional Halfword Vectors And Shift Right to Halve Results +MIPS_subqh_r_w, // Subtract Fractional Words And Shift Right to Halve Results +MIPS_mul_ph, // Multiply Vector Integer HalfWords to Same Size Products +MIPS_mul_s_ph, // Multiply Vector Integer HalfWords to Same Size Products +MIPS_mulq_s_w, // Multiply Fractional Words to Same Size Product with Saturation +MIPS_mulq_rs_w, // Multiply Fractional Words to Same Size Product with Saturation and Rounding +MIPS_append, // Left Shift and Append Bits to the LSB +MIPS_balign, // Byte Align Contents from Two Registers +MIPS_prepend, // Right Shift and Prepend Bits to the MSB + +// Cavium Octeon II instructions +MIPS_laa, // Load Atomic Add Word +MIPS_laad, // Load Atomic Add Doubleword +MIPS_lac, // Load Atomic Clear Word +MIPS_lacd, // Load Atomic Clear Doubleword +MIPS_lad, // Load Atomic Decrement Word +MIPS_ladd, // Load Atomic Decrement Doubleword +MIPS_lai, // Load Atomic Increment Word +MIPS_laid, // Load Atomic Increment Doubleword +MIPS_las, // Load Atomic Set Word +MIPS_lasd, // Load Atomic Set Doubleword +MIPS_law, // Load Atomic Swap Word +MIPS_lawd, // Load Atomic Swap Doubleword +// we don't know the following mnemonics for sure +MIPS_lbx, // Load Byte Indexed +MIPS_lhux, // Load Halfword Unsigned Indexed +MIPS_lwux, // Load Word Unsigned Indexed +MIPS_qmac_00, // Q15 Multiply Accumulate +MIPS_qmac_01, // Q15 Multiply Accumulate +MIPS_qmac_02, // Q15 Multiply Accumulate +MIPS_qmac_03, // Q15 Multiply Accumulate +MIPS_qmacs_00, // Q15 Multiply Accumulate +MIPS_qmacs_01, // Q15 Multiply Accumulate +MIPS_qmacs_02, // Q15 Multiply Accumulate +MIPS_qmacs_03, // Q15 Multiply Accumulate +MIPS_zcb, // Zero Cache Block +MIPS_zcbt, // Zero Cache Block + +// MSA ASE +// some of these have the same name as existing mnemonics; to avoid ambiguity +// we use an msa_ prefix on all of them +MIPS_msa_sll_b, // Vector Shift Left +MIPS_msa_sll_h, // Vector Shift Left +MIPS_msa_sll_w, // Vector Shift Left +MIPS_msa_sll_d, // Vector Shift Left +MIPS_msa_slli_b, // Immediate Shift Left +MIPS_msa_slli_h, // Immediate Shift Left +MIPS_msa_slli_w, // Immediate Shift Left +MIPS_msa_slli_d, // Immediate Shift Left +MIPS_msa_sra_b, // Vector Shift Right Arithmetic +MIPS_msa_sra_h, // Vector Shift Right Arithmetic +MIPS_msa_sra_w, // Vector Shift Right Arithmetic +MIPS_msa_sra_d, // Vector Shift Right Arithmetic +MIPS_msa_srai_b, // Immediate Shift Right Arithmetic +MIPS_msa_srai_h, // Immediate Shift Right Arithmetic +MIPS_msa_srai_w, // Immediate Shift Right Arithmetic +MIPS_msa_srai_d, // Immediate Shift Right Arithmetic +MIPS_msa_srl_b, // Vector Shift Right Logical +MIPS_msa_srl_h, // Vector Shift Right Logical +MIPS_msa_srl_w, // Vector Shift Right Logical +MIPS_msa_srl_d, // Vector Shift Right Logical +MIPS_msa_srli_b, // Immediate Shift Right Logical +MIPS_msa_srli_h, // Immediate Shift Right Logical +MIPS_msa_srli_w, // Immediate Shift Right Logical +MIPS_msa_srli_d, // Immediate Shift Right Logical +MIPS_msa_bclr_b, // Vector Bit Clear +MIPS_msa_bclr_h, // Vector Bit Clear +MIPS_msa_bclr_w, // Vector Bit Clear +MIPS_msa_bclr_d, // Vector Bit Clear +MIPS_msa_bclri_b, // Immediate Bit Clear +MIPS_msa_bclri_h, // Immediate Bit Clear +MIPS_msa_bclri_w, // Immediate Bit Clear +MIPS_msa_bclri_d, // Immediate Bit Clear +MIPS_msa_bset_b, // Vector Bit Set +MIPS_msa_bset_h, // Vector Bit Set +MIPS_msa_bset_w, // Vector Bit Set +MIPS_msa_bset_d, // Vector Bit Set +MIPS_msa_bseti_b, // Immediate Bit Set +MIPS_msa_bseti_h, // Immediate Bit Set +MIPS_msa_bseti_w, // Immediate Bit Set +MIPS_msa_bseti_d, // Immediate Bit Set +MIPS_msa_bneg_b, // Vector Bit Negate +MIPS_msa_bneg_h, // Vector Bit Negate +MIPS_msa_bneg_w, // Vector Bit Negate +MIPS_msa_bneg_d, // Vector Bit Negate +MIPS_msa_bnegi_b, // Immediate Bit Negate +MIPS_msa_bnegi_h, // Immediate Bit Negate +MIPS_msa_bnegi_w, // Immediate Bit Negate +MIPS_msa_bnegi_d, // Immediate Bit Negate +MIPS_msa_binsl_b, // Vector Bit Insert Left +MIPS_msa_binsl_h, // Vector Bit Insert Left +MIPS_msa_binsl_w, // Vector Bit Insert Left +MIPS_msa_binsl_d, // Vector Bit Insert Left +MIPS_msa_binsli_b, // Immediate Bit Insert Left +MIPS_msa_binsli_h, // Immediate Bit Insert Left +MIPS_msa_binsli_w, // Immediate Bit Insert Left +MIPS_msa_binsli_d, // Immediate Bit Insert Left +MIPS_msa_binsr_b, // Vector Bit Insert Right +MIPS_msa_binsr_h, // Vector Bit Insert Right +MIPS_msa_binsr_w, // Vector Bit Insert Right +MIPS_msa_binsr_d, // Vector Bit Insert Right +MIPS_msa_binsri_b, // Immediate Bit Insert Right +MIPS_msa_binsri_h, // Immediate Bit Insert Right +MIPS_msa_binsri_w, // Immediate Bit Insert Right +MIPS_msa_binsri_d, // Immediate Bit Insert Right +MIPS_msa_addv_b, // Vector Add +MIPS_msa_addv_h, // Vector Add +MIPS_msa_addv_w, // Vector Add +MIPS_msa_addv_d, // Vector Add +MIPS_msa_addvi_b, // Immediate Add +MIPS_msa_addvi_h, // Immediate Add +MIPS_msa_addvi_w, // Immediate Add +MIPS_msa_addvi_d, // Immediate Add +MIPS_msa_subv_b, // Vector Subtract +MIPS_msa_subv_h, // Vector Subtract +MIPS_msa_subv_w, // Vector Subtract +MIPS_msa_subv_d, // Vector Subtract +MIPS_msa_subvi_b, // Immediate Subtract +MIPS_msa_subvi_h, // Immediate Subtract +MIPS_msa_subvi_w, // Immediate Subtract +MIPS_msa_subvi_d, // Immediate Subtract +MIPS_msa_max_s_b, // Vector Signed Maximum +MIPS_msa_max_s_h, // Vector Signed Maximum +MIPS_msa_max_s_w, // Vector Signed Maximum +MIPS_msa_max_s_d, // Vector Signed Maximum +MIPS_msa_maxi_s_b, // Immediate Signed Maximum +MIPS_msa_maxi_s_h, // Immediate Signed Maximum +MIPS_msa_maxi_s_w, // Immediate Signed Maximum +MIPS_msa_maxi_s_d, // Immediate Signed Maximum +MIPS_msa_max_u_b, // Vector Unsigned Maximum +MIPS_msa_max_u_h, // Vector Unsigned Maximum +MIPS_msa_max_u_w, // Vector Unsigned Maximum +MIPS_msa_max_u_d, // Vector Unsigned Maximum +MIPS_msa_maxi_u_b, // Immediate Unsigned Maximum +MIPS_msa_maxi_u_h, // Immediate Unsigned Maximum +MIPS_msa_maxi_u_w, // Immediate Unsigned Maximum +MIPS_msa_maxi_u_d, // Immediate Unsigned Maximum +MIPS_msa_min_s_b, // Vector Signed Minimum +MIPS_msa_min_s_h, // Vector Signed Minimum +MIPS_msa_min_s_w, // Vector Signed Minimum +MIPS_msa_min_s_d, // Vector Signed Minimum +MIPS_msa_mini_s_b, // Immediate Signed Minimum +MIPS_msa_mini_s_h, // Immediate Signed Minimum +MIPS_msa_mini_s_w, // Immediate Signed Minimum +MIPS_msa_mini_s_d, // Immediate Signed Minimum +MIPS_msa_min_u_b, // Vector Unsigned Minimum +MIPS_msa_min_u_h, // Vector Unsigned Minimum +MIPS_msa_min_u_w, // Vector Unsigned Minimum +MIPS_msa_min_u_d, // Vector Unsigned Minimum +MIPS_msa_mini_u_b, // Immediate Unsigned Minimum +MIPS_msa_mini_u_h, // Immediate Unsigned Minimum +MIPS_msa_mini_u_w, // Immediate Unsigned Minimum +MIPS_msa_mini_u_d, // Immediate Unsigned Minimum +MIPS_msa_max_a_b, // Vector Maximum Based on Absolute Values +MIPS_msa_max_a_h, // Vector Maximum Based on Absolute Values +MIPS_msa_max_a_w, // Vector Maximum Based on Absolute Values +MIPS_msa_max_a_d, // Vector Maximum Based on Absolute Values +MIPS_msa_min_a_b, // Vector Minimum Based on Absolute Value +MIPS_msa_min_a_h, // Vector Minimum Based on Absolute Value +MIPS_msa_min_a_w, // Vector Minimum Based on Absolute Value +MIPS_msa_min_a_d, // Vector Minimum Based on Absolute Value +MIPS_msa_ceq_b, // Vector Compare Equal +MIPS_msa_ceq_h, // Vector Compare Equal +MIPS_msa_ceq_w, // Vector Compare Equal +MIPS_msa_ceq_d, // Vector Compare Equal +MIPS_msa_ceqi_b, // Immediate Compare Equal +MIPS_msa_ceqi_h, // Immediate Compare Equal +MIPS_msa_ceqi_w, // Immediate Compare Equal +MIPS_msa_ceqi_d, // Immediate Compare Equal +MIPS_msa_clt_s_b, // Vector Compare Signed Less Than +MIPS_msa_clt_s_h, // Vector Compare Signed Less Than +MIPS_msa_clt_s_w, // Vector Compare Signed Less Than +MIPS_msa_clt_s_d, // Vector Compare Signed Less Than +MIPS_msa_clti_s_b, // Immediate Compare Signed Less Than +MIPS_msa_clti_s_h, // Immediate Compare Signed Less Than +MIPS_msa_clti_s_w, // Immediate Compare Signed Less Than +MIPS_msa_clti_s_d, // Immediate Compare Signed Less Than +MIPS_msa_clt_u_b, // Vector Compare Unsigned Less Than +MIPS_msa_clt_u_h, // Vector Compare Unsigned Less Than +MIPS_msa_clt_u_w, // Vector Compare Unsigned Less Than +MIPS_msa_clt_u_d, // Vector Compare Unsigned Less Than +MIPS_msa_clti_u_b, // Immediate Compare Unsigned Less Than +MIPS_msa_clti_u_h, // Immediate Compare Unsigned Less Than +MIPS_msa_clti_u_w, // Immediate Compare Unsigned Less Than +MIPS_msa_clti_u_d, // Immediate Compare Unsigned Less Than +MIPS_msa_cle_s_b, // Vector Compare Signed Less Than or Equal +MIPS_msa_cle_s_h, // Vector Compare Signed Less Than or Equal +MIPS_msa_cle_s_w, // Vector Compare Signed Less Than or Equal +MIPS_msa_cle_s_d, // Vector Compare Signed Less Than or Equal +MIPS_msa_clei_s_b, // Immediate Compare Signed Less Than or Equal +MIPS_msa_clei_s_h, // Immediate Compare Signed Less Than or Equal +MIPS_msa_clei_s_w, // Immediate Compare Signed Less Than or Equal +MIPS_msa_clei_s_d, // Immediate Compare Signed Less Than or Equal +MIPS_msa_cle_u_b, // Vector Compare Unsigned Less Than or Equal +MIPS_msa_cle_u_h, // Vector Compare Unsigned Less Than or Equal +MIPS_msa_cle_u_w, // Vector Compare Unsigned Less Than or Equal +MIPS_msa_cle_u_d, // Vector Compare Unsigned Less Than or Equal +MIPS_msa_clei_u_b, // Immediate Compare Unsigned Less Than or Equal +MIPS_msa_clei_u_h, // Immediate Compare Unsigned Less Than or Equal +MIPS_msa_clei_u_w, // Immediate Compare Unsigned Less Than or Equal +MIPS_msa_clei_u_d, // Immediate Compare Unsigned Less Than or Equal +MIPS_msa_ld_b, // Vector Load +MIPS_msa_ld_h, // Vector Load +MIPS_msa_ld_w, // Vector Load +MIPS_msa_ld_d, // Vector Load +MIPS_msa_st_b, // Vector Store +MIPS_msa_st_h, // Vector Store +MIPS_msa_st_w, // Vector Store +MIPS_msa_st_d, // Vector Store +MIPS_msa_sat_s_b, // Immediate Signed Saturate +MIPS_msa_sat_s_h, // Immediate Signed Saturate +MIPS_msa_sat_s_w, // Immediate Signed Saturate +MIPS_msa_sat_s_d, // Immediate Signed Saturate +MIPS_msa_sat_u_b, // Immediate Unsigned Saturate +MIPS_msa_sat_u_h, // Immediate Unsigned Saturate +MIPS_msa_sat_u_w, // Immediate Unsigned Saturate +MIPS_msa_sat_u_d, // Immediate Unsigned Saturate +MIPS_msa_add_a_b, // Vector Add Absolute Values +MIPS_msa_add_a_h, // Vector Add Absolute Values +MIPS_msa_add_a_w, // Vector Add Absolute Values +MIPS_msa_add_a_d, // Vector Add Absolute Values +MIPS_msa_adds_a_b, // Vector Saturated Add of Absolute Values +MIPS_msa_adds_a_h, // Vector Saturated Add of Absolute Values +MIPS_msa_adds_a_w, // Vector Saturated Add of Absolute Values +MIPS_msa_adds_a_d, // Vector Saturated Add of Absolute Values +MIPS_msa_adds_s_b, // Vector Signed Saturated Add of Signed Values +MIPS_msa_adds_s_h, // Vector Signed Saturated Add of Signed Values +MIPS_msa_adds_s_w, // Vector Signed Saturated Add of Signed Values +MIPS_msa_adds_s_d, // Vector Signed Saturated Add of Signed Values +MIPS_msa_adds_u_b, // Vector Unsigned Saturated Add of Unsigned Values +MIPS_msa_adds_u_h, // Vector Unsigned Saturated Add of Unsigned Values +MIPS_msa_adds_u_w, // Vector Unsigned Saturated Add of Unsigned Values +MIPS_msa_adds_u_d, // Vector Unsigned Saturated Add of Unsigned Values +MIPS_msa_ave_s_b, // Vector Signed Average +MIPS_msa_ave_s_h, // Vector Signed Average +MIPS_msa_ave_s_w, // Vector Signed Average +MIPS_msa_ave_s_d, // Vector Signed Average +MIPS_msa_ave_u_b, // Vector Unsigned Average +MIPS_msa_ave_u_h, // Vector Unsigned Average +MIPS_msa_ave_u_w, // Vector Unsigned Average +MIPS_msa_ave_u_d, // Vector Unsigned Average +MIPS_msa_aver_s_b, // Vector Signed Average Rounded +MIPS_msa_aver_s_h, // Vector Signed Average Rounded +MIPS_msa_aver_s_w, // Vector Signed Average Rounded +MIPS_msa_aver_s_d, // Vector Signed Average Rounded +MIPS_msa_aver_u_b, // Vector Unsigned Average Rounded +MIPS_msa_aver_u_h, // Vector Unsigned Average Rounded +MIPS_msa_aver_u_w, // Vector Unsigned Average Rounded +MIPS_msa_aver_u_d, // Vector Unsigned Average Rounded +MIPS_msa_subs_s_b, // Vector Signed Saturated Subtract of Signed Values +MIPS_msa_subs_s_h, // Vector Signed Saturated Subtract of Signed Values +MIPS_msa_subs_s_w, // Vector Signed Saturated Subtract of Signed Values +MIPS_msa_subs_s_d, // Vector Signed Saturated Subtract of Signed Values +MIPS_msa_subs_u_b, // Vector Unsigned Saturated Subtract of Unsigned Values +MIPS_msa_subs_u_h, // Vector Unsigned Saturated Subtract of Unsigned Values +MIPS_msa_subs_u_w, // Vector Unsigned Saturated Subtract of Unsigned Values +MIPS_msa_subs_u_d, // Vector Unsigned Saturated Subtract of Unsigned Values +MIPS_msa_subsus_u_b, // Vector Unsigned Saturated Subtract of Signed from Unsigned +MIPS_msa_subsus_u_h, // Vector Unsigned Saturated Subtract of Signed from Unsigned +MIPS_msa_subsus_u_w, // Vector Unsigned Saturated Subtract of Signed from Unsigned +MIPS_msa_subsus_u_d, // Vector Unsigned Saturated Subtract of Signed from Unsigned +MIPS_msa_subsuu_s_b, // Vector Signed Saturated Subtract of Unsigned Values +MIPS_msa_subsuu_s_h, // Vector Signed Saturated Subtract of Unsigned Values +MIPS_msa_subsuu_s_w, // Vector Signed Saturated Subtract of Unsigned Values +MIPS_msa_subsuu_s_d, // Vector Signed Saturated Subtract of Unsigned Values +MIPS_msa_asub_s_b, // Vector Absolute Values of Signed Subtract +MIPS_msa_asub_s_h, // Vector Absolute Values of Signed Subtract +MIPS_msa_asub_s_w, // Vector Absolute Values of Signed Subtract +MIPS_msa_asub_s_d, // Vector Absolute Values of Signed Subtract +MIPS_msa_asub_u_b, // Vector Absolute Values of Unsigned Subtract +MIPS_msa_asub_u_h, // Vector Absolute Values of Unsigned Subtract +MIPS_msa_asub_u_w, // Vector Absolute Values of Unsigned Subtract +MIPS_msa_asub_u_d, // Vector Absolute Values of Unsigned Subtract +MIPS_msa_mulv_b, // Vector Multiply +MIPS_msa_mulv_h, // Vector Multiply +MIPS_msa_mulv_w, // Vector Multiply +MIPS_msa_mulv_d, // Vector Multiply +MIPS_msa_maddv_b, // Vector Multiply and Add +MIPS_msa_maddv_h, // Vector Multiply and Add +MIPS_msa_maddv_w, // Vector Multiply and Add +MIPS_msa_maddv_d, // Vector Multiply and Add +MIPS_msa_msubv_b, // Vector Multiply and Subtract +MIPS_msa_msubv_h, // Vector Multiply and Subtract +MIPS_msa_msubv_w, // Vector Multiply and Subtract +MIPS_msa_msubv_d, // Vector Multiply and Subtract +MIPS_msa_div_s_b, // Vector Signed Divide +MIPS_msa_div_s_h, // Vector Signed Divide +MIPS_msa_div_s_w, // Vector Signed Divide +MIPS_msa_div_s_d, // Vector Signed Divide +MIPS_msa_div_u_b, // Vector Unsigned Divide +MIPS_msa_div_u_h, // Vector Unsigned Divide +MIPS_msa_div_u_w, // Vector Unsigned Divide +MIPS_msa_div_u_d, // Vector Unsigned Divide +MIPS_msa_mod_s_b, // Vector Signed Modulo +MIPS_msa_mod_s_h, // Vector Signed Modulo +MIPS_msa_mod_s_w, // Vector Signed Modulo +MIPS_msa_mod_s_d, // Vector Signed Modulo +MIPS_msa_mod_u_b, // Vector Unsigned Modulo +MIPS_msa_mod_u_h, // Vector Unsigned Modulo +MIPS_msa_mod_u_w, // Vector Unsigned Modulo +MIPS_msa_mod_u_d, // Vector Unsigned Modulo +MIPS_msa_dotp_s_h, // Vector Signed Dot Product +MIPS_msa_dotp_s_w, // Vector Signed Dot Product +MIPS_msa_dotp_s_d, // Vector Signed Dot Product +MIPS_msa_dotp_u_h, // Vector Unsigned Dot Product +MIPS_msa_dotp_u_w, // Vector Unsigned Dot Product +MIPS_msa_dotp_u_d, // Vector Unsigned Dot Product +MIPS_msa_dpadd_s_h, // Vector Signed Dot Product and Add +MIPS_msa_dpadd_s_w, // Vector Signed Dot Product and Add +MIPS_msa_dpadd_s_d, // Vector Signed Dot Product and Add +MIPS_msa_dpadd_u_h, // Vector Unsigned Dot Product and Add +MIPS_msa_dpadd_u_w, // Vector Unsigned Dot Product and Add +MIPS_msa_dpadd_u_d, // Vector Unsigned Dot Product and Add +MIPS_msa_dpsub_s_h, // Vector Signed Dot Product and Subtract +MIPS_msa_dpsub_s_w, // Vector Signed Dot Product and Subtract +MIPS_msa_dpsub_s_d, // Vector Signed Dot Product and Subtract +MIPS_msa_dpsub_u_h, // Vector Unsigned Dot Product and Subtract +MIPS_msa_dpsub_u_w, // Vector Unsigned Dot Product and Subtract +MIPS_msa_dpsub_u_d, // Vector Unsigned Dot Product and Subtract +MIPS_msa_sld_b, // GPR Columns Slide +MIPS_msa_sld_h, // GPR Columns Slide +MIPS_msa_sld_w, // GPR Columns Slide +MIPS_msa_sld_d, // GPR Columns Slide +MIPS_msa_sldi_b, // Immediate Columns Slide +MIPS_msa_sldi_h, // Immediate Columns Slide +MIPS_msa_sldi_w, // Immediate Columns Slide +MIPS_msa_sldi_d, // Immediate Columns Slide +MIPS_msa_splat_b, // GPR Element Splat +MIPS_msa_splat_h, // GPR Element Splat +MIPS_msa_splat_w, // GPR Element Splat +MIPS_msa_splat_d, // GPR Element Splat +MIPS_msa_splati_b, // Immediate Element Splat +MIPS_msa_splati_h, // Immediate Element Splat +MIPS_msa_splati_w, // Immediate Element Splat +MIPS_msa_splati_d, // Immediate Element Splat +MIPS_msa_pckev_b, // Vector Pack Even +MIPS_msa_pckev_h, // Vector Pack Even +MIPS_msa_pckev_w, // Vector Pack Even +MIPS_msa_pckev_d, // Vector Pack Even +MIPS_msa_pckod_b, // Vector Pack Odd +MIPS_msa_pckod_h, // Vector Pack Odd +MIPS_msa_pckod_w, // Vector Pack Odd +MIPS_msa_pckod_d, // Vector Pack Odd +MIPS_msa_ilvl_b, // Vector Interleave Left +MIPS_msa_ilvl_h, // Vector Interleave Left +MIPS_msa_ilvl_w, // Vector Interleave Left +MIPS_msa_ilvl_d, // Vector Interleave Left +MIPS_msa_ilvr_b, // Vector Interleave Right +MIPS_msa_ilvr_h, // Vector Interleave Right +MIPS_msa_ilvr_w, // Vector Interleave Right +MIPS_msa_ilvr_d, // Vector Interleave Right +MIPS_msa_ilvev_b, // Vector Interleave Even +MIPS_msa_ilvev_h, // Vector Interleave Even +MIPS_msa_ilvev_w, // Vector Interleave Even +MIPS_msa_ilvev_d, // Vector Interleave Even +MIPS_msa_ilvod_b, // Vector Interleave Odd +MIPS_msa_ilvod_h, // Vector Interleave Odd +MIPS_msa_ilvod_w, // Vector Interleave Odd +MIPS_msa_ilvod_d, // Vector Interleave Odd +MIPS_msa_vshf_b, // Vector Data Preserving Shuffle +MIPS_msa_vshf_h, // Vector Data Preserving Shuffle +MIPS_msa_vshf_w, // Vector Data Preserving Shuffle +MIPS_msa_vshf_d, // Vector Data Preserving Shuffle +MIPS_msa_srar_b, // Vector Shift Right Arithmetic Rounded +MIPS_msa_srar_h, // Vector Shift Right Arithmetic Rounded +MIPS_msa_srar_w, // Vector Shift Right Arithmetic Rounded +MIPS_msa_srar_d, // Vector Shift Right Arithmetic Rounded +MIPS_msa_srari_b, // Immediate Shift Right Arithmetic Rounded +MIPS_msa_srari_h, // Immediate Shift Right Arithmetic Rounded +MIPS_msa_srari_w, // Immediate Shift Right Arithmetic Rounded +MIPS_msa_srari_d, // Immediate Shift Right Arithmetic Rounded +MIPS_msa_srlr_b, // Vector Shift Right Logical Rounded +MIPS_msa_srlr_h, // Vector Shift Right Logical Rounded +MIPS_msa_srlr_w, // Vector Shift Right Logical Rounded +MIPS_msa_srlr_d, // Vector Shift Right Logical Rounded +MIPS_msa_srlri_b, // Immediate Shift Right Logical Rounded +MIPS_msa_srlri_h, // Immediate Shift Right Logical Rounded +MIPS_msa_srlri_w, // Immediate Shift Right Logical Rounded +MIPS_msa_srlri_d, // Immediate Shift Right Logical Rounded +MIPS_msa_hadd_s_h, // Vector Signed Horizontal Add +MIPS_msa_hadd_s_w, // Vector Signed Horizontal Add +MIPS_msa_hadd_s_d, // Vector Signed Horizontal Add +MIPS_msa_hadd_u_h, // Vector Unsigned Horizontal Add +MIPS_msa_hadd_u_w, // Vector Unsigned Horizontal Add +MIPS_msa_hadd_u_d, // Vector Unsigned Horizontal Add +MIPS_msa_hsub_s_h, // Vector Signed Horizontal Subtract +MIPS_msa_hsub_s_w, // Vector Signed Horizontal Subtract +MIPS_msa_hsub_s_d, // Vector Signed Horizontal Subtract +MIPS_msa_hsub_u_h, // Vector Unsigned Horizontal Subtract +MIPS_msa_hsub_u_w, // Vector Unsigned Horizontal Subtract +MIPS_msa_hsub_u_d, // Vector Unsigned Horizontal Subtract +MIPS_msa_and_v, // Vector Logical And +MIPS_msa_andi_b, // Immediate Logical And +MIPS_msa_or_v, // Vector Logical Or +MIPS_msa_ori_b, // Immediate Logical Or +MIPS_msa_nor_v, // Vector Logical Negated Or +MIPS_msa_nori_b, // Immediate Logical Negated Or +MIPS_msa_xor_v, // Vector Logical Exclusive Or +MIPS_msa_xori_b, // Immediate Logical Exclusive Or +MIPS_msa_bmnz_v, // Vector Bit Move If Not Zero +MIPS_msa_bmnzi_b, // Immediate Bit Move If Not Zero +MIPS_msa_bmz_v, // Vector Bit Move If Zero +MIPS_msa_bmzi_b, // Immediate Bit Move If Zero +MIPS_msa_bsel_v, // Vector Bit Select +MIPS_msa_bseli_b, // Immediate Bit Select +MIPS_msa_shf_b, // Immediate Set Shuffle Elements +MIPS_msa_shf_h, // Immediate Set Shuffle Elements +MIPS_msa_shf_w, // Immediate Set Shuffle Elements +MIPS_msa_bnz_v, // Immediate Branch If Not Zero (At Least One Element of Any Format Is Not Zero) +MIPS_msa_bz_v, // Immediate Branch If Zero (All Elements of Any Format Are Zero) +MIPS_msa_fill_b, // Vector Fill from GPR +MIPS_msa_fill_h, // Vector Fill from GPR +MIPS_msa_fill_w, // Vector Fill from GPR +MIPS_msa_fill_d, // Vector Fill from GPR +MIPS_msa_pcnt_b, // Vector Population Count +MIPS_msa_pcnt_h, // Vector Population Count +MIPS_msa_pcnt_w, // Vector Population Count +MIPS_msa_pcnt_d, // Vector Population Count +MIPS_msa_nloc_b, // Vector Leading Ones Count +MIPS_msa_nloc_h, // Vector Leading Ones Count +MIPS_msa_nloc_w, // Vector Leading Ones Count +MIPS_msa_nloc_d, // Vector Leading Ones Count +MIPS_msa_nlzc_b, // Vector Leading Zeros Count +MIPS_msa_nlzc_h, // Vector Leading Zeros Count +MIPS_msa_nlzc_w, // Vector Leading Zeros Count +MIPS_msa_nlzc_d, // Vector Leading Zeros Count +MIPS_msa_copy_s_b, // Element Copy to GPR Signed +MIPS_msa_copy_s_h, // Element Copy to GPR Signed +MIPS_msa_copy_s_w, // Element Copy to GPR Signed +MIPS_msa_copy_s_d, // Element Copy to GPR Signed +MIPS_msa_copy_u_b, // Element Copy to GPR Unsigned +MIPS_msa_copy_u_h, // Element Copy to GPR Unsigned +MIPS_msa_copy_u_w, // Element Copy to GPR Unsigned +MIPS_msa_copy_u_d, // Element Copy to GPR Unsigned +MIPS_msa_insert_b, // GPR Insert Element +MIPS_msa_insert_h, // GPR Insert Element +MIPS_msa_insert_w, // GPR Insert Element +MIPS_msa_insert_d, // GPR Insert Element +MIPS_msa_insve_b, // Element Insert Element +MIPS_msa_insve_h, // Element Insert Element +MIPS_msa_insve_w, // Element Insert Element +MIPS_msa_insve_d, // Element Insert Element +MIPS_msa_bnz_b, // Immediate Branch If All Elements Are Not Zero +MIPS_msa_bnz_h, // Immediate Branch If All Elements Are Not Zero +MIPS_msa_bnz_w, // Immediate Branch If All Elements Are Not Zero +MIPS_msa_bnz_d, // Immediate Branch If All Elements Are Not Zero +MIPS_msa_bz_b, // Immediate Branch If At Least One Element Is Zero +MIPS_msa_bz_h, // Immediate Branch If At Least One Element Is Zero +MIPS_msa_bz_w, // Immediate Branch If At Least One Element Is Zero +MIPS_msa_bz_d, // Immediate Branch If At Least One Element Is Zero +MIPS_msa_ldi_b, // Immediate Load +MIPS_msa_ldi_h, // Immediate Load +MIPS_msa_ldi_w, // Immediate Load +MIPS_msa_ldi_d, // Immediate Load +MIPS_msa_fcaf_w, // Vector Floating-Point Quiet Compare Always False +MIPS_msa_fcaf_d, // Vector Floating-Point Quiet Compare Always False +MIPS_msa_fcun_w, // Vector Floating-Point Quiet Compare Unordered +MIPS_msa_fcun_d, // Vector Floating-Point Quiet Compare Unordered +MIPS_msa_fceq_w, // Vector Floating-Point Quiet Compare Equal +MIPS_msa_fceq_d, // Vector Floating-Point Quiet Compare Equal +MIPS_msa_fcueq_w, // Vector Floating-Point Quiet Compare Unordered or Equal +MIPS_msa_fcueq_d, // Vector Floating-Point Quiet Compare Unordered or Equal +MIPS_msa_fclt_w, // Vector Floating-Point Quiet Compare Less Than +MIPS_msa_fclt_d, // Vector Floating-Point Quiet Compare Less Than +MIPS_msa_fcult_w, // Vector Floating-Point Quiet Compare Unordered or Less Than +MIPS_msa_fcult_d, // Vector Floating-Point Quiet Compare Unordered or Less Than +MIPS_msa_fcle_w, // Vector Floating-Point Quiet Compare Less or Equal +MIPS_msa_fcle_d, // Vector Floating-Point Quiet Compare Less or Equal +MIPS_msa_fcule_w, // Vector Floating-Point Quiet Compare Unordered or Less or Equal +MIPS_msa_fcule_d, // Vector Floating-Point Quiet Compare Unordered or Less or Equal +MIPS_msa_fsaf_w, // Vector Floating-Point Signaling Compare Always False +MIPS_msa_fsaf_d, // Vector Floating-Point Signaling Compare Always False +MIPS_msa_fsun_w, // Vector Floating-Point Signaling Compare Unordered +MIPS_msa_fsun_d, // Vector Floating-Point Signaling Compare Unordered +MIPS_msa_fseq_w, // Vector Floating-Point Signaling Compare Equal +MIPS_msa_fseq_d, // Vector Floating-Point Signaling Compare Equal +MIPS_msa_fsueq_w, // Vector Floating-Point Signaling Compare Unordered or Equal +MIPS_msa_fsueq_d, // Vector Floating-Point Signaling Compare Unordered or Equal +MIPS_msa_fslt_w, // Vector Floating-Point Signaling Compare Less Than +MIPS_msa_fslt_d, // Vector Floating-Point Signaling Compare Less Than +MIPS_msa_fsult_w, // Vector Floating-Point Signaling Compare Unordered or Less Than +MIPS_msa_fsult_d, // Vector Floating-Point Signaling Compare Unordered or Less Than +MIPS_msa_fsle_w, // Vector Floating-Point Signaling Compare Less or Equal +MIPS_msa_fsle_d, // Vector Floating-Point Signaling Compare Less or Equal +MIPS_msa_fsule_w, // Vector Floating-Point Signaling Compare Unordered or Less or Equal +MIPS_msa_fsule_d, // Vector Floating-Point Signaling Compare Unordered or Less or Equal +MIPS_msa_fadd_w, // Vector Floating-Point Addition +MIPS_msa_fadd_d, // Vector Floating-Point Addition +MIPS_msa_fsub_w, // Vector Floating-Point Subtraction +MIPS_msa_fsub_d, // Vector Floating-Point Subtraction +MIPS_msa_fmul_w, // Vector Floating-Point Multiplication +MIPS_msa_fmul_d, // Vector Floating-Point Multiplication +MIPS_msa_fdiv_w, // Vector Floating-Point Division +MIPS_msa_fdiv_d, // Vector Floating-Point Division +MIPS_msa_fmadd_w, // Vector Floating-Point Multiply-Add +MIPS_msa_fmadd_d, // Vector Floating-Point Multiply-Add +MIPS_msa_fmsub_w, // Vector Floating-Point Multiply-Sub +MIPS_msa_fmsub_d, // Vector Floating-Point Multiply-Sub +MIPS_msa_fexp2_w, // Vector Floating-Point Base 2 Exponentiation +MIPS_msa_fexp2_d, // Vector Floating-Point Base 2 Exponentiation +MIPS_msa_fexdo_h, // Vector Floating-Point Down-Convert Interchange Format +MIPS_msa_fexdo_w, // Vector Floating-Point Down-Convert Interchange Format +MIPS_msa_ftq_h, // Vector Floating-Point Convert to Fixed-Point +MIPS_msa_ftq_w, // Vector Floating-Point Convert to Fixed-Point +MIPS_msa_fmin_w, // Vector Floating-Point Minimum +MIPS_msa_fmin_d, // Vector Floating-Point Minimum +MIPS_msa_fmin_a_w, // Vector Floating-Point Minimum Based on Absolute Values +MIPS_msa_fmin_a_d, // Vector Floating-Point Minimum Based on Absolute Values +MIPS_msa_fmax_w, // Vector Floating-Point Maximum +MIPS_msa_fmax_d, // Vector Floating-Point Maximum +MIPS_msa_fmax_a_w, // Vector Floating-Point Maximum Based on Absolute Values +MIPS_msa_fmax_a_d, // Vector Floating-Point Maximum Based on Absolute Values +MIPS_msa_fcor_w, // Vector Floating-Point Quiet Compare Ordered +MIPS_msa_fcor_d, // Vector Floating-Point Quiet Compare Ordered +MIPS_msa_fcune_w, // Vector Floating-Point Quiet Compare Unordered or Not Equal +MIPS_msa_fcune_d, // Vector Floating-Point Quiet Compare Unordered or Not Equal +MIPS_msa_fcne_w, // Vector Floating-Point Quiet Compare Not Equal +MIPS_msa_fcne_d, // Vector Floating-Point Quiet Compare Not Equal +MIPS_msa_mul_q_h, // Vector Fixed-Point Multiply +MIPS_msa_mul_q_w, // Vector Fixed-Point Multiply +MIPS_msa_madd_q_h, // Vector Fixed-Point Multiply and Add +MIPS_msa_madd_q_w, // Vector Fixed-Point Multiply and Add +MIPS_msa_msub_q_h, // Vector Fixed-Point Multiply and Subtract +MIPS_msa_msub_q_w, // Vector Fixed-Point Multiply and Subtract +MIPS_msa_fsor_w, // Vector Floating-Point Signaling Compare Ordered +MIPS_msa_fsor_d, // Vector Floating-Point Signaling Compare Ordered +MIPS_msa_fsune_w, // Vector Floating-Point Signaling Compare Unordered or Not Equal +MIPS_msa_fsune_d, // Vector Floating-Point Signaling Compare Unordered or Not Equal +MIPS_msa_fsne_w, // Vector Floating-Point Signaling Compare Not Equal +MIPS_msa_fsne_d, // Vector Floating-Point Signaling Compare Not Equal +MIPS_msa_mulr_q_h, // Vector Fixed-Point Multiply Rounded +MIPS_msa_mulr_q_w, // Vector Fixed-Point Multiply Rounded +MIPS_msa_maddr_q_h, // Vector Fixed-Point Multiply and Add Rounded +MIPS_msa_maddr_q_w, // Vector Fixed-Point Multiply and Add Rounded +MIPS_msa_msubr_q_h, // Vector Fixed-Point Multiply and Subtract Rounded +MIPS_msa_msubr_q_w, // Vector Fixed-Point Multiply and Subtract Rounded +MIPS_msa_fclass_w, // Vector Floating-Point Class Mask +MIPS_msa_fclass_d, // Vector Floating-Point Class Mask +MIPS_msa_ftrunc_s_w, // Vector Floating-Point Truncate and Convert to Signed Integer +MIPS_msa_ftrunc_s_d, // Vector Floating-Point Truncate and Convert to Signed Integer +MIPS_msa_ftrunc_u_w, // Vector Floating-Point Truncate and Convert to Unsigned Integer +MIPS_msa_ftrunc_u_d, // Vector Floating-Point Truncate and Convert to Unsigned Integer +MIPS_msa_fsqrt_w, // Vector Floating-Point Square Root +MIPS_msa_fsqrt_d, // Vector Floating-Point Square Root +MIPS_msa_frsqrt_w, // Vector Approximate Floating-Point Reciprocal of Square Root +MIPS_msa_frsqrt_d, // Vector Approximate Floating-Point Reciprocal of Square Root +MIPS_msa_frcp_w, // Vector Approximate Floating-Point Reciprocal +MIPS_msa_frcp_d, // Vector Approximate Floating-Point Reciprocal +MIPS_msa_frint_w, // Vector Floating-Point Round to Integer +MIPS_msa_frint_d, // Vector Floating-Point Round to Integer +MIPS_msa_flog2_w, // Vector Floating-Point Base 2 Logarithm +MIPS_msa_flog2_d, // Vector Floating-Point Base 2 Logarithm +MIPS_msa_fexupl_w, // Vector Floating-Point Up-Convert Interchange Format Left +MIPS_msa_fexupl_d, // Vector Floating-Point Up-Convert Interchange Format Left +MIPS_msa_fexupr_w, // Vector Floating-Point Up-Convert Interchange Format Right +MIPS_msa_fexupr_d, // Vector Floating-Point Up-Convert Interchange Format Right +MIPS_msa_ffql_w, // Vector Floating-Point Convert from Fixed-Point Left +MIPS_msa_ffql_d, // Vector Floating-Point Convert from Fixed-Point Left +MIPS_msa_ffqr_w, // Vector Floating-Point Convert from Fixed-Point Right +MIPS_msa_ffqr_d, // Vector Floating-Point Convert from Fixed-Point Right +MIPS_msa_ftint_s_w, // Vector Floating-Point Convert to Signed Integer +MIPS_msa_ftint_s_d, // Vector Floating-Point Convert to Signed Integer +MIPS_msa_ftint_u_w, // Vector Floating-Point Round and Convert to Unsigned Integer +MIPS_msa_ftint_u_d, // Vector Floating-Point Round and Convert to Unsigned Integer +MIPS_msa_ffint_s_w, // Vector Floating-Point Round and Convert from Signed Integer +MIPS_msa_ffint_s_d, // Vector Floating-Point Round and Convert from Signed Integer +MIPS_msa_ffint_u_w, // Vector Floating-Point Convert from Unsigned Integer +MIPS_msa_ffint_u_d, // Vector Floating-Point Convert from Unsigned Integer +MIPS_msa_ctcmsa, // GPR Copy to MSA Control Register +MIPS_msa_cfcmsa, // GPR Copy from MSA Control Register +MIPS_msa_move_v, // Vector Move + +// MIPS R6 (LSA is also part of MSA) +MIPS_lsa, // Left Shift Add +MIPS_dlsa, // Doubleword Left Shift Add + +// MIPS R3 EVA +MIPS_lbe, // Load Byte EVA +MIPS_lbue, // Load Byte Unsigned EVA +MIPS_lhe, // Load Halfword EVA +MIPS_lhue, // Load Halfword Unsigned EVA +MIPS_lwe, // Load Word EVA +MIPS_sbe, // Store Byte EVA +MIPS_she, // Store Halfword EVA +MIPS_swe, // Store Word EVA +MIPS_lle, // Load Linked Word EVA +MIPS_sce, // Store Conditional Word EVA +MIPS_cachee, // Perform Cache Operation EVA +MIPS_prefe, // Prefetch EVA +MIPS_lwle, // Load Word Left EVA +MIPS_lwre, // Load Word Right EVA +MIPS_swle, // Store Word Left EVA +MIPS_swre, // Store Word Right EVA + +// new MIPS16e2 instructions +MIPS_movtz, // Move Conditional on T Equal to Zero +MIPS_movtn, // Move Zero Conditional on T Not Equal to Zero + +// interAptiv core custom instructions +MIPS_copyw, // Copy Data to Aligned Memory Location +MIPS_ucopyw, // Copy Data to Unaligned Memory Location + +// MIPSr6 instructions for NanoMIPS +MIPS_muh, // Multiply High +MIPS_mulu, // Multiply Unsigned +MIPS_muhu, // Multiply High Unsigned +MIPS_mod, // Modulo +MIPS_modu, // Modulo Unsigned +MIPS_sov, // Set on Overflow +MIPS_crc32b, // CRC32 Byte +MIPS_crc32h, // CRC32 Half +MIPS_crc32w, // CRC32 Word +MIPS_crc32cb, // CRC32 Castagnoli Byte +MIPS_crc32ch, // CRC32 Castagnoli Half +MIPS_crc32cw, // CRC32 Castagnoli Word +MIPS_sbx, // Store Byte indeXed +MIPS_shx, // Store Half indeXed +MIPS_shxs, // Store Half indeXed Scaled +MIPS_swx, // Store Word indeXed +MIPS_swxs, // Store Word indeXed Scaled +MIPS_lhxs, // Load Half indeXed Scaled +MIPS_lhuxs, // Load Half Unsigned indeXed Scaled +MIPS_mfhc0, // Move From High Coprocessor 0 +MIPS_mthc0, // Move To High Coprocessor 0 +MIPS_extw, // Extend Word +MIPS_tlbinv, // TLB Invalidate +MIPS_tlbinvf, // TLB Invalidate Flush +MIPS_ginvt, // Globally Invalidate TLBs +MIPS_ginvi, // Globally Invalidate Instruction caches +MIPS_eretnc, // Exception Return Not Clearing LLBit +MIPS_lwpc, // Load Word PC relative +MIPS_swpc, // Store Word PC relative +MIPS_rotx, // Rotate and eXchange +MIPS_llwp, // Load Linked Word Pair +MIPS_llwpe, // Load Linked Word Pair using EVA addressing +MIPS_scwp, // Load Linked Word Pair +MIPS_scwpe, // Load Linked Word Pair using EVA addressing +MIPS_ualwm, // Unaligned Load Word Multiple +MIPS_uaswm, // Unaligned Store Word Multiple +MIPS_ualh, // Unaligned Load Half +MIPS_uash, // Unaligned Store Half +MIPS_syncie, // SYNChronize Instruction cache using EVA addressing +MIPS_dvp, // Disable Virtual Processor +MIPS_evp, // Enable Virtual Processor +MIPS_sigrie, // Signal Reserved Instruction Exception +MIPS_bc, // Branch Always, Compact +MIPS_balc, // Branch Always and Link, Compact +MIPS_restore_jrc, // Restore callee saved registers and Jump to Return address, Compact +MIPS_move_balc, // Move and Branch and Link, Compact +MIPS_addiupc, // Add Immediate (Untrapped) to PC +MIPS_jalrc_hb, // Jump And Link Register, Compact, with Hazard Barrier +MIPS_aluipc, // Add aLigned Upper Immediate to PC +MIPS_beqic, // Branch if Equal to Immediate, Compact +MIPS_bbeqzc, // Branch if Bit Equals Zero, Compact +MIPS_bgeic, // Branch if Greater than or Equal to Immediate, Compact +MIPS_bgeiuc, // Branch if Greater than or Equal to Immediate Unsigned, Compact +MIPS_bneic, // Branch if Not Equal to Immediate, Compact +MIPS_bbnezc, // Branch if Bit Not Equal to Zero, Compact +MIPS_bltic, // Branch if Less Than Immediate, Compact +MIPS_bltiuc, // Branch if Less Than Immediate Unsigned Compact +MIPS_bnec, // Branch Not Equal, Compact +MIPS_bltc, // Branch if Less Than, Compact +MIPS_bltuc, // Branch if Less Than Unsigned, Compact +MIPS_beqc, // Branch if Equal, Compact +MIPS_bgec, // Branch if Greater than or Equal, Compact +MIPS_bgeuc, // Branch if Greater than or Equal to Unsigned, Compact +MIPS_brsc, // Branch Register Scaled, Compact +MIPS_balrsc, // Branch And Link Register Scaled, Compact +MIPS_bitrevw, // Reverse all bits +MIPS_bitrevh, // Reverse bits in halfs +MIPS_bitrevb, // Reverse bits in bytes +MIPS_byterevw, // Reverse all bytes +MIPS_byterevh, // Reverse bytes in halfs +MIPS_lwm2, // Load Word Multiple (w/o reglist) +MIPS_swm2, // Store Word Multiple (w/o reglist) + +MIPS_last, + +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + +H8_null = 0, // Unknown Operation + +H8_add, // Add binary +H8_adds, // Add with sign extension +H8_addx, // Add with extend carry +H8_and, // Logical AND +H8_andc, // Logical AND with control register +H8_band, // Bit AND +H8_bra, // Branch always +H8_brn, // Branch never +H8_bhi, // Branch if higher +H8_bls, // Branch if lower or same +H8_bcc, // Branch if carry clear (higher or same) +H8_bcs, // Branch if carry set (lower) +H8_bne, // Branch if not equal +H8_beq, // Branch if equal +H8_bvc, // Branch if overflow clear +H8_bvs, // Branch if overflow set +H8_bpl, // Branch if plus +H8_bmi, // Branch if minus +H8_bge, // Branch if greates or equal +H8_blt, // Branch if less +H8_bgt, // Branch if greater +H8_ble, // Branch if less or equal +H8_bclr, // Bit clear +H8_biand, // Bit invert AND +H8_bild, // Bit invert load +H8_bior, // Bit invert OR +H8_bist, // Bit invert store +H8_bixor, // Bit invert XOR +H8_bld, // Bit load +H8_bnot, // Bit NOT +H8_bor, // Bit OR +H8_bset, // Bit set +H8_bsr, // Branch to subroutine +H8_bst, // Bit store +H8_btst, // Bit test +H8_bxor, // Bit XOR +H8_clrmac, // Clear MAC register +H8_cmp, // Compare +H8_daa, // Decimal adjust add +H8_das, // Decimal adjust subtract +H8_dec, // Decrement +H8_divxs, // Divide extended as signed +H8_divxu, // Divide extended as unsigned +H8_eepmov, // Move data to EEPROM +H8_exts, // Extend as signed +H8_extu, // Extend as unsigned +H8_inc, // Increment +H8_jmp, // Jump +H8_jsr, // Jump to subroutine +H8_ldc, // Load to control register +H8_ldm, // Load to multiple registers +H8_ldmac, // Load to MAC register +H8_mac, // Multiply and accumulate +H8_mov, // Move data +H8_movfpe, // Move from peripheral with E clock +H8_movtpe, // Move to peripheral with E clock +H8_mulxs, // Multiply extend as signed +H8_mulxu, // Multiply extend as unsigned +H8_neg, // Negate +H8_nop, // No operation +H8_not, // Logical complement +H8_or, // Logical OR +H8_orc, // Logical OR with control register +H8_pop, // Pop data from stack +H8_push, // Push data on stack +H8_rotl, // Rotate left +H8_rotr, // Rotate right +H8_rotxl, // Rotate with extend carry left +H8_rotxr, // Rotate with extend carry right +H8_rte, // Return from exception +H8_rts, // Return from subroutine +H8_shal, // Shift arithmetic left +H8_shar, // Shift arithmetic right +H8_shll, // Shift logical left +H8_shlr, // Shift logical right +H8_sleep, // Power down mode +H8_stc, // Store from control register +H8_stm, // Store from multiple registers +H8_stmac, // Store from MAC register +H8_sub, // Subtract binary +H8_subs, // Subtract with sign extension +H8_subx, // Subtract with extend carry +H8_tas, // Test and set +H8_trapa, // Trap always +H8_xor, // Logical XOR +H8_xorc, // Logical XOR with control register + +// H8SX +H8_rtel, // Returns from an exception, restoring data to multiple general registers +H8_rtsl, // Returns from a subroutine, restoring data to multiple general registers +H8_movmd, // Transfers a data block +H8_movsd, // Transfers a data block with zero detection +H8_bras, // Branch always after the next instruction (delay slot) +H8_movab, // MOVe effective Address/B +H8_movaw, // MOVe effective Address/W +H8_moval, // MOVe effective Address/L +H8_bsetne, // Bit SET if Not Equal +H8_bseteq, // Bit SET if EQual +H8_bclrne, // Bit CLeaR if Not Equal +H8_bclreq, // Bit CLeaR if Equal +H8_bstz, // Bit STore Zero flag +H8_bistz, // Bit Invert STore Zero flag +H8_bfld, // Bit Field LoaD +H8_bfst, // Bit Field STore +H8_muls, // MULtiply as Signed +H8_divs, // DIVide as Signed +H8_mulu, // MULtiply as Unsigned +H8_divu, // DIVide as Unsigned +H8_mulsu, // MULtiply as Signed +H8_muluu, // MULtiply as Unsigned +H8_brabc, // BRAnch if Bit Cleared +H8_brabs, // BRAnch if Bit Set +H8_bsrbc, // Branch to SubRoutine if Bit Cleared +H8_bsrbs, // Branch to SubRoutine if Bit Set + +H8_last, + +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + +PIC_null = 0, // Unknown Operation + +// BYTE-ORIENTED FILE REGISTER OPERATIONS + +PIC_addwf, // Add W and f +PIC_andwf, // AND W with f +PIC_clrf, // Clear f +PIC_clrw, // Clear W +PIC_comf, // Complement f +PIC_decf, // Decrement f +PIC_decfsz, // Decrement f, Skip if 0 +PIC_incf, // Increment f +PIC_incfsz, // Increment f, Skip if 0 +PIC_iorwf, // Inclusive OR W with f +PIC_movf, // Move f +PIC_movwf, // Move W to f +PIC_nop, // No Operation +PIC_rlf, // Rotate Left f through Carry +PIC_rrf, // Rotate Right f through Carry +PIC_subwf, // Subtract W from f +PIC_swapf, // Swap nibbles in f +PIC_xorwf, // Exclusive OR W with f + +// BIT-ORIENTED FILE REGISTER OPERATIONS + +PIC_bcf, // Bit Clear f +PIC_bsf, // Bit Set f +PIC_btfsc, // Bit Test f, Skip if Clear +PIC_btfss, // Bit Test f, Skip if Set + + +// LITERAL AND CONTROL OPERATIONS + +PIC_addlw, // Add literal and W +PIC_andlw, // AND literal with W +PIC_call, // Call subroutine +PIC_clrwdt, // Clear Watchdog Timer +PIC_goto, // Go to address +PIC_iorlw, // Inclusive OR literal with W +PIC_movlw, // Move literal to W +PIC_retfie, // Return from interrupt +PIC_retlw, // Return with literal in W +PIC_return, // Return from Subroutine +PIC_sleep, // Go into standby mode +PIC_sublw, // Subtract W from literal +PIC_xorlw, // Exclusive OR literal with W + +// ADDITIONAL INSTRUCTIONS TO MAINTAIN COMPITIBILITY WITH 12C5xx,16C5x + +PIC_option, // Load OPTION register +PIC_tris, // Load TRIS Register + +// MACROS + +PIC_movfw, // Move Contents of File Reg to W +PIC_tstf, // Test Contents of File Register +PIC_negf, // Negate File Register Contents +PIC_b, // Branch to Address +PIC_clrc, // Clear Carry +PIC_clrdc, // Clear Digit Carry +PIC_clrz, // Clear Zero +PIC_setc, // Set Carry +PIC_setdc, // Set Digit Carry +PIC_setz, // Set Zero +PIC_skpc, // Skip on Carry +PIC_skpdc, // Skip on Digit Carry +PIC_skpnc, // Skip on No Carry +PIC_skpndc, // Skip on No Digit Carry +PIC_skpnz, // Skip on No Zero +PIC_skpz, // Skip on Zero +PIC_bc, // Branch on Carry to Address k +PIC_bdc, // Branch on Digit Carry to k +PIC_bnc, // Branch on No Carry to k +PIC_bndc, // Branch on No Digit Carry to k +PIC_bnz, // Branch on No Zero to Address +PIC_bz, // Branch on Zero to Address k +PIC_addcf, // Add Carry to File Register +PIC_adddcf, // Add Digit to File Register +PIC_subcf, // Subtract Carry from File Reg + +// ADDITIONAL INSTRUCTIONS FOR 18Cxx + +// BYTE-ORIENTED FILE REGISTER OPERATIONS + +PIC_addwf3, // Add W and f +PIC_addwfc3, // Add W and Carry to f +PIC_andwf3, // AND W with f +PIC_clrf2, // Clear f +PIC_comf3, // Complement f +PIC_cpfseq2, // Compare f with W, Skip if == +PIC_cpfsgt2, // Compare f with W, Skip if > +PIC_cpfslt2, // Compare f with W, Skip if < +PIC_decf3, // Decrement f +PIC_decfsz3, // Decrement f, Skip if 0 +PIC_dcfsnz3, // Decrement f, Skip if not 0 +PIC_incf3, // Increment f +PIC_incfsz3, // Increment f, Skip if 0 +PIC_infsnz3, // Increment f, Skip if not 0 +PIC_iorwf3, // Inclusive OR W with f +PIC_movf3, // Move f +PIC_movff2, // Move fs to fd +PIC_movwf2, // Move W to f +PIC_mulwf2, // Multiply W with f +PIC_negf2, // Negate f +PIC_rlcf3, // Rotate Left f through Carry +PIC_rlncf3, // Rotate Left f +PIC_rrcf3, // Rotate Right f through Carry +PIC_rrncf3, // Rotate Right f +PIC_setf2, // Set f +PIC_subfwb3, // Substract f from W with borrow +PIC_subwf3, // Substract W from f +PIC_subwfb3, // Substract W from f with borrow +PIC_swapf3, // Swap nibbles in f +PIC_tstfsz2, // Test f, Skip if 0 +PIC_xorwf3, // Exclusive OR W with f + +// BIT-ORIENTED FILE REGISTER OPERATIONS + +PIC_bcf3, // Bit Clear f +PIC_bsf3, // Bit Set f +PIC_btfsc3, // Bit Test f, Skip if Clear +PIC_btfss3, // Bit Test f, Skip if Set +PIC_btg3, // Bit Toggle f + +// CONTROL OPERATIONS + +PIC_bc1, // Branch if Carry +PIC_bn1, // Branch if Negative +PIC_bnc1, // Branch if not Carry +PIC_bnn1, // Branch if not Negative +PIC_bnov1, // Branch if not Overflow +PIC_bnz1, // Branch if not Zero +PIC_bov1, // Branch if Overflow +PIC_bra1, // Branch unconditionally +PIC_bz1, // Branch if Zero +PIC_call2, // Call subroutine +// PIC_clrwdt +PIC_daw0, // Decimal Adjust W +// PIC_goto +// PIC_nop +// PIC_nop +PIC_pop0, // Pop top of return stack +PIC_push0, // Push top of return stack +PIC_rcall1, // Relative Call subroutine +PIC_reset0, // Software device Reset +PIC_retfie1, // Return from interrupt enable +// PIC_retlw +PIC_return1, // Return from Subroutine +// PIC_sleep + +// LITERAL OPERATIONS + +// PIC_addlw +// PIC_andlw +// PIC_iorlw +PIC_lfsr2, // Move literal to FSR +PIC_movlb1, // Move literal to BSR +// PIC_movlw +PIC_mullw1, // Multiply literal with W +// PIC_retlw +// PIC_sublw +// PIC_xorlw + +// DATA MEMORY <-> PROGRAM MEMORY OPERATIONS + +PIC_tblrd0, // Table Read +PIC_tblrd0p, // Table Read with post-increment +PIC_tblrd0m, // Table Read with post-decrement +PIC_tblrdp0, // Table Read with pre-increment +PIC_tblwt0, // Table Write +PIC_tblwt0p, // Table Write with post-increment +PIC_tblwt0m, // Table Write with post-decrement +PIC_tblwtp0, // Table Write with pre-increment + +// ADDITIONAL INSTRUCTIONS FOR 16F1x and 12F1x + +PIC_addwfc, // Add W and Carry to f +PIC_movlp, // Move literal to PCLATH +PIC_movlb, // Move literal to BSR +PIC_addfsr, // Add Literal to FSRn +PIC_asrf, // Arithmetic Right Shift +PIC_lslf, // Logical Left Shift +PIC_lsrf, // Logical Right Shift +PIC_subwfb, // Subtract with Borrow W from f +PIC_bra, // Relative Branch +PIC_brw, // Relative Branch with W +PIC_callw, // Call Subroutine with W +PIC_reset, // Software device Reset +PIC_moviw, // Move INDFn to W +PIC_movwi, // Move W to INDFn + +PIC_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + +PIC16_null = 0, // Unknown Operation + +// Move Instructions +PIC16_EXCH, // Exchange Wns and Wnd +PIC16_MOV, // Move +PIC16_SWAP, // Byte or Nibble Swap Wn +PIC16_TBLRDH, // Table Read High +PIC16_TBLRDL, // Table Read Low +PIC16_TBLWTH, // Table Write High +PIC16_TBLWTL, // Table Write Low +// Only for PIC24E and dsPIC33E +PIC16_MOVPAG, // Move to Page Register + +// Math Instructions +PIC16_ADD, // Add +PIC16_ADDC, // Add with carry +PIC16_DAWB, // Decimal Adjust Wn +PIC16_DEC, // Decrement +PIC16_DEC2, // Decrement by 2 +PIC16_DIV, // Signed/Unsigned Integer Divide +PIC16_INC, // Increment +PIC16_INC2, // Increment by 2 +PIC16_MUL, // Integer Multiply +PIC16_SE, // Sign-Extend Ws +PIC16_SUB, // Subtract +PIC16_SUBB, // Subtract with Borrow +PIC16_SUBBR, // Subtract with Borrow +PIC16_SUBR, // Subtract +PIC16_ZE, // Zero - Extend Ws +// Only for PIC24E and dsPIC33E +PIC16_MULW, // Integer Multiply with 16-bit Result +// Only for dsPIC +PIC16_DIVF, // Fractional Divide + +// Logic Instructions +PIC16_AND, // AND +PIC16_CLR, // Clear f or WREG | Clear Wd +PIC16_COM, // Complement f | Complement Ws +PIC16_IOR, // Inclusive OR +PIC16_NEG, // Negate f | Negate Ws +PIC16_SETM, // Set f or WREG | Set Ws +PIC16_XOR, // Exclusive OR + +// Rotate/Shift Instructions +PIC16_ASR, // Arithmetic Shift Right +PIC16_LSR, // Logical Shift Right +PIC16_RLC, // Rotate Left through Carry +PIC16_RLNC, // Rotate Left without Carry +PIC16_RRC, // Rotate Right through Carry +PIC16_RRNC, // Rotate Right without Carry +PIC16_SL, // Shift Left + +// Bit Instructions +PIC16_BCLR, // Bit Clear f | Bit Clear in Ws +PIC16_BSET, // Bit Set f | Bit Set in Ws +PIC16_BSW, // Bit Write in Ws +PIC16_BTG, // Bit Toggle f | Bit Toggle in Ws +PIC16_BTST, // Bit Test f | Bit Test in Ws +PIC16_BTSTS, // Bit Test/Set f | Bit Test/Set in Ws +PIC16_FBCL, // Find First Bit Change from Left +PIC16_FF1L, // Find First One from Left +PIC16_FF1R, // Find First One from Right + + +// Compare/Skip and Compare/Branch Instructions +PIC16_BTSC, // Bit Test, Skip if Clear +PIC16_BTSS, // Bit Test, Skip if Set +PIC16_CP, // Compare, Set Status Flags +PIC16_CP0, // Compare, Set Status Flags +PIC16_CPB, // Compare using Borrow, set Status Flags +PIC16_CPSEQ, // Compare Wb with Wn, Skip if Equal (Wb = Wn) +PIC16_CPSGT, // Signed Compare Wb with Wn, Skip if Greater Than (Wb > Wn) +PIC16_CPSLT, // Signed Compare Wb with Wn, Skip if Less Than (Wb < Wn) +PIC16_CPSNE, // Signed Compare Wb with Wn, Skip if Not Equal (Wb != Wn) +// Only for PIC24E and dsPIC33E +PIC16_CPBEQ, // Compare Wb with Wn, branch if = +PIC16_CPBNE, // Compare Wb with Wn, Branch if Not Equal (Wb != Wn) +PIC16_CPBGT, // Signed Compare Wb with Wn, Branch if Greater Than (Wb > Wn) +PIC16_CPBLT, // Signed Compare Wb with Wn, Branch if Less Than (Wb < Wn) + +// Program Flow Instructions +PIC16_BRA, // Branch unconditionally/conditionally +PIC16_CALL, // Call Subroutine | Call Indirect Subroutine +PIC16_GOTO, // Unconditional Jump +PIC16_RCALL, // Relative Call | Relative Call +PIC16_REPEAT, // Repeat next instruction +PIC16_RETFIE, // Return from Interrupt +PIC16_RETLW, // Return with Literal in Wn +PIC16_RETURN, // Return +// Only for dsPIC +PIC16_DO, // Initialize Hardware Loop + +// Shadow/Stack Instructions +PIC16_LNK, // Allocate Stack Frame +PIC16_POP, // Pop TOS | Pop Shadow Registers +PIC16_PUSH, // Push to TOS | Push Shadow Registers +PIC16_ULNK, // De-allocate Stack Frame + +// Control Instructions +PIC16_CLRWDT, // Clear Watchdog Timer +PIC16_DISI, // Disable Interrupts Temporarily +PIC16_NOP, // No operation +PIC16_NOPR, // No operation +PIC16_PWRSAV, // Enter Power Saving Mode +PIC16_RESET, // Reset + +// DSP Instructions [Only for dsPIC30F, dsPIC33F and dsPIC33E] +PIC16_LAC, // Load Accumulator +PIC16_SAC, // Store Accumulator +PIC16_SFTAC, // Arithmetic Shift Accumulator +PIC16_CLR1, // Clear Accumulator, Prefetch Operands +PIC16_ED, // Euclidean Distance (No Accumulate) +PIC16_EDAC, // Euclidean Distance +PIC16_MAC, // Multiply and Accumulate +PIC16_MOVSAC, // Prefetch Operands and Store Accumulator +PIC16_MPY, // Multiply to Accumulator +PIC16_MSC, // Multiply and Subtract from Accumulator + +// ICD instructions +PIC16_BREAK, // ICD Break +PIC16_URUN, // ICD Run +PIC16_SSTEP, // ICD Single Step +PIC16_FEX, // ICD Execute + +PIC16_last, + + }; +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +SPARC_null = 0, // Unknown Operation + +// SPARC V9 instructions + +SPARC_add, // Add +SPARC_addcc, // Add and modify condition codes +SPARC_addc, // Add with carry +SPARC_addccc, // Add with carry and modify condition codes +SPARC_and, // And +SPARC_andcc, // And and modify condition codes +SPARC_andn, // And not +SPARC_andncc, // And not (and modify condition codes) +SPARC_b, // Branch on integer condition codes +SPARC_bp, // Branch on integer condition codes with prediction +SPARC_bpr, // Branch on contents of integer register with prediction +SPARC_call, // Call and link +SPARC_casa, // Compare and swap word in alternate space +SPARC_casxa, // Compare and swap doubleword in alternate space +SPARC_done, // Return from trap +SPARC_fabs, // Floating-point absolute value +SPARC_fadd, // Floating-point add +SPARC_fbp, // Branch on floating-point condition codes with prediction +SPARC_fb, // Branch on floating-point condition codes +SPARC_fcmp, // Floating-point compare +SPARC_fcmpe, // Floating-point compare (exception if unordered) +SPARC_fdiv, // Floating-point divide +SPARC_fdmulq, // Floating-point multiply double to quad +SPARC_flush, // Flush instruction memory +SPARC_flushw, // Flush register windows +SPARC_fmov, // Floating-point move +SPARC_fmovcc, // Conditional floating-point move +SPARC_fmovr, // Move floating-point register if integer register contents satisfy condition +SPARC_fmul, // Floating-point multiply +SPARC_fneg, // Floating-point negate +SPARC_fsmuld, // Floating-point multiply single to double +SPARC_fsqrt, // Floating-point square root +SPARC_fsub, // Floating-point subtract +SPARC_fstox, // Convert single floating point to 64-bit integer +SPARC_fdtox, // Convert doublee floating point to 64-bit integer +SPARC_fqtox, // Convert quad floating point to 64-bit integer +SPARC_fxtos, // Convert 64-bit integer to single floating point +SPARC_fxtod, // Convert 64-bit integer to double floating point +SPARC_fxtoq, // Convert 64-bit integer to quad floating point +SPARC_fitos, // Convert 32-bit integer to single floating point +SPARC_fdtos, // Convert single floating point to single floating point +SPARC_fqtos, // Convert quade floating point to single floating point +SPARC_fitod, // Convert 32-bit integer to double floating point +SPARC_fstod, // Convert single floating point to double floating point +SPARC_fqtod, // Convert quade floating point to double floating point +SPARC_fitoq, // Convert 32-bit integer to quad floating point +SPARC_fstoq, // Convert single floating point to quad floating point +SPARC_fdtoq, // Convert quade floating point to quad floating point +SPARC_fstoi, // Convert single floating point to 32-bit integer +SPARC_fdtoi, // Convert doublee floating point to 32-bit integer +SPARC_fqtoi, // Convert quad floating point to 32-bit integer +SPARC_illtrap, // Illegal instruction +SPARC_impdep1, // Implementation-dependent instruction +SPARC_impdep2, // Implementation-dependent instruction +SPARC_jmpl, // Jump and link +SPARC_ldd, // Load doubleword +SPARC_ldda, // Load doubleword from alternate space +SPARC_lddf, // Load double floating-point +SPARC_lddfa, // Load double floating-point from alternate space +SPARC_ldf, // Load floating-point +SPARC_ldfa, // Load floating-point from alternate space +SPARC_ldfsr, // Load floating-point state register lower +SPARC_ldqf, // Load quad floating-point +SPARC_ldqfa, // Load quad floating-point from alternate space +SPARC_ldsb, // Load signed byte +SPARC_ldsba, // Load signed byte from alternate space +SPARC_ldsh, // Load signed halfword +SPARC_ldsha, // Load signed halfword from alternate space +SPARC_ldstub, // Load-store unsigned byte +SPARC_ldstuba, // Load-store unsigned byte in alternate space +SPARC_ldsw, // Load signed word +SPARC_ldswa, // Load signed word from alternate space +SPARC_ldub, // Load unsigned byte +SPARC_lduba, // Load unsigned byte from alternate space +SPARC_lduh, // Load unsigned halfword +SPARC_lduha, // Load unsigned halfword from alternate space +SPARC_lduw, // Load unsigned word +SPARC_lduwa, // Load unsigned word from alternate space +SPARC_ldx, // Load extended +SPARC_ldxa, // Load extended from alternate space +SPARC_ldxfsr, // Load extended floating-point state register +SPARC_membar, // Memory barrier +SPARC_mov, // Move integer register if condition is satisfied +SPARC_movr, // Move integer register on contents of integer register +SPARC_mulscc, // Multiply step and modify condition codes +SPARC_mulx, // Multiply 64-bit integers +SPARC_nop, // No operation +SPARC_or, // Inclusive-or +SPARC_orcc, // Inclusive-or (and modify condition codes) +SPARC_orn, // Inclusive-or not +SPARC_orncc, // Inclusive-or not (and modify condition codes) +SPARC_popc, // Population count +SPARC_prefetch, // Prefetch data +SPARC_prefetcha, // Prefetch data from alternate space +SPARC_rd, // Read state register +SPARC_rdpr, // Read privileged register +SPARC_restore, // Restore caller's window +SPARC_restored, // Window has been restored +SPARC_retry, // Return from trap and retry +SPARC_return, // Return +SPARC_save, // Save caller's window +SPARC_saved, // Window has been saved +SPARC_sdiv, // 32-bit signed integer divide +SPARC_sdivcc, // 32-bit signed integer divide and modify condition codes +SPARC_sdivx, // 64-bit signed integer divide +SPARC_sethi, // Set high 22 bits of low word of integer register +SPARC_sir, // Software-initiated reset +SPARC_sll, // Shift left logical +SPARC_sllx, // Shift left logical, extended +SPARC_smul, // Signed integer multiply +SPARC_smulcc, // Signed integer multiply (and modify condition codes) +SPARC_sra, // Shift right arithmetic +SPARC_srax, // Shift right arithmetic, extended +SPARC_srl, // Shift right logical +SPARC_srlx, // Shift right logical, extended +SPARC_stb, // Store byte +SPARC_stba, // Store byte into alternate space +SPARC_stbar, // Store barrier +SPARC_std, // Store doubleword +SPARC_stda, // Store doubleword into alternate space +SPARC_stdf, // Store double floating-point +SPARC_stdfa, // Store double floating-point into alternate space +SPARC_stf, // Store floating-point +SPARC_stfa, // Store floating-point into alternate space +SPARC_stfsr, // Store floating-point state register +SPARC_sth, // Store halfword +SPARC_stha, // Store halfword into alternate space +SPARC_stqf, // Store quad floating-point +SPARC_stqfa, // Store quad floating-point into alternate space +SPARC_stw, // Store word +SPARC_stwa, // Store word into alternate space +SPARC_stx, // Store extended +SPARC_stxa, // Store extended into alternate space +SPARC_stxfsr, // Store extended floating-point state register +SPARC_sub, // Subtract +SPARC_subcc, // Subtract and modify condition codes +SPARC_subc, // Subtract with carry +SPARC_subccc, // Subtract with carry and modify condition codes +SPARC_swap, // Swap integer register with memory +SPARC_swapa, // Swap integer register with memory in alternate space +SPARC_taddcc, // Tagged add and modify condition codes +SPARC_taddcctv, // Tagged add and modify condition codes (trap on overflow) +SPARC_tsubcc, // Tagged subtract and modify condition codes +SPARC_tsubcctv, // Tagged subtract and modify condition codes (trap on overflow) +SPARC_t, // Trap on integer condition codes +SPARC_udiv, // Unsigned integer divide +SPARC_udivcc, // Unsigned integer divide and modify condition codes +SPARC_udivx, // 64-bit unsigned integer divide +SPARC_umul, // Unsigned integer multiply +SPARC_umulcc, // Unsigned integer multiply and modify condition codes +SPARC_wr, // Write Y register +SPARC_wrpr, // Write privileged register +SPARC_xnor, // Exclusive-nor +SPARC_xnorcc, // Exclusive-nor (and modify condition codes) +SPARC_xor, // Exclusive-or +SPARC_xorcc, // Exclusive-or (and modify condition codes) + +// Synthetic instructions + +SPARC_cmp, // Compare +SPARC_jmp, // Jump +SPARC_iprefetch, // Instruction prefetch +SPARC_tst, // Test +SPARC_ret, // Return from subroutine +SPARC_retl, // Return from leaf subroutine +SPARC_setuw, // Load unsigned constant +SPARC_setsw, // Load signed constant +SPARC_setx, // Load 64-bit constant +SPARC_signx, // Sign-extend 32-bit value to 64 bits +SPARC_not, // One's complement +SPARC_neg, // Two's complement +SPARC_cas, // Compare and swap +SPARC_casl, // Compare and swap, little endian +SPARC_casx, // Compare and swap extended +SPARC_casxl, // Compare and swap extended, little endian +SPARC_inc, // Increment +SPARC_inccc, // Increment and modify condition codes +SPARC_dec, // Deccrement +SPARC_deccc, // Decrement and modify condition codes +SPARC_btst, // Bit test +SPARC_bset, // Bit set +SPARC_bclr, // Bit clear +SPARC_btog, // Bit toggle +SPARC_clr, // Clear +SPARC_clrb, // Clear byte +SPARC_clrh, // Clear halfword +SPARC_clrx, // Clear extended word +SPARC_clruw, // Clear upper word +SPARC_pseudo_mov, // Move register + +// UtlraSPARC II extensions + +SPARC_alignaddress, // Calculate address for misaligned data access +SPARC_array, // 3-D address to blocked byte address conversion +SPARC_edge, // Edge boundary processing {little-endian} +SPARC_faligndata, // Perform data alignment for misaligned data +SPARC_fandnot1, // Negated src1 AND src2 (single precision) +SPARC_fandnot2, // src1 AND negated src2 (single precision) +SPARC_fand, // Logical AND (single precision) +SPARC_fcmpeq, // Four 16-bit/two 32-bit compare; set integer dest if src1 = src2 +SPARC_fcmpgt, // Four 16-bit/two 32-bit compare; set integer dest if src1 > src2 +SPARC_fcmple, // Four 16-bit/two 32-bit compare; set integer dest if src1 <= src2 +SPARC_fcmpne, // Four 16-bit/two 32-bit compare; set integer dest if src1 != src2 +SPARC_fexpand, // Four 8-bit to 16-bit expands +SPARC_fmul8sux16, // Signed upper 8/16-bit partitioned product of corresponding components +SPARC_fmul8ulx16, // Unsigned lower 8/16-bit partitioned product of corresponding components +SPARC_fmul8x16, // 8/16-bit partitioned product of corresponding components +SPARC_fmul8x16al, // 8/16-bit lower a partitioned product of 4 components +SPARC_fmul8x16au, // 8/16-bit upper a partitioned product of 4 components +SPARC_fmuld8sux16, // Signed upper 8/16-bit multiply 32-bit partitioned product of components +SPARC_fmuld8ulx16, // Unsigned lower 8/16-bit multiply 32-bit partitioned product of components +SPARC_fnand, // Logical NAND (single precision) +SPARC_fnor, // Logical NOR (single precision) +SPARC_fnot1, // Negate (1's complement) src1 (single precision) +SPARC_fnot2, // Negate (1's complement) src2 (single precision) +SPARC_fone, // One fill (single precision) +SPARC_fornot1, // Negated src1 OR src2 (single precision) +SPARC_fornot2, // src1 OR negated src2 (single precision) +SPARC_for, // Logical OR (single precision) +SPARC_fpackfix, // Two 32-bit to 16-bit fixed pack +SPARC_fpack, // Four 16-bit/two 32-bit pixel pack +SPARC_fpadd, // Four 16-bit/two 32-bit partitioned add (single precision) +SPARC_fpmerge, // Two 32-bit pixel to 64-bit pixel merge +SPARC_fpsub, // Four 16-bit/two 32-bit partitioned subtract (single precision) +SPARC_fsrc1, // Copy src1 (single precision) +SPARC_fsrc2, // Copy src2 (single precision) +SPARC_fxnor, // Logical XNOR (single precision) +SPARC_fxor, // Logical XOR (single precision) +SPARC_fzero, // Zero fill (single precision) +SPARC_pdist, // Distance between 8 8-bit components +SPARC_shutdown, // Power-down support +// SPARC_stdfa, // 8/16-bit store from a double precision FP register + +// V8 only instructions +SPARC_rett, // Return from trap + +// UA 2005 instructions +SPARC_bmask, // Byte Mask +SPARC_bshuffle, // Byte Shuffle +SPARC_edgen, // Edge boundary processing (no CC) {little-endian} +SPARC_rdhpr, // Read Hyperprivileged Register +SPARC_wrhpr, // Write Hyperprivileged Register +SPARC_siam, // Set Interval Arithmetic Mode + +SPARC_last, + +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +HPPA_null = 0, // Unknown Operation + +HPPA_add, // Add +HPPA_addb, // Add and Branch +HPPA_addi, // Add to Immediate +HPPA_addib, // Add Immediate and Branch +HPPA_addil, // Add to Immediate Left +HPPA_and, // AND +HPPA_andcm, // AND complement +HPPA_b, // Branch +HPPA_bb, // Branch on Bit +HPPA_be, // Branch External +HPPA_blr, // Branch and Link Register +HPPA_break, // Break +HPPA_bv, // Branch Vectored +HPPA_bve, // Branch Vectored External +HPPA_cldd, // Coprocessor Load Doubleword +HPPA_cldw, // Coprocessor Load Word +HPPA_clrbts, // Clear Branch Target Stack +HPPA_cmpb, // Compare and Branch +HPPA_cmpclr, // Compare and Clear +HPPA_cmpib, // Compare Immediate and Branch +HPPA_cmpiclr, // Compare Immediate and Clear +HPPA_copr, // Coprocessor Operation +HPPA_cstd, // Coprocessor Store Doubleword +HPPA_cstw, // Coprocessor Store Word +HPPA_dcor, // Decimal Correct +HPPA_depd, // Deposit Doubleword +HPPA_depdi, // Deposit Doubleword Immediate +HPPA_depw, // Deposit Word +HPPA_depwi, // Deposit Word Immediate +HPPA_diag, // Diagnose +HPPA_ds, // Divide Step +HPPA_extrd, // Extract Doubleword +HPPA_extrw, // Extract Word +HPPA_fdc, // Flush Data Cache +HPPA_fdce, // Flush Data Cache Entry +HPPA_fic, // Flush Instruction Cache +HPPA_fice, // Flush Instruction Cache Entry +HPPA_hadd, // Halfword Parallel Add +HPPA_havg, // Halfword Parallel Average +HPPA_hshl, // Halfword Parallel Shift Left +HPPA_hshladd, // Halfword Parallel Shift Left and Add +HPPA_hshr, // Halfword Parallel Shift Right +HPPA_hshradd, // Halfword Parallel Shift Right and Add +HPPA_hsub, // Halfword Parallel Subtract +HPPA_idtlbt, // Insert Data TLB Translation +HPPA_iitlbt, // Insert Instruction TLB Translation +HPPA_lci, // Load Coherence Index +HPPA_ldb, // Load Byte +HPPA_ldcd, // Load and Clear Doubleword +HPPA_ldcw, // Load and Clear Word +HPPA_ldd, // Load Doubleword +HPPA_ldda, // Load Doubleword Absolute +HPPA_ldh, // Load Halfword +HPPA_ldil, // Load Immediate Left +HPPA_ldo, // Load Offset +HPPA_ldsid, // Load Space Identifier +HPPA_ldw, // Load Word +HPPA_ldwa, // Load Word Absolute +HPPA_lpa, // Load Physical Address +HPPA_mfctl, // Move From Control Register +HPPA_mfia, // Move From Instruction Address +HPPA_mfsp, // Move From Space Register +HPPA_mixh, // Mix Halfwords +HPPA_mixw, // Mix Words +HPPA_movb, // Move and Branch +HPPA_movib, // Move Immediate and Branch +HPPA_mtctl, // Move To Control Register +HPPA_mtsarcm, // Move To Shift Amount Register Complement +HPPA_mtsm, // Move To System Mask +HPPA_mtsp, // Move To Space Register +HPPA_or, // Inclusive OR +HPPA_pdc, // Purge Data Cache +HPPA_pdtlb, // Purge Data TLB +HPPA_pdtlbe, // Purge Data TLB Entry +HPPA_permh, // Permute Halfwords +HPPA_pitlb, // Purge Instruction TLB +HPPA_pitlbe, // Purge Instruction TLB Entry +HPPA_popbts, // Pop Branch Target Stack +HPPA_probe, // Probe Access +HPPA_probei, // Probe Access Immediate +HPPA_pushbts, // Push Branch Target Stack +HPPA_pushnom, // Push Nominated +HPPA_rfi, // Return From Interruption +HPPA_rsm, // Reset System Mask +HPPA_shladd, // Shift Left and Add +HPPA_shrpd, // Sihft Right Pair Doubleword +HPPA_shrpw, // Sihft Right Pair Word +HPPA_spop0, // Special Operation Zero +HPPA_spop1, // Special Operation One +HPPA_spop2, // Special Operation Two +HPPA_spop3, // Special Operation Three +HPPA_ssm, // Set System Mask +HPPA_stb, // Store Byte +HPPA_stby, // Store Bytes +HPPA_std, // Store Doubleword +HPPA_stda, // Store Doubleword Absolute +HPPA_stdby, // Store Doubleword Bytes +HPPA_sth, // Store Halfword +HPPA_stw, // Store Word +HPPA_stwa, // Store Word Absolute +HPPA_sub, // Subtract +HPPA_subi, // Subtract from Immediate +HPPA_sync, // Synchronize Caches +HPPA_syncdma, // Synchronize DMA +HPPA_uaddcm, // Unit Add Complement +HPPA_uxor, // Unit XOR +HPPA_xor, // Exclusive OR + +// Floating point instructions + +HPPA_fabs, // Floating-Point Absolute Value +HPPA_fadd, // Floating-Point Add +HPPA_fcmp, // Floating-Point Compare +HPPA_fcnv, // Floating-Point Convert +HPPA_fcpy, // Floating-Point Copy +HPPA_fdiv, // Floating-Point Divide +HPPA_fid, // Floating-Point Identity +HPPA_fldd, // Floating-Point Load Doubleword +HPPA_fldw, // Floating-Point Load Word +HPPA_fmpy, // Floating-Point Multiply +HPPA_fmpyadd, // Floating-Point Multiply/Add +HPPA_fmpyfadd, // Floating-Point Multiply Fused Add +HPPA_fmpynfadd, // Floating-Point Multiply Negate Fused Add +HPPA_fmpysub, // Floating-Point Multiply/Subtract +HPPA_fneg, // Floating-Point Negate +HPPA_fnegabs, // Floating-Point Negate Absolute Value +HPPA_frem, // Floating-Point Remainder +HPPA_frnd, // Floating-Point Round to Integer +HPPA_fsqrt, // Floating-Point Square Root +HPPA_fstd, // Floating-Point Store Doubleword +HPPA_fstw, // Floating-Point Store Word +HPPA_fsub, // Floating-Point Subtract +HPPA_ftest, // Floating-Point Test +HPPA_xmpyu, // Fixed-Point Multiply Unsigned + +// Performance Monitor Coprocessor + +HPPA_pmdis, // Performance Monitor Disable +HPPA_pmenb, // Performance Monitor Enable + +// Macros + +HPPA_call, // Call Subroutine +HPPA_ret, // Return From Subroutine +HPPA_shld, // Shift Left Doubleword +HPPA_shlw, // Shift Left Word +HPPA_shrd, // Shift Right Doubleword +HPPA_shrw, // Shift Right Word +HPPA_ldi, // Load Immediate +HPPA_copy, // Copy Register +HPPA_mtsar, // Move To %SAR +HPPA_nop, // No Operation + +HPPA_last, + +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + +H8500_null = 0, // Unknown Operation + +// Data transfer + +H8500_mov_g, // B/W Move data +H8500_mov_e, // B Move data +H8500_mov_i, // W Move data +H8500_mov_f, // B/W Move data +H8500_mov_l, // B/W Move data +H8500_mov_s, // B/W Move data +H8500_ldm, // W Pop data from the stack to one or more registers +H8500_stm, // W Push data from one or more registers onto the stack +H8500_xch, // W Exchange data between two general registers +H8500_swap, // B Exchange the upper and lower bytes in a general register +H8500_movtpe, // B Transfer data from a general register to memory +H8500_movfpe, // B Transfer data from memory to a general register + +// Arithmetic operations + +H8500_add_g, // B/W Addition +H8500_add_q, // B/W Addition +H8500_sub, // B/W Subtraction +H8500_adds, // B/W Addition +H8500_subs, // B/W Subtraction +H8500_addx, // B/W Addition with carry +H8500_subx, // B/W Subtraction with borrow +H8500_dadd, // B Decimal addition +H8500_dsub, // B Decimal subtraction +H8500_mulxu, // B/W Unsigned multiplication +H8500_divxu, // B/W Unsigned division +H8500_cmp_g, // B/W Compare data +H8500_cmp_e, // B Compare data +H8500_cmp_i, // W Compare data +H8500_exts, // B Convert byte to word by extending the sign bit +H8500_extu, // B Convert byte to word data by padding with zero bits +H8500_tst, // B/W Compare with 0 +H8500_neg, // B/W Negate +H8500_clr, // B/W Make zero +H8500_tas, // B Test and set + +// Logic Operations + +H8500_and, // B/W Logical AND +H8500_or, // B/W Logical OR +H8500_xor, // B/W Exclusive OR +H8500_not, // B/W Bitwise NOT + +// Shift Operations + +H8500_shal, // B/W Arithmetic shift left +H8500_shar, // B/W Arithmetic shift right +H8500_shll, // B/W Logical shift left +H8500_shlr, // B/W Logical shift right +H8500_rotl, // B/W Rotate left +H8500_rotr, // B/W Rotate right +H8500_rotxl, // B/W Rotate through carry left +H8500_rotxr, // B/W Rotate through carry right + +// Bit Manipulations + +H8500_bset, // B/W Test bit and set +H8500_bclr, // B/W Test bit and clear +H8500_bnot, // B/W Test bit and invert +H8500_btst, // B/W Test bit + +// Branching Instructions + +H8500_bra, // Branch Always +H8500_brn, // Branch Never +H8500_bhi, // Branch if High (C|Z = 0) +H8500_bls, // Branch if Low or Same (C|Z = 1) +H8500_bcc, // Branch if Carry Clear (C = 0) +H8500_bcs, // Branch if Carry Set (C = 1) +H8500_bne, // Branch if Not Equal (Z = 0) +H8500_beq, // Branch if Equal (Z = 1) +H8500_bvc, // Branch if Overflow Clear (V = 0) +H8500_bvs, // Branch if Overflow Set (V = 1) +H8500_bpl, // Branch if Plus (N = 0) +H8500_bmi, // Branch if Minus (N = 1) +H8500_bge, // Branch if Greater or Equal (N^V = 0) +H8500_blt, // Branch if Less Than (N^V = 1) +H8500_bgt, // Branch if Greater Than (Z|(N^V) = 0) +H8500_ble, // Branch if Less or Equal (Z|(N^V) = 1) +H8500_jmp, // Branch unconditionally (same page) +H8500_pjmp, // Branch unconditionally (specified page) +H8500_bsr, // Branch to subroutine (same page) +H8500_jsr, // Branch to subroutine (same page) +H8500_pjsr, // Branch to subroutine (specified page) +H8500_rts, // Return from subroutine (same page) +H8500_prts, // Return from subroutine (different page) +H8500_rtd, // Return from subroutine (same page) and adjust SP +H8500_prtd, // Return from subroutine (different page) and adjust SP +H8500_scb, // Control loop + +// System Control Instructions + +H8500_trapa, // Generate trap exception +H8500_trap_vs, // Generate trap exception if the V bit is set +H8500_rte, // Return from exception-handling routine +H8500_link, // Create stack frame +H8500_unlk, // Deallocate stack frame +H8500_sleep, // Go to power-down state +H8500_ldc, // B/W Move to control register +H8500_stc, // B/W Move from control register +H8500_andc, // B/W Logically AND control register +H8500_orc, // B/W Logically OR control register +H8500_xorc, // B/W Logically exclusive-OR control register +H8500_nop, // No operation +H8500_bpt, // + +H8500_last, + + }; + + + + +enum +{ +DSP56_null = 0, // Unknown Operation + +DSP56_abs, // Absolute Value +DSP56_adc, // Add Long with Carry +DSP56_add, // Addition +DSP56_addl, // Shift Left and Add +DSP56_addr, // Shift Right and Add +DSP56_and, // Logical AND +DSP56_andi, // AND Immediate to Control Register +DSP56_asl, // Arithmetic Shift Left +DSP56_asl4, // Arithmetic Shift Left 4 +DSP56_asr, // Arithmetic Shift Right +DSP56_asr4, // Arithmetic Shift Right 4 +DSP56_asr16, // Arithmetic Shift Right 16 +DSP56_bfchg, // Test Bit Field and Change +DSP56_bfclr, // Clear Bit Field +DSP56_bfset, // Set Bit Field +DSP56_bftsth, // Test Bit Field High +DSP56_bftstl, // Test Bit Field Low +DSP56_bcc, // Branch Conditionaly +DSP56_bchg, // Bit Test and Change +DSP56_bclr, // Bit Test and Clear +DSP56_bra, // Branch Always +DSP56_brclr, // Branch if Bit Clear +DSP56_brkcc, // Exit Current DO Loop Conditionally +DSP56_brset, // Branch if Bit Set +DSP56_bscc, // Branch to Subroutine Conditionaly +DSP56_bsclr, // Branch to Subroutine if Bit Clear +DSP56_bset, // Bit Test and Set +DSP56_bsr, // Branch to Subroutine +DSP56_bsset, // Branch to Subroutine if Bit Set +DSP56_btst, // Bit Test on Memory and Registers +DSP56_chkaau, // Check address ALU result +DSP56_clb, // Count Leading Bits +DSP56_clr, // Clear an Operand +DSP56_clr24, // Clear 24 MS-bits of Accumulator +DSP56_cmp, // Compare +DSP56_cmpm, // Compare Magnitude +DSP56_cmpu, // Compare Unsigned +DSP56_debug, // Enter Debug Mode +DSP56_debugcc, // Enter Debug Mode Conditionally +DSP56_dec, // Decrement by One +DSP56_dec24, // Decrement 24 MS-bit of Accumulator +DSP56_div, // Divide Iteration +DSP56_dmac, // Double-Precision Multiply-Accumulate With Right Shift +DSP56_do, // Start Hardware Loop +DSP56_do_f, // Start Infinite Loop +DSP56_dor, // Start PC-Relative Hardware Loop +DSP56_dor_f, // Start PC-Relative Infinite Loop +DSP56_enddo, // Exit from Hardware Loop +DSP56_eor, // Logical Exclusive OR +DSP56_extract, // Extract Bit Field +DSP56_extractu, // Extract Unsigned Bit Field +DSP56_ext, // Sign Extend Accumulator +DSP56_ill, // Illegal Instruction +DSP56_imac, // Integer Multiply-Accumulate +DSP56_impy, // Integer Multiply +DSP56_inc, // Increment by One +DSP56_inc24, // Increment 24 MS-bit of Accumulator +DSP56_insert, // Insert Bit Field +DSP56_jcc, // Jump Conditionally +DSP56_jclr, // Jump if Bit Clear +DSP56_jmp, // Jump +DSP56_jscc, // Jump to Subroutine Conditionally +DSP56_jsclr, // Jump to Subroutine if Bit Clear +DSP56_jset, // Jump if Bit Set +DSP56_jsr, // Jump to Subroutine +DSP56_jsset, // Jump to Subroutine if Bit Set +DSP56_lra, // Load PC-Reliative Address +DSP56_lsl, // Logical Shift Left +DSP56_lsr, // Logical Shift Right +DSP56_lua, // Load Updated Address +DSP56_lea, // Load Updated Address +DSP56_mac, // Signed Multiply-Accumulate +DSP56_maci, // Signed Multiply-Accumulate With Immediate Operand +DSP56_mac_s_u, // Mixed Multiply-Accumulate +DSP56_macr, // Signed Multiply-Accumulate and Round +DSP56_macri, // Signed Multiply-Accumulate and Round With Immediate Operand +DSP56_max, // Transfer by Signed Value +DSP56_maxm, // Transfer by Magnitude +DSP56_merge, // Merge Two Half Words +DSP56_move, // Move Data +DSP56_movec, // Move Control Register +DSP56_movei, // Move Immediate Short +DSP56_movem, // Move Program Memory +DSP56_movep, // Move Peripheral Data +DSP56_moves, // Move Absolute Short +DSP56_mpy, // Signed Multiply +DSP56_mpyi, // Signed Multiply With Immediate Operand +DSP56_mpy_s_u, // Mixed Multiply +DSP56_mpyr, // Signed Multiply and Round +DSP56_mpyri, // Signed Multiply and Round With Immediate Operand +DSP56_neg, // Negate Accumulator +DSP56_negc, // Negate Accumulator +DSP56_nop, // No Operation +DSP56_norm, // Norm Accumulator Iteration +DSP56_normf, // Fast Accumulator Normalization +DSP56_not, // Logical Complement +DSP56_or, // Logical Inclusive OR +DSP56_ori, // OR Immediate to Control Register +DSP56_pflush, // Program Cache Flush +DSP56_pflushun, // Program Cache Flush Unlocked Sectors +DSP56_pfree, // Program Cache Global Unlock +DSP56_plock, // Lock Instruction Cache Sector +DSP56_plockr, // Lock Instruction Cache Relative Sector +DSP56_punlock, // Unlock Instruction Cache Sector +DSP56_punlockr, // Unlock Instruction Cache Relative Sector +DSP56_rep, // Repeat Next Instruction +DSP56_repcc, // Repeat Next Instruction +DSP56_reset, // Reset On-Chip Peripheral Devices +DSP56_rnd, // Round Accumulator +DSP56_rol, // Rotate Left +DSP56_ror, // Rotate Right +DSP56_rti, // Return from Interrupt +DSP56_rts, // Return from Subroutine +DSP56_sbc, // Subtract Long with Carry +DSP56_stop, // Stop Processing (Low-Power Standby) +DSP56_sub, // Subtract +DSP56_subl, // Shift Left and Subtract +DSP56_subr, // Shift Right and Subtract +DSP56_swap, // Swap Accumulator Words +DSP56_tcc, // Transfer Conditionally +DSP56_tfr, // Transfer Data ALU Register +DSP56_tfr2, // Transfer Data ALU Register +DSP56_tfr3, // Transfer Data ALU Register +DSP56_trap, // Software Interrupt +DSP56_trapcc, // Software Interrupt Conditionally +DSP56_tst, // Test an Operand +DSP56_tst2, // Test an Operand +DSP56_vsl, // Viterbi Shift Left +DSP56_wait, // Wait for Interrupt or DMA Request (Low-Power Standby) +DSP56_zero, // Zero Extend Accumulator +DSP56_swi, // Software Interrupt (only for 56000) +DSP56_pmov, // Pseudo insn +DSP56_last, + +}; + + + + +enum +{ +DSP96_null = 0, // Unknown Operation + +DSP96_abs, // Absolute Value +DSP96_add, // Addition +DSP96_addc, // Add with Carry +DSP96_and, // Logical AND +DSP96_andc, // AND Immediate with Complement +DSP96_andi, // AND Immediate to Control Register +DSP96_asl, // Arithmetic Shift Left +DSP96_asr, // Arithmetic Shift Right +DSP96_bcc, // Branch Conditionally (indirect) +DSP96_bccd, // Branch Conditionally (direct) +DSP96_bchg, // Bit Test and Change +DSP96_bclr, // Bit Test and Clear +DSP96_bfind, // Find Leading One +DSP96_bra, // Branch Always +DSP96_brclr, // Branch if Bit Clear +DSP96_brset, // Branch if Bit Set +DSP96_bscc, // Branch to Subroutine Conditionally (indirect) +DSP96_bsccd, // Branch to Subroutine Conditionally (direct) +DSP96_bsclr, // Branch to Subroutine if Bit Clear +DSP96_bset, // Bit Test and Set +DSP96_bsr, // Branch to Subroutine (indirect) +DSP96_bsrd, // Branch to Subroutine (direct) +DSP96_bsset, // Branch to Subroutine if Bit Set +DSP96_btst, // Bit Test on Memory and Registers +DSP96_clr, // Clear an Operand +DSP96_cmp, // Compare +DSP96_cmpg, // Graphics Compare with Trivial Accept.Reject Flags +DSP96_debugcc, // Enter Debug Mode Conditionally +DSP96_dec, // Decrement by One +DSP96_do, // Start Hardware Loop +DSP96_dor, // Start PC-Relative Hardware Loop +DSP96_enddo, // Exit from Hardware Loop +DSP96_eor, // Logical Exclusive OR +DSP96_ext, // Sign Extend 16-Bit To 32-Bit +DSP96_extb, // Sign Extend 8-Bit to 32-Bit +DSP96_fabs, // Absolute Value +DSP96_fadd, // Add +DSP96_faddsub, // Add and Subtract +DSP96_fbcc, // Branch Conditionally (Floating-Point Condition) (indirect) +DSP96_fbccd, // Branch Conditionally (Floating-Point Condition) (direct) +DSP96_fbscc, // Branch to Subroutine Conditionally (Floating-Point Condition) (indirect) +DSP96_fbsccd, // Branch to Subroutine Conditionally (Floating-Point Condition) (direct) +DSP96_fclr, // Clear a Floating-Point Operand +DSP96_fcmp, // Compare +DSP96_fcmpg, // Graphics Compare with Trivial Accept.Reject Flags +DSP96_fcmpm, // Compare Magnitude +DSP96_fcopys, // Copy Sign +DSP96_fdebugcc, // Enter Debug Mode Conditionally +DSP96_fgetman, // Get Mantissa +DSP96_fint, // Convert to Floating-Point Integer +DSP96_fjcc, // Jump Conditionally (indirect) +DSP96_fjccd, // Jump Conditionally (direct) +DSP96_fjscc, // Jump to Subroutine Conditionally (indirect) +DSP96_fjsccd, // Jump to Subroutine Conditionally (direct) +DSP96_float, // Integer to Floating-Point Conversion +DSP96_floatu, // Unsigned Integer to SP Floating-Point Conversion +DSP96_floor, // Convert to Floating-Point Integer round to -Infinity +DSP96_fmove, // Move FP Data Register(s) +DSP96_fmpyfadd, // Multiply and Add +// DSP96_fmpyfaddx, // Multiply and Add (Single Extended Precision) (Handled with DSP96_fmpyfadd) +DSP96_fmpyfaddsub, // Multiply, Add and Subtract +// DSP96_fmpyfaddsubx, // Multiply, Add and Subtract (Single Extended Precision) (Handled with DSP96_fmpyfaddsub) +DSP96_fmpyfsub, // Multiply and Subtract +// DSP96_fmpyfsubx, // Multiply and Subtract (Single Extended Precision) (Handled with DSP96_fmpyfsub) +DSP96_fmpy, // Multiply +DSP96_fneg, // Change sign +DSP96_fscale, // Scale a Floating-Point Operand +DSP96_fseedd, // Reciprocal Approximation +DSP96_fseedr, // Square-Root Reciprocal Approximation +DSP96_fsub, // Subtract +DSP96_ftfr, // Transfer Floating-Point Register +DSP96_ftrapcc, // Conditional Software Interrupt +DSP96_ftst, // Test a floating-Point Operand +DSP96_getexp, // Get Exponent +DSP96_illegal, // Illegal Instruction +DSP96_inc, // Increment by One +DSP96_int, // Floating-Point to Integer Conversion +DSP96_intrz, // Floating-Point to Integer Conversion Round to Zero +DSP96_intu, // Floating-Point to Unsigned Integer Conversion +DSP96_inturz, // Floating-Point to Unsigned Integer Conversion Round to Zero +DSP96_jcc, // Jump Conditionally (indirect) +DSP96_jccd, // Jump Conditionally (direct) +DSP96_jclr, // Jump if Bit Clear +// DSP96_jmp, // Jump (The same as jcc with condition code = 11111) +DSP96_join, // Join two 16-Bit Integers +DSP96_joinb, // Join two 8-Bit Integers +DSP96_jscc, // Jump to Subroutine Conditionally +DSP96_jsccd, // Jump to Subroutine Conditionally +DSP96_jsclr, // Jump to Subroutine if Bit Clear +DSP96_jset, // Jump if Bit Set +// DSP96_jsr, // Jump to Subroutine ( The same as jscc with condition code = 11111) +DSP96_jsset, // Jump to Subroutine if Bit Set +DSP96_lea, // Load Effective Address +DSP96_lra, // Load PC Relative Address +DSP96_lsl, // Logical Shift Left +DSP96_lsr, // Logical Shift Right +DSP96_move, // Move Data Register(s) +DSP96_movec, // Move Control Register +DSP96_movei, // Move Immediate +DSP96_movem, // Move Program Memory +DSP96_movep, // Move Peripheral Data +DSP96_moves, // Move Absolute Short +DSP96_moveta, // Move Data Register(s) and Test Address +DSP96_mpys, // Signed Multiply +DSP96_mpyu, // Unsigned Multiply +DSP96_neg, // Negate Accumulator +DSP96_negc, // Negate with Carry +DSP96_nop, // No Operation +DSP96_not, // Logical Complement +DSP96_or, // Logical Inclusive OR +DSP96_orc, // OR With Compliment +DSP96_ori, // OR Immediate to Control Register +DSP96_rep, // Repeat Next Instruction +DSP96_reset, // Reset On-Chip Peripheral Devices +DSP96_rol, // Rotate Left +DSP96_ror, // Rotate Right +DSP96_rti, // Return from Interrupt +DSP96_rtr, // Return from Subroutine and Restore Status Register +DSP96_rts, // Return from Subroutine +DSP96_setw, // Set an Operand +DSP96_split, // Extract 16-Bit Integer +DSP96_splitb, // Extract 8-Bit Integer +DSP96_stop, // Stop Processing (Low Power Stand-by) +DSP96_sub, // Subtract +DSP96_subc, // Subtract with Carry +DSP96_tfr, // Transfer Data ALU Register +DSP96_trapcc, // Software Interrupt Conditionally +DSP96_tst, // Test an Operand +DSP96_wait, // Wait for Interrupt or DMA Request (Low Power Stand-by) +DSP96_last, + +}; + +enum PMNum // Parallel Move enumeration +{ + PM96_NoMove, + PM96_R2R, // Register to register parallel move + PM96_Update, // Move update + PM96_XYMem, // X or Y memory move + PM96_XYmemR, // X or Y memory and Registry move + PM96_Long, // Long Memory move + PM96_XY, // XY Memory move + + PM96_IFcc, // Integer if conditional Instruction without CCR update + // All the following moves are handled by IFcc + // PM96_FFcc, // Floating-Point if conditional Instruction without CCR, ER, IER update + // PM96_FFccU, // Floating-Point if conditional Instruction with CCR, ER, IER update + // PM96_IFccU, // Integer if conditional Instruction with CCR update +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + +C166_null = 0, // Unknown Operation + +C166_add, // Integer Addition +C166_addb, // Integer Addition Byte +C166_addc, // Integer Addition with Carry +C166_addcb, // Integer Addition with Carry Byte +C166_and, // Logical AND +C166_andb, // Logical AND Byte +C166_ashr, // Arithmetic Shift Right +C166_atomic, // Begin ATOMIC Sequence +C166_band, // Bit Logical AND +C166_bclr, // Bit Clear +C166_bcmp, // Bit to Bit Compare +C166_bfldh, // Bit Field High Byte +C166_bfldl, // Bit Field Low Byte +C166_bmov, // Bit to Bit Move +C166_bmovn, // Bit to Bit Move and Negate +C166_bor, // Bit Logical OR +C166_bset, // Bit Set +C166_bxor, // Bit Logical XOR +C166_calla, // Call Subroutine Absolute +C166_calli, // Call Subroutine Indirect +C166_callr, // Call Subroutine Relative +C166_calls, // Call Inter-Segment Subroutine +C166_cmp, // Integer Compare +C166_cmpb, // Integer Compare Byte +C166_cmpd1, // Integer Compare and Decrement by 1 +C166_cmpd2, // Integer Compare and Decrement by 2 +C166_cmpi1, // Integer Compare and Increment by 1 +C166_cmpi2, // Integer Compare and Increment by 2 +C166_cpl, // Integer One's Complement +C166_cplb, // Integer One's Complement Byte +C166_diswdt, // Disable Watchdog Timer +C166_div, // 16-by-16 Signed Division +C166_divl, // 32-by-16 Signed Division +C166_divlu, // 32-by-16 Unsigned Division +C166_divu, // 16-by-16 Unsigned Division +C166_einit, // End of Intialization +C166_extr, // Begin Extended Register Sequence +C166_extp, // Begin Extended Page Sequence +C166_extpr, // Begin Extended Page and Register Sequence +C166_exts, // Begin Extended Segment Sequence +C166_extsr, // Begin Extended Segment and Register Sequence +C166_idle, // Enter Idle Mode +C166_jb, // Relative Jump if Bit Set +C166_jbc, // Relative Jump if Bit Set and Clear Bit +C166_jmpa, // Absolute Conditional Jump +C166_jmpi, // Indirect Conditional Jump +C166_jmpr, // Relative Conditional Jump +C166_jmps, // Absolute Inter-Segment Jump +C166_jnb, // Relative Jump if Bit Clear +C166_jnbs, // Relative Jump if Bit Clear and Set Bit +C166_mov, // Move Word +C166_movb, // Move Byte +C166_movbs, // Move Byte Sign Extend +C166_movbz, // Move Byte Zero Extend +C166_mul, // Signed Multiplication +C166_mulu, // Unsigned Multiplication +C166_neg, // Integer Two's Complement +C166_negb, // Integer Two's Complement Byte +C166_nop, // No Operation +C166_or, // Logical OR +C166_orb, // Logical OR Byte +C166_pcall, // Push Word and Call Subroutine Absolute +C166_pop, // Pop Word from System Stack +C166_prior, // Prioritize Register +C166_push, // Push Word on System Stack +C166_pwrdn, // Enter Power Down Mode +C166_ret, // Return from Subroutine +C166_reti, // Return from Interrupt Routine +C166_retp, // Return from Subroutine and Pop Word +C166_rets, // Return from Inter-Segment Subroutine +C166_rol, // Rotate Left +C166_ror, // Rotate Right +C166_scxt, // Switch Context +C166_shl, // Shift Left +C166_shr, // Shift Rightt +C166_srst, // Software Reset +C166_srvwdt, // Service Watchdog Timer +C166_sub, // Integer Subtraction +C166_subb, // Integer Subtraction Byte +C166_subc, // Integer Subtraction with Carry +C166_subcb, // Integer Subtraction with Carry Byte +C166_trap, // Software Trap +C166_xor, // Logical Exclusive OR +C166_xorb, // Logical Exclusive OR Byte + +// ST10 MAC instructions + +ST10_CoABS, // Absolute Value +ST10_CoADD, // Add +ST10_CoASHR, // Accumulator Arithmetic Shift Right with Optional Round +ST10_CoCMP, // Compare +ST10_CoLOAD, // Load Accumulator +ST10_CoMAC, // Multiply-Accumulate & Optional Round +ST10_CoMACM, // Multiply-Accumulate Parallel Data Move & Optional Round +ST10_CoMAX, // Maximum +ST10_CoMIN, // Minimum +ST10_CoMOV, // Memory to Memory Move +ST10_CoMUL, // Multiply & Optional Round +ST10_CoNEG, // Negate Accumulator with Optional Rounding +ST10_CoNOP, // No-Operation +ST10_CoRND, // Round Accumulator +ST10_CoSHL, // Accumulator Logical Shift Left +ST10_CoSHR, // Accumulator Logical Shift Right +ST10_CoSTORE, // Store a MAC-Unit Register +ST10_CoSUB, // Subtract + +// missing C166v2/ST10 instructions + +C166_enwdt, // Enable Watchdog Timer +C166_sbrk, // Software Break + +C166_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +ST20_null = 0, // Unknown Operation + +// C1 instructions + +ST20_adc, // add constant +ST20_add, // add +ST20_addc, // add with carry +ST20_ajw, // adjust work space +ST20_and, // and +ST20_arot, // anti-rotate stack +ST20_ashr, // arithmetic shift right +ST20_biquad, // biquad IIR filter step +ST20_bitld, // load bit +ST20_bitmask, // create bit mask +ST20_bitst, // store bit +ST20_breakpoint, // breakpoint +ST20_cj, // conditional jump +ST20_dequeue, // dequeue a process +ST20_divstep, // divide step +ST20_dup, // duplicate +ST20_ecall, // exception call +ST20_enqueue, // enqueue a process +ST20_eqc, // equals constant +ST20_eret, // exception return +ST20_fcall, // function call +ST20_gajw, // general adjust workspace +ST20_gt, // greater than +ST20_gtu, // greater than unsigned +ST20_io, // input/output +ST20_j, // jump +ST20_jab, // jump absolute +ST20_lbinc, // load byte and increment +ST20_ldc, // load constant +ST20_ldl, // load local +ST20_ldlp, // load local pointer +ST20_ldnl, // load non-local +ST20_ldnlp, // load non-local pointer +ST20_ldpi, // load pointer to instruction +ST20_ldprodid, // load product identity +ST20_ldtdesc, // load task descriptor +ST20_lsinc, // load sixteen and increment +ST20_lsxinc, // load sixteen sign extended and increment +ST20_lwinc, // load word and increment +ST20_mac, // multiply accumulate +ST20_mul, // multiply +ST20_nfix, // negative prefix +ST20_nop, // no operation +ST20_not, // bitwise not +ST20_opr, // operate +ST20_or, // or +ST20_order, // order +ST20_orderu, // unsigned order +ST20_pfix, // prefix +ST20_rev, // reverse +ST20_rmw, // read modify write +ST20_rot, // rotate stack +ST20_run, // run process +ST20_saturate, // saturate +ST20_sbinc, // store byte and increment +ST20_shl, // shift left +ST20_shr, // shift right +ST20_signal, // signal +ST20_smacinit, // initialize short multiply accumulate loop +ST20_smacloop, // short multiply accumulate loop +ST20_smul, // short multiply +ST20_ssinc, // store sixteen and increment +ST20_statusclr, // clear bits in status register +ST20_statusset, // set bits in status register +ST20_statustst, // test status register +ST20_stl, // store local +ST20_stnl, // store non-local +ST20_stop, // stop process +ST20_sub, // subtract +ST20_subc, // subtract with carry +ST20_swap32, // byte swap 32 +ST20_swinc, // store word and increment +ST20_timeslice, // timeslice +ST20_umac, // unsigned multiply accumulate +ST20_unsign, // unsign argument +ST20_wait, // wait +ST20_wsub, // word subscript +ST20_xbword, // sign extend byte to word +ST20_xor, // exclusive or +ST20_xsword, // sign extend sixteen to word + +// C2-C4 instructions + +ST20_alt, // alt start +ST20_altend, // alt end +ST20_altwt, // alt wait +ST20_bcnt, // byte count +ST20_bitcnt, // count bits set in word +ST20_bitrevnbits, // reverse bottom n bits in word +ST20_bitrevword, // reverse bits in word +ST20_bsub, // byte subscript +ST20_call, // call +ST20_causeerror, // cause error +ST20_cb, // check byte +ST20_cbu, // check byte unsigned +ST20_ccnt1, // check count from 1 +ST20_cflerr, // check floating point error +ST20_cir, // check in range +ST20_ciru, // check in range unsigned +ST20_clockdis, // clock disable +ST20_clockenb, // clock enable +ST20_clrhalterr, // clear halt-on error flag +ST20_crcbyte, // calculate CRC on byte +ST20_crcword, // calculate CRC on word +ST20_cs, // check sixteen +ST20_csngl, // check single +ST20_csu, // check sixteen unsigned +ST20_csub0, // check subscript from 0 +ST20_cword, // check word +ST20_devlb, // device load byte +ST20_devls, // device load sixteen +ST20_devlw, // device load word +ST20_devmove, // device move +ST20_devsb, // device store byte +ST20_devss, // device store sixteen +ST20_devsw, // device store word +ST20_diff, // difference +ST20_disc, // disable channel +ST20_diss, // disable skip +ST20_dist, // disable timer +ST20_div, // divide +ST20_enbc, // enable channel +ST20_enbs, // enable skip +ST20_enbt, // enable timer +ST20_endp, // end process +ST20_fmul, // fractional multiply +ST20_fptesterr, // test for FPU error +ST20_gcall, // general call +ST20_gintdis, // general interrupt disable +ST20_gintenb, // general interrupt enable +ST20_in, // input message +ST20_insertqueue, // insert at front of scheduler queue +ST20_intdis, // (localised) interrupt disable +ST20_intenb, // (localised) interrupt enable +ST20_iret, // interrupt return +ST20_ladd, // long add +ST20_lb, // load byte +ST20_lbx, // load byte and sign extend +ST20_ldclock, // load clock +ST20_lddevid, // load device identity +ST20_ldiff, // long diff +ST20_ldinf, // load infinity +ST20_ldiv, // long divide +ST20_ldmemstartval, // load value of MemStart address +ST20_ldpri, // load current priority +ST20_ldshadow, // load shadow registers +ST20_ldtimer, // load timer +ST20_ldtraph, // load trap handler +ST20_ldtrapped, // load trapped process status +ST20_lend, // loop end +ST20_lmul, // long multiply +ST20_ls, // load sixteen +ST20_lshl, // long shift left +ST20_lshr, // long shift right +ST20_lsub, // long subtract +ST20_lsum, // long sum +ST20_lsx, // load sixteen and sign extend +ST20_mint, // minimum integer +ST20_move, // move message +ST20_move2dall, // 2D block copy +ST20_move2dinit, // initialize data for 2D block move +ST20_move2dnonzero, // 2D block copy non-zero bytes +ST20_move2dzero, // 2D block copy zero bytes +ST20_norm, // normalize +ST20_out, // output message +ST20_outbyte, // output byte +ST20_outword, // output word +ST20_pop, // pop processor stack +ST20_postnormsn, // post-normalize correction of single length fp number +ST20_prod, // product +ST20_reboot, // reboot +ST20_rem, // remainder +ST20_resetch, // reset channel +ST20_restart, // restart +ST20_ret, // return +ST20_roundsn, // round single length floating point number +ST20_runp, // run process +ST20_satadd, // saturating add +ST20_satmul, // saturating multiply +ST20_satsub, // saturating subtract +ST20_saveh, // save high priority queue registers +ST20_savel, // save low priority queue registers +ST20_sb, // store byte +ST20_seterr, // set error flags +ST20_sethalterr, // set halt-on error flag +ST20_settimeslice, // set timeslicing status +ST20_slmul, // signed long multiply +ST20_ss, // store sixteen +ST20_ssub, // sixteen subscript +ST20_startp, // start process +ST20_stclock, // store clock register +ST20_sthb, // store high priority back pointer +ST20_sthf, // store high priority front pointer +ST20_stlb, // store low priority back pointer +ST20_stlf, // store low priority front pointer +ST20_stoperr, // stop on error +ST20_stopp, // stop process +ST20_stshadow, // store shadow registers +ST20_sttimer, // store timer +ST20_sttraph, // store trap handler +ST20_sttrapped, // store trapped process +ST20_sulmul, // signed timer unsigned long multiply +ST20_sum, // sum +ST20_swapqueue, // swap scheduler queue +ST20_swaptimer, // swap timer queue +ST20_talt, // timer alt start +ST20_taltwt, // timer alt wait +ST20_testerr, // test error flag +ST20_testhalterr, // test halt-on error flag +ST20_testpranal, // test processor analysing +ST20_tin, // timer input +ST20_trapdis, // trap disable +ST20_trapenb, // trap enable +ST20_tret, // trap return +ST20_unpacksn, // unpack single length fp number +ST20_wcnt, // word count +ST20_wsubdb, // form double word subscript +ST20_xdble, // extend to double +ST20_xword, // extend word + +ST20_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +ST7_null = 0, // Unknown Operation + +ST7_adc, // Add with Carry +ST7_add, // Addition +ST7_and, // Logical And +ST7_bcp, // Bit compare +ST7_bres, // Bit Reset +ST7_bset, // Bit Set +ST7_btjf, // Jump if bit is false +ST7_btjt, // Jump if bit is true +ST7_call, // Call subroutine +ST7_callr, // Call subroutine relative +ST7_clr, // Clear +ST7_cp, // Arithmetic Compare +ST7_cpl, // One Complement +ST7_dec, // Decrement +ST7_halt, // Halt +ST7_iret, // Interrupt routine return +ST7_inc, // Increment +ST7_jp, // Absolute Jump +ST7_jra, // Jump relative always +ST7_jrt, // Jump relative +ST7_jrf, // Never jump +ST7_jrih, // Jump if Port INT pin = 1 +ST7_jril, // Jump if Port INT pin = 0 +ST7_jrh, // Jump if H = 1 +ST7_jrnh, // Jump if H = 0 +ST7_jrm, // Jump if I = 1 +ST7_jrnm, // Jump if I = 0 +ST7_jrmi, // Jump if N = 1 (minus) +ST7_jrpl, // Jump if N = 0 (plus) +ST7_jreq, // Jump if Z = 1 (equal) +ST7_jrne, // Jump if Z = 0 (not equal) +ST7_jrc, // Jump if C = 1 +ST7_jrnc, // Jump if C = 0 +ST7_jrult, // Jump if C = 1 +ST7_jruge, // Jump if C = 0 +ST7_jrugt, // Jump if (C + Z = 0) +ST7_jrule, // Jump if (C + Z = 1) +ST7_ld, // Load +ST7_mul, // Multiply +ST7_neg, // Negate +ST7_nop, // No Operation +ST7_or, // OR Operation +ST7_pop, // Pop from the Stack +ST7_push, // Push onto the Stack +ST7_rcf, // Reset carry flag +ST7_ret, // Subroutine Return +ST7_rim, // Enable Interrupts +ST7_rlc, // Rotate left true +ST7_rrc, // Rotate right true +ST7_rsp, // Reset Stack Pointer +ST7_sbc, // Subtract with Carry +ST7_scf, // Set carry flag +ST7_sim, // Disable Interrupts +ST7_sla, // Shift left Arithmetic +ST7_sll, // Shift left Logic +ST7_srl, // Shift right Logic +ST7_sra, // Shift right Arithmetic +ST7_sub, // Substraction +ST7_swap, // SWAP nibbles +ST7_tnz, // Test for Neg & Zero +ST7_trap, // S/W trap +ST7_wfi, // Wait for Interrupt +ST7_xor, // Exclusive OR + +ST7_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +IA64_null, // 0 +IA64_0, // 1 +IA64_1, // 2 +IA64_a, // 3 +IA64_acq, // 4 +IA64_add, // 5 Add +IA64_addl, // 6 +IA64_addp4, // 7 Add pointer +IA64_adds, // 8 +IA64_alloc, // 9 Allocate stack frame +IA64_and, // 10 Logical AND +IA64_andcm, // 11 And complement +IA64_b, // 12 +IA64_bias, // 13 +IA64_br, // 14 Branch +IA64_break, // 15 Break +IA64_brl, // 16 Branch long +IA64_brp, // 17 Branch predict +IA64_bsw, // 18 Bank switch +IA64_c, // 19 +IA64_call, // 20 +IA64_cexit, // 21 +IA64_chk, // 22 Speculation check +IA64_cloop, // 23 +IA64_clr, // 24 +IA64_clrrrb, // 25 Clear RRB +IA64_cmp, // 26 Compare +IA64_cmp4, // 27 Compare word +IA64_cmpxchg1, // 28 Compare and exchange byte +IA64_cmpxchg2, // 29 Compare and exchange halfword +IA64_cmpxchg4, // 30 Compare and exchange word +IA64_cmpxchg8, // 31 Compare and exchange double word +IA64_cond, // 32 +IA64_cover, // 33 Cover stack frame +IA64_ctop, // 34 +IA64_czx1, // 35 Compute zero index +IA64_czx2, // 36 Compute zero index +IA64_d, // 37 +IA64_dep, // 38 Deposit +IA64_dpnt, // 39 +IA64_dptk, // 40 +IA64_e, // 41 +IA64_epc, // 42 Enter privileged code +IA64_eq, // 43 +IA64_excl, // 44 +IA64_exit, // 45 +IA64_exp, // 46 +IA64_extr, // 47 Extract +IA64_f, // 48 +IA64_fabs, // 49 Floating point absolute value +IA64_fadd, // 50 Floating point add +IA64_famax, // 51 Floating point absolute maximum +IA64_famin, // 52 Floating point absolute minimum +IA64_fand, // 53 Floating point logical and +IA64_fandcm, // 54 Floating point and complement +IA64_fault, // 55 +IA64_fc, // 56 Flush cache +IA64_fchkf, // 57 Floating point check flags +IA64_fclass, // 58 Floating point class +IA64_fclrf, // 59 Floating point clear flags +IA64_fcmp, // 60 Floating point compare +IA64_fcvt, // 61 Convert floating point to/from integer +IA64_fetchadd4, // 62 Fetch word and add immediate +IA64_fetchadd8, // 63 Fetch double word and add immediate +IA64_few, // 64 +IA64_fill, // 65 +IA64_flushrs, // 66 Flush register stack +IA64_fma, // 67 Floating point multiply add +IA64_fmax, // 68 Floating point maximum +IA64_fmerge, // 69 Floating point merge +IA64_fmin, // 70 Floating point minimum +IA64_fmix, // 71 Floating point mix +IA64_fmpy, // 72 Floating point multiply +IA64_fms, // 73 Floating point multiply subtract +IA64_fneg, // 74 Floating point negate +IA64_fnegabs, // 75 Floating point negate absolute value +IA64_fnma, // 76 Floating point negative multiply add +IA64_fnmpy, // 77 Floating point negative multiply +IA64_fnorm, // 78 Floating point normalize +IA64_for, // 79 Floating point logical or +IA64_fpabs, // 80 Floating point parallel absolute value +IA64_fpack, // 81 Floating point pack +IA64_fpamax, // 82 Floating point parallel absolute maximum +IA64_fpamin, // 83 Floating point parallel absolute minimum +IA64_fpcmp, // 84 Floating point parallel compare +IA64_fpcvt, // 85 Floating point parallel conversion +IA64_fpma, // 86 Floating point parallel multiply add +IA64_fpmax, // 87 Floating point parallel maximum +IA64_fpmerge, // 88 Floating point parallel merge +IA64_fpmin, // 89 Floating point parallel minimum +IA64_fpmpy, // 90 Floating point parallel multiply +IA64_fpms, // 91 Floating point parallel multiply subtract +IA64_fpneg, // 92 Floating point parallel negate +IA64_fpnegabs, // 93 Floating point parallel negate absolute value +IA64_fpnma, // 94 Floating point parallel negative multiply add +IA64_fpnmpy, // 95 Floating point parallel negative multiply +IA64_fprcpa, // 96 Floating point parallel reciprocal approximation +IA64_fprsqrta, // 97 Floating point parallel reciprocal square root approximation +IA64_frcpa, // 98 Floating point reciprocal approximation +IA64_frsqrta, // 99 Floating point reciprocal square root approximation +IA64_fselect, // 100 Floating point select +IA64_fsetc, // 101 Floating point set controls +IA64_fsub, // 102 Floating point subtract +IA64_fswap, // 103 Floating point swap +IA64_fsxt, // 104 Floating point sign extend +IA64_fwb, // 105 Flush write buffers +IA64_fx, // 106 +IA64_fxor, // 107 Floating point exclusive or +IA64_fxu, // 108 +IA64_g, // 109 +IA64_ga, // 110 +IA64_ge, // 111 +IA64_getf, // 112 Get floating point value or exponent or significand +IA64_geu, // 113 +IA64_gt, // 114 +IA64_gtu, // 115 +IA64_h, // 116 +IA64_hu, // 117 +IA64_i, // 118 +IA64_ia, // 119 +IA64_imp, // 120 +IA64_invala, // 121 Invalidate ALAT +IA64_itc, // 122 Insert translation cache +IA64_itr, // 123 Insert translation register +IA64_l, // 124 +IA64_ld1, // 125 Load byte +IA64_ld2, // 126 Load halfword +IA64_ld4, // 127 Load word +IA64_ld8, // 128 Load double word +IA64_ldf, // 129 Floating point load +IA64_ldf8, // 130 Floating point load +IA64_ldfd, // 131 Floating point load +IA64_ldfe, // 132 Floating point load +IA64_ldfp8, // 133 Floating point load pair +IA64_ldfpd, // 134 Floating point load pair +IA64_ldfps, // 135 Floating point load pair +IA64_ldfs, // 136 Floating point load +IA64_le, // 137 +IA64_leu, // 138 +IA64_lfetch, // 139 Line prefetch +IA64_loadrs, // 140 Load register stack +IA64_loop, // 141 +IA64_lr, // 142 +IA64_lt, // 143 +IA64_ltu, // 144 +IA64_lu, // 145 +IA64_m, // 146 +IA64_many, // 147 +IA64_mf, // 148 Memory fence +IA64_mix1, // 149 Mix bytes +IA64_mix2, // 150 Mix halfwords +IA64_mix4, // 151 Mix words +IA64_mov, // 152 Move register +IA64_movl, // 153 Move long immediate +IA64_mux1, // 154 Mux byte +IA64_mux2, // 155 Mux halfword +IA64_nc, // 156 +IA64_ne, // 157 +IA64_neq, // 158 +IA64_nge, // 159 +IA64_ngt, // 160 +IA64_nl, // 161 +IA64_nle, // 162 +IA64_nlt, // 163 +IA64_nm, // 164 +IA64_nop, // 165 No operation +IA64_nr, // 166 +IA64_ns, // 167 +IA64_nt1, // 168 +IA64_nt2, // 169 +IA64_nta, // 170 +IA64_nz, // 171 +IA64_or, // 172 Logical or +IA64_orcm, // 173 +IA64_ord, // 174 +IA64_pack2, // 175 Pack +IA64_pack4, // 176 Pack +IA64_padd1, // 177 Parallel add bytes +IA64_padd2, // 178 Parallel add halfwords +IA64_padd4, // 179 Parallel add words +IA64_pavg1, // 180 Parallel average bytes +IA64_pavg2, // 181 Parallel average halfwords +IA64_pavgsub1, // 182 Parallel average subtract bytes +IA64_pavgsub2, // 183 Parallel average subtract halfwords +IA64_pcmp1, // 184 Parallel compare bytes +IA64_pcmp2, // 185 Parallel compare halfwords +IA64_pcmp4, // 186 Parallel compare words +IA64_pmax1, // 187 Parallel maxmimum bytes +IA64_pmax2, // 188 Parallel maxmimum halfwords +IA64_pmin1, // 189 Parallel minimum bytes +IA64_pmin2, // 190 Parallel minimum halfwords +IA64_pmpy2, // 191 Parallel multiply +IA64_pmpyshr2, // 192 Parallel multiply and shift right +IA64_popcnt, // 193 Population count +IA64_pr, // 194 +IA64_probe, // 195 Probe address +IA64_psad1, // 196 Parallel sum of absolute difference +IA64_pshl2, // 197 Parallel shift left halfwords +IA64_pshl4, // 198 Parallel shift left words +IA64_pshladd2, // 199 Parallel shift left and Add +IA64_pshr2, // 200 Parallel shift right halfwords +IA64_pshr4, // 201 Parallel shift right words +IA64_pshradd2, // 202 Parallel shift right and Add +IA64_psub1, // 203 Parallel subtract bytes +IA64_psub2, // 204 Parallel subtract halfwords +IA64_psub4, // 205 Parallel subtract words +IA64_ptc, // 206 Purge translation cache +IA64_ptr, // 207 Purge translation register +IA64_r, // 208 +IA64_raz, // 209 +IA64_rel, // 210 +IA64_ret, // 211 +IA64_rfi, // 212 Return from interruption +IA64_rsm, // 213 Reset system mask +IA64_rum, // 214 Reset user mask +IA64_rw, // 215 +IA64_s, // 216 +IA64_s0, // 217 +IA64_s1, // 218 +IA64_s2, // 219 +IA64_s3, // 220 +IA64_sa, // 221 +IA64_se, // 222 +IA64_setf, // 223 Set floating point value, exponent, or significand +IA64_shl, // 224 Shift left +IA64_shladd, // 225 Shift left and Add +IA64_shladdp4, // 226 Shift left and Add pointer +IA64_shr, // 227 Shift right +IA64_shrp, // 228 Shift right pair +IA64_sig, // 229 +IA64_spill, // 230 +IA64_spnt, // 231 +IA64_sptk, // 232 +IA64_srlz, // 233 Serialize +IA64_ssm, // 234 Set system mask +IA64_sss, // 235 +IA64_st1, // 236 Store byte +IA64_st2, // 237 Store halfword +IA64_st4, // 238 Store word +IA64_st8, // 239 Store double word +IA64_stf, // 240 Store floating point +IA64_stf8, // 241 Store floating point +IA64_stfd, // 242 Store floating point +IA64_stfe, // 243 Store floating point +IA64_stfs, // 244 Store floating point +IA64_sub, // 245 Subtract +IA64_sum, // 246 Set user mask +IA64_sxt1, // 247 Sign extend byte +IA64_sxt2, // 248 Sign extend halfword +IA64_sxt4, // 249 Sign extend word +IA64_sync, // 250 Memory synchronization +IA64_tak, // 251 Translation access key +IA64_tbit, // 252 Test bit +IA64_thash, // 253 Translation hashed entry address +IA64_tnat, // 254 Test NaT +IA64_tpa, // 255 Translate to physical address +IA64_trunc, // 256 +IA64_ttag, // 257 Translation hashed entry tag +IA64_u, // 258 +IA64_unc, // 259 +IA64_unord, // 260 +IA64_unpack1, // 261 Unpack byte +IA64_unpack2, // 262 Unpack halfword +IA64_unpack4, // 263 Unpack word +IA64_uss, // 264 +IA64_uus, // 265 +IA64_uuu, // 266 +IA64_w, // 267 +IA64_wexit, // 268 +IA64_wtop, // 269 +IA64_x, // 270 +IA64_xchg1, // 271 Exchange bytes +IA64_xchg2, // 272 Exchange halfwords +IA64_xchg4, // 273 Exchange words +IA64_xchg8, // 274 Exchange double words +IA64_xf, // 275 +IA64_xma, // 276 Fixed point multiply add +IA64_xmpy, // 277 Fixed point multiply +IA64_xor, // 278 Exclusive or +IA64_xuf, // 279 +IA64_z, // 280 +IA64_zxt1, // 281 Zero extend byte +IA64_zxt2, // 282 Zero extend halfword +IA64_zxt4, // 283 Zero extend word +IA64_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +NET_null = 0, // Unknown Operation + +NET_add, // Add two values, returning a new value +NET_add_ovf, // Add signed integer values with overflow check +NET_add_ovf_un, // Add unsigned integer values with overflow check +NET_and, // Bitwise AND of two integral values, returns an integral value +NET_ann_arg, // +NET_ann_call, // Start of simple calling sequence +NET_ann_catch, // Start an exception filter or handler +NET_ann_data, // Multi-byte no operation +NET_ann_data_s, // Multi-byte no operation, short form +NET_ann_dead, // Stack location is no longer live +NET_ann_def, // SSA definition node +NET_ann_hoisted, // Start of the simple portion of a hoisted calling sequence +NET_ann_hoisted_call, // Start of complex argument evaluation +NET_ann_lab, // +NET_ann_live, // Mark a stack location as live +NET_ann_phi, // SSA definition node +NET_ann_ref, // SSA reference node +NET_ann_ref_s, // SSA reference node, short form +NET_arglist, // Return argument list handle for the current method +NET_beq, // Branch to target if equal +NET_beq_s, // Branch to target if equal, short form +NET_bge, // Branch to target if greater than or equal to +NET_bge_s, // Branch to target if greater than or equal to, short form +NET_bge_un, // Branch to target if greater than or equal to (unsigned or unordered) +NET_bge_un_s, // Branch to target if greater than or equal to (unsigned or unordered), short form +NET_bgt, // Branch to target if greater than +NET_bgt_s, // Branch to target if greater than, short form +NET_bgt_un, // Branch to target if greater than (unsigned or unordered) +NET_bgt_un_s, // Branch to target if greater than (unsigned or unordered), short form +NET_ble, // Branch to target if less than or equal to +NET_ble_s, // Branch to target if less than or equal to, short form +NET_ble_un, // Branch to target if less than or equal to (unsigned or unordered) +NET_ble_un_s, // Branch to target if less than or equal to (unsigned or unordered), short form +NET_blt, // Branch to target if less than +NET_blt_s, // Branch to target if less than, short form +NET_blt_un, // Branch to target if less than (unsigned or unordered) +NET_blt_un_s, // Branch to target if less than (unsigned or unordered), short form +NET_bne_un, // Branch to target if unequal or unordered +NET_bne_un_s, // Branch to target if unequal or unordered, short form +NET_box, // Convert value type to object reference +NET_br, // Branch to target +NET_br_s, // Branch to target, short form +NET_break, // Inform a debugger that a breakpoint has been reached +NET_brfalse, // Branch to target if value is zero (false) +NET_brfalse_s, // Branch to target if value is zero (false), short form +NET_brtrue, // Branch to target if value is non-zero (true) +NET_brtrue_s, // Branch to target if value is non-zero (true), short form +NET_call, // Call a method +NET_calli, // Call an indirect method +NET_callvirt, // Call a method associated with obj +NET_castclass, // Cast object to class +NET_ceq, // Push 1 (of type I4) if value1 equals value2, else 0 +NET_cgt, // Push 1 (of type I4) if value1 > value2, else 0 +NET_cgt_un, // Push 1 (of type I4) if value1 > value2, unsigned or unordered, else 0 +NET_ckfinite, // Throw ArithmeticException if value is not a finite number +NET_clt, // Push 1 (of type I4) if value1 < value2, else 0 +NET_clt_un, // Push 1 (of type I4) if value1 < value2, unsigned or unordered, else 0 +NET_conv_i, // Convert to I, pushing I on stack +NET_conv_i1, // Convert to I1, pushing I4 on stack +NET_conv_i2, // Convert to I2, pushing I4 on stack +NET_conv_i4, // Convert to I4, pushing I4 on stack +NET_conv_i8, // Convert to I8, pushing I8 on stack +NET_conv_ovf_i, // Convert to an I (on the stack as I) and throw an exception on overflow +NET_conv_ovf_i1, // Convert to an I1 (on the stack as I4) and throw an exception on overflow +NET_conv_ovf_i1_un, // Convert unsigned to an I1 (on the stack as I4) and throw an exception on overflow +NET_conv_ovf_i2, // Convert to an I2 (on the stack as I4) and throw an exception on overflow +NET_conv_ovf_i2_un, // Convert unsigned to an I2 (on the stack as I4) and throw an exception on overflow +NET_conv_ovf_i4, // Convert to an I4 (on the stack as I4) and throw an exception on overflow +NET_conv_ovf_i4_un, // Convert unsigned to an I4 (on the stack as I4) and throw an exception on overflow +NET_conv_ovf_i8, // Convert to an I8 (on the stack as I8) and throw an exception on overflow +NET_conv_ovf_i8_un, // Convert unsigned to an I8 (on the stack as I8) and throw an exception on overflow +NET_conv_ovf_i_un, // Convert to unsigned an I (on the stack as I) and throw an exception on overflow +NET_conv_ovf_u, // Convert to a U (on the stack as I) and throw an exception on overflow +NET_conv_ovf_u1, // Convert to a U1 (on the stack as I4) and throw an exception on overflow +NET_conv_ovf_u1_un, // Convert to unsigned a U1 (on the stack as I4) and throw an exception on overflow +NET_conv_ovf_u2, // Convert to a U2 (on the stack as I4) and throw an exception on overflow +NET_conv_ovf_u2_un, // Convert to unsigned a U2 (on the stack as I4) and throw an exception on overflow +NET_conv_ovf_u4, // Convert to a U4 (on the stack as I4) and throw an exception on overflow +NET_conv_ovf_u4_un, // Convert to unsigned a U3 (on the stack as I4) and throw an exception on overflow +NET_conv_ovf_u8, // Convert to a U8 (on the stack as I8) and throw an exception on overflow +NET_conv_ovf_u8_un, // Convert to unsigned a U8 (on the stack as I8) and throw an exception on overflow +NET_conv_ovf_u_un, // Convert to unsigned a U (on the stack as I) and throw an exception on overflow +NET_conv_r4, // Convert to R4, pushing F on stack +NET_conv_r8, // Convert to R8, pushing F on stack +NET_conv_r_un, // Convert unsigned integer to floating point, pushing F on stack +NET_conv_u, // Convert to U, pushing I on stack +NET_conv_u1, // Convert to U1, pushing I4 on stack +NET_conv_u2, // Convert to U2, pushing I4 on stack +NET_conv_u4, // Convert to U4, pushing I4 on stack +NET_conv_u8, // Convert to U8, pushing I8 on stack +NET_cpblk, // Copy data from memory to memory +NET_cpobj, // Copy a value type +NET_div, // Divide two values to return a quotient or floating point result +NET_div_un, // Divide two values, unsigned, returning a quotient +NET_dup, // Duplicate value on the top of the stack +NET_endfilter, // End filter clause of SEH exception handling +NET_endfinally, // End fault clause of an exception block +NET_initblk, // Set a block of memory to a given byte +NET_initobj, // Initialize a value type +NET_isinst, // Test if an object is an instance of a class or interface, returning NULL or an instance of that class or interface +NET_jmp, // Exit current method and jump to specified method +NET_ldarg, // Load argument numbered num onto stack +NET_ldarg_0, // Load argument 0 onto stack +NET_ldarg_1, // Load argument 1 onto stack +NET_ldarg_2, // Load argument 2 onto stack +NET_ldarg_3, // Load argument 3 onto stack +NET_ldarg_s, // Load argument numbered num onto stack, short form +NET_ldarga, // Fetch the address of argument +NET_ldarga_s, // Fetch the address of argument, short form +NET_ldc_i4, // Push num of type I4 onto the stack as I4 +NET_ldc_i4_0, // Push 0 onto the stack as I4 +NET_ldc_i4_1, // Push 1 onto the stack as I4 +NET_ldc_i4_2, // Push 2 onto the stack as I4 +NET_ldc_i4_3, // Push 3 onto the stack as I4 +NET_ldc_i4_4, // Push 4 onto the stack as I4 +NET_ldc_i4_5, // Push 5 onto the stack as I4 +NET_ldc_i4_6, // Push 6 onto the stack as I4 +NET_ldc_i4_7, // Push 7 onto the stack as I4 +NET_ldc_i4_8, // Push 8 onto the stack as I4 +NET_ldc_i4_m1, // Push -1 onto the stack as I4 +NET_ldc_i4_s, // Push num onto the stack as I4, short form +NET_ldc_i8, // Push num of type I8 onto the stack as I8 +NET_ldc_r4, // Push num of type R4 onto the stack as F +NET_ldc_r8, // Push num of type R8 onto the stack as F +NET_ldelem_i, // Load the element at index with type I onto the top of the stack as an I +NET_ldelem_i1, // Load the element at index with type I1 onto the top of the stack as an I4 +NET_ldelem_i2, // Load the element at index with type I2 onto the top of the stack as an I4 +NET_ldelem_i4, // Load the element at index with type I4 onto the top of the stack as an I4 +NET_ldelem_i8, // Load the element at index with type I8 onto the top of the stack as an I8 +NET_ldelem_r4, // Load the element at index with type R4 onto the top of the stack as an F +NET_ldelem_r8, // Load the element at index with type R8 onto the top of the stack as an F +NET_ldelem_ref, // Load the element at index, an object, onto the top of the stack as an O +NET_ldelem_u1, // Load the element at index with type U1 onto the top of the stack as an I4 +NET_ldelem_u2, // Load the element at index with type U2 onto the top of the stack as an I4 +NET_ldelem_u4, // Load the element at index with type U4 onto the top of the stack as an I4 +NET_ldelema, // Load the address of element at index onto the top of the stack +NET_ldfld, // Push the value of field of object on the stack +NET_ldflda, // Push the address of field of object on the stack +NET_ldftn, // Push a pointer to a method referenced by method on the stack +NET_ldind_i, // Indirect load value of type I as I stack +NET_ldind_i1, // Indirect load value of type I1 as I4 on stack +NET_ldind_i2, // Indirect load value of type I2 as I4 on stack +NET_ldind_i4, // Indirect load value of type I4 as I4 on stack +NET_ldind_i8, // Indirect load value of type I8 as I8 on stack +NET_ldind_r4, // Indirect load value of type R4 as F stack +NET_ldind_r8, // Indirect load value of type R8 as F stack +NET_ldind_ref, // Indirect load value of type object ref as O on stack +NET_ldind_u1, // Indirect load value of type U1 as I4 stack +NET_ldind_u2, // Indirect load value of type U2 as I4 stack +NET_ldind_u4, // Indirect load value of type U4 as I4 stack +NET_ldlen, // Push the length (of type U) of array on the stack +NET_ldloc, // Load local variable of index indx onto stack +NET_ldloc_0, // Load local variable 0 onto stack +NET_ldloc_1, // Load local variable 1 onto stack +NET_ldloc_2, // Load local variable 2 onto stack +NET_ldloc_3, // Load local variable 3 onto stack +NET_ldloc_s, // Load local variable of index indx onto stack, short form +NET_ldloca, // Load address of local variable +NET_ldloca_s, // Load address of local variable, short form +NET_ldnull, // Push null GC reference on the stack +NET_ldobj, // Copy instance of value type to the stack. +NET_ldsfld, // Push the value of field on the stack +NET_ldsflda, // Push the address of the static field on the stack +NET_ldstr, // Push a string object for the literal string +NET_ldtoken, // Convert metadata token to its runtime representation +NET_ldvirtftn, // Push address of virtual method on the stack +NET_leave, // Exit a protected region of code +NET_leave_s, // Exit a protected region of code, short form +NET_localloc, // Allocate space from the local memory pool +NET_mkrefany, // Push a typed reference to pointer of type class onto the stack +NET_mul, // Multiply values +NET_mul_ovf, // Multiply signed integer values. Signed result must fit in same size +NET_mul_ovf_un, // Multiply unsigned integer values. Unsigned result must fit in same size +NET_neg, // Negate value +NET_newarr, // Create a zero-based, one-dimensional array +NET_newobj, // Create a new object +NET_nop, // Do nothing +NET_not, // Bitwise complement +NET_or, // Bitwise OR of two integer values, returns an integer +NET_pop, // Pop a value from the stack +NET_refanytype, // Push the type token stored in a typed reference +NET_refanyval, // Push the address stored in a typed reference +NET_rem, // Remainder of dividing value1 by value2 +NET_rem_un, // Remainder of unsigned dividing value1 by value2 +NET_ret, // Return from method, possibly returning a value +NET_rethrow, // Rethrow the current exception +NET_shl, // Shift an integer to the left (shifting in zeros) +NET_shr, // Shift an integer right, (shift in sign), return an integer +NET_shr_un, // Shift an integer right, (shift in zero), return an integer +NET_sizeof, // Push the size, in bytes, of a value type as a U4 +NET_starg, // Store a value to argument +NET_starg_s, // Store a value to argument, short form +NET_stelem_i, // Replace array element at index with the I value on the stack +NET_stelem_i1, // Replace array element at index with the I1 value on the stack +NET_stelem_i2, // Replace array element at index with the I2 value on the stack +NET_stelem_i4, // Replace array element at index with the I4 value on the stack +NET_stelem_i8, // Replace array element at index with the I8 value on the stack +NET_stelem_r4, // Replace array element at index with the R4 value on the stack +NET_stelem_r8, // Replace array element at index with the R8 value on the stack +NET_stelem_ref, // Replace array element at index with the ref value on the stack +NET_stfld, // Store into a field of an object +NET_stind_i, // Store value of type I into memory at address +NET_stind_i1, // Store value of type I1 into memory at address +NET_stind_i2, // Store value of type I2 into memory at address +NET_stind_i4, // Store value of type I4 into memory at address +NET_stind_i8, // Store value of type I8 into memory at address +NET_stind_r4, // Store value of type R4 into memory at address +NET_stind_r8, // Store value of type R8 into memory at address +NET_stind_ref, // Store value of type object ref (type O) into memory at address +NET_stloc, // Pop value from stack into local variable +NET_stloc_0, // Pop value from stack into local variable 0 +NET_stloc_1, // Pop value from stack into local variable 1 +NET_stloc_2, // Pop value from stack into local variable 2 +NET_stloc_3, // Pop value from stack into local variable 3 +NET_stloc_s, // Pop value from stack into local variable, short form +NET_stobj, // Store a value type from the stack into memory +NET_stsfld, // Store a static field of a class +NET_sub, // Subtract value2 from value1, returning a new value +NET_sub_ovf, // Subtract I from an I. Signed result must fit in same size +NET_sub_ovf_un, // Subtract U from a U. Unsigned result must fit in same size +NET_switch, // Jump to one of N values +NET_tail_, // (prefix) subsequent call terminates current method +NET_throw, // Throw an exception +NET_unaligned_, // (prefix) subsequent pointer instruction may be unaligned +NET_unbox, // Convert boxed value type to its raw form +NET_volatile_, // (prefix) subsequent pointer reference is volatile +NET_xor, // Bitwise XOR of integer values, returns an integer +NET_ldelem, // Load the element at index onto the top of the stack +NET_stelem, // Replace array element at index with the ref value on the stack + +// 2.0 instructions +NET_unbox_any, // Extract a value-type from obj, its boxed representation +NET_constrained_, // (prefix) invoke a member on a value of a variable type +NET_no_, // (prefix) possibly skip a fault check +NET_readonly_, // (prefix) following instruction returns a controlled-mutability managed pointer + +NET_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + * HCS12X opcodes added by alexbratovic@bratspeed.com 12/12/2007 + * XGATE opcodes added by alexbratovic@bratspeed.com 12/09/2009 + */ + + + + +enum +{ +MC12_null = 0, // Unknown Operation + +MC12_aba, // Add B to A +MC12_abx, // Add B to X +MC12_aby, // Add B to Y +MC12_adca, // Add with carry to A +MC12_adcb, // Add with carry to B +MC12_adda, // Add memory to A +MC12_addb, // Add memory to B +MC12_addd, // Add to D +MC12_anda, // AND A with memory +MC12_andb, // AND B with memory +MC12_andcc, // Logical AND CCR with memory +MC12_asl, // Arithmetic shift left memory +MC12_asla, // Arithmetic shift left A +MC12_aslb, // Arithmetic shift left B +MC12_asld, // Arithmetic shift left D +MC12_asr, // Arithmetic shift right memory +MC12_asra, // Arithmetic shift right A +MC12_asrb, // Arithmetic shift right B +MC12_bcc, // Branch if carry clear +MC12_bclr, // Clear bits in memory +MC12_bcs, // Branch if carry set +MC12_beq, // Branch if equal +MC12_bge, // Branch if greater than or equal +MC12_bgnd, // Enter background debug mode +MC12_bgt, // Branch if greater than +MC12_bhi, // Branch if higher +MC12_bhs, // Branch if higher or same +MC12_bita, // Bit test A +MC12_bitb, // Bit test B +MC12_ble, // Branch if less than or equal +MC12_blo, // Branch if lower +MC12_bls, // Branch if lower or same +MC12_blt, // Branch if less than +MC12_bmi, // Branch if minus +MC12_bne, // Branch if not equal +MC12_bpl, // Branch if plus +MC12_bra, // Branch always +MC12_brclr, // Branch if selected bits clear +MC12_brn, // Branch never +MC12_brset, // Branch if selected bits set +MC12_bset, // Set bits in memory +MC12_bsr, // Branch to subroutine +MC12_bvc, // Branch if overflow clear +MC12_bvs, // Branch if overflow set +MC12_call, // Call subroutine in expanded memory +MC12_cba, // Compare A to B +MC12_clc, // Clear C bit +MC12_cli, // Clear I bit +MC12_clr, // Clear memory +MC12_clra, // Clear A +MC12_clrb, // Clear B +MC12_clv, // Clear V bit +MC12_cmpa, // Compare A to memory +MC12_cmpb, // Compare B to memory +MC12_com, // One's complement memory +MC12_coma, // One's complement A +MC12_comb, // One's complement B +MC12_cpd, // Compare D to memory (16-bit) +MC12_cps, // Compare SP to memory (16-bit) +MC12_cpx, // Compare X to memory (16-bit) +MC12_cpy, // Compare Y to memory (16-bit) +MC12_daa, // Decimal adjust A +MC12_dbeq, // Decrement counter and branch if = 0 +MC12_dbne, // Decrement counter and branch if != 0 +MC12_dec, // Decrement memory +MC12_deca, // Decrement A +MC12_decb, // Decrement B +MC12_des, // Decrement SP +MC12_dex, // Decrement X +MC12_dey, // Decrement Y +MC12_ediv, // 32 by 16 divide (unsigned) Quotient->Y Remainder->D +MC12_edivs, // 32 by 16 divide (signed) Quotient->Y Remainder->D +MC12_emacs, // Multiply and accumulate (signed) +MC12_emaxd, // MAX of two unsigned 16-bit values result to accumulator +MC12_emaxm, // MAX of two unsigned 16-bit values result to memory +MC12_emind, // MIN of two unsigned 16-bit values result to accumulator +MC12_eminm, // MIN of two unsigned 16-bit values result to memory +MC12_emul, // 16 by 16 multiply (unsigned) +MC12_emuls, // 16 by 16 multiply (signed) +MC12_eora, // Exclusive OR A with memory +MC12_eorb, // Exclusive OR B with memory +MC12_etbl, // 16-bit table lookup and interpolate +MC12_exg, // Exchange register to register +MC12_fdiv, // 16 by 16 fractional divide Remainder->D +MC12_ibeq, // Increment counter and branch if = 0 +MC12_ibne, // Increment counter and branch if != 0 +MC12_idiv, // 16 by 16 integer divide (unsigned) Remainder->D +MC12_idivs, // 16 by 16 integer divide (signed) Remainder->D +MC12_inc, // Increment memory +MC12_inca, // Increment A +MC12_incb, // Increment B +MC12_ins, // Increment SP +MC12_inx, // Increment X +MC12_iny, // Increment Y +MC12_jmp, // Jump Address +MC12_jsr, // Jump to subroutine +MC12_lbcc, // Long branch if carry clear +MC12_lbcs, // Long branch if carry set +MC12_lbeq, // Long branch if equal +MC12_lbge, // Long branch if greater than or equal +MC12_lbgt, // Long branch if greater than +MC12_lbhi, // Long branch if higher +MC12_lbhs, // Long branch if higher or same +MC12_lble, // Long branch if less than or equal +MC12_lblo, // Long branch if lower +MC12_lbls, // Long branch if lower or same +MC12_lblt, // Long branch if less than +MC12_lbmi, // Long branch if minus +MC12_lbne, // Long branch if not equal +MC12_lbpl, // Long branch if plus +MC12_lbra, // Long branch always +MC12_lbrn, // Long branch never +MC12_lbvc, // Long branch if overflow clear +MC12_lbvs, // Long branch if overflow set +MC12_ldaa, // Load A +MC12_ldab, // Load B +MC12_ldd, // Load D +MC12_lds, // Load SP +MC12_ldx, // Load X +MC12_ldy, // Load Y +MC12_leas, // Load effective address into SP +MC12_leax, // Load effective address into X +MC12_leay, // Load effective address into Y +MC12_lsl, // Logic shift left memory +MC12_lsla, // Logic shift left A +MC12_lslb, // Logic shift left B +MC12_lsld, // Logic shift left D +MC12_lsr, // Logic shift right memory +MC12_lsra, // Logic shift right A +MC12_lsrb, // Logic shift right B +MC12_lsrd, // Logic shift right D +MC12_maxa, // MAX of two unsigned 8-bit values result to accumulator +MC12_maxm, // MAX of two unsigned 8-bit values result to memory +MC12_mem, // Membership function +MC12_mina, // MIN of two unsigned 8-bit values result to accumulator +MC12_minm, // MIN of two unsigned 8-bit values result to memory +MC12_movb, // Move byte (8-bit) +MC12_movw, // Move word (16-bit) +MC12_mul, // 8 by 8 multiply (unsigned) +MC12_neg, // Two's complement memory +MC12_nega, // Two's complement A +MC12_negb, // Two's complement B +MC12_nop, // Null operation +MC12_oraa, // OR A with memory +MC12_orab, // OR B with memory +MC12_orcc, // Logical OR CCR with memory +MC12_psha, // Push A +MC12_pshb, // Push B +MC12_pshc, // Push CCR +MC12_pshd, // Push D +MC12_pshx, // Push X +MC12_pshy, // Push Y +MC12_pula, // Pull A +MC12_pulb, // Pull B +MC12_pulc, // Pull CCR +MC12_puld, // Pull D +MC12_pulx, // Pull X +MC12_puly, // Pull Y +MC12_rev, // MIN-MAX rule evaluation +MC12_revw, // MIN-MAX rule evaluation +MC12_rol, // Rotate left memory through carry +MC12_rola, // Rotate left A through carry +MC12_rolb, // Rotate left B through carry +MC12_ror, // Rotate right memory through carry +MC12_rora, // Rotate right A through carry +MC12_rorb, // Rotate right B through carry +MC12_rtc, // Return from call +MC12_rti, // Return from interrupt +MC12_rts, // Return from subroutine +MC12_sba, // Subtract B from A +MC12_sbca, // Subtract with borrow from A +MC12_sbcb, // Subtract with borrow from B +MC12_sec, // Set C bit +MC12_sei, // Set I bit +MC12_sev, // Set V bit +MC12_sex, // Sign extend 8-Bit operand +MC12_staa, // Store A +MC12_stab, // Store B +MC12_std, // Store D +MC12_stop, // Stop +MC12_sts, // Store SP +MC12_stx, // Store X +MC12_sty, // Store Y +MC12_suba, // Subtract memory from A +MC12_subb, // Subtract memory from B +MC12_subd, // Subtract memory from D +MC12_swi, // Software interrupt +MC12_tab, // Transfer A to B +MC12_tap, // Transfer A to CCR +MC12_tba, // Transfer B to A +MC12_tbeq, // Test counter and branch if = 0 +MC12_tbl, // 8-bit table lookup and interpolate +MC12_tbne, // Test counter and branch if != 0 +MC12_tfr, // Transfer register to register +MC12_tpa, // Transfer CCR to A +MC12_trap, // Unimplemented opcode interrupt +MC12_tst, // Test memory for zero or minus +MC12_tsta, // Test A for zero or minus +MC12_tstb, // Test B for zero or minus +MC12_tsx, // Transfer SP to X +MC12_tsy, // Transfer SP to Y +MC12_txs, // Transfer X to SP +MC12_tys, // Transfer Y to SP +MC12_wai, // Wait for interrupt +MC12_wav, // Calculates numerator and denominator for weighted average calculation +MC12_wavr, // Resumes execution of interrupted WAV instruction +MC12_xgdx, // Exchange D with X +MC12_xgdy, // Exchange D with Y + +// Pseudoinstructions +MC12_skip1, // Skip one byte +MC12_skip2, // Skip two bytes + +// hcs12x only instructions, 16-bit unless indicated +MC12X_addx, // Add without Carry to X +MC12X_addy, // Add without Carry to Y +MC12X_aded, // Add with Carry to D(A:B) +MC12X_adex, // Add with Carry to X +MC12X_adey, // Add with Carry to Y +MC12X_andx, // Logical AND X with Memory +MC12X_andy, // Logical AND Y with Memory +MC12X_aslw, // Arithmetic Shift Left +MC12X_aslx, // Arithmetic Shift Left X +MC12X_asly, // Arithmetic Shift Left Y +MC12X_asrw, // Arithmetic Shift Right +MC12X_asrx, // Arithmetic Shift Right X +MC12X_asry, // Arithmetic Shift Right Y +MC12X_bitx, // Logical AND X with Memory, does not change X or Memory +MC12X_bity, // Logical AND Y with Memory, does not change Y or Memory +MC12X_btas, // Bit(s) test and Set in Memory, Set CCR flags +MC12X_clrw, // Clear Memory Location +MC12X_clrx, // Clear X +MC12X_clry, // Clear Y +MC12X_comw, // One's Complement Memory +MC12X_comx, // One's Complement X +MC12X_comy, // One's Complement Y +MC12X_cped, // Compare D to Memory with Borrow +MC12X_cpes, // Compare SP to Memory with Borrow +MC12X_cpex, // Compare X to Memory with Borrow +MC12X_cpey, // Compare Y to Memory with Borrow +MC12X_decw, // Decrement Memory +MC12X_decx, // Decrement X +MC12X_decy, // Decrement Y +MC12X_eorx, // Exclusive OR X with Memory +MC12X_eory, // Exclusive OR Y with Memory +MC12X_gldaa, // Load A from Global Memory (8-bit) +MC12X_gldab, // Load B from Global Memory (8-bit) +MC12X_gldd, // Load D(A:B) from Global Memory +MC12X_glds, // Load SP from Global Memory +MC12X_gldx, // Load X from Global Memory +MC12X_gldy, // Load Y from Global Memory +MC12X_gstaa, // Store A to Global Memory (8-bit) +MC12X_gstab, // Store B to Global Memory (8-bit) +MC12X_gstd, // Store D(A:B) to Global Memory +MC12X_gsts, // Store SP to Global Memory +MC12X_gstx, // Store X to Global Memory +MC12X_gsty, // Store Y to Global Memory +MC12X_incw, // Increment Memory +MC12X_incx, // Increment X +MC12X_incy, // Increment Y +MC12X_lsrw, // Logical Shift Memory to Right +MC12X_lsrx, // Logical Shift X to Right +MC12X_lsry, // Logical Shift Y to Right +MC12X_negw, // Two's Complement Memory +MC12X_negx, // Two's Complement X +MC12X_negy, // Two's Complement Y +MC12X_orx, // Logical OR X with memory +MC12X_ory, // Logical OR Y with memory +MC12X_pshcw, // Push CCR onto Stack (16-bit) +MC12X_pulcw, // Pull CCR from Stack (16-bit) +MC12X_rolw, // Rotate Memory Left through Carry +MC12X_rolx, // Rotate X Left through Carry +MC12X_roly, // Rotate Y Left through Carry +MC12X_rorw, // Rotate Memory Right through Carry +MC12X_rorx, // Rotate X Right through Carry +MC12X_rory, // Rotate Y Right through Carry +MC12X_sbed, // Subtract with Borrow from D +MC12X_sbex, // Subtract with Borrow from X +MC12X_sbey, // Subtract with Borrow from Y +MC12X_subx, // Subtract Memory from X +MC12X_suby, // Subtract Memory from Y +MC12X_tstw, // Test Memory for Zero or Minus +MC12X_tstx, // Test X for Zero or Minus +MC12X_tsty, // Test Y for Zero or Minus + +// hcs12x V2 cpu only instructions +MC12X_sys, // System call interrupt + +// hcs12x XGATE co-processor instructions +MC12XGATE_adc, // Add with Carry +MC12XGATE_add, // Add without Carry +MC12XGATE_addh, // Add Immediate 8 bit Constant (High Byte) +MC12XGATE_addl, // Add Immediate 8 bit Constant (Low Byte) +MC12XGATE_and, // Logical AND +MC12XGATE_andh, // Logical AND Immediate 8 bit Constant (High Byte) +MC12XGATE_andl, // Logical AND Immediate 8 bit Constant (Low Byte) +MC12XGATE_asr, // Arithmetic Shift Right +MC12XGATE_bcc, // Branch if Carry Cleared (Same as BHS) +MC12XGATE_bcs, // Branch if Carry Set (Same as BLO) +MC12XGATE_beq, // Branch if Equal +MC12XGATE_bfext, // Bit Field Extract +MC12XGATE_bffo, // Bit Field Find First One +MC12XGATE_bfins, // Bit Field Insert +MC12XGATE_bfinsi, // Bit Field Insert and Invert +MC12XGATE_bfinsx, // Bit Field Insert and XNOR +MC12XGATE_bge, // Branch if Greater than or Equal to Zero +MC12XGATE_bgt, // Branch if Greater than Zero +MC12XGATE_bhi, // Branch if Higher +MC12XGATE_bhs, // Branch if Higher or Same (Same as BCC) +MC12XGATE_bith, // Bit Test Immediate 8 bit Constant (High Byte) +MC12XGATE_bitl, // Bit Test Immediate 8 bit Constant (Low Byte) +MC12XGATE_ble, // Branch if Less or Equal to Zero +MC12XGATE_blo, // Branch if Carry Set (Same as BCS) +MC12XGATE_bls, // Branch if Lower or Same +MC12XGATE_blt, // Branch if Lower than Zero +MC12XGATE_bmi, // Branch if Minus +MC12XGATE_bne, // Branch if Not Equal +MC12XGATE_bpl, // Branch if Plus +MC12XGATE_bra, // Branch Always +MC12XGATE_brk, // Break +MC12XGATE_bvc, // Branch if Overflow Cleared +MC12XGATE_bvs, // Branch if Overflow Set +MC12XGATE_cmp, // Compare +MC12XGATE_cmpl, // Compare Immediate 8 bit Constant (Low Byte) +MC12XGATE_com, // One's Complement +MC12XGATE_cpc, // Compare with Carry +MC12XGATE_cpch, // Compare Immediate 8 bit Constant with Carry(High Byte) +MC12XGATE_csem, // Clear Semaphore +MC12XGATE_csl, // Logical Shift Left with Carry +MC12XGATE_csr, // Logical Shift Right with Carry +MC12XGATE_jal, // Jump and Link +MC12XGATE_ldb, // Load Byte from Memory (Low Byte) +MC12XGATE_ldh, // Load Immediate 8 bit Constant (High Byte) +MC12XGATE_ldl, // Load Immediate 8 bit Constant (Low Byte) +MC12XGATE_ldw, // Load Word from Memory +MC12XGATE_lsl, // Logical Shift Left +MC12XGATE_lsr, // Logical Shift Right +MC12XGATE_mov, // Move Register Content +MC12XGATE_neg, // Two's Complement +MC12XGATE_nop, // No Operation +MC12XGATE_or, // Logical OR +MC12XGATE_orh, // Logical OR Immediate 8 bit Constant (High Byte) +MC12XGATE_orl, // Logical OR Immediate 8 bit Constant (Low Byte) +MC12XGATE_par, // Calculate Parity +MC12XGATE_rol, // Rotate Left +MC12XGATE_ror, // Rotate Right +MC12XGATE_rts, // Return to Scheduler +MC12XGATE_sbc, // Subtract with Carry +MC12XGATE_sex, // Sign Extend Byte to Word +MC12XGATE_sif, // Set Interrupt Flag +MC12XGATE_ssem, // Set Semaphore +MC12XGATE_stb, // Store Byte to Memory (Low Byte) +MC12XGATE_stw, // Store Word to Memory +MC12XGATE_sub, // Subtract without Carry +MC12XGATE_subh, // Subtract Immediate 8 bit Constant (High Byte) +MC12XGATE_subl, // Subtract Immediate 8 bit Constant (Low Byte) +MC12XGATE_tfr, // Transfer from and to Special Registers +MC12XGATE_tst, // Test Register +MC12XGATE_xnor, // Logical Exclusive NOR +MC12XGATE_xnorh, // Logical Exclusive NOR Immediate 8 bit Constant (High Byte) +MC12XGATE_xnorl, // Logical Exclusive NOR Immediate 8 bit Constant (Low Byte) + +// Pseudo instructions that combine two IMM8 into IMM16 +MC12XGATE_add16, // Add Immediate 16 bit Constant +MC12XGATE_and16, // Logical AND Immediate 16 bit Constant +MC12XGATE_cmp16, // Compare Immediate 16 bit Constant +MC12XGATE_ldw16, // Load Immediate 16 bit Constant +MC12XGATE_or16, // Logical OR Immediate 16 bit Constant +MC12XGATE_sub16, // Subtract Immediate 16 bit Constant +MC12XGATE_xnor16, // Logical Exclusive NOR Immediate 16 bit Constant + +MC12_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + + MC6816_null = 0, // Unknown Operation + + // DATA MOVEMENT INSTRUCTIONS + // -------------------------- + + // LOAD INSTRUCTIONS + MC6816_ldaa, // Load A + MC6816_ldab, // Load B + MC6816_ldd, // Load D + MC6816_lde, // Load E + MC6816_lded, // Load Concatenated D and E + + // MOVE INSTRUCTIONS + MC6816_movb, // Move Byte + MC6816_movw, // Move Word + + // STORE INSTRUCTIONS + MC6816_staa, // Store A + MC6816_stab, // Store B + MC6816_std, // Store D + MC6816_ste, // Store E + MC6816_sted, // Store Concatenated D and E + + // TRANSFER INSTRUCTIONS + MC6816_tab, // Transfer A to B + MC6816_tba, // Transfer B to A + MC6816_tde, // Transfer D to E + MC6816_ted, // Transfer E to D + + // EXCHANGE INSTRUCTIONS + MC6816_xgab, // Exchange A with B + MC6816_xgde, // Exchange D with E + + // MATHEMATIC INSTRUCTIONS + // ----------------------- + + // ADDITION AND SUBSTRACTION INSTRUCTIONS + MC6816_aba, // Add B to A + MC6816_adca, // Add with Carry to A + MC6816_adcb, // Add with Carry to B + MC6816_adcd, // Add with Carry to D + MC6816_adce, // Add with Carry to E + MC6816_adda, // Add to A + MC6816_addb, // Add to B + MC6816_addd, // Add to D + MC6816_adde, // Add to E + MC6816_ade, // Add D to E + MC6816_sba, // Subtract B from A + MC6816_sbca, // Subtract with Carry from A + MC6816_sbcb, // Subtract with Carry from B + MC6816_sbcd, // Subtract with Carry from D + MC6816_sbce, // Subtract with Carry from E + MC6816_sde, // Subtract D from E + MC6816_suba, // Subtract from A + MC6816_subb, // Subtract from B + MC6816_subd, // Subtract from D + MC6816_sube, // Subtract from E + + // BINARY CODED DECIMAL INSTRUCTIONS + MC6816_daa, // Decimal Adjust A + MC6816_sxt, // Sign Extend B into A + + // COMPARE AND TEST INSTRUCTIONS + MC6816_cba, // Compare A to B + MC6816_cmpa, // Compare A to Memory + MC6816_cmpb, // Compare B to Memory + MC6816_cpd, // Compare D to Memory + MC6816_cpe, // Compare E to Memory + MC6816_tst, // Test for Zero or Minus + MC6816_tsta, // Test A for Zero or Minus + MC6816_tstb, // Test B for Zero or Minus + MC6816_tstd, // Test D for Zero or Minus + MC6816_tste, // Test E for Zero or Minus + MC6816_tstw, // Test for Zero or Minus Word + + // MULTIPLICATION AND DIVISION INSTRUCTIONS + MC6816_ediv, // Extended Unsigned Divide + MC6816_edivs, // Extended Signed Divide + MC6816_emul, // Extended Unsigned Multiply + MC6816_emuls, // Extended Signed Multiply + MC6816_fdiv, // Unsigned Fractional Divide + MC6816_fmuls, // Signed Fractional Multiply + MC6816_idiv, // Integer Divide + MC6816_mul, // Multiply + + // DECREMENT AND INCREMENT INSTRUCTIONS + MC6816_dec, // Decrement Memory + MC6816_deca, // Decrement A + MC6816_decb, // Decrement B + MC6816_decw, // Decrement Memory Word + MC6816_inc, // Increment Memory + MC6816_inca, // Increment A + MC6816_incb, // Increment B + MC6816_incw, // Increment Memory Word + + // CLEAR, COMPLEMENT, AND NEGATE INSTRUCTIONS + MC6816_clr, // Clear Memory + MC6816_clra, // Clear A + MC6816_clrb, // Clear B + MC6816_clrd, // Clear D + MC6816_clre, // Clear E + MC6816_clrw, // Clear Memory Word + MC6816_com, // One's Complement Byte + MC6816_coma, // One's Complement A + MC6816_comb, // One's Complement B + MC6816_comd, // One's Complement D + MC6816_come, // One's Complement E + MC6816_comw, // One's Complement Word + MC6816_neg, // Two's Complement Byte + MC6816_nega, // Two's Complement A + MC6816_negb, // Two's Complement B + MC6816_negd, // Two's Complement D + MC6816_nege, // Two's Complement E + MC6816_negw, // Two's Complement Word + + // BOOLEAN LOGIC INSTRUCTIONS + MC6816_anda, // AND A + MC6816_andb, // AND B + MC6816_andd, // AND D + MC6816_ande, // AND E + MC6816_eora, // Exclusive OR A + MC6816_eorb, // Exclusive OR B + MC6816_eord, // Exclusive OR D + MC6816_eore, // Exclusive OR E + MC6816_oraa, // OR A + MC6816_orab, // OR B + MC6816_ord, // OR D + MC6816_ore, // OR E + + // BIT TEST AND MANIPULATION INSTRUCTIONS + // -------------------------------------- + MC6816_bita, // Bit Test A + MC6816_bitb, // Bit Test B + MC6816_bclr, // Clear Bit(s) + MC6816_bclrw, // Clear Bit(s) Word + MC6816_bset, // Set Bit(s) + MC6816_bsetw, // Set Bit(s) Word + + // SHIFT AND ROTATE INSTRUCTIONS + // ----------------------------- + MC6816_lsr, // Logic Shift Right + MC6816_lsra, // Logic Shift Right A + MC6816_lsrb, // Logic Shift Right B + MC6816_lsrd, // Logic Shift Right D + MC6816_lsre, // Logic Shift Right E + MC6816_lsrw, // Logic Shift Right Word + MC6816_asl, // Arithmetic Shift Left + MC6816_asla, // Arithmetic Shift Left A + MC6816_aslb, // Arithmetic Shift Left B + MC6816_asld, // Arithmetic Shift Left D + MC6816_asle, // Arithmetic Shift Left E + MC6816_aslw, // Arithmetic Shift Left Word + MC6816_asr, // Arithmetic Shift Right + MC6816_asra, // Arithmetic Shift Right A + MC6816_asrb, // Arithmetic Shift Right B + MC6816_asrd, // Arithmetic Shift Right D + MC6816_asre, // Arithmetic Shift Right E + MC6816_asrw, // Arithmetic Shift Right Word + MC6816_rol, // Rotate Left + MC6816_rola, // Rotate Left A + MC6816_rolb, // Rotate Left B + MC6816_rold, // Rotate Left D + MC6816_role, // Rotate Left E + MC6816_rolw, // Rotate Left Word + MC6816_ror, // Rotate Right + MC6816_rora, // Rotate Right A + MC6816_rorb, // Rotate Right B + MC6816_rord, // Rotate Right D + MC6816_rore, // Rotate Right E + MC6816_rorw, // Rotate Right Word + + // PROGRAM CONTROL INSTRUCTIONS + // ---------------------------- + + // SHORT BRANCH INSTRUCTIONS + MC6816_bra, // Branch Always + MC6816_brn, // Branch Never + MC6816_bcc, // Branch If Carry Clear + MC6816_bcs, // Branch If Carry Set + MC6816_beq, // Branch If Equal to Zero + MC6816_bmi, // Branch If Minus + MC6816_bne, // Branch If Not Equal to Zero + MC6816_bpl, // Branch If Plus + MC6816_bvc, // Branch If Overflow Clear + MC6816_bvs, // Branch If Overflow Set + MC6816_bhi, // Branch If Higher + MC6816_bls, // Branch If Lower or Same + MC6816_bge, // Branch If Greater than or Equal to Zero + MC6816_bgt, // Branch If Greater than Zero + MC6816_ble, // Branch If Less than or Equal to Zero + MC6816_blt, // Branch If Less than Zero + + // LONG BRANCH INSTRUCTIONS + MC6816_lbra, // Long Branch Always + MC6816_lbrn, // Long Branch Never + MC6816_lbcc, // Long Branch If Carry Clear + MC6816_lbcs, // Long Branch If Carry Set + MC6816_lbeq, // Long Branch If Equal to Zero + MC6816_lbev, // Long Branch If EV Set + MC6816_lbmi, // Long Branch If Minus + MC6816_lbmv, // Long Branch If MV Set + MC6816_lbne, // Long Branch If Not Equal to Zero + MC6816_lbpl, // Long Branch If Plus + MC6816_lbvc, // Long Branch If Overflow Clear + MC6816_lbvs, // Long Branch If Overflow Set + MC6816_lbhi, // Long Branch If Higher + MC6816_lbls, // Long Branch If Lower or Same + MC6816_lbge, // Long Branch If Greater than or Equal to Zero + MC6816_lbgt, // Long Branch If Greater than Zero + MC6816_lble, // Long Branch If Less than or Equal to Zero + MC6816_lblt, // Long Branch If Less than Zero + + // BIT CONDITION BRANCH INSTRUCTIONS + MC6816_brclr, // Branch if Bits Clear + MC6816_brset, // Branch if Bits Set + + // JUMP INSTRUCTION + MC6816_jmp, // Jump + + // SUBROUTINE INSTRUCTIONS + MC6816_bsr, // Branch to Subroutine + MC6816_jsr, // Jump to Subroutine + MC6816_lbsr, // Long Branch to Subroutine + MC6816_rts, // Return From Subroutine + + // INTERRUPT INSTRUCTIONS + MC6816_rti, // Return from Interrupt + MC6816_swi, // Software Interrupt + + // INDEXING AND ADDRESS EXTENSION INSTRUCTIONS + // ------------------------------------------- + + // INDEXING INSTRUCTIONS + MC6816_abx, // Add B to IX + MC6816_aby, // Add B to IY + MC6816_abz, // Add B to IZ + MC6816_adx, // Add D to IX + MC6816_ady, // Add D to IY + MC6816_adz, // Add D to IZ + MC6816_aex, // Add E to IX + MC6816_aey, // Add E to IY + MC6816_aez, // Add E to IZ + MC6816_aix, // Add Immediate Value to IX + MC6816_aiy, // Add Immediate Value to IY + MC6816_aiz, // Add Immediate Value to IZ + MC6816_cpx, // Compare IX to Memory + MC6816_cpy, // Compare IY to Memory + MC6816_cpz, // Compare IZ to Memory + MC6816_ldx, // Load IX + MC6816_ldy, // Load IY + MC6816_ldz, // Load IZ + MC6816_stx, // Store IX + MC6816_sty, // Store IY + MC6816_stz, // Store IZ + MC6816_tsx, // Transfer SP to IX + MC6816_tsy, // Transfer SP to IY + MC6816_tsz, // Transfer SP to IZ + MC6816_txs, // Transfer IX to SP + MC6816_txy, // Transfer IX to IY + MC6816_txz, // Transfer IX to IZ + MC6816_tys, // Transfer IY to SP + MC6816_tyx, // Transfer IY to IX + MC6816_tyz, // Transfer IY to IZ + MC6816_tzs, // Transfer IZ to SP + MC6816_tzx, // Transfer IZ to IX + MC6816_tzy, // Transfer IZ to IY + MC6816_xgdx, // Exchange D with IX + MC6816_xgdy, // Exchange D with IY + MC6816_xgdz, // Exchange D with IZ + MC6816_xgex, // Exchange E with IX + MC6816_xgey, // Exchange E with IY + MC6816_xgez, // Exchange E with IZ + + // ADDRESS EXTENSION INSTRUCTIONS + MC6816_tbek, // Transfer B to EK + MC6816_tbsk, // Transfer B to SK + MC6816_tbxk, // Transfer B to XK + MC6816_tbyk, // Transfer B to YK + MC6816_tbzk, // Transfer B to ZK + MC6816_tekb, // Transfer EK to B + MC6816_tskb, // Transfer SK to B + MC6816_txkb, // Transfer XK to B + MC6816_tykb, // Transfer YK to B + MC6816_tzkb, // Transfer ZK to B + + // STACKING INSTRUCTIONS + // --------------------- + MC6816_ais, // Add Immediate Data to SP + MC6816_cps, // Compare SP to Memory + MC6816_lds, // Load SP + MC6816_sts, // Store SP + MC6816_psha, // Push A + MC6816_pshb, // Push B + MC6816_pshm, // Push Multiple Registers + MC6816_pula, // Pull A + MC6816_pulb, // Pull B + MC6816_pulm, // Pull Multiple Registers + + // CONDITION CODE INSTRUCTIONS + // --------------------------- + MC6816_andp, // AND CCR + MC6816_orp, // OR CCR + MC6816_tap, // Transfer A to CCR + MC6816_tdp, // Transfer D to CCR + MC6816_tpa, // Transfer CCR MSB to A + MC6816_tpd, // Transfer CCR to D + + // DIGITAL SIGNAL PROCESSING INSTRUCTIONS + // -------------------------------------- + MC6816_ace, // Add E to AM[31:15] + MC6816_aced, // Add concatenated E and D to AM + MC6816_aslm, // Arithmetic Shift Left AM + MC6816_asrm, // Arithmetic Shift Right AM + MC6816_clrm, // Clear AM + MC6816_ldhi, // Initialize HR and IR + MC6816_mac, // Multiply and Accumulate + MC6816_pshmac, // Push MAC State + MC6816_pulmac, // Pull MAC State + MC6816_rmac, // Repeating Multiply and Accumulate + MC6816_tdmsk, // Transfer D to XMSK:YMSK + MC6816_tedm, // Transfer E and T to AM[31:0] + MC6816_tem, // Transfer E to AM[31:16] + MC6816_tmer, // Transfer AM to E Rounded + MC6816_tmet, // Transfer AM to E Truncated + MC6816_tmxed, // Transfer AM t IX:E:D + + // STOP AND WAIT INSTRUCTIONS + // -------------------------- + MC6816_lpstop, // Low Power Stop + MC6816_wai, // Wait for Interrupt + + // BACKGROUND MODE AND NULL OPERATIONS + // ----------------------------------- + MC6816_bgnd, // Enter Background Debugging Mode + MC6816_nop, // Null operation + + MC6816_last +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +I960_null = 0, // Unknown Operation + +I960_addc, // Add ordinal with carry +I960_addi, // Add integer +I960_addo, // Add ordinal +I960_alterbit, // Alter bit +I960_and, // Src2 AND src1 +I960_andnot, // Src2 AND (NOT src1) +I960_atadd, // Atomic add +I960_atmod, // Atomic modify +I960_b, // Branch +I960_bal, // Branch and Link +I960_balx, // Branch and Link Extended +I960_bbc, // Check bit and branch if clear +I960_bbs, // Check bit and branch if set +I960_bno, // Branch if unordered/false +I960_bg, // Branch if greater +I960_be, // Branch if equal/true +I960_bge, // Branch if greater or equal +I960_bl, // Branch if less +I960_bne, // Branch if not equal +I960_ble, // Branch if less or equal +I960_bo, // Branch if ordered +I960_bx, // Branch Extended +I960_call, // Call +I960_calls, // Call system +I960_callx, // Call extended +I960_chkbit, // Check bit +I960_clrbit, // Clear bit +I960_cmpdeci, // Compare and decrement integer +I960_cmpdeco, // Compare and decrement ordinal +I960_cmpi, // Compare integer +I960_cmpibno, // Compare integer and branch if unordered +I960_cmpibg, // Compare integer and branch if greater +I960_cmpibe, // Compare integer and branch if equal +I960_cmpibge, // Compare integer and branch if greater or equal +I960_cmpibl, // Compare integer and branch if less +I960_cmpibne, // Compare integer and branch if not equal +I960_cmpible, // Compare integer and branch if less or equal +I960_cmpibo, // Compare integer and branch if ordered +I960_cmpinci, // Compare and increment integer +I960_cmpinco, // Compare and increment ordinal +I960_cmpo, // Compare ordinal +I960_cmpobg, // Compare ordinal and branch if greater +I960_cmpobe, // Compare ordinal and branch if equal +I960_cmpobge, // Compare ordinal and branch if greater or equal +I960_cmpobl, // Compare ordinal and branch if less +I960_cmpobne, // Compare ordinal and branch if not equal +I960_cmpoble, // Compare ordinal and branch if less or equal +I960_concmpi, // Conditional compare integer +I960_concmpo, // Conditional compare ordinal +I960_divi, // Divide integer +I960_divo, // Divide ordinal +I960_ediv, // Extended divide +I960_emul, // Extended multiply +I960_eshro, // Extended shift right ordinal +I960_extract, // Extract +I960_faultno, // Fault if unordered +I960_faultg, // Fault if greater +I960_faulte, // Fault if equal +I960_faultge, // Fault if greater or equal +I960_faultl, // Fault if less +I960_faultne, // Fault if not equal +I960_faultle, // Fault if less or equal +I960_faulto, // Fault if ordered +I960_flushreg, // Flush cached local register sets to memory +I960_fmark, // Force mark +I960_ld, // Load word +I960_lda, // Load address +I960_ldib, // Load integer byte +I960_ldis, // Load integer short +I960_ldl, // Load long +I960_ldob, // Load ordinal byte +I960_ldos, // Load ordinal short +I960_ldq, // Load quad +I960_ldt, // Load triple +I960_mark, // Mark +I960_modac, // Modify the AC register +I960_modi, // Modulo integer +I960_modify, // Modify +I960_modpc, // Modify the process controls register +I960_modtc, // Modify trace controls +I960_mov, // Move word +I960_movl, // Move long word +I960_movq, // Move quad word +I960_movt, // Move triple word +I960_muli, // Multiply integer +I960_mulo, // Multiply ordinal +I960_nand, // NOT (src2 AND src1) +I960_nor, // NOT (src2 OR src1) +I960_not, // NOT src1 +I960_notand, // (NOT src2) AND src1 +I960_notbit, // Not bit +I960_notor, // (NOT src2) or src1 +I960_or, // Src2 OR src1 +I960_ornot, // Src2 or (NOT src1) +I960_remi, // Remainder integer +I960_remo, // Remainder ordinal +I960_ret, // Return +I960_rotate, // Rotate left +I960_scanbit, // Scan for bit +I960_scanbyte, // Scan byte equal +I960_setbit, // Set bit +I960_shli, // Shift left integer +I960_shlo, // Shift left ordinal +I960_shrdi, // Shift right dividing integer +I960_shri, // Shift right integer +I960_shro, // Shift right ordinal +I960_spanbit, // Span over bit +I960_st, // Store word +I960_stib, // Store integer byte +I960_stis, // Store integer short +I960_stl, // Store long +I960_stob, // Store ordinal byte +I960_stos, // Store ordinal short +I960_stq, // Store quad +I960_stt, // Store triple +I960_subc, // Subtract ordinal with carry +I960_subi, // Subtract integer +I960_subo, // Subtract ordinal +I960_syncf, // Synchronize faults +I960_testno, // Test for unordered +I960_testg, // Test for greater +I960_teste, // Test for equal +I960_testge, // Test for greater or equal +I960_testl, // Test for less +I960_testne, // Test for not equal +I960_testle, // Test for less or equal +I960_testo, // Test for ordered +I960_xnor, // Src2 XNOR src1 +I960_xor, // Src2 XOR src1 + +// Cx instructions + +I960_sdma, // Set up a DMA controller channel +I960_sysctl, // Perform system control function +I960_udma, // Copy current DMA pointers to internal data RAM + +// Unknown instructions + +I960_dcinva, +I960_cmpob, +I960_cmpib, +I960_cmpos, +I960_cmpis, +I960_bswap, +I960_intdis, +I960_inten, +I960_synmov, +I960_synmovl, +I960_synmovq, +I960_cmpstr, +I960_movqstr, +I960_movstr, +I960_inspacc, +I960_ldphy, +I960_synld, +I960_fill, +I960_daddc, +I960_dsubc, +I960_dmovt, +I960_condrec, +I960_receive, +I960_intctl, +I960_icctl, +I960_dcctl, +I960_halt, +I960_send, +I960_sendserv, +I960_resumprcs, +I960_schedprcs, +I960_saveprcs, +I960_condwait, +I960_wait, +I960_signal, +I960_ldtime, +I960_addono, +I960_addino, +I960_subono, +I960_subino, +I960_selno, +I960_addog, +I960_addig, +I960_subog, +I960_subig, +I960_selg, +I960_addoe, +I960_addie, +I960_suboe, +I960_subie, +I960_sele, +I960_addoge, +I960_addige, +I960_suboge, +I960_subige, +I960_selge, +I960_addol, +I960_addil, +I960_subol, +I960_subil, +I960_sell, +I960_addone, +I960_addine, +I960_subone, +I960_subine, +I960_selne, +I960_addole, +I960_addile, +I960_subole, +I960_subile, +I960_selle, +I960_addoo, +I960_addio, +I960_suboo, +I960_subio, +I960_selo, + +// Floating point instructions + +I960_faddr, I960_fp_first = I960_faddr, +I960_faddrl, +I960_fatanr, +I960_fatanrl, +I960_fclassr, +I960_fclassrl, +I960_fcmpor, +I960_fcmporl, +I960_fcmpr, +I960_fcmprl, +I960_fcosr, +I960_fcosrl, +I960_fcpyrsre, +I960_fcpysre, +I960_fcvtilr, +I960_fcvtir, +I960_fcvtri, +I960_fcvtril, +I960_fcvtzri, +I960_fcvtzril, +I960_fdivr, +I960_fdivrl, +I960_fexpr, +I960_fexprl, +I960_flogbnr, +I960_flogbnrl, +I960_flogepr, +I960_flogeprl, +I960_flogr, +I960_flogrl, +I960_fmovr, +I960_fmovre, +I960_fmovrl, +I960_fmulr, +I960_fmulrl, +I960_fremr, +I960_fremrl, +I960_froundr, +I960_froundrl, +I960_fscaler, +I960_fscalerl, +I960_fsinr, +I960_fsinrl, +I960_fsqrtr, +I960_fsqrtrl, +I960_fsubr, +I960_fsubrl, +I960_ftanr, +I960_ftanrl, I960_fp_last = I960_ftanrl, + +I960_last, + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + +F2MC_null = 0, // Unknown Operation + +// TRANSFER INSTRUCTIONS + +F2MC_mov, // Move byte data from source to destination +F2MC_movn, // Move immediate nibble data to A +F2MC_movx, // Move byte data with sign extension from source to A +F2MC_xch, // Exchange byte data of source to destination +F2MC_movw, // Move word data from source to destination +F2MC_xchw, // Exchange word data of source to destination +F2MC_movl, // Move long word data from source to destination + +// NUMERIC DATA OPERATIONS INSTRUCTIONS + +F2MC_add, // Add byte data of destination and source to destination +F2MC_addc1, // Add byte data of AL and AH with Carry to AL +F2MC_addc2, // Add byte data of A and effective address with Carry to A +F2MC_adddc, // Add decimal data of AL and AH with Carry to AL +F2MC_sub, // Subtract byte data of source from festination to destination +F2MC_subc1, // Subtract byte data of AL from AH with Carry to AL +F2MC_subc2, // Subtract byte data of effective address from A with Carry to A +F2MC_subdc, // Subtract decimal data of AL from AH with Carry to AL +F2MC_addw1, // Add word data of AH and AL to AL +F2MC_addw2, // Add word data of destination and source to destination +F2MC_addcw, // Add word data of A and effective address from A with Carry to A +F2MC_subw1, // Subtract word data of AL from AH to AL +F2MC_subw2, // Subtract word data of source from festination to destination +F2MC_subcw, // Subtract word data of A and effective address from A with carry to A +F2MC_addl, // Add long word data of destination and source to destination +F2MC_subl, // Subtract long word data of source from festination to destination +F2MC_inc, // Increment byte data +F2MC_dec, // Decrement byte data +F2MC_incw, // Increment word data +F2MC_decw, // Decrement word data +F2MC_incl, // Increment long word data +F2MC_decl, // Decrement long word data +F2MC_cmp1, // Compare byte data of AH and AL +F2MC_cmp2, // Compare byte data of destination and source +F2MC_cmpw1, // Compare word data of AH and AL +F2MC_cmpw2, // Compare word data of destination and source +F2MC_cmpl, // Compare long word data of destination and source +F2MC_divu1, // Divide unsigned AH by AL +F2MC_divu2, // Divide unsigned word data by unsigned byte data +F2MC_divuw, // Divide unsigned long word data by unsigned word data +F2MC_mulu1, // Multiply unsigned byte AH by AL +F2MC_mulu2, // Multiply unsigned byte data +F2MC_muluw1, // Multiply unsigned word AH by AL +F2MC_muluw2, // Multiply unsigned word data +F2MC_div1, // Divide AH by AL +F2MC_div2, // Divide word data by byte data +F2MC_divw, // Divide long word data by word data +F2MC_mul1, // Multiply byte AH by AL +F2MC_mul2, // Multiply byte data +F2MC_mulw1, // Multiply word AH by AL +F2MC_mulw2, // Multiply word data + +// LOGICAL DATA OPERATION INSTRUCTIONS + +F2MC_and, // And byte data of destination and source to destination +F2MC_or, // Or byte data of destination and source to destination +F2MC_xor, // Exclusive or byte data of destination and source to destination +F2MC_not, // Not byte data of destination +F2MC_andw1, // And word data of AH and AL to AL +F2MC_andw2, // And word data of destination and source to destination +F2MC_orw1, // Or word data of AH and AL to AL +F2MC_orw2, // Or word data of destination and source to destination +F2MC_xorw1, // Exclusive or word data of AH and AL to AL +F2MC_xorw2, // Exclusive or word data of destination and source to destination +F2MC_notw, // Not word data of destination +F2MC_andl, // And long word data of destination and source to destination +F2MC_orl, // Or long word data of destination and source to destination +F2MC_xorl, // Exclusive or long word data of destination and source to destination +F2MC_neg, // Negate byte data of destination +F2MC_negw, // Negate word data of destination +F2MC_nrml, // Normalize long word + +// SHIFT INSTRUCTIONS + +F2MC_rorc, // Rotate byte data of A with Carry to right +F2MC_rolc, // Rotate byte data of A with Carry to left +F2MC_asr, // Arithmetic shift byte data of A to right +F2MC_lsr, // Logical shift byte data of A to right +F2MC_lsl, // Logical shift byte data of A to left +F2MC_asrw1, // Arithmetic shift word data of A to right +F2MC_asrw2, // Arithmetic shift word data of A to right +F2MC_lsrw1, // Logical shift word data of A to right +F2MC_lsrw2, // Logical shift word data of A to right +F2MC_lslw1, // Logical shift word data of A to left +F2MC_lslw2, // Logical shift word data of A to left +F2MC_asrl, // Arithmetic shift long word data of A to right +F2MC_lsrl, // Logical shift long word data of A to right +F2MC_lsll, // Logical shift long word data of A to left + +// BRANCH INSTRUCTIONS + +F2MC_bz, // Branch if Zero +F2MC_bnz, // Branch if Not Zero +F2MC_bc, // Branch if Carry +F2MC_bnc, // Branch if Not Carry +F2MC_bn, // Branch if Negative +F2MC_bp, // Branch if Not Negative +F2MC_bv, // Branch if Overflow +F2MC_bnv, // Branch if Not Overflow +F2MC_bt, // Branch if Sticky +F2MC_bnt, // Branch if Not Sticky +F2MC_blt, // Branch if Overflow or Negative +F2MC_bge, // Branch if Not (Overflow or Negative) +F2MC_ble, // Branch if (Overflow xor Negative) or Zero +F2MC_bgt, // Branch if Not ((Overflow xor Negative) or Zero) +F2MC_bls, // Branch if Carry or Zero +F2MC_bhi, // Branch if Not (Carry or Zero) +F2MC_bra, // Branch unconditionally +F2MC_jmp, // Jump destination address +F2MC_jmpp, // Jump destination physical address +F2MC_call, // Call subroutine +F2MC_callv, // Call vectored subroutine +F2MC_callp, // Call physical address +F2MC_cbne, // Compare byte data and branch if not Equal +F2MC_cwbne, // Compare word data and branch if not Equal +F2MC_dbnz, // Decrement byte data and branch if not Zero +F2MC_dwbnz, // Decrement word data and branch if not Zero +F2MC_int, // Software interrupt +F2MC_intp, // Software interrupt +F2MC_int9, // Software interrupt +F2MC_reti, // Return from interrupt +F2MC_link, // Link and create new stack frame +F2MC_unlink, // Unlink and create new stack frame +F2MC_ret, // Return from subroutine +F2MC_retp, // Return from physical address + +// OTHER INSTRUCTIONS + +F2MC_pushw, // Push to stack memory +F2MC_popw, // Pop from stack memory +F2MC_jctx, // Jump context +// F2MC_and, +// F2MC_or, +// F2MC_mov, +F2MC_movea, // Move effective address to destination +F2MC_addsp, // Add word data of SP and immediate data to SP +// F2MC_mov, +F2MC_nop, // No operation +F2MC_adb, // ADB register +F2MC_dtb, // DTB register +F2MC_pcb, // PCB register +F2MC_spb, // SPB register +F2MC_ncc, // Flag change inhibit +F2MC_cmr, // Common register bank +F2MC_movb, // Move bit data +F2MC_setb, // Set bit +F2MC_clrb, // Clear bit +F2MC_bbc, // Branch if bit condition satisfied +F2MC_bbs, // Branch if bit condition satisfied +F2MC_sbbs, // Set bit and branch if bit set +F2MC_wbts, // Wait until bit condition satisfied +F2MC_wbtc, // Wait until bit condition satisfied +F2MC_swap, // Swap byte data of A +F2MC_swapw, // Swap word data of A +F2MC_ext, // Sign extend from byte data to word data +F2MC_extw, // Sign extend from word data to long word data +F2MC_zext, // Zero extendfrom byte data to word data +F2MC_zextw, // Zero extendfrom word data to long word data +F2MC_movsi, // Move string byte with addresses incremented +F2MC_movsd, // Move string byte with addresses decremented +F2MC_sceqi, // Scan string byte until Equal with address incremented +F2MC_sceqd, // Scan string byte until Equal with address decremented +F2MC_filsi, // Fill string byte +F2MC_movswi, // Move string word with address incremented +F2MC_movswd, // Move string word with address decremented +F2MC_scweqi, // Scan string word until Equal with address incremented +F2MC_scweqd, // Scan string word until Equal with address decremented +F2MC_filswi, // Fill string word + +// MACROS + +F2MC_bz16, // Branch if Zero +F2MC_bnz16, // Branch if Not Zero +F2MC_bc16, // Branch if Carry +F2MC_bnc16, // Branch if Not Carry +F2MC_bn16, // Branch if Negative +F2MC_bp16, // Branch if Not Negative +F2MC_bv16, // Branch if Overflow +F2MC_bnv16, // Branch if Not Overflow +F2MC_bt16, // Branch if Sticky +F2MC_bnt16, // Branch if Not Sticky +F2MC_blt16, // Branch if Overflow or Negative +F2MC_bge16, // Branch if Not (Overflow or Negative) +F2MC_ble16, // Branch if (Overflow xor Negative) or Zero +F2MC_bgt16, // Branch if Not ((Overflow xor Negative) or Zero) +F2MC_bls16, // Branch if Carry or Zero +F2MC_bhi16, // Branch if Not (Carry or Zero) + +F2MC_cbne16, // Compare byte data and branch if not Equal +F2MC_cwbne16, // Compare word data and branch if not Equal + +F2MC_dbnz16, // Decrement byte data and branch if not Zero +F2MC_dwbnz16, // Decrement word data and branch if not Zero + +F2MC_bbc16, // Branch if bit condition satisfied +F2MC_bbs16, // Branch if bit condition satisfied +F2MC_sbbs16, // Set bit and branch if bit set + +F2MC_last, + +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +TMS320C3X_null = 0, // Unknown Operation + +TMS320C3X_ABSF, // Absolute value of a floating-point number +TMS320C3X_ABSI, // Absolute value of an integer +TMS320C3X_ADDC, // Add integers with carry +TMS320C3X_ADDF, // Add Floating-Point Values +TMS320C3X_ADDI, // Add Integer +TMS320C3X_AND, // Bitwise-Logical AND +TMS320C3X_ANDN, // Bitwise-Logical AND With Complement +TMS320C3X_ASH, // Arithmetic Shift +TMS320C3X_CMPF, // Compare Floating-Point Value +TMS320C3X_CMPI, // Compare Integer +TMS320C3X_FIX, // Floating-Point-to-Integer Conversion +TMS320C3X_FLOAT, // Integer-to-Floating-Point Conversion +TMS320C3X_IDLE, // Idle Until Interrupt +TMS320C3X_IDLE2, // Low-Power Idle +TMS320C3X_LDE, // Load Floating-Point Exponent +TMS320C3X_LDF, // Load Floating-Point Value +TMS320C3X_LDFI, // Load Floating-Point Value, Interlocked +TMS320C3X_LDI, // Load Integer +TMS320C3X_LDII, // Load Integer, Interlocked +TMS320C3X_LDM, // Load Floating-Point Mantissa +TMS320C3X_LSH, // Logical Shift +TMS320C3X_MPYF, // Multiply Floating-Point Value +TMS320C3X_MPYI, // Multiply Integer +TMS320C3X_NEGB, // Negative Integer With Borrow +TMS320C3X_NEGF, // Negate Floating-Point Value +TMS320C3X_NEGI, // Negate Integer +TMS320C3X_NOP, // No Operation +TMS320C3X_NORM, // Normalize +TMS320C3X_NOT, // Bitwise-Logical Complement +TMS320C3X_POP, // Pop Integer +TMS320C3X_POPF, // Pop Floating-Point Value +TMS320C3X_PUSH, // PUSH Integer +TMS320C3X_PUSHF, // PUSH Floating-Point Value +TMS320C3X_OR, // Bitwise-Logical OR +TMS320C3X_LOPOWER, // Divide Clock by 16 +TMS320C3X_MAXSPEED, // Restore Clock to Regular Speed +TMS320C3X_RND, // Round Floating-Point Value +TMS320C3X_ROL, // Rotate Left +TMS320C3X_ROLC, // Rotate Left Through Carry +TMS320C3X_ROR, // Rotate Right +TMS320C3X_RORC, // Rotate Right Through Carry +TMS320C3X_RPTS, // Repeat Single Instruction +TMS320C3X_STF, // Store Floating-Point Value +TMS320C3X_STFI, // Store Floating-Point Value, Interlocked +TMS320C3X_STI, // Store Integer +TMS320C3X_STII, // Store Integer, Interlocked +TMS320C3X_SIGI, // Signal, Interlocked +TMS320C3X_SUBB, // Subtract Integer With Borrow +TMS320C3X_SUBC, // Subtract Integer Conditionally +TMS320C3X_SUBF, // Subtract Floating-Point Value +TMS320C3X_SUBI, // Subtract Integer +TMS320C3X_SUBRB, // Subtract Reverse Integer With Borrow +TMS320C3X_SUBRF, // Subtract Reverse Floating-Point Value +TMS320C3X_SUBRI, // Subtract Reverse Integer +TMS320C3X_TSTB, // Test Bit Fields +TMS320C3X_XOR, // Bitwise-Exclusive OR +TMS320C3X_IACK, // Interrupt acknowledge + +TMS320C3X_ADDC3, // Add integers with carry (3-operand) +TMS320C3X_ADDF3, // Add floating-point values (3-operand) +TMS320C3X_ADDI3, // Add integers (3 operand) +TMS320C3X_AND3, // Bitwise-logical AND (3-operand) +TMS320C3X_ANDN3, // Bitwise-logical ANDN (3-operand) +TMS320C3X_ASH3, // Arithmetic shift (3-operand) +TMS320C3X_CMPF3, // Compare floating-point values (3-operand) +TMS320C3X_CMPI3, // Compare integers (3-operand) +TMS320C3X_LSH3, // Logical shift (3-operand) +TMS320C3X_MPYF3, // Multiply floating-point value (3-operand) +TMS320C3X_MPYI3, // Multiply integers (3-operand) +TMS320C3X_OR3, // Bitwise-logical OR (3-operand) +TMS320C3X_SUBB3, // Subtract integers with borrow (3-operand) +TMS320C3X_SUBF3, // Subtract floating-point values (3-operand) +TMS320C3X_SUBI3, // Subtract integers (3-operand) +TMS320C3X_TSTB3, // Test Bit Fields, 3-Operand +TMS320C3X_XOR3, // Bitwise-Exclusive OR, 3-Operand + +TMS320C3X_LDFcond, // Load floating-point value conditionally +TMS320C3X_LDIcond, // Load integer conditionally +TMS320C3X_BR, // Branch unconditionally (standard) +TMS320C3X_BRD, // Branch unconditionally (delayed) +TMS320C3X_CALL, // Call subroutine +TMS320C3X_RPTB, // Repeat block of instructions +TMS320C3X_SWI, // Software Interrupt +TMS320C3X_Bcond, // Branch conditionally +TMS320C3X_DBcond, // Decrement and branch conditionally +TMS320C3X_CALLcond, // Call subroutine conditionally +TMS320C3X_TRAPcond, // Trap Conditionally +TMS320C3X_RETIcond, // Return from interrupt conditionally +TMS320C3X_RETScond, // Return from subroutine conditionally +TMS320C3X_RETIU, // Return from interrupt unconditionally +TMS320C3X_RETSU, // Return from subroutine unconditionally + +TMS320C3X_NONE, // Pseudo insn (more accurate definition need) +TMS320C3X_MV_IDX, // Pseudo insn (move to next index need) +TMS320C3X_last, // last ID + +}; + + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + +TMS320C54_null = 0, // Unknown Operation + +// ARITHMETIC OPERATIONS + +// ADD INSTRUCTIONS + +TMS320C54_add1, // Add to Accumulator +TMS320C54_add2, // Add to Accumulator +TMS320C54_add3, // Add to Accumulator +TMS320C54_addc, // Add to Accumulator With Carry +TMS320C54_addm, // Add Long-Immediate Value to Memory +TMS320C54_adds, // Add to Accumulator With Sign-Extension Suppressed + +// SUBTRACT INSTRUCTIONS + +TMS320C54_sub1, // Sub From Accumulator +TMS320C54_sub2, // Sub From Accumulator +TMS320C54_sub3, // Sub From Accumulator +TMS320C54_subb, // Sub From Accumulator With Borrow +TMS320C54_subc, // Subtract Conditionally +TMS320C54_subs, // Subtract From Accumulator With Sign Extension Suppressed + +// MULTIPLY INSTRUCTIONS + +TMS320C54_mpy2, // Multiply Without Rounding +TMS320C54_mpy3, // Multiply Without Rounding +TMS320C54_mpyr2, // Multiply With Rounding +TMS320C54_mpya, // Multiply by Accumulator A +TMS320C54_mpyu, // Multiply Unsigned +TMS320C54_squr, // Square + +// MULTIPLY-ACCUMULATE AND MULTIPLY-SUBTRACT INSTRUCTIONS + +TMS320C54_mac2, // Multiply Accumulate Without Rounding +TMS320C54_mac3, // Multiply Accumulate Without Rounding +TMS320C54_macr2, // Multiply Accumulate With Rounding +TMS320C54_macr3, // Multiply Accumulate With Rounding +TMS320C54_maca1, // Multiply by Accumulator A and Accumulate Without Rounding +TMS320C54_maca2, // Multiply by Accumulator A and Accumulate Without Rounding +TMS320C54_maca3, // Multiply by Accumulator A and Accumulate Without Rounding +TMS320C54_macar1, // Multiply by Accumulator A and Accumulate With Rounding +TMS320C54_macar2, // Multiply by Accumulator A and Accumulate With Rounding +TMS320C54_macar3, // Multiply by Accumulator A and Accumulate With Rounding +TMS320C54_macd, // Multiply by Program Memory and Accumulate With Delay +TMS320C54_macp, // Multiply by Program Memory and Accumulate +TMS320C54_macsu, // Multiply Signed by Unsigned and Accumulate +TMS320C54_mas2, // Multiply and Subtract Without Rounding +TMS320C54_mas3, // Multiply and Subtract Without Rounding +TMS320C54_masr2, // Multiply and Subtract With Rounding +TMS320C54_masr3, // Multiply and Subtract With Rounding +// TMS320C54_mas, +TMS320C54_masa1, // Multiply by Accumulator A and Subtract Without Rounding +TMS320C54_masa2, // Multiply by Accumulator A and Subtract Without Rounding +TMS320C54_masa3, // Multiply by Accumulator A and Subtract Without Rounding +TMS320C54_masar1, // Multiply by Accumulator A and Subtract With Rounding +TMS320C54_masar2, // Multiply by Accumulator A and Subtract With Rounding +TMS320C54_masar3, // Multiply by Accumulator A and Subtract With Rounding +TMS320C54_squra, // Square and Accumulate +TMS320C54_squrs, // Square and Subtract + +// DOUBLE INSTRUCTIONS + +TMS320C54_dadd2, // Double-Precision/Dual 16-Bit Add to Accumulator +TMS320C54_dadd3, // Double-Precision/Dual 16-Bit Add to Accumulator +TMS320C54_dadst, // Double-Precision Load With T Add/Dual 16-Bit Load With T Add/Subtract +TMS320C54_drsub, // Double-Precision/Dual 16-Bit Subtract From Long Word +TMS320C54_dsadt, // Long-Word Load With T Add/Dual 16-Bit Load With T Subtract/Add +TMS320C54_dsub, // Double-Precision/Dual 16-Bit Subtract From Accumulator +TMS320C54_dsubt, // Long-Word Load With T Subtract/Dual 16-Bit Load With T Subtract + +// APPLICATION-SPECIFIC INSTRUCTIONS + +TMS320C54_abdst, // Absolute distance +TMS320C54_abs1, // Absolute Value of Accumulator +TMS320C54_abs2, // Absolute Value of Accumulator +TMS320C54_cmpl1, // Complement Accumulator +TMS320C54_cmpl2, // Complement Accumulator +TMS320C54_delay, // Memory Delay +TMS320C54_exp, // Accumulator Exponent +TMS320C54_firs, // Symmetrical Finite Impulse Response Filter +TMS320C54_lms, // Least Mean Square +TMS320C54_max, // Accumulator Maximum +TMS320C54_min, // Accumulator Minimum +TMS320C54_neg1, // Negate Accumulator +TMS320C54_neg2, // Negate Accumulator +TMS320C54_norm1, // Normalization +TMS320C54_norm2, // Normalization +TMS320C54_poly, // Polynominal Evaluation +TMS320C54_rnd1, // Round Accumulator +TMS320C54_rnd2, // Round Accumulator +TMS320C54_sat, // Saturate Accumulator +TMS320C54_sqdst, // Square Distance + +// LOGICAL OPERATIONS + +// AND INSTRUCTIONS + +TMS320C54_and1, // AND With Accumulator +TMS320C54_and2, // AND With Accumulator +TMS320C54_and3, // AND With Accumulator +TMS320C54_andm, // AND Memory With Long Immediate + +// OR INSTRUCTIONS + +TMS320C54_or1, // OR With Accumulator +TMS320C54_or2, // OR With Accumulator +TMS320C54_or3, // OR With Accumulator +TMS320C54_orm, // OR Memory With Constant + +// XOR INSTRUCTIONS + +TMS320C54_xor1, // Exclusive OR With Accumulator +TMS320C54_xor2, // Exclusive OR With Accumulator +TMS320C54_xor3, // Exclusive OR With Accumulator +TMS320C54_xorm, // Exclusive OR Memory With Constant + +// SHIFT INSTRUCTIONS + +TMS320C54_rol, // Rotate Accumulator +TMS320C54_roltc, // Rotate Accumulator Left Using TC +TMS320C54_ror, // Rotate Accumulator Right +TMS320C54_sfta2, // Shift Accumulator Arithmetically +TMS320C54_sfta3, // Shift Accumulator Arithmetically +TMS320C54_sftc, // Shift Accumulator Conditionally +TMS320C54_sftl2, // Shift Accumulator Logically +TMS320C54_sftl3, // Shift Accumulator Logically + +// TEST INSTRUCTIONS + +TMS320C54_bit, // Test Bit +TMS320C54_bitf, // Test Bit Field Specified by Immediate Value +TMS320C54_bitt, // Test Bit Specified by T +TMS320C54_cmpm, // Compare Memory With Long Immediate +TMS320C54_cmpr, // Compare Auxiliary Register with AR0 + +// PROGRAM CONTROL OPERATIONS + +// BRANCH INSTRUCTIONS + +TMS320C54_b, // Branch Unconditionally +TMS320C54_bd, // Branch Unconditionally +TMS320C54_bacc, // Branch to Location Specified by Accumulator +TMS320C54_baccd, // Branch to Location Specified by Accumulator +TMS320C54_banz, // Branch on Auxiliary Register Not Zero +TMS320C54_banzd, // Branch on Auxiliary Register Not Zero +TMS320C54_bc2, // Branch Conditionally +TMS320C54_bc3, // Branch Conditionally +TMS320C54_bcd2, // Branch Conditionally +TMS320C54_bcd3, // Branch Conditionally +TMS320C54_fb, // Far Branch Unconditionally +TMS320C54_fbd, // Far Branch Unconditionally +TMS320C54_fbacc, // Far Branch to Location Specified by Accumulator +TMS320C54_fbaccd, // Far Branch to Location Specified by Accumulator + +// CALL INSTRUCTIONS + +TMS320C54_cala, // Call Subroutine at Location Specified by Accumulator +TMS320C54_calad, // Call Subroutine at Location Specified by Accumulator +TMS320C54_call, // Call Unconditionally +TMS320C54_calld, // Call Unconditionally +TMS320C54_cc2, // Call Conditionally +TMS320C54_cc3, // Call Conditionally +TMS320C54_ccd2, // Call Conditionally +TMS320C54_ccd3, // Call Conditionally +TMS320C54_fcala, // Far Call Subroutine at Location Specified by Accumulator +TMS320C54_fcalad, // Far Call Subroutine at Location Specified by Accumulator +TMS320C54_fcall, // Far Call Unconditionally +TMS320C54_fcalld, // Far Call Unconditionally + +// INTERRUPT INSTRUCTIONS + +TMS320C54_intr, // Software Interrupt +TMS320C54_trap, // Software Interrupt + +// RETURN INSTRUCTIONS + +TMS320C54_fret, // Far Return +TMS320C54_fretd, // Far Return +TMS320C54_frete, // Enable Interrupts and Far Return From Interrupt +TMS320C54_freted, // Enable Interrupts and Far Return From Interrupt +TMS320C54_rc1, // Return Conditionally +TMS320C54_rc2, // Return Conditionally +TMS320C54_rc3, // Return Conditionally +TMS320C54_rcd1, // Return Conditionally +TMS320C54_rcd2, // Return Conditionally +TMS320C54_rcd3, // Return Conditionally +TMS320C54_ret, // Return +TMS320C54_retd, // Return +TMS320C54_rete, // Enable Interrupts and Return From Interrupt +TMS320C54_reted, // Enable Interrupts and Return From Interrupt +TMS320C54_retf, // Enable Interrupts and Fast Return From Interrupt +TMS320C54_retfd, // Enable Interrupts and Fast Return From Interrupt + +// REPEAT INSTRUCTIONS + +TMS320C54_rpt, // Repeat Next Instruction +TMS320C54_rptb, // Block Repeat +TMS320C54_rptbd, // Block Repeat +TMS320C54_rptz, // Repeat Next Instruction And Clear Accumulator + +// STACK MANIPULATING INSTRUCTIONS + +TMS320C54_frame, // Stack Pointer Immediate Offset +TMS320C54_popd, // Pop Top of Stack to Data Memory +TMS320C54_popm, // Pop Top of Stack to Memory-Mapped Register +TMS320C54_pshd, // Push Data-Memory Value Onto Stack +TMS320C54_pshm, // Push Memory-Mapped Register Onto Stack + +// MISCELLANEOUS PROGRAM-CONTROL INSTRUCTIONS + +TMS320C54_idle, // Idle Until Interrupt +TMS320C54_mar, // Modify Auxiliary Register +TMS320C54_nop, // No Operation +TMS320C54_reset, // Software Reset +TMS320C54_rsbx1, // Reset Status Register Bit +TMS320C54_rsbx2, // Reset Status Register Bit +TMS320C54_ssbx1, // Set Status Register Bit +TMS320C54_ssbx2, // Set Status Register Bit +TMS320C54_xc2, // Execute Conditionally +TMS320C54_xc3, // Execute Conditionally + +// LOAD AND STORE OPERATIONS + +// LOAD INSTRUCTIONS + +TMS320C54_dld, // Double-Precision/Dual 16-Bit Long-Word Load to Accumulator +TMS320C54_ld1, // Load Accumulator With Shift +TMS320C54_ld2, // Load Accumulator With Shift +TMS320C54_ld3, // Load Accumulator With Shift +TMS320C54_ldm, // Load Memory-Mapped Register +TMS320C54_ldr, // Load Memory Value in Accumulator High With Rounding +TMS320C54_ldu, // Load Unsigned Memory Value +TMS320C54_ltd, // Load T and insert Delay + +// STORE INSTRUCTIONS + +TMS320C54_dst, // Store Accumulator in Long Word +TMS320C54_st, // Store T, TRN, or Immediate Value into Memory +TMS320C54_sth2, // Store Accumulator High Into Memory +TMS320C54_sth3, // Store Accumulator High Into Memory +TMS320C54_stl2, // Store Accumulator Low Into Memory +TMS320C54_stl3, // Store Accumulator Low Into Memory +TMS320C54_stlm, // Store Accumulator Low Into Memory-Mapped Register +TMS320C54_stm, // Store Immediate Value Into Memory-Mapped Register + +// CONDITIONAL STORE INSTRUCTIONS + +TMS320C54_cmps, // Compare, Select and Store Maximum +TMS320C54_saccd, // Store Accumulator Conditionally +TMS320C54_srccd, // Store Block Repeat Counter Conditionally +TMS320C54_strcd, // Store T Conditionally + +// PARALLEL LOAD AND STORE INSTRUCTIONS + +TMS320C54_st_ld, // Store Accumulator With Parallel Load + +// PARALLEL LOAD AND MULTIPLY INSTRUCTIONS + +TMS320C54_ld_mac, // Load Accumulator With Parallel Multiply Accumulate Without Rounding +TMS320C54_ld_macr, // Load Accumulator With Parallel Multiply Accumulate With Rounding +TMS320C54_ld_mas, // Load Accumulator With Parallel Multiply Subtract Without Rounding +TMS320C54_ld_masr, // Load Accumulator With Parallel Multiply Subtract With Rounding + +// PARALLEL STORE AND ADD/SUBSTRACT INSTRUCTIONS + +TMS320C54_st_add, // Store Accumulator With Parallel Add +TMS320C54_st_sub, // Store Accumulator With Parallel Subtract + +// PARALLEL STORE AND MULTIPLY INSTRUCTIONS + +TMS320C54_st_mac, // Store Accumulator With Parallel Multiply Accumulate Without Rounding +TMS320C54_st_macr, // Store Accumulator With Parallel Multiply Accumulate With Rounding +TMS320C54_st_mas, // Store Accumulator With Parallel Multiply Subtract Without Rounding +TMS320C54_st_masr, // Store Accumulator With Parallel Multiply Subtract With Rounding +TMS320C54_st_mpy, // Store Accumulator With Parallel Multiply + +// MISCELLANEOUS LOAD-TYPE AND STORE-TYPE INSTRUCTIONS + +TMS320C54_mvdd, // Move Data From Data Memory to Data Memory With X,Y Addressing +TMS320C54_mvdk, // Move Data From Data Memory to Data Memory With Destination Addressing +TMS320C54_mvdm, // Move Data From Data Memory to Memory-Mapped Register +TMS320C54_mvdp, // Move Data From Data Memory to Program Memory +TMS320C54_mvkd, // Move Data From Data Memory to Data Memory With Source Addressing +TMS320C54_mvmd, // Move Data From Memory-Mapped Register to Data Memory +TMS320C54_mvmm, // Move Data From Memory-Mapped Register to Memory-Mapped Register +TMS320C54_mvpd, // Move Data From Program Memory to Data Memory +TMS320C54_portr, // Read Data From Port +TMS320C54_portw, // Write Data to Port +TMS320C54_reada, // Read Program Memory Addressed by Accumulator A and Store in Data Memory +TMS320C54_writa, // Write Data to Program Memory Addressed by Accumulator A + +TMS320C54_last + +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + +TMS320C55_null = 0, // Unknown Operation + +// ARITHMETICAL OPERATIONS + +TMS320C55_abdst, // Absolute Distance +TMS320C55_abs1, // Absolute Value +TMS320C55_abs2, // Absolute Value + +TMS320C55_add1, // Addition +TMS320C55_add2, // Addition +TMS320C55_add3, // Addition +TMS320C55_add4, // Addition +TMS320C55_addv1, // Addition +TMS320C55_addv2, // Addition +TMS320C55_addrv1, // Addition and Round +TMS320C55_addrv2, // Addition and Round + +TMS320C55_maxdiff, // Compare and Select Maximum +TMS320C55_dmaxdiff, // Compare and Select 40-bit Maximum +TMS320C55_mindiff, // Compare and Select Minimum +TMS320C55_dmindiff, // Compare and Select 40-bit Minimum + +TMS320C55_addsubcc4, // Conditional Add or Subtract +TMS320C55_addsubcc5, // Conditional Add or Subtract +TMS320C55_addsub2cc, // Conditional Add or Subtract + +TMS320C55_sftcc, // Conditional Shift + +TMS320C55_subc2, // Conditional Subtract +TMS320C55_subc3, // Conditional Subtract + +TMS320C55_addsub, // Paralleled Add - Subtract +TMS320C55_subadd, // Parallel Subtract - Add + +TMS320C55_mpy_mpy, // Two Parallel Multiply +TMS320C55_mpy_mpyr, // Two Parallel Multiply, and Round +TMS320C55_mpy_mpy40, // Two Parallel Multiply, on 40 bits +TMS320C55_mpy_mpyr40, // Two Parallel Multiply, and Round on 40 bits +TMS320C55_mac_mpy, // Parallel Multiply - Accumulate +TMS320C55_macr_mpyr, // Parallel Multiply - Accumulate, and Round +TMS320C55_mac40_mpy40, // Parallel Multiply - Accumulate, on 40 bits +TMS320C55_macr40_mpyr40, // Parallel Multiply - Accumulate, and Round on 40 bits +TMS320C55_mas_mpy, // Parallel Multiply - Subtract +TMS320C55_masr_mpyr, // Parallel Multiply - Subtract, and Round +TMS320C55_mas40_mpy40, // Parallel Multiply - Subtract, on 40 bits +TMS320C55_masr40_mpyr40, // Parallel Multiply - Subtract, and Round on 40 bits +TMS320C55_amar_mpy, // Parallel Modify Auxiliary Register - Multiply +TMS320C55_amar_mpyr, // Parallel Modify Auxiliary Register - Multiply, and Round +TMS320C55_amar_mpy40, // Parallel Modify Auxiliary Register - Multiply, on 40 bits +TMS320C55_amar_mpyr40, // Parallel Modify Auxiliary Register - Multiply, and Round on 40 bits +TMS320C55_mac_mac, // Two Parallel Multiply and Accumulate +TMS320C55_macr_macr, // Two Parallel Multiply and Accumulate, and Round +TMS320C55_mac40_mac40, // Two Parallel Multiply and Accumulate, on 40 bits +TMS320C55_macr40_macr40, // Two Parallel Multiply and Accumulate, and Round on 40 bits +TMS320C55_mas_mac, // Parallel Multiply and Subtract - Multiply and Accumulate +TMS320C55_masr_macr, // Parallel Multiply and Subtract - Multiply and Accumulate, and Round +TMS320C55_mas40_mac40, // Parallel Multiply and Subtract - Multiply and Accumulate, on 40 bits +TMS320C55_masr40_macr40, // Parallel Multiply and Subtract - Multiply and Accumulate, and Round on 40 bits +TMS320C55_amar_mac, // Parallel Modify Auxiliary Register - Multiply and Accumulate +TMS320C55_amar_macr, // Parallel Modify Auxiliary Register - Multiply and Accumulate, and Round +TMS320C55_amar_mac40, // Parallel Modify Auxiliary Register - Multiply and Accumulate, on 40 bits +TMS320C55_amar_macr40, // Parallel Modify Auxiliary Register - Multiply and Accumulate, and Round on 40 bits +TMS320C55_mas_mas, // Two Parallel Multiply and Subtract +TMS320C55_masr_masr, // Two Parallel Multiply and Subtract, and Round +TMS320C55_mas40_mas40, // Two Parallel Multiply and Subtract, on 40 bits +TMS320C55_masr40_masr40, // Two Parallel Multiply and Subtract, and Round on 40 bits +TMS320C55_amar_mas, // Parallel Modify Auxiliary Register - Multiply and Subtract +TMS320C55_amar_masr, // Parallel Modify Auxiliary Register - Multiply and Subtract, and Round +TMS320C55_amar_mas40, // Parallel Modify Auxiliary Register - Multiply and Subtract, on 40 bits +TMS320C55_amar_masr40, // Parallel Modify Auxiliary Register - Multiply and Subtract, and Round on 40 bits +TMS320C55_mpy_mac, // Parallel Multiply - Multiply and Accumulate +TMS320C55_mpyr_macr, // Parallel Multiply - Multiply and Accumulate, and Round +TMS320C55_mpy40_mac40, // Parallel Multiply - Multiply and Accumulate, on 40 bits +TMS320C55_mpyr40_macr40, // Parallel Multiply - Multiply and Accumulate, and Round on 40 bits +TMS320C55_amar3, // Three Parallel Modify Auxiliary Registers + +TMS320C55_firsadd, // Parallel Multiply and Accumulate - Add +TMS320C55_firssub, // Parallel Multiply and Accumulate - Subtract + +TMS320C55_mpym_mov, // Parallel Multiply - Store +TMS320C55_mpymr_mov, // Parallel Multiply - Store, and Round +TMS320C55_macm_mov, // Parallel Multiply and Accumulate - Store +TMS320C55_macmr_mov, // Parallel Multiply and Accumulate - Store, and Round +TMS320C55_masm_mov, // Parallel Multiply and Subtract - Store +TMS320C55_masmr_mov, // Parallel Multiply and Subtract - Store, and Round +TMS320C55_add_mov, // Parallel Add - Store +TMS320C55_sub_mov, // Parallel Subtract - Store +TMS320C55_mov_mov, // Parallel Load - Store +TMS320C55_mov_aadd, // Parallel Store - aadd +TMS320C55_mov_add, // Parallel Store - Add +TMS320C55_amar_amar, // Parallel Modify Auxiliary Register - Modify Auxiliary Register +TMS320C55_add_asub, // Parallel Add - asub +TMS320C55_btst_mov, // Parallel Bit Test - Store +TMS320C55_mov_asub, // Parallel Store - asub + +TMS320C55_lms, // Least Mean Square + +TMS320C55_max1, // Maximum Comparison +TMS320C55_max2, // Maximum Comparison +TMS320C55_min1, // Minimum Comparison +TMS320C55_min2, // Minimum Comparison + +TMS320C55_cmp, // Memory Comparison +TMS320C55_cmpu, // Unsigned memory Comparison + +TMS320C55_aadd, // Add Two Registers +TMS320C55_asub, // Subtract Two Registers +TMS320C55_amov, // Move From Register to Register +TMS320C55_amar1, // Auxiliary Register Modification + +TMS320C55_sqr1, // Square +TMS320C55_sqr2, // Square +TMS320C55_sqrr1, // Square and Round +TMS320C55_sqrr2, // Square and Round +TMS320C55_mpy1, // Multiply +TMS320C55_mpy2, // Multiply +TMS320C55_mpy3, // Multiply +TMS320C55_mpyr1, // Multiply and Round +TMS320C55_mpyr2, // Multiply and Round +TMS320C55_mpyr3, // Multiply and Round +TMS320C55_mpyk2, // Multiply by Constant +TMS320C55_mpyk3, // Multiply by Constant +TMS320C55_mpykr2, // Multiply by Constant and Round +TMS320C55_mpykr3, // Multiply by Constant and Round +TMS320C55_mpym2, // Multiply Memory Value +TMS320C55_mpym3, // Multiply Memory Values +TMS320C55_mpymr2, // Multiply Memory Value and Round +TMS320C55_mpymr3, // Multiply Memory Values and Round +TMS320C55_mpym403, // Multiply Memory Values on 40 bits +TMS320C55_mpymr403, // Multiply Memory Values and Round on 40 bits +TMS320C55_mpymu3, // Unsigned multiply Memory Values +TMS320C55_mpymru3, // Unsigned multiply Memory Values and Round +TMS320C55_sqrm, // Square Memory Value +TMS320C55_sqrmr, // Square Memory Value, and Round +TMS320C55_mpymk, // Multiply Memory Value by Constant +TMS320C55_mpymkr, // Multiply Memory Value by Constant and Round + +TMS320C55_sqa1, // Square and Accumulate +TMS320C55_sqa2, // Square and Accumulate +TMS320C55_sqar1, // Square, Accumulate and Round +TMS320C55_sqar2, // Square, Accumulate and Round +TMS320C55_mac3, // Multiply and Accumulate +TMS320C55_mac4, // Multiply and Accumulate +TMS320C55_macr3, // Multiply, Accumulate and Round +TMS320C55_macr4, // Multiply, Accumulate and Round +TMS320C55_mack3, // Multiply by Constant and Accumulate +TMS320C55_mack4, // Multiply by Constant and Accumulate +TMS320C55_mackr3, // Multiply by Constant, Round and Accumulate +TMS320C55_mackr4, // Multiply by Constant, Round and Accumulate +TMS320C55_macm2, // Multiply and Accumulate Memory Values +TMS320C55_macm3, // Multiply and Accumulate Memory Values +TMS320C55_macm4, // Multiply and Accumulate Memory Values +TMS320C55_macmr2, // Multiply and Accumulate Memory Values, and Round +TMS320C55_macmr3, // Multiply and Accumulate Memory Values, and Round +TMS320C55_macmr4, // Multiply and Accumulate Memory Values, and Round +TMS320C55_macm403, // Multiply and Accumulate Memory Values, on 40 bits +TMS320C55_macm404, // Multiply and Accumulate Memory Values, on 40 bits +TMS320C55_macmr403, // Multiply and Accumulate Memory Values, and Round on 40 bits +TMS320C55_macmr404, // Multiply and Accumulate Memory Values, and Round on 40 bits +TMS320C55_macmz, // Multiply and Accumulate Memory Values +TMS320C55_macmrz, // Multiply and Accumulate Memory Values, and Round +TMS320C55_sqam2, // Square and Accumulate Memory Value +TMS320C55_sqam3, // Square and Accumulate Memory Values +TMS320C55_sqamr2, // Square and Accumulate Memory Value, and Round +TMS320C55_sqamr3, // Square and Accumulate Memory Values, and Round +TMS320C55_macmk3, // Multiply Memory Value by Constant and Accumulate +TMS320C55_macmk4, // Multiply Memory Value by Constant and Accumulate +TMS320C55_macmkr3, // Multiply Memory Value by Constant - Accumulate, and Round +TMS320C55_macmkr4, // Multiply Memory Value by Constant - Accumulate, and Round + +TMS320C55_sqs1, // Square and Subtract +TMS320C55_sqs2, // Square and Subtract +TMS320C55_sqsr1, // Square, Subtract and Round +TMS320C55_sqsr2, // Square, Subtract and Round + +TMS320C55_mas2, // Multiply and Subtract +TMS320C55_mas3, // Multiply and Subtract +TMS320C55_masr2, // Multiply, Subtract and Round +TMS320C55_masr3, // Multiply, Subtract and Round +TMS320C55_masm2, // Multiply and Subtract Memory Value +TMS320C55_masm3, // Multiply and Subtract Memory Values +TMS320C55_masm4, // Multiply and Subtract Memory Values +TMS320C55_masmr2, // Multiply and Subtract Memory Values, and Round +TMS320C55_masmr3, // Multiply and Subtract Memory Values, and Round +TMS320C55_masmr4, // Multiply and Subtract Memory Values, and Round +TMS320C55_masm403, // Multiply and Subtract Memory Values, on 40 bits +TMS320C55_masm404, // Multiply and Subtract Memory Values, on 40 bits +TMS320C55_masmr403, // Multiply and Subtract Memory Values, and Round on 40 bits +TMS320C55_masmr404, // Multiply and Subtract Memory Values, and Round on 40 bits +TMS320C55_sqsm2, // Square and Subtract Memory Values +TMS320C55_sqsm3, // Square and Subtract Memory Values +TMS320C55_sqsmr2, // Square and Subtract Memory Values, and Round +TMS320C55_sqsmr3, // Square and Subtract Memory Values, and Round + +TMS320C55_neg1, // Negation +TMS320C55_neg2, // Negation + +TMS320C55_mant_nexp, // Exponent and Mantissa +TMS320C55_exp, // Exponent + +TMS320C55_cmpand, // Compare and AND +TMS320C55_cmpandu, // Unsigned compare and AND +TMS320C55_cmpor, // Compare and OR +TMS320C55_cmporu, // Unsigned compare and OR + +TMS320C55_round1, // Round +TMS320C55_round2, // Round + +TMS320C55_sat1, // Saturate +TMS320C55_sat2, // Saturate +TMS320C55_satr1, // Saturate and Round +TMS320C55_satr2, // Saturate and Round + +TMS320C55_sfts2, // Signed Shift +TMS320C55_sfts3, // Signed Shift +TMS320C55_sftsc2, // Signed Shift with Carry +TMS320C55_sftsc3, // Signed Shift with Carry + +TMS320C55_sqdst, // Square distance + +TMS320C55_sub1, // Subtract +TMS320C55_sub2, // Subtract +TMS320C55_sub3, // Subtract +TMS320C55_sub4, // Subtract + +TMS320C55_band, // Bit Field Comparison + +TMS320C55_bfxpa, // Bit Field Expand + +TMS320C55_bfxtr, // Bit Field Extract + +TMS320C55_btst, // Bit Test +TMS320C55_bnot, // Bit NOT +TMS320C55_bclr2, // Bit Clear +TMS320C55_bset2, // Bit Set +TMS320C55_btstset, // Bit Test and Set +TMS320C55_btstclr, // Bit Test and Clear +TMS320C55_btstnot, // Bit Test and NOT +TMS320C55_btstp, // Bit Pair Test +TMS320C55_bclr1, // Bit Clear +TMS320C55_bset1, // Bit Set + +TMS320C55_amar2, // Load Effective Address to Extended Auxiliary Register +TMS320C55_popboth, // Pop Extended Auxiliary Register from Stack Pointers +TMS320C55_pshboth, // Push Extended Auxiliary Register to Stack Pointers + +// LOGICAL OPERATIONS + +TMS320C55_bcnt, // Count Bit Field + +TMS320C55_not1, // NOT +TMS320C55_not2, // NOT + +TMS320C55_and1, // AND +TMS320C55_and2, // AND +TMS320C55_and3, // AND + +TMS320C55_or1, // OR +TMS320C55_or2, // OR +TMS320C55_or3, // OR + +TMS320C55_xor1, // XOR +TMS320C55_xor2, // XOR +TMS320C55_xor3, // XOR + +TMS320C55_sftl2, // Logical Shift +TMS320C55_sftl3, // Logical Shift + +TMS320C55_rol, // Rotate Left + +TMS320C55_ror, // Rotate Right + +// MISCELLANEOUS OPERATIONS + +// MOVE OPERATIONS + +TMS320C55_swap, // Swap Registers +TMS320C55_swapp, // Swap Pair Registers +TMS320C55_swap4, // Swap 4 Registers + +TMS320C55_mov2, // Move Data +TMS320C55_mov3, // Move 2 Data +TMS320C55_mov402, // Move Data on 40 bits + +TMS320C55_delay, // Memory Delay + +TMS320C55_pop1, // Pop Top of Stack1 +TMS320C55_pop2, // Pop Top of Stack2 + +TMS320C55_psh1, // Pop Top of Stack3 +TMS320C55_psh2, // Pop Top of Stack4 + +// PROGRAM CONTROL OPERATIONS + +TMS320C55_bcc, // Branch Conditionally +TMS320C55_bccu, // Branch Conditionally + +TMS320C55_b, // Branch Unconditionally + +TMS320C55_callcc, // Call Conditionally + +TMS320C55_call, // Call Unconditionally + +TMS320C55_xcc, // Execute Conditionally +TMS320C55_xccpart, // Execute Conditionally + +TMS320C55_idle, // Idle + +TMS320C55_nop, // No Operation +TMS320C55_nop_16, // No Operation + +TMS320C55_rptblocal, // Repeat Block of Instructions Unconditionally +TMS320C55_rptb, // Repeat Block of Instructions Unconditionally + +TMS320C55_rptcc, // Repeat Single Instruction Conditionally + +TMS320C55_rpt, // Repeat Single Instruction Unconditionally +TMS320C55_rptadd, // Repeat Single Instruction Unconditionally and Add to Register +TMS320C55_rptsub, // Repeat Single Instruction Unconditionally and Subtract to Register + +TMS320C55_retcc, // Return Conditionally +TMS320C55_ret, // Return Unconditionally +TMS320C55_reti, // Return from Interrupt + +TMS320C55_intr, // Software Interrupt + +TMS320C55_reset, // Software Reset + +TMS320C55_trap, // Software Trap + +TMS320C55_last + +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +TRIMEDIA_null, + +TRIMEDIA_igtri, // signed compare greater with immediate +TRIMEDIA_igeqi, // signed compare greater or equal with immediate +TRIMEDIA_ilesi, // signed compare less with immediate +TRIMEDIA_ineqi, // signed compare not equal with immediate +TRIMEDIA_ieqli, // signed compare equal with immediate +TRIMEDIA_iaddi, // add with immediate +TRIMEDIA_ild16d, // signed 16-bit load with displacement +TRIMEDIA_ld32d, // 32-bit load with displacement +TRIMEDIA_uld8d, // unsigned 8-bit load with displacement +TRIMEDIA_lsri, // logical shift right immediate +TRIMEDIA_asri, // arithmetic shift right immediate +TRIMEDIA_asli, // arithmetic shift left immediate +TRIMEDIA_iadd, // signed add +TRIMEDIA_isub, // signed subtract +TRIMEDIA_igeq, // signed compare greater or equal +TRIMEDIA_igtr, // signed compare greater +TRIMEDIA_bitand, // bitwise logical AND +TRIMEDIA_bitor, // bitwise logical OR +TRIMEDIA_asr, // arithmetic shift right +TRIMEDIA_asl, // arithmetic shift left +TRIMEDIA_ifloat, // convert signed integer to floating-point +TRIMEDIA_ifixrz, // convert floating-point to integer with round toward zero +TRIMEDIA_fadd, // floating-point add +TRIMEDIA_imin, // signed minimum +TRIMEDIA_imax, // signed maximum +TRIMEDIA_iavgonep, // signed average +TRIMEDIA_ume8uu, // sum of absolute values of unsigned 8-bit differences +TRIMEDIA_imul, // signed multiply +TRIMEDIA_fmul, // floating-point multiply +TRIMEDIA_h_st8d, // hardware 8-bit store with displacement +TRIMEDIA_h_st16d, // hardware 16-bit store with displacement +TRIMEDIA_h_st32d, // hardware 32-bit store with displacement +TRIMEDIA_isubi, // subtract with immediate +TRIMEDIA_ugtr, // unsigned compare greater +TRIMEDIA_ugtri, // unsigned compare greater with immediate +TRIMEDIA_ugeq, // unsigned compare greater or equal +TRIMEDIA_ugeqi, // unsigned compare greater or equal with immediate +TRIMEDIA_ieql, // signed compare equal +TRIMEDIA_ueqli, // unsigned compare equal with immediate +TRIMEDIA_ineq, // signed compare not equal +TRIMEDIA_uneqi, // unsigned compare not equal +TRIMEDIA_ulesi, // unsigned compare less with immediate +TRIMEDIA_ileqi, // signed compare less or equal with immediate +TRIMEDIA_uleqi, // unsigned compare less or equal with immediate +TRIMEDIA_h_iabs, // hardware absolute value +TRIMEDIA_carry, // compute carry bit from unsigned add +TRIMEDIA_izero, // if zero select zero +TRIMEDIA_inonzero, // if nonzero select zero +TRIMEDIA_bitxor, // bitwise logical exclusive OR +TRIMEDIA_bitandinv, // bitwise logical AND NOT +TRIMEDIA_bitinv, // bitwise logical NOT +TRIMEDIA_sex16, // sign extend 16 bits +TRIMEDIA_packbytes, // pack least-significant bytes +TRIMEDIA_pack16lsb, // pack least-significant 16-bit halfwords +TRIMEDIA_pack16msb, // pack most-significant 16-bit halfwords +TRIMEDIA_ubytesel, // select unsigned byte +TRIMEDIA_ibytesel, // signed select byte +TRIMEDIA_mergelsb, // merge least-significant byte +TRIMEDIA_mergemsb, // merge most-significant byte +TRIMEDIA_ume8ii, // unsigned sum of absolute values of signed 8-bit differences +TRIMEDIA_h_dspiabs, // clipped signed absolute value +TRIMEDIA_dspiadd, // clipped signed add +TRIMEDIA_dspuadd, // clipped unsigned add +TRIMEDIA_dspisub, // clipped signed subtract +TRIMEDIA_dspusub, // clipped unsigned subtract +TRIMEDIA_dspidualadd, // dual clipped add of signed 16-bit halfwords +TRIMEDIA_dspidualsub, // dual clipped subtract of signed 16-bit halfwords +TRIMEDIA_h_dspidualabs, // dual clipped absolute value of signed 16-bit halfwords +TRIMEDIA_quadavg, // unsigned byte-wise quad average +TRIMEDIA_iclipi, // clip signed to signed +TRIMEDIA_uclipi, // clip signed to unsigned +TRIMEDIA_uclipu, // clip unsigned to unsigned +TRIMEDIA_iflip, // if non-zero negate +TRIMEDIA_dspuquadaddui, // quad clipped add of unsigned/signed bytes +TRIMEDIA_quadumin, // unsigned byte-wise quad minimum +TRIMEDIA_quadumax, // unsigned byte-wise quad maximum +TRIMEDIA_dualiclipi, // dual-16 clip signed to signed +TRIMEDIA_dualuclipi, // dual-16 clip signed to unsigned +TRIMEDIA_quadumulmsb, // unsigned quad 8-bit multiply most significant +TRIMEDIA_ufir8uu, // unsigned sum of products of unsigned bytes +TRIMEDIA_ifir8ui, // signed sum of products of unsigned/signed bytes +TRIMEDIA_ifir8ii, // signed sum of products of signed bytes +TRIMEDIA_ifir16, // sum of products of signed 16-bit halfwords +TRIMEDIA_ufir16, // sum of products of unsigned 16-bit halfwords +TRIMEDIA_dspidualmul, // dual clipped multiply of signed 16-bit halfwords +TRIMEDIA_lsr, // logical shift right +TRIMEDIA_rol, // rotate left +TRIMEDIA_roli, // rotate left by immediate +TRIMEDIA_funshift1, // funnel-shift 1 byte +TRIMEDIA_funshift2, // funnel-shift 2 bytes +TRIMEDIA_funshift3, // funnel-shift 3 bytes +TRIMEDIA_dualasr, // dual-16 arithmetic shift right +TRIMEDIA_mergedual16lsb,// merge dual 16-bit lsb bytes +TRIMEDIA_fdiv, // floating-point divide +TRIMEDIA_fdivflags, // IEEE status flags from floating-point divide +TRIMEDIA_fsqrt, // floating-point square root +TRIMEDIA_fsqrtflags, // IEEE status flags from floating-point square root +TRIMEDIA_faddflags, // IEEE status flags from floating-point add +TRIMEDIA_fsub, // floating-point subtract +TRIMEDIA_fsubflags, // IEEE status flags from floating-point subtract +TRIMEDIA_fabsval, // floating-point absolute value +TRIMEDIA_fabsvalflags, // IEEE status flags from floating-point absolute value +TRIMEDIA_ifloatrz, // convert signed integer to floating-point with rounding toward zero +TRIMEDIA_ifloatrzflags, // IEEE status flags from convert signed integer to floating-point with rounding toward zero +TRIMEDIA_ufloatrz, // convert unsigned integer to floating-point with round toward zero +TRIMEDIA_ufloatrzflags, // IEEE status flags from convert unsigned integer to floating-point with round toward zero +TRIMEDIA_ifixieee, // convert floating-point to integer using PCSW rounding mode +TRIMEDIA_ifixieeeflags, // IEEE status flags from convert floating-point to integer using PCSW rounding mode +TRIMEDIA_ufixieee, // convert floating-point to unsigned integer using PCSW rounding mode +TRIMEDIA_ufixieeeflags, // IEEE status flags from convert floating-point to unsigned integer using PCSW rounding mode +TRIMEDIA_ufixrz, // convert floating-point to unsigned integer with round toward zero +TRIMEDIA_ufixrzflags, // IEEE status flags from convert floating-point to unsigned integer with round toward zero +TRIMEDIA_ufloat, // convert unsigned integer to floating-point +TRIMEDIA_ufloatflags, // IEEE status flags from convert unsigned integer to floating-point +TRIMEDIA_ifixrzflags, // IEEE status flags from convert floating-point to integer with round toward zero +TRIMEDIA_ifloatflags, // IEEE status flags from convert signed integer to floating-point +TRIMEDIA_umul, // unsigned multiply +TRIMEDIA_imulm, // signed multiply, return most-significant 32 bits +TRIMEDIA_umulm, // unsigned multiply, return most-significant 32 bits +TRIMEDIA_dspimul, // clipped signed multiply +TRIMEDIA_dspumul, // clipped unsigned multiply +TRIMEDIA_fmulflags, // IEEE status flags from floating-point multiply +TRIMEDIA_fgtr, // floating-point compare greater +TRIMEDIA_fgtrflags, // IEEE status flags from floating-point compare greater +TRIMEDIA_fgeq, // floating-point compare greater or equal +TRIMEDIA_fgeqflags, // IEEE status flags from floating-point compare greater or equal +TRIMEDIA_feql, // floating-point compare equal +TRIMEDIA_feqlflags, // IEEE status flags from floating-point compare equal +TRIMEDIA_fneq, // floating-point compare not equal +TRIMEDIA_fneqflags, // IEEE status flags from floating-point compare not equal +TRIMEDIA_fsign, // sign of floating-point value +TRIMEDIA_fsignflags, // IEEE status flags from floating-point sign +TRIMEDIA_cycles, // read clock cycle counter, least-significant word +TRIMEDIA_hicycles, // read clock cycle counter, most-significant word +TRIMEDIA_readdpc, // read destination program counter +TRIMEDIA_readspc, // read source program counter +TRIMEDIA_readpcsw, // read program control and status word +TRIMEDIA_writespc, // write source program counter +TRIMEDIA_writedpc, // write destination program counter +TRIMEDIA_writepcsw, // write program control and status word +TRIMEDIA_curcycles, // read current clock cycle, least-significant word +TRIMEDIA_jmpt, // indirect jump on true +TRIMEDIA_ijmpt, // interruptible indirect jump on true +TRIMEDIA_jmpi, // jump immediate +TRIMEDIA_ijmpi, // interruptible jump immediate +TRIMEDIA_jmpf, // indirect jump on false +TRIMEDIA_ijmpf, // interruptible indirect jump on false +TRIMEDIA_iclr, // invalidate all instruction cache blocks +TRIMEDIA_uimm, // unsigned immediate +TRIMEDIA_ild8d, // signed 8-bit load with displacement +TRIMEDIA_ild8r, // signed 8-bit load with index +TRIMEDIA_uld8r, // unsigned 8-bit load with index +TRIMEDIA_ild16r, // signed 16-bit load with index +TRIMEDIA_ild16x, // signed 16-bit load with scaled index +TRIMEDIA_uld16d, // unsigned 16-bit load with displacement +TRIMEDIA_uld16r, // unsigned 16-bit load with index +TRIMEDIA_uld16x, // unsigned 16-bit load with scaled index +TRIMEDIA_ld32r, // 32-bit load with index +TRIMEDIA_ld32x, // 32-bit load with scaled index +TRIMEDIA_rdtag, // read data cache address tag +TRIMEDIA_rdstatus, // read data cache status bits +TRIMEDIA_dcb, // data cache copy back +TRIMEDIA_dinvalid, // invalidate data cache block +TRIMEDIA_prefd, // prefetch with displacement +TRIMEDIA_prefr, // prefetch with index +TRIMEDIA_pref16x, // prefetch with 16-bit scaled index +TRIMEDIA_pref32x, // prefetch with 32-bit scaled index +TRIMEDIA_allocd, // allocate a cache block with displacement +TRIMEDIA_allocr, // allocate a cache block with index +TRIMEDIA_allocx, // allocate a cache block with scaled index +TRIMEDIA_nop, + +// Pseudo-operations + +TRIMEDIA_alloc, // allocate a cache block +TRIMEDIA_dspiabs, // clipped signed absolute value +TRIMEDIA_dspidualabs, // dual clipped absolute value of signed 16-bit halfwords +TRIMEDIA_iabs, // absolute value +TRIMEDIA_ild16, // signed 16-bit load +TRIMEDIA_ild8, // signed 8-bit load +TRIMEDIA_ineg, // signed negate +TRIMEDIA_ld32, // 32-bit load +TRIMEDIA_pref, // prefetch +TRIMEDIA_sex8, // sign extend 8 bits +TRIMEDIA_st16, // 16-bit store +TRIMEDIA_st16d, // 16-bit store with displacement +TRIMEDIA_st32, // 32-bit store +TRIMEDIA_st32d, // 32-bit store with displacement +TRIMEDIA_st8, // 8-bit store +TRIMEDIA_st8d, // 8-bit store with displacement +TRIMEDIA_uld16, // unsigned 16-bit load +TRIMEDIA_uld8, // unsigned 8-bit load +TRIMEDIA_zex16, // zero extend 16 bits +TRIMEDIA_zex8, // zero extend 8 bits +TRIMEDIA_ident, // identity + +// Unused pseudo-operations: + +TRIMEDIA_iles, +TRIMEDIA_ileq, +TRIMEDIA_ules, +TRIMEDIA_uleq, +TRIMEDIA_fles, +TRIMEDIA_fleq, +TRIMEDIA_ueql, +TRIMEDIA_uneq, +TRIMEDIA_flesflags, +TRIMEDIA_fleqflags, +TRIMEDIA_borrow, +TRIMEDIA_umin, +TRIMEDIA_lsl, +TRIMEDIA_lsli, + +TRIMEDIA_last, + + }; + +/* + * NEC 78K0 processor module for IDA. + * Copyright (c) 2006 Konstantin Norvatoff, + * Freeware. + */ + + + +enum +{ +NEC_78K_0_null = 0, // Unknown Operation + +NEC_78K_0_mov, // Move Byte Data Transfer +NEC_78K_0_xch, // Exchange Byte Data +NEC_78K_0_movw, // Move Word Data Transfer / Word Data Transfer with Stack Pointer +NEC_78K_0_xchw, // Exchange Word Data + +NEC_78K_0_add, // Add Byte Data Addition +NEC_78K_0_addc, // Add with Carry Addition of Byte Data with Carry +NEC_78K_0_sub, // Subtract Byte Data Subtraction +NEC_78K_0_subc, // Subtract with Carry Subtraction of Byte Data with Carry +NEC_78K_0_and, // And Logical Product of Byte Data +NEC_78K_0_or, // Or Logical Sum of Byte Data +NEC_78K_0_xor, // Exclusive Or Exclusive Logical Sum of Byte Data +NEC_78K_0_cmp, // Compare Byte Data Comparison + +NEC_78K_0_addw, // Add Word Data Addition +NEC_78K_0_subw, // Subtract Word Data Subtraction +NEC_78K_0_cmpw, // Compare Word Data Comparison + +NEC_78K_0_mulu, // Multiply Unsigned Multiplication of Data +NEC_78K_0_divuw, // Divide Unsigned Word Unsigned Division of Word Data + +NEC_78K_0_inc, // Increment Byte Data Increment +NEC_78K_0_dec, // Decrement Byte Data Decrement +NEC_78K_0_incw, // Increment Word Data Increment +NEC_78K_0_decw, // Decrement Word Data Decrement + +NEC_78K_0_ror, // Rotate Right Byte Data Rotation to the Right +NEC_78K_0_rol, // Rotate Left Byte Data Rotation to the Left +NEC_78K_0_rorc, // Rotate Right with Carry Byte Data Rotation to the Right with Carry +NEC_78K_0_rolc, // Rotate Left with Carry Byte Data Rotation to the Left with Carry +NEC_78K_0_ror4, // Rotate Right Digit Digit Rotation to the Right +NEC_78K_0_rol4, // Rotate Left Digit Digit Rotation to the Left + +NEC_78K_0_adjba, // Decimal Adjust Register for Addition Decimal Adjustment of Addition Result +NEC_78K_0_adjbs, // Decimal Adjust Register for Subtraction Decimal Adjustment of Subtraction Result + +NEC_78K_0_mov1, // Move Single Bit 1 Bit Data Transfer +NEC_78K_0_and1, // And Single Bit 1 Bit Data Logical Product +NEC_78K_0_or1, // Or Single Bit 1 Bit Data Logical Sum +NEC_78K_0_xor1, // Exclusive Or Single Bit 1 Bit Data Exclusive Logical Sum +NEC_78K_0_set1, // Set Single Bit (Carry Flag) 1 Bit Data Set +NEC_78K_0_clr1, // Clear Single Bit (Carry Flag) 1 Bit Data Clear +NEC_78K_0_not1, // Not Single Bit (Carry Flag) 1 Bit Data Logical Negation + +NEC_78K_0_call, // Call Subroutine Call (16 Bit Direct) +NEC_78K_0_callf, // Call Flag Subroutine Call (11 Bit Direct Specification) +NEC_78K_0_callt, // Call Table Subroutine Call (Refer to the Call Table) +NEC_78K_0_brk, // Break Software Vectored Interrupt +NEC_78K_0_ret, // Return Return from Subroutine +NEC_78K_0_retb, // Return from Interrupt Return from Hardware Vectored Interrupt +NEC_78K_0_reti, // Return from Break Return from Software Vectored Interrupt + +NEC_78K_0_push, // Push +NEC_78K_0_pop, // Pop + +NEC_78K_0_br, // Branch Unconditional Branch +NEC_78K_0_bc, // Branch if Carry Conditional Branch with Carry Flag (CY = 1) +NEC_78K_0_bnc, // Branch if Not Carry Conditional Branch with Carry Flag (CY = 0) +NEC_78K_0_bz, // Branch if Zero Conditional Branch with Zero Flag (Z = 1) +NEC_78K_0_bnz, // Branch if Not Zero Conditional Branch with Zero Flag (Z = 0) +NEC_78K_0_bt, // Branch if True Conditional Branch by Bit Test (Byte Data Bit = 1) +NEC_78K_0_bf, // Branch if False Conditional Branch by Bit Test (Byte Data Bit = 0) +NEC_78K_0_btclr, // Branch if True and Clear Conditional Branch and Clear by Bit Test (Byte Data Bit = 1) +NEC_78K_0_dbnz, // Decrement and Branch if Not Zero Conditional Loop (R1!= 0) + +NEC_78K_0_sel, // Select Register Bank Register Bank Selection + + +NEC_78K_0_nop, // No Operation +NEC_78K_0_EI, // Enable Interrupt +NEC_78K_0_DI, // Disable Interrupt +NEC_78K_0_HALT, // HALT Mode Set +NEC_78K_0_STOP, // Stop Mode Set + + +NEC_78K_0_last + + }; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + NEC_78K_0S_null = 0, // Unknown Operation + NEC_78K_0S_cmp, // Compare Byte Data Comparison + NEC_78K_0S_xor, // Exclusive Or Exclusive Logical Sum of Byte Data + NEC_78K_0S_and, // AND Logical Product of Byte Data + NEC_78K_0S_or, // OR Logical Sum of Byte Data + NEC_78K_0S_add, // ADD Byte Data Addition + NEC_78K_0S_sub, // Subtract Byte Data Subtraction + NEC_78K_0S_addc, // Add with Carry Addition of Byte Data with Carry + NEC_78K_0S_subc, // Subtract with Carry Subtraction of Byte Data with Carry + NEC_78K_0S_subw, // Subtract Word Data Subtraction + NEC_78K_0S_addw, // Add Word Data Addition + NEC_78K_0S_cmpw, // Compare Word Data Comparison + NEC_78K_0S_inc, // Increment Byte Data Increment + NEC_78K_0S_dec, // Decrement Byte Data Decrement + NEC_78K_0S_incw, // Increment Word Data Increment + NEC_78K_0S_decw, // Decrement Word Data Decrement + NEC_78K_0S_ror, // Rotate Right Byte Data Rotation to the Right + NEC_78K_0S_rol, // Rotate Left Byte Data Rotation to the Left + NEC_78K_0S_rorc, // Rotate Right with Carry Byte Data Rotation to the Right with Carry + NEC_78K_0S_rolc, // Rotate Left with Carry Byte Data Rotation to the Left with Carry + NEC_78K_0S_call, // CALL Subroutine Call (16 Bit Direct) + NEC_78K_0S_callt, // Call Table Subroutine Call (Call Table Reference) + NEC_78K_0S_ret, // Return from Subroutine + NEC_78K_0S_reti, // Return from Interrupt / Return from Hardware Vectored Interrupt + NEC_78K_0S_mov, // Move Byte Data Transfer + NEC_78K_0S_xch, // Exchange Byte Data Exchange + NEC_78K_0S_xchw, // Exchange Word Data Exchange + NEC_78K_0S_set1, // Set Single Bit (Carry Flag) 1 Bit Data Set + NEC_78K_0S_clr1, // Clear Single Bit (Carry Flag) 1 Bit Data Clear + NEC_78K_0S_not1, // Not Single Bit (Carry Flag) 1 Bit Data Logical Negation + NEC_78K_0S_push, // Push + NEC_78K_0S_pop, // Pop + NEC_78K_0S_movw, // Move Word Data Transfer / Word Data Transfer with Stack Pointer + NEC_78K_0S_br, // Unconditional Branch + NEC_78K_0S_bc, // Branch if Carry Conditional Branch with Carry Flag (CY = 1) + NEC_78K_0S_bnc, // Branch if Not Carry Conditional Branch with Carry Flag (CY = 0) + NEC_78K_0S_bz, // Branch if Zero Conditional Branch with Zero Flag (Z = 1) + NEC_78K_0S_bnz, // Branch if Not Zero Conditional Branch with Zero Flag (Z = 0) + NEC_78K_0S_bt, // Branch if True Conditional Branch by Bit Test (Byte Data Bit = 1) + NEC_78K_0S_bf, // Branch if False Conditional Branch by Bit Test (Byte Data Bit = 0) + NEC_78K_0S_dbnz, // Decrement and Branch if Not Zero Conditional Loop (R1 != 0) + NEC_78K_0S_nop, // No Operation + NEC_78K_0S_EI, // Enable Interrupt + NEC_78K_0S_DI, // Disable Interrupt + NEC_78K_0S_HALT, // HALT Mode Set + NEC_78K_0S_STOP, // Stop Mode Set + NEC_78K_0S_last +}; + + + + +// The instruction types (``itype''s) +// M16C/60, M16C/20, M16C/Tiny and M16C/80 CPUs implements. + +enum m16c_itype_t +{ + M16C_null=0, // Unknown Operation + M16C_abs, // Absolute value + M16C_adc, // Add with carry + M16C_adcf, // Add carry flag + M16C_add, // Add without carry + M16C_adjnz, // Add & conditional jump + M16C_and, // Logically AND + M16C_band, // Logically AND bits + M16C_bclr, // Clear bit + M16C_bmcnd, // Conditional bit transfer + // conds (order is important; don't shuffle) (same order as the enum am_cnd8_t in m16c.hpp) + M16C_bmgeu, // Conditional bit transfer: Equal to or greater than; C == 1 + M16C_bmgtu, // Conditional bit transfer: Greater than + M16C_bmeq, // Conditional bit transfer: Equal to; Z == 1 + M16C_bmn, // Conditional bit transfer: Negative + M16C_bmle, // Conditional bit transfer: Equal to or smaller than (signed) + M16C_bmo, // Conditional bit transfer: Overflow; O == 1 + M16C_bmge, // Conditional bit transfer: Equal to or greater than (signed) + M16C_bmltu, // Conditional bit transfer: Smaller than; C == 0 + M16C_bmleu, // Conditional bit transfer: Equal to or smaller than + M16C_bmne, // Conditional bit transfer: Not equal; Z == 0 + M16C_bmpz, // Conditional bit transfer: Positive or zero + M16C_bmgt, // Conditional bit transfer: Greater than (signed) + M16C_bmno, // Conditional bit transfer: No overflow; O == 0 + M16C_bmlt, // Conditional bit transfer: Smaller than (signed) + // --- + M16C_bnand, // Logically AND inverted bits + M16C_bnor, // Logically OR inverted bits + M16C_bnot, // Invert bit + M16C_bntst, // Test inverted bit + M16C_bnxor, // Exclusive OR inverted bits + M16C_bor, // Logically OR bits + M16C_brk, // Debug interrupt + M16C_bset, // Set bit + M16C_btst, // Test bit + M16C_btstc, // Test bit & clear + M16C_btsts, // Test bit & set + M16C_bxor, // Exclusive OR bits + M16C_cmp, // Compare + M16C_dadc, // Decimal add with carry + M16C_dadd, // Decimal add without carry + M16C_dec, // Decrement + M16C_div, // Signed divide + M16C_divu, // Unsigned divide + M16C_divx, // Signed divide (extension) + M16C_dsbb, // Decimal subtract with borrow + M16C_dsub, // Decimal subtract without borrow + M16C_enter, // Build stack frame + M16C_exitd, // Deallocate stack frame + M16C_exts, // Extend sign + M16C_fclr, // Clear flag register bit + M16C_fset, // Set flag register bit + M16C_inc, // Increment + M16C_int, // Interrupt by INT instruction + M16C_into, // Interrupt on overflow + M16C_jcnd, // Jump on condition + // conds (order is important; don't shuffle) (same order as the enum am_cnd8_t in m16c.hpp) + M16C_jgeu, // Conditional jump: Equal to or greater than; C == 1 + M16C_jgtu, // Conditional jump: Greater than + M16C_jeq, // Conditional jump: Equal to; Z == 1 + M16C_jn, // Conditional jump: Negative + M16C_jle, // Conditional jump: Equal to or smaller than (signed) + M16C_jo, // Conditional jump: Overflow; O == 1 + M16C_jge, // Conditional jump: Equal to or greater than (signed) + M16C_jltu, // Conditional jump: Smaller than; C == 0 + M16C_jleu, // Conditional jump: Equal to or smaller than + M16C_jne, // Conditional jump: Not equal; Z == 0 + M16C_jpz, // Conditional jump: Positive or zero + M16C_jgt, // Conditional jump: Greater than (signed) + M16C_jno, // Conditional jump: No overflow; O == 0 + M16C_jlt, // Conditional jump: Smaller than (signed) + // --- + M16C_jmp, // Unconditional jump + M16C_jmpi, // Jump indirect + M16C_jmps, // Jump to special page + M16C_jsr, // Subroutine call + M16C_jsri, // Indirect subroutine call + M16C_jsrs, // Special page subroutine call + M16C_ldc, // Transfer to control register + M16C_ldctx, // Restore context + M16C_lde, // Transfer from extended data area + M16C_ldintb, // Load to INTB register + M16C_ldipl, // Set interrupt enable level + M16C_mov, // Transfer + M16C_mova, // Transfer effective address + M16C_movhh, // Xfer 4-bit data: src(8-bits)'s 4 hi bits -> dest(8-bits)'s 4 hi bits. + M16C_movhl, // Xfer 4-bit data: src(8-bits)'s 4 hi bits -> dest(8-bits)'s 4 lo bits. + M16C_movlh, // Xfer 4-bit data: src(8-bits)'s 4 lo bits -> dest(8-bits)'s 4 hi bits. + M16C_movll, // Xfer 4-bit data: src(8-bits)'s 4 lo bits -> dest(8-bits)'s 4 lo bits. + M16C_mul, // Signed multiply + M16C_mulu, // Unsigned multiply + M16C_neg, // Two's complement + M16C_nop, // No operation + M16C_not, // Invert all bits + M16C_or, // Logically OR + M16C_pop, // Restore register/memory + M16C_popc, // Restore control register + M16C_popm, // Restore multiple registers + M16C_push, // Save register/memory/immediate data + M16C_pusha, // Save effective address + M16C_pushc, // Save control register + M16C_pushm, // Save multiple registers + M16C_reit, // Return from interrupt + M16C_rmpa, // Calculate sum-of-products + M16C_rolc, // Rotate left with carry + M16C_rorc, // Rotate right with carry + M16C_rot, // Rotate + M16C_rts, // Return from subroutine + M16C_sbb, // Subtract with borrow + M16C_sbjnz, // Subtract & conditional jump + M16C_sha, // Shift arithmetic + M16C_shl, // Shift logical + M16C_smovb, // Transfer string backward + M16C_smovf, // Transfer string forward + M16C_sstr, // Store string + M16C_stc, // Transfer from control register + M16C_stctx, // Save context + M16C_ste, // Store to extended data area + M16C_stnz, // Conditional transfer: store on not zero + M16C_stz, // Conditional transfer: store on zero + M16C_stzx, // Conditional transfer: store on zero extension + M16C_sub, // Subtract without borrow + M16C_tst, // Test + M16C_und, // Interrupt for undefined instruction + M16C_wait, // Wait + M16C_xchg, // Exchange + M16C_xor, // Exclusive OR + + // M16C/80 specifics + M16C_addx, + M16C_bitindex, + M16C_brk2, // Debugger-only interrupt + M16C_clip, + M16C_cmpx, + M16C_extz, + M16C_freit, // Fast return from interrupt + M16C_indexb, + M16C_indexbd, + M16C_indexbs, + M16C_indexl, + M16C_indexld, + M16C_indexls, + M16C_indexw, + M16C_indexwd, + M16C_indexws, + M16C_max, + M16C_min, + M16C_movx, + M16C_mulex, + M16C_sccnd, + // conds (order is important; don't shuffle) (same order as the enum am_cnd8_t in m16c.hpp) + M16C_scgeu, // Conditional store: Equal to or greater than; C == 1 + M16C_scgtu, // Conditional store: Greater than + M16C_sceq, // Conditional store: Equal to; Z == 1 + M16C_scn, // Conditional store: Negative + M16C_scle, // Conditional store: Equal to or smaller than (signed) + M16C_sco, // Conditional store: Overflow; O == 1 + M16C_scge, // Conditional store: Equal to or greater than (signed) + M16C_scltu, // Conditional store: Smaller than; C == 0 + M16C_scleu, // Conditional store: Equal to or smaller than + M16C_scne, // Conditional store: Not equal; Z == 0 + M16C_scpz, // Conditional store: Positive or zero + M16C_scgt, // Conditional store: Greater than (signed) + M16C_scno, // Conditional store: No overflow; O == 0 + M16C_sclt, // Conditional store: Smaller than (signed) + // --- + M16C_scmpu, + M16C_sin, + M16C_smovu, + M16C_sout, + M16C_subx, + + // M32C specifics + M16C_shanc, // Shift arithmetic no carry + M16C_shlnc, // Shift logical no carry + + // R32C specifics + M16C_addf, + M16C_adsf, + M16C_cmpf, + M16C_cnvif, + M16C_divf, + M16C_ediv, + M16C_edivu, + M16C_edivx, + M16C_emul, + M16C_emulu, + M16C_exiti, + M16C_index1, + M16C_index2, + M16C_mulf, + M16C_mulx, + M16C_round, + M16C_stop, + M16C_subf, + M16C_suntil, + M16C_swhile, + + M16C_last +}; + + + + + + + +// exporting the ins.cpp array + +// m32r instructions declaration +enum +{ + m32r_null = 0, // Null instruction + m32r_add, // Add + m32r_add3, // Add 3-operand + m32r_addi, // Add immediate + m32r_addv, // Add with overflow checking + m32r_addv3, // Add 3-operand with overflow checking + m32r_addx, // Add with carry + m32r_and, // AND + m32r_and3, // AND 3-operand + m32r_bc, // Branch on C-bit + m32r_beq, // Branch on equal + m32r_beqz, // Branch on equal zero + m32r_bgez, // Branch on greater than or equal zero + m32r_bgtz, // Branch on greater than zero + m32r_bl, // Branch and link + m32r_blez, // Branch on less than or equal zero + m32r_bltz, // Branch on less than zero + m32r_bnc, // Branch on not C-bit + m32r_bne, // Branch on not equal + m32r_bnez, // Branch on not equal zero + m32r_bra, // Branch + m32r_cmp, // Compare + m32r_cmpi, // Compare immediate + m32r_cmpu, // Compare unsigned + m32r_cmpui, // Compare unsigned immediate + m32r_div, // Divide + m32r_divu, // Divide unsigned + m32r_jl, // Jump and link + m32r_jmp, // Jump + m32r_ld, // Load + m32r_ld24, // Load 24-bit immediate + m32r_ldb, // Load byte + m32r_ldh, // Load halfword + m32r_ldi, // Load immediate + m32r_ldub, // Load unsigned byte + m32r_lduh, // Load unsigned halfword + m32r_lock, // Load locked + m32r_machi, // Multiply-accumulate high-order halfwords + m32r_maclo, // Multiply-accumulate low-order halfwords + m32r_macwhi, // Multiply-accumulate word and high-order halfword + m32r_macwlo, // Multiply-accumulate word and low-order halfword + m32r_mul, // Multiply + m32r_mulhi, // Multiply high-order halfwords + m32r_mullo, // Multiply low-order halfwords + m32r_mulwhi, // Multiply word high-order halfwords + m32r_mulwlo, // Multiply word low-order halfwords + m32r_mv, // Move register + m32r_mvfachi, // Move from accumulator high-order word + m32r_mvfaclo, // Move from accumulator low-order word + m32r_mvfacmi, // Move from accumulator middle-order word + m32r_mvfc, // Move from control register + m32r_mvtachi, // Move to accumulator high-order word + m32r_mvtaclo, // Move to accumulator low-order word + m32r_mvtc, // Move to control register + m32r_neg, // Negate + m32r_nop, // No operation + m32r_not, // Logical NOT + m32r_or, // OR + m32r_or3, // OR 3-operand + m32r_push, // Push, mnem for st reg, @-sp + m32r_pop, // Pop, mnem for ld reg, @sp+ + m32r_rac, // Round accumulator + m32r_rach, // Round accumulator halfword + m32r_rem, // Remainder + m32r_remu, // Remainder unsigned + m32r_rte, // Return from EIT + m32r_seth, // Set high-order 16-bit + m32r_sll, // Shift left logical + m32r_sll3, // Shift left logical 3-operand + m32r_slli, // Shift left logical immediate + m32r_sra, // Shirt right arithmetic + m32r_sra3, // Shirt right arithmetic 3-operand + m32r_srai, // Shirt right arithmetic immediate + m32r_srl, // Shift right logical + m32r_srl3, // Shift right logical 3-operand + m32r_srli, // Shift right logical immediate + m32r_st, // Store + m32r_stb, // Store byte + m32r_sth, // Store halfword + m32r_sub, // Substract + m32r_subv, // Substract with overflow checking + m32r_subx, // Substract with borrow + m32r_trap, // Trap + m32r_unlock, // Store unlocked + m32r_xor, // Exclusive OR + m32r_xor3, // Exclusive OR 3-operand + + // M32RX : + + m32rx_bcl, + m32rx_bncl, + m32rx_cmpeq, + m32rx_cmpz, + m32rx_divh, + m32rx_jc, + m32rx_jnc, + m32rx_machi, // 'machi' 3-operand + m32rx_maclo, // 'maclo' 3-operand + m32rx_macwhi, // 'macwhi' 3-operand + m32rx_macwlo, // 'macwlo' 3-operand + m32rx_mulhi, // 'mulhi' 3-operand + m32rx_mullo, // 'mullo' 3-operand + m32rx_mulwhi, // 'mulwhi' 3-operand + m32rx_mulwlo, // 'mulwlo' 3-operand + m32rx_mvfachi, // 'mvfachi' 3-operand + m32rx_mvfaclo, // 'mvfaclo' 3-operand + m32rx_mvfacmi, // 'mvfacmi' 3-operand + m32rx_mvtachi, // 'mvtachi' 3-operand + m32rx_mvtaclo, // 'mvtaclo' 3-operand + m32rx_rac, // 'rac' 3 operand + m32rx_rach, // 'rach' 3 operand + m32rx_satb, + m32rx_sath, + m32rx_sat, + m32rx_pcmpbz, + m32rx_sadd, + m32rx_macwu1, + m32rx_msblo, + m32rx_mulwu1, + m32rx_maclh1, + m32rx_sc, + m32rx_snc, + +// Floating point + m32r_fadd, // Floating-point add + m32r_fsub, // Floating-point subtract + m32r_fmul, // Floating-point multiply + m32r_fdiv, // Floating-point divede + m32r_fmadd, // Floating-point multiply and add + m32r_fmsub, // Floating-point multiply and subtract + m32r_itof, // Integer to float + m32r_utof, // Unsigned integer to float + m32r_ftoi, // Float to integer + m32r_ftos, // Float to short + m32r_fcmp, // Floating-point compare + m32r_fcmpe, // Floating-point compare with exeption if unordered +// Bit Operation Instructions + m32r_bset, // Bit set + m32r_bclr, // Bit clear + m32r_btst, // Bit test + m32r_setpsw, // Set PSW + m32r_clrpsw, // Clear PSW + + m32r_last +}; + + + + + +enum +{ + m740_null = 0, // null instruction + m740_adc, // add with carry + m740_and, // logical and + m740_asl, // arithmetic shift left + m740_bbc, // branch on bit clear + m740_bbs, // branch on bit set + m740_bcc, // branch on carry clear + m740_bcs, // branch on carry set + m740_beq, // branch on equal + m740_bit, // test bit in memory with accumulator + m740_bmi, // branch on result minus + m740_bne, // branch on not equal + m740_bpl, // branch on result plus + m740_bra, // branch always + m740_brk, // force break + m740_bvc, // branch on overflow clear + m740_bvs, // branch on overflow set + m740_clb, // clear bit + m740_clc, // clear carry flag + m740_cld, // clear decimal mode + m740_cli, // clear interrupt disable status + m740_clt, // clear transfer flag + m740_clv, // clear overflow flag + m740_cmp, // compare + m740_com, // complement + m740_cpx, // compare memory and index register X + m740_cpy, // compare memory and index register Y + m740_dec, // decrement by one + m740_dex, // decrement index register X by one + m740_dey, // decrement index register Y by one + m740_div, // divide memory by accumulator + m740_eor, // exclusive or memory with accumulator + m740_inc, // increment by one + m740_inx, // increment index register X by one + m740_iny, // increment index register Y by one + m740_jmp, // jump + m740_jsr, // jump to subroutine + m740_lda, // load accumulator with memory + m740_ldm, // load immediate data to memory + m740_ldx, // load index register X from memory + m740_ldy, // load index register Y from memory + m740_lsr, // logical shift right + m740_mul, // multiply accumulator and memory + m740_nop, // no operation + m740_ora, // or memory with accumulator + m740_pha, // push accumulator on stack + m740_php, // push processor status on stack + m740_pla, // pull accumulator from stack + m740_plp, // pull processor status from stack + m740_rol, // rotate one bit left + m740_ror, // rotate one bit right + m740_rrf, // rotate right of four bits + m740_rti, // return from interrupt + m740_rts, // return from subroutine + m740_sbc, // subtract with carry + m740_seb, // set bit + m740_sec, // set carry flag + m740_sed, // set decimal mode + m740_sei, // set interrupt disable flag + m740_set, // set transfert flag + m740_sta, // store accumulator in memory + m740_stp, // stop + m740_stx, // store index register X in memory + m740_sty, // store index register Y in memory + m740_tax, // transfert accumulator to index register X + m740_tay, // transfert accumulator to index register Y + m740_tst, // test for negative or zero + m740_tsx, // transfert stack pointer to index register X + m740_txa, // transfert index register X to accumulator + m740_txs, // transfert index register X to stack pointer + m740_tya, // transfert index register Y to accumulator + m740_wit, // wait + + m740_last +}; + + + + + +enum +{ + // 7700 : + + m7700_null = 0, // null instruction + m7700_adc, // addition with carry + m7700_and, // logical AND + m7700_asl, // arithmetic shift left + m7700_bbc, // branch on bit clear + m7700_bbs, // branch on bit set + m7700_bcc, // branch on carry clear + m7700_bcs, // branch on carry set + m7700_beq, // branch on equal + m7700_bmi, // branch on result minus + m7700_bne, // branch on not equal + m7700_bpl, // branch on result plus + m7700_bra, // branch always + m7700_brk, // force break + m7700_bvc, // branch on overflow clear + m7700_bvs, // branch on overflow set + m7700_clb, // clear bit + m7700_clc, // clear carry flag + m7700_cli, // clear interrupt disable status + m7700_clm, // clear m flag + m7700_clp, // clear processor status + m7700_clv, // clear overflow flag + m7700_cmp, // compare + m7700_cpx, // compare memory and index register X + m7700_cpy, // compare memory and index register Y + m7700_dec, // decrement by one + m7700_dex, // decrement index register X by one + m7700_dey, // decrement index register Y by one + m7700_div, // divide + m7700_eor, // exclusive OR memory with accumulator + m7700_inc, // increment by one + m7700_inx, // increment index register X by one + m7700_iny, // increment index register Y by one + m7700_jmp, // jump + m7700_jsr, // jump to subroutine + m7700_lda, // load accumulator from memory + m7700_ldm, // load immediate to memory + m7700_ldt, // load immediate to data bank register + m7700_ldx, // load index register X from memory + m7700_ldy, // load index register Y from memory + m7700_lsr, // logical shift right + m7700_mpy, // multiply + m7700_mvn, // move negative + m7700_mvp, // move positive + m7700_nop, // no operation + m7700_ora, // OR memory with accumulator + m7700_pea, // push effective address + m7700_pei, // push effective indirect address + m7700_per, // push effective program counter relative address + m7700_pha, // push accumulator A on stack + m7700_phb, // push accumulator B on stack + m7700_phd, // push direct page register on stack + m7700_phg, // push program bank register on stack + m7700_php, // push processor status on stack + m7700_pht, // push data bank register on stack + m7700_phx, // push index register X on stack + m7700_phy, // push index register Y on stack + m7700_pla, // pull accumulator A from stack + m7700_plb, // pull accumulator B from stack + m7700_pld, // pull direct page register from stack + m7700_plp, // pull processor status from stack + m7700_plt, // pull data bank register from stack + m7700_plx, // pull index register X from stack + m7700_ply, // pull index register Y from stack + m7700_psh, // push + m7700_pul, // pull + m7700_rla, // rotate left accumulator A + m7700_rol, // rotate one bit left + m7700_ror, // rotate one bit right + m7700_rti, // return from interrupt + m7700_rtl, // return from subroutine long + m7700_rts, // return from subroutine + m7700_sbc, // subtract with carry + m7700_seb, // set bit + m7700_sec, // set carry flag + m7700_sei, // set interrupt disable status + m7700_sem, // set m flag + m7700_sep, // set processor status + m7700_sta, // store accumulator in memory + m7700_stp, // stop + m7700_stx, // store index register X in memory + m7700_sty, // store index register Y in memory + m7700_tad, // transfer accumulator A to direct page register + m7700_tas, // transfer accumulator A to stack pointer + m7700_tax, // transfer accumulator A to index register X + m7700_tay, // transfer accumulator A to index register Y + m7700_tbd, // transfer accumulator B to direct page register + m7700_tbs, // transfer accumulator B to stack pointer + m7700_tbx, // transfer accumulator B to index register X + m7700_tby, // transfer accumulator B to index register Y + m7700_tda, // transfer direct page register to accumulator A + m7700_tdb, // transfer direct page register to accumulator B + m7700_tsa, // transfer stack pointer to accumulator A + m7700_tsb, // transfer stack pointer to accumulator B + m7700_tsx, // transfer stack pointer to index register X + m7700_txa, // transfer index register X to accumulator A + m7700_txb, // transfer index register X to accumulator B + m7700_txs, // transfer index register X to stack pointer + m7700_txy, // transfer index register X to Y + m7700_tya, // transfer index register Y to accumulator A + m7700_tyb, // transfer index register Y to accumulator B + m7700_tyx, // transfer index register Y to X + m7700_wit, // wait + m7700_xab, // exchange accumulator A and B + + // 7750 : + + m7750_asr, // arithmetic shift right + m7750_divs, // divide with sign + m7750_exts, // extention with sign + m7750_extz, // extention zero + m7750_mpys, // multiply with sign + + m7700_last +}; + + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + * MITSUBISHI 7900 Family + */ + + + +enum +{ +m7900_null = 0, // Unknown Operation + + +m7900_abs, // ABSolute +m7900_absd, // ABSolute at Double-word + +m7900_adc, // ADd with Carry +m7900_adcb, // ADd with Carry at Byte +m7900_adcd, // ADd with Carry at Double-word + +m7900_add, // Addition + +m7900_addb, // ADD at Byte +m7900_addd, // ADD at Double-word +m7900_addm, // ADD immediate and Memory +m7900_addmb, // ADD immediate and Memory at Byte +m7900_addmd, // ADD immediate and Memory at Double-word +m7900_adds, // ADD Stack pointer and immediate +m7900_addx, // ADD index register X and immediate +m7900_addy, // ADD index register Y and immediate + +m7900_and, // Logical AND +m7900_andb, // logical AND between immediate (Byte) + +m7900_andm, // logical AND between immediate value and Memory +m7900_andmb, // logical AND between immediate value and Memory (Byte) +m7900_andmd, // logical AND between immediate value and Memory (Double word) + +m7900_asl, // Arithmetic Shift to Left +m7900_asln, // Arithmetic Shift to Left by n bits +m7900_asldn, // Arithmetic Shift to Left by n bits (Double word) + + +m7900_asr, // Arithmeticshift to the right +m7900_asrn, // Arithmetic Shift to Right by n bits +m7900_asrdn, // Arithmetic Shift to Right by n bits (Double word) + +m7900_bbc, // Branch on Bit Clear +m7900_bbcb, // Branch on Bit Clear (Byte) +m7900_bbs, // Branch on Bit Set +m7900_bbsb, // Branch on Bit Set (Byte) + +m7900_bcc, // Branch on Carry Clear +m7900_bcs, // Branch on Carry Set +m7900_beq, // Branch on EQual +m7900_bge, // Branch on Greater or Equal +m7900_bgt, // Branch on Greater Than +m7900_bgtu, // Branch on Greater Than with Unsign +m7900_ble, // Branch on Less or Equal +m7900_bleu, // Branch on Less Equal with Unsign +m7900_blt, // Branch on Less Than +m7900_bmi, // Branch on result MInus +m7900_bne, // Branch on Not Equal +m7900_bpl, // Branch on result PLus +m7900_bra, // BRanch Always +m7900_bral, // BRanch Always + +m7900_brk, // force BReaK + +m7900_bsc, // Branch on Single bit Clear +m7900_bsr, // Branch to SubRoutine +m7900_bss, // Branch on Single bit Set + +m7900_bvc, // Branch on oVerflow Clear +m7900_bvs, // Branch on oVerflow Set + +m7900_cbeq, // Compare immediate and Branch on EQual +m7900_cbeqb, // Compare immediate and Branch on EQual at Byte +m7900_cbne, // Compare immediate and Branch on Not Equal +m7900_cbneb, // Compare immediate and Branch on Not Equal at Byte + +m7900_clc, // CLear Carry flag +m7900_cli, // CLear Interrupt disable status +m7900_clm, // CLear M flag +m7900_clp, // CLear Processor status + +m7900_clr, // CLeaR accumulator +m7900_clrb, // CLeaR accumulator at Byte +m7900_clrm, // CLeaR Memory +m7900_clrmb, // CLeaR Memory at Byte +m7900_clrx, // CLeaR index register X +m7900_clry, // CLeaR index register Y + +m7900_clv, // CLear oVerflow flag + +m7900_cmp, // CoMPare +m7900_cmpb, // CoMPare at Byte +m7900_cmpd, // CoMPare at Double-word +m7900_cmpm, // CoMPare immediate with Memory +m7900_cmpmb, // CoMPare immediate with Memory at Byte +m7900_cmpmd, // CoMPare immediate with Memory at Double-word + +m7900_cpx, // ComPare memory and index register X +m7900_cpy, // ComPare memory and index register Y + +m7900_debne, // DEcrement memory and Branch on Not Equal + +m7900_dec, // DECrement by one +m7900_dex, // DEcrement index register X by one +m7900_dey, // DEcrement index register Y by one + +m7900_div, // DIVide unsigned +m7900_divs, // DIVide with Sign +m7900_dxbne, // Decrement index register X and Branch on Not Equal +m7900_dybne, // Decrement index register Y and Branch on Not Equal + +m7900_eor, // Exclusive OR memory with accumulator +m7900_eorb, // Exclusive OR immediate with accumulator at Byte +m7900_eorm, // Exclusive OR immediate with Memory +m7900_eormb, // Exclusive OR immediate with Memory at Byte +m7900_eormd, // Exclusive OR immediate with Memory at Double-word + +m7900_exts, // EXTension Sign +m7900_extsd, // EXTension Sign at Double-word +m7900_extz, // EXTension Zero +m7900_extzd, // EXTension Zero at Double-word + +m7900_inc, // INCrement by one +m7900_inx, // INcrement index register X by one +m7900_iny, // INcrement index register y by one + +m7900_jmp, // Jump +m7900_jmpl, // Jump + +m7900_jsr, // Jump to SubRoutine +m7900_jsrl, // Jump to SubRoutine + + +m7900_lda, // LoaD Accumulator from memory +m7900_ldab, // LoaD Accumulator from memory at Byte +m7900_ldad, // LoaD Accumulator from memory at Double-word +m7900_lddn, // LoaD immediate to Direct page register n + +m7900_ldt, // LoaD immediate to DaTa bank register +m7900_ldx, // LoaD index register X from memory +m7900_ldxb, // LoaD index register X from memory at Byte +m7900_ldy, // LoaD index register Y from memory +m7900_ldyb, // LoaD index register Y from memory at Byte + +m7900_lsr, // Logical Shift Right +m7900_lsrn, // Logical n bits Shift Right +m7900_lsrdn, // Logical n bits Shift Right at Double-word + +m7900_movm, // MOVe Memory to memory +m7900_movmb, // MOVe Memory to memory at Byte +m7900_movr, // MOVe Repeat memory to memory +m7900_movrb, // MOVe Repeat memory to memory at Byte + +m7900_mpy, // MultiPlY +m7900_mpys, // MultiPlY with Sign + +m7900_mvn, // MoVe Negative +m7900_mvp, // MoVe Positive + +m7900_neg, // NEGative +m7900_negd, // NEGative at Double-word + +m7900_nop, // No OPeration + +m7900_ora, // OR memory with Accumulator +m7900_orab, // OR immediate with Accumulator at Byte +m7900_oram, // OR immediAte with Memory +m7900_oramb, // OR immediAte with Memory at Byte +m7900_oramd, // OR immediAte with Memory at Double-word + +m7900_pea, // Push Effective Address +m7900_pei, // Push Effective Indirect address +m7900_per, // Push Effective program counter Relative address +m7900_pha, // PusH accumulator A on stack +m7900_phb, // PusH accumulator B on stack +m7900_phd, // PusH Direct page register on stack +m7900_phdn, // PusH Direct page register n on stack +m7900_phg, // PusH proGram bank register on stack + +m7900_phldn, // PusH dpr n to stack and Load immediate to Dpr n + +m7900_php, // PusH Processor status on stack +m7900_pht, // PusH daTa bank register on stack +m7900_phx, // PusH index register X on stack +m7900_phy, // PusH index register Y on stack + +m7900_pla, // PuLl accumulator A from stack +m7900_plb, // PuLl accumulator B from stack +m7900_pld, // PuLl Direct page register from stack +m7900_pldn, // PuLl Direct page register n from stack +m7900_plp, // PuLl Processor status from stack +m7900_plt, // PuLl daTa bank register from stack +m7900_plx, // PuLl index register X from stack +m7900_ply, // PuLl index register Y from stack + +m7900_psh, // PuSH +m7900_pul, // PuLl + +m7900_rla, // Rotate Left accumulator A +m7900_rmpa, // Repeat Multiply and Accumulate + +m7900_rol, // ROtate one bit Left +m7900_roln, // n bits ROtate Left + +m7900_roldn, // n bits ROtate Left at Double-word + +m7900_ror, // ROtate one bit Right +m7900_rorn, // n bits ROtate Right +m7900_rordn, // n bits ROtate Right at Double-word + +m7900_rti, // Return from Interrupt +m7900_rtl, // ReTurn from subroutine Long +m7900_rtld, // ReTurn from subroutine Long and pull Direct page register n +m7900_rts, // ReTurn from Subroutine +m7900_rtsdn, // ReTurn from Subroutine and pull Direct page register n + + +m7900_sbc, // SuBtract with Carry +m7900_sbcb, // SuBtract with Carry at Byte +m7900_sbcd, // SuBtract with Carry at Double-word + + +m7900_sec, // SEt Carry flag +m7900_sei, // SEt Interrupt disable status +m7900_sem, // SEt M flag +m7900_sep, // SEt Processor status + + +m7900_sta, // STore Accumulator in memory +m7900_stab, // STore Accumulator in memory at Byte +m7900_stad, // STore Accumulator in memory at Double-word + + +m7900_stp, // SToP +m7900_stx, // STore index register X in memory +m7900_sty, // STore index register Y in memory + +m7900_sub, // SUBtract +m7900_subb, // SUBtract at Byte +m7900_subd, // SUBtract at Double-word +m7900_subm, // SUBtract immediate from Memory +m7900_submb, // SUBtract immediate from Memory at Byte +m7900_submd, // SUBtract immediate from Memory at Double-word +m7900_subs, // SUBtract Stack pointer +m7900_subx, // SUBtract immediate from index register X +m7900_suby, // SUBtract immediate from index register Y + + + +m7900_tadn, // Transfer accumulator A to Direct page register n + + +m7900_tas, // Transfer accumulator A to Stack pointer +m7900_tax, // Transfer accumulator A to index register X +m7900_tay, // Transfer accumulator A to index register Y + +m7900_tbdn, // Transfer accumulator B to Direct page register n + +m7900_tbs, // Transfer accumulator B to Stack pointer +m7900_tbx, // Transfer accumulator B to index register X +m7900_tby, // Transfer accumulator B to index register Y + +m7900_tdan, // Transfer Direct page register n to accumulator A +m7900_tdbn, // Transfer Direct page register n to accumulator B + +m7900_tds, // Transfer Direct page register to Stack pointer + + +m7900_tsa, // Transfer Stack pointer to accumulator A +m7900_tsb, // Transfer Stack pointer to accumulator B +m7900_tsd, // Transfer Stack pointer to Direct page register +m7900_tsx, // Transfer Stack pointer to index register X +m7900_txa, // Transfer index register X to accumulator A +m7900_txb, // Transfer index register X to accumulator B +m7900_txs, // Transfer index register X to Stack pointer +m7900_txy, // Transfer index register X to Y +m7900_tya, // Transfer index register Y to accumulator A +m7900_tyb, // Transfer index register Y to accumulator B +m7900_tyx, // Transfer index register Y to X + +m7900_wit, // WaIT + +m7900_xab, // eXchange accumulator A and B + +m7900_last // + + }; + + + + +enum +{ + st9_null = 0, // Unknown Operation. + st9_ld, // Load. + st9_ldw, // Load word. + st9_ldpp, // Load (using CSR) => (using CSR). + st9_ldpd, // Load (using DPRx) => (using CSR). + st9_lddp, // Load (using CSR) => (using DPRx). + st9_lddd, // Load (using DPRx) => (using DPRx). + st9_add, // Add. + st9_addw, // Add Word. + st9_adc, // Add with Carry. + st9_adcw, // Add Word with Carry. + st9_sub, // Substract. + st9_subw, // Substract Word. + st9_sbc, // Substract with Carry. + st9_sbcw, // Substract Word with Carry. + st9_and, // Logical AND. + st9_andw, // Logical Word AND. + st9_or, // Logical OR. + st9_orw, // Logical Word OR. + st9_xor, // Logical Exclusive OR. + st9_xorw, // Logical Word Exclusive OR. + st9_cp, // Compare. + st9_cpw, // Compare Word. + st9_tm, // Test under Mask. + st9_tmw, // Test Word under Mask. + st9_tcm, // Test Complement under Mask. + st9_tcmw, // Test Word Complement under Mask. + st9_inc, // Increment. + st9_incw, // Increment Word. + st9_dec, // Decrement. + st9_decw, // Decrement Word. + st9_sla, // Shift Left Arithmetic. + st9_slaw, // Shift Word Left Arithmetic. + st9_sra, // Shift Right Arithmetic. + st9_sraw, // Shift Word Right Arithmetic. + st9_rrc, // Rotate Right through Carry. + st9_rrcw, // Rotate Word Right through Carry. + st9_rlc, // Rotate Left through Carry. + st9_rlcw, // Rotate Word Left through Carry. + st9_ror, // Rotate Right. + st9_rol, // Rotate Left. + st9_clr, // Clear Register. + st9_cpl, // Complement Register. + st9_swap, // Swap Nibbles. + st9_da, // Decimal ajust. + st9_push, // Push on System Stack. + st9_pushw, // Push Word on System Stack. + st9_pea, // Push Effective Address on System Stack. + st9_pop, // Pop from System Stack. + st9_popw, // Pop Word from System Stack. + st9_pushu, // Push on User Stack. + st9_pushuw, // Push Word on User Stack. + st9_peau, // Push Effective Address on User Stack. + st9_popu, // Pop from User Stack. + st9_popuw, // Pop Word from User Stack. + st9_link, // Move System Stack Pointer upward; support for high-level language. + st9_unlink, // Move System Stack Pointer backward; support for high-level language. + st9_linku, // Move User Stack Pointer upward; support for high-level language. + st9_unlinku, // Move User Stack Pointer backward; support for high-level language. + st9_mul, // Multiply 8x8. + st9_div, // Divide 8x8. + st9_divws, // Divide Word Stepped 32/16. + st9_bset, // Bit Set. + st9_bres, // Bit Reset . + st9_bcpl, // Bit Complement. + st9_btset, // Bit Test and Set. + st9_bld, // Bit Load. + st9_band, // Bit AND. + st9_bor, // Bit OR. + st9_bxor, // Bit XOR. + st9_ret, // Return from Subroutine. + st9_rets, // Inter-segment Return to Subroutine. + st9_iret, // Return from Interrupt. + st9_jrcc, // Jump Relative if Condition ``cc'' is Met. + st9_jpcc, // Jump if Condition ``cc'' is Met. + st9_jp, // Unconditional Jump. + st9_jps, // Unconditional Inter-segment Jump. + st9_call, // Unconditional Call. + st9_calls, // Inter-segment Call to Subroutine. + st9_btjf, // Bit Test and Jump if False. + st9_btjt, // Bit Test and Jump if True. + st9_djnz, // Decrement a Working Register and Jump if Non Zero. + st9_dwjnz, // Decrement a Register Pair and Jump if Non Zero. + st9_cpjfi, // Compare and Jump on False. Otherwise Post Increment. + st9_cpjti, // Compare and Jump on True. Otherwise Post Increment. + st9_xch, // Exchange Registers. + st9_srp, // Set Register Pointer Long (16 working registers). + st9_srp0, // Set Register Pointer 0 (8 LSB working registers). + st9_srp1, // Set Register Pointer 1 (8 MSB working registers). + st9_spp, // Set Page Pointer. + st9_ext, // Sign Extend. + st9_ei, // Enable Interrupts. + st9_di, // Disable Interrupts. + st9_scf, // Set Carry Flag. + st9_rcf, // Reset Carry Flag. + st9_ccf, // Complement Carry Flag. + st9_spm, // Select Extended Memory addressing scheme through CSR Register. + st9_sdm, // Select Extended Memory addressing scheme through DPR Registers. + st9_nop, // No Operation. + st9_wfi, // Stop Program Execution and Wait for the next Enable Interrupt. + st9_halt, // Stop Program Execution until System Reset. + st9_etrap, // Undocumented instruction. + st9_eret, // Undocumented instruction. + st9_ald, // PSEUDO INSTRUCTION. SHOULD NEVER BE USED. + st9_aldw, // PSEUDO INSTRUCTION. SHOULD NEVER BE USED. + st9_last +}; + + + + + +enum +{ + fr_null = 0, // null instruction + + fr_add, // add word data of source register / 4-bit immediate data to destination register + fr_add2, // add 4-bit immediate data to destination register + fr_addc, // add word data of source register and carry bit to destination register + fr_addn, // add word data of source register / immediate data to destination register + fr_addn2, // add immediate data to destination register + fr_sub, // subtract word data in source register from destination register + fr_subc, // subtract word data in source register and carry bit from destination register + fr_subn, // subtract word data in source register from destination register + fr_cmp, // compare word / immediate data in source register and destination register + fr_cmp2, // compare immediate data and destination register + fr_and, // and word data of source register to destination register / data in memory + fr_andh, // and half-word data of source register to data in memory + fr_andb, // and byte data of source register to data in memory + fr_or, // or word data of source register to destination register / data in memory + fr_orh, // or half-word data of source register to data in memory + fr_orb, // or byte data of source register to data in memory + fr_eor, // exclusive or word data of source register to destination register / data in memory + fr_eorh, // exclusive or half-word data of source register to data in memory + fr_eorb, // exclusive or byte data of source register to data in memory + fr_bandl, // and 4-bit immediate data to lower 4 bits of byte data in memory + fr_bandh, // and 4-bit immediate data to higher 4 bits of byte data in memory + fr_borl, // or 4-bit immediate data to lower 4 bits of byte data in memory + fr_borh, // or 4-bit immediate data to higher 4 bits of byte data in memory + fr_beorl, // eor 4-bit immediate data to lower 4 bits of byte data in memory + fr_beorh, // eor 4-bit immediate data to higher 4 bits of byte data in memory + fr_btstl, // test lower 4 bits of byte data in memory + fr_btsth, // test higher 4 bits of byte data in memory + fr_mul, // multiply word data + fr_mulu, // multiply unsigned word data + fr_mulh, // multiply half-word data + fr_muluh, // multiply unsigned half-word data + fr_div0s, // initial setting up for signed division + fr_div0u, // initial setting up for unsigned division + fr_div1, // main process of division + fr_div2, // correction when remainder is 0 + fr_div3, // correction when remainder is 0 + fr_div4s, // correction answer for signed division + fr_lsl, // logical shift to the left direction + fr_lsl2, // logical shift to the left direction + fr_lsr, // logical shift to the right direction + fr_lsr2, // logical shift to the right direction + fr_asr, // arithmetic shift to the right direction + fr_asr2, // arithmetic shift to the right direction + fr_ldi_32, // load immediate 32-bit data to destination register + fr_ldi_20, // load immediate 20-bit data to destination register + fr_ldi_8, // load immediate 8-bit data to destination register + fr_ld, // load word data in memory to register / program status register + fr_lduh, // load half-word data in memory to register + fr_ldub, // load byte data in memory to register + fr_st, // store word data in register / program status register to memory + fr_sth, // store half-word data in register to memory + fr_stb, // store byte data in register to memory + fr_mov, // move word data in source register / program status register to destination register / program status register + fr_jmp, // jump + fr_call, // call subroutine + fr_ret, // return from subroutine + fr_int, // software interrupt + fr_inte, // software interrupt for emulator + fr_reti, // return from interrupt + fr_bra, // branch relative if condition satisfied + fr_bno, // branch relative if condition satisfied + fr_beq, // branch relative if condition satisfied + fr_bne, // branch relative if condition satisfied + fr_bc, // branch relative if condition satisfied + fr_bnc, // branch relative if condition satisfied + fr_bn, // branch relative if condition satisfied + fr_bp, // branch relative if condition satisfied + fr_bv, // branch relative if condition satisfied + fr_bnv, // branch relative if condition satisfied + fr_blt, // branch relative if condition satisfied + fr_bge, // branch relative if condition satisfied + fr_ble, // branch relative if condition satisfied + fr_bgt, // branch relative if condition satisfied + fr_bls, // branch relative if condition satisfied + fr_bhi, // branch relative if condition satisfied + fr_dmov, // move word data from register / address to register / address + fr_dmovh, // move half-word data from register / address to register / address + fr_dmovb, // move byte data from register / address to register / address + fr_ldres, // load word data in memory to resource + fr_stres, // store word data in resource to memory + fr_copop, // coprocessor operation + fr_copld, // load 32-bit data from register to coprocessor register + fr_copst, // store 32-bit data from coprocessor register to register + fr_copsv, // save 32-bit data from coprocessor register to register + fr_nop, // no operation + fr_andccr, // and condition code register and immediate data + fr_orccr, // or condition code register and immediate data + fr_stilm, // set immediate data to interrupt level mask register + fr_addsp, // add stack pointer and immediate data + fr_extsb, // sign extend from byte data to word data + fr_extub, // unsign extend from byte data to word data + fr_extsh, // sign extend from byte data to word data + fr_extuh, // unsigned extend from byte data to word data + fr_ldm0, // load multiple registers + fr_ldm1, // load multiple registers + fr_stm0, // store multiple registers + fr_stm1, // store multiple registers + fr_enter, // enter function + fr_leave, // leave function + fr_xchb, // exchange byte data + fr_last // last instruction +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ +ALPHA_null = 0, // Unknown Operation + +ALPHA_addf, // Add F_floating +ALPHA_addg, // Add G_floating +ALPHA_addl, // Add longword +ALPHA_addl_v, // Add longword with overflow +ALPHA_addq, // Add quadword +ALPHA_addq_v, // Add quadword with overflow +ALPHA_adds, // Add S_floating +ALPHA_addt, // Add T_floating +ALPHA_amask, // Architecture mask +ALPHA_and, // Logical product +ALPHA_beq, // Branch if = zero +ALPHA_bge, // Branch if >= zero +ALPHA_bgt, // Branch if > zero +ALPHA_bic, // Bit clear +ALPHA_bis, // Logical sum +ALPHA_blbc, // Branch if low bit clear +ALPHA_blbs, // Branch if low bit set +ALPHA_ble, // Branch if <= zero +ALPHA_blt, // Branch if < zero +ALPHA_bne, // Branch if != zero +ALPHA_br, // Unconditional branch +ALPHA_bsr, // Branch to subroutine +ALPHA_call_pal, // Trap to PALcode +ALPHA_cmoveq, // CMOVE if = zero +ALPHA_cmovge, // CMOVE if >= zero +ALPHA_cmovgt, // CMOVE if > zero +ALPHA_cmovlbc, // CMOVE if low bit clear +ALPHA_cmovlbs, // CMOVE if low bit set +ALPHA_cmovle, // CMOVE if <= zero +ALPHA_cmovlt, // CMOVE if < zero +ALPHA_cmovne, // CMOVE if != zero +ALPHA_cmpbge, // Compare byte +ALPHA_cmpeq, // Compare signed quadword equal +ALPHA_cmpgeq, // Compare G_floating equal +ALPHA_cmpgle, // Compare G_floating less than or equal +ALPHA_cmpglt, // Compare G_floating less than +ALPHA_cmple, // Compare signed quadword less than or equal +ALPHA_cmplt, // Compare signed quadword less than +ALPHA_cmpteq, // Compare T_floating equal +ALPHA_cmptle, // Compare T_floating less than or equal +ALPHA_cmptlt, // Compare T_floating less than +ALPHA_cmptun, // Compare T_floating unordered +ALPHA_cmpule, // Compare unsigned quadword less than or equal +ALPHA_cmpult, // Compare unsigned quadword less than +ALPHA_cpys, // Copy sign +ALPHA_cpyse, // Copy sign and exponent +ALPHA_cpysn, // Copy sign negate +ALPHA_ctlz, // Count leading zero +ALPHA_ctpop, // Count population +ALPHA_cttz, // Count trailing zero +ALPHA_cvtdg, // Convert D_floating to G_floating +ALPHA_cvtgd, // Convert G_floating to D_floating +ALPHA_cvtgf, // Convert G_floating to F_floating +ALPHA_cvtgq, // Convert G_floating to quadword +ALPHA_cvtlq, // Convert longword to quadword +ALPHA_cvtqf, // Convert quadword to F_floating +ALPHA_cvtqg, // Convert quadword to G_floating +ALPHA_cvtql, // Convert quadword to longword +ALPHA_cvtqs, // Convert quadword to S_floating +ALPHA_cvtqt, // Convert quadword to T_floating +ALPHA_cvtst, // Convert S_floating to T_floating +ALPHA_cvttq, // Convert T_floating to quadword +ALPHA_cvtts, // Convert T_floating to S_floating +ALPHA_divf, // Divide F_floating +ALPHA_divg, // Divide G_floating +ALPHA_divs, // Divide S_floating +ALPHA_divt, // Divide T_floating +ALPHA_ecb, // Evict cache block +ALPHA_eqv, // Logical equivalence +ALPHA_excb, // Exception barrier +ALPHA_extbl, // Extract byte low +ALPHA_extlh, // Extract longword high +ALPHA_extll, // Extract longword low +ALPHA_extqh, // Extract quadword high +ALPHA_extql, // Extract quadword low +ALPHA_extwh, // Extract word high +ALPHA_extwl, // Extract word low +ALPHA_fbeq, // Floating branch if = zero +ALPHA_fbge, // Floating branch if >= zero +ALPHA_fbgt, // Floating branch if > zero +ALPHA_fble, // Floating branch if <= zero +ALPHA_fblt, // Floating branch if < zero +ALPHA_fbne, // Floating branch if != zero +ALPHA_fcmoveq, // FCMOVE if = zero +ALPHA_fcmovge, // FCMOVE if >= zero +ALPHA_fcmovgt, // FCMOVE if > zero +ALPHA_fcmovle, // FCMOVE if <= zero +ALPHA_fcmovlt, // FCMOVE if < zero +ALPHA_fcmovne, // FCMOVE if != zero +ALPHA_fetch, // Prefetch data +ALPHA_fetch_m, // Prefetch data, modify intent +ALPHA_ftois, // Floating to integer move, S_floating +ALPHA_ftoit, // Floating to integer move, T_floating +ALPHA_implver, // Implementation version +ALPHA_insbl, // Insert byte low +ALPHA_inslh, // Insert longword high +ALPHA_insll, // Insert longword low +ALPHA_insqh, // Insert quadword high +ALPHA_insql, // Insert quadword low +ALPHA_inswh, // Insert word high +ALPHA_inswl, // Insert word low +ALPHA_itoff, // Integer to floating move, F_floating +ALPHA_itofs, // Integer to floating move, S_floating +ALPHA_itoft, // Integer to floating move, T_floating +ALPHA_jmp, // Jump +ALPHA_jsr, // Jump to subroutine +ALPHA_jsr_coroutine,// Jump to subroutine return +ALPHA_lda, // Load address +ALPHA_ldah, // Load address high +ALPHA_ldbu, // Load zero-extended byte +ALPHA_ldwu, // Load zero-extended word +ALPHA_ldf, // Load F_floating +ALPHA_ldg, // Load G_floating +ALPHA_ldl, // Load sign-extended longword +ALPHA_ldl_l, // Load sign-extended longword locked +ALPHA_ldq, // Load quadword +ALPHA_ldq_l, // Load quadword locked +ALPHA_ldq_u, // Load unaligned quadword +ALPHA_lds, // Load S_floating +ALPHA_ldt, // Load T_floating +ALPHA_maxsb8, // Vector signed byte maximum +ALPHA_maxsw4, // Vector signed word maximum +ALPHA_maxub8, // Vector unsigned byte maximum +ALPHA_maxuw4, // Vector unsigned word maximum +ALPHA_mb, // Memory barrier +ALPHA_mf_fpcr, // Move from FPCR +ALPHA_minsb8, // Vector signed byte minimum +ALPHA_minsw4, // Vector signed word minimum +ALPHA_minub8, // Vector unsigned byte minimum +ALPHA_minuw4, // Vector unsigned word minimum +ALPHA_mskbl, // Mask byte low +ALPHA_msklh, // Mask longword high +ALPHA_mskll, // Mask longword low +ALPHA_mskqh, // Mask quadword high +ALPHA_mskql, // Mask quadword low +ALPHA_mskwh, // Mask word high +ALPHA_mskwl, // Mask word low +ALPHA_mt_fpcr, // Move to FPCR +ALPHA_mulf, // Multiply F_floating +ALPHA_mulg, // Multiply G_floating +ALPHA_mull, // Multiply longword +ALPHA_mull_v, // Multiply longword with overflow +ALPHA_mulq, // Multiply quadword +ALPHA_mulq_v, // Multiply quadword with overflow +ALPHA_muls, // Multiply S_floating +ALPHA_mult, // Multiply T_floating +ALPHA_ornot, // Logical sum with complement +ALPHA_perr, // Pixel error +ALPHA_pklb, // Pack longwords to bytes +ALPHA_pkwb, // Pack words to bytes +ALPHA_rc, // Read and clear +ALPHA_ret, // Return from subroutine +ALPHA_rpcc, // Read process cycle counter +ALPHA_rs, // Read and set +ALPHA_s4addl, // Scaled add longword by 4 +ALPHA_s4addq, // Scaled add quadword by 4 +ALPHA_s4subl, // Scaled subtract longword by 4 +ALPHA_s4subq, // Scaled subtract quadword by 4 +ALPHA_s8addl, // Scaled add longword by 8 +ALPHA_s8addq, // Scaled add quadword by 8 +ALPHA_s8subl, // Scaled subtract longword by 8 +ALPHA_s8subq, // Scaled subtract quadword by 8 +ALPHA_sextb, // Sign extend byte +ALPHA_sextw, // Sign extend word +ALPHA_sll, // Shift left logical +ALPHA_sqrtf, // Square root F_floating +ALPHA_sqrtg, // Square root G_floating +ALPHA_sqrts, // Square root S_floating +ALPHA_sqrtt, // Square root T_floating +ALPHA_sra, // Shift right arithmetic +ALPHA_srl, // Shift right logical +ALPHA_stb, // Store byte +ALPHA_stf, // Store F_floating +ALPHA_stg, // Store G_floating +ALPHA_sts, // Store S_floating +ALPHA_stl, // Store longword +ALPHA_stl_c, // Store longword conditional +ALPHA_stq, // Store quadword +ALPHA_stq_c, // Store quadword conditional +ALPHA_stq_u, // Store unaligned quadword +ALPHA_stt, // Store T_floating +ALPHA_stw, // Store word +ALPHA_subf, // Subtract F_floating +ALPHA_subg, // Subtract G_floating +ALPHA_subl, // Subtract longword +ALPHA_subl_v, // Subtract longword with overflow +ALPHA_subq, // Subtract quadword +ALPHA_subq_v, // Subtract quadword with overflow +ALPHA_subs, // Subtract S_floating +ALPHA_subt, // Subtract T_floating +ALPHA_trapb, // Trap barrier +ALPHA_umulh, // Unsigned multiply quadword high +ALPHA_unpkbl, // Unpack bytes to longwords +ALPHA_unpkbw, // Unpack bytes to words +ALPHA_wh64, // Write hint 64 bytes +ALPHA_wmb, // Write memory barrier +ALPHA_xor, // Logical difference +ALPHA_zap, // Zero bytes +ALPHA_zapnot, // Zero bytes not + +// Macros + +ALPHA_unop, // Universal NOP +ALPHA_nop, // Integer NOP +ALPHA_fnop, // Floating point NOP +ALPHA_clr, // Clear +ALPHA_fabs, // Floating point absolute value +ALPHA_fclr, // Floating point clear +ALPHA_fmov, // Floating point register move +ALPHA_fneg, // Floating point negate +ALPHA_mov, // Move to register +ALPHA_negl, // Negate longword +ALPHA_negl_v, // Negate longword with overflow +ALPHA_negq, // Negate quadword +ALPHA_negq_v, // Negate quadword with overflow +ALPHA_negf, // Negate F_floating +ALPHA_negg, // Negate G_floating +ALPHA_negs, // Negate S_floating +ALPHA_negt, // Negate T_floating +ALPHA_not, // Bitwise NOT +ALPHA_sextl, // Move sign extended +ALPHA_or, // Bitwise OR +ALPHA_andnot, // Bit clear +ALPHA_xornot, // Logical equivalence +ALPHA_br0, // Branch + +ALPHA_last, + +}; + + + + +enum +{ +KR1878_null = 0, // Unknown Operation + +KR1878_mov, +KR1878_cmp, // Compare +KR1878_add, // Addition +KR1878_sub, // Subtract +KR1878_and, // Logical AND +KR1878_or, // Logical Inclusive OR +KR1878_xor, // Logical Exclusive OR + +KR1878_movl, +KR1878_cmpl, // Compare +KR1878_addl, // Addition +KR1878_subl, // Subtract +KR1878_bic, +KR1878_bis, +KR1878_btg, +KR1878_btt, + +KR1878_swap, +KR1878_neg, +KR1878_not, +KR1878_shl, // Shift Left +KR1878_shr, // Shift Right +KR1878_shra, // Arithmetic Shift Right +KR1878_rlc, // Rotate Left +KR1878_rrc, // Rotate Right +KR1878_adc, // Add with Carry +KR1878_sbc, // Subtract with Carry + +KR1878_ldr, +KR1878_mtpr, +KR1878_mfpr, +KR1878_push, +KR1878_pop, +KR1878_sst, +KR1878_cst, +KR1878_tof, +KR1878_tdc, + +KR1878_jmp, // Jump +KR1878_jsr, // Jump to Subroutine +KR1878_jnz, // Jump +KR1878_jz, // Jump +KR1878_jns, // Jump +KR1878_js, // Jump +KR1878_jnc, // Jump +KR1878_jc, // Jump +KR1878_ijmp, // Jump +KR1878_ijsr, // Jump to Subroutine +KR1878_rts, // Return from Subroutine +KR1878_rtsc, // Return from Subroutine +KR1878_rti, // Return from Interrupt + +KR1878_nop, // No Operation +KR1878_wait, +KR1878_stop, +KR1878_reset, +KR1878_sksp, + +KR1878_last, + +}; + + + + +enum +{ +AD218X_null = 0, // Unknown Operation +// AMF subcodes +AD218X_amf_01, // op = op * op (rnd) +AD218X_amf_03, // op = mr + op * op (rnd) +AD218X_amf_02, // op = mr - op * op (rnd) +AD218X_amf_04, // op = op * op (ss) +AD218X_amf_05, // op = op * op (su) +AD218X_amf_06, // op = op * op (us) +AD218X_amf_07, // op = op * op (uu) +AD218X_amf_08, // op = mr + op * op +AD218X_amf_09, // op = mr + op * op +AD218X_amf_0a, // op = mr + op * op +AD218X_amf_0b, // op = mr + op * op +AD218X_amf_0c, // op = mr - op * op +AD218X_amf_0d, // op = mr - op * op +AD218X_amf_0e, // op = mr - op * op +AD218X_amf_0f, // op = mr - op * op +AD218X_amf_10, // op = op +AD218X_amf_11, // op = op + 1 +AD218X_amf_12, // op = op + op + C +AD218X_amf_13, // op = op + op +AD218X_amf_14, // op = not op +AD218X_amf_15, // op = - op +AD218X_amf_16, // op = op - op + C - 1 +AD218X_amf_17, // op = op - op +AD218X_amf_18, // op = op - 1 +AD218X_amf_19, // op = op - op +AD218X_amf_1a, // op = op - op + C - 1 +AD218X_amf_1b, // op = not op +AD218X_amf_1c, // op = op and op +AD218X_amf_1d, // op = op or op +AD218X_amf_1e, // op = op xor op +AD218X_amf_1f, // op = abs op +// Shifter subcodes +AD218X_shft_0, // sr = lshift op (hi) +AD218X_shft_1, // sr = sr or lshift op (hi) +AD218X_shft_2, // sr = lshift op (lo) +AD218X_shft_3, // sr = sr or lshift op (lo) +AD218X_shft_4, // sr = ashift op (hi) +AD218X_shft_5, // sr = sr or ashift op (hi) +AD218X_shft_6, // sr = ashift op (lo) +AD218X_shft_7, // sr = sr or ashift op (lo) +AD218X_shft_8, // sr = norm op (hi) +AD218X_shft_9, // sr = sr or norm op (hi) +AD218X_shft_a, // sr = norm op (lo) +AD218X_shft_b, // sr = sr or norm op (lo) +AD218X_shft_c, // sr = exp op (hi) +AD218X_shft_d, // sr = exp op (hix) +AD218X_shft_e, // sr = exp op (lo) +AD218X_shft_f, // sr = expadj op +// ALU/MAC specific options +AD218X_alu_00, // op = op + op + C [IF COND] X + Const +AD218X_alu_01, // op = op + op +AD218X_alu_02, // op = op - op + C - 1 [IF COND] X - Const +AD218X_alu_03, // op = op - op +AD218X_alu_04, // op = op - op + C - 1 [IF COND] Const - X +AD218X_alu_05, // op = op - op +AD218X_alu_06, // op = op and op [IF COND] X and,or,xor Const +AD218X_alu_07, // op = op or op +AD218X_alu_08, // op = op xor op +AD218X_alu_09, // op = op [IF COND] PASS constant; constant <> 0,1, -1 +AD218X_alu_0a, // op = op + 1 +AD218X_alu_0b, // op = op - 1 +AD218X_alu_0c, // divq op ALU Type 23 +AD218X_alu_0d, // divs op, op ALU Type 24 +AD218X_alu_0e, // none = op ALU Type 9, with no result +AD218X_alu_0f, // none = op + 1 +AD218X_alu_10, // none = op + op + C +AD218X_alu_11, // none = op + op +AD218X_alu_12, // none = not op +AD218X_alu_13, // none = - op +AD218X_alu_14, // none = op - op + C - 1 +AD218X_alu_15, // none = op - op +AD218X_alu_16, // none = op - 1 +AD218X_alu_17, // none = op - op + C - 1 +AD218X_alu_18, // none = op - op +AD218X_alu_19, // none = not op +AD218X_alu_1a, // none = op and op +AD218X_alu_1b, // none = op or op +AD218X_alu_1c, // none = op xor op +AD218X_alu_1d, // none = abs op +AD218X_mac_0, // op = 0 [IF COND] MAC CLEAR +AD218X_mac_1, // op = mr (rnd) [IF COND] MAC TRANSFER +AD218X_mac_2, // op = op * op (ss) [IF COND] X * X +AD218X_mac_3, // op = op * op (uu) +AD218X_mac_4, // op = op * op (rnd) +AD218X_mac_5, // op = mr + op * op (ss) [IF COND] MR + X * X +AD218X_mac_6, // op = mr + op * op (uu) +AD218X_mac_7, // op = mr + op * op (rnd) +AD218X_mac_8, // op = mr - op * op (ss) [IF COND] MR - X * X +AD218X_mac_9, // op = mr - op * op (uu) +AD218X_mac_a, // op = mr - op * op (rnd) +AD218X_mac_b, // if mv sat mr ALU/MAC Type 25 +// ALU/MAC Common case +AD218X_amf, // [IF COND] AMF +// Shifter Common case +AD218X_shft, // [IF COND] Shifter +// Shifter specific options. Shifter Type 15, const +AD218X_shifter_0, // sr = ashift op by op (hi) +AD218X_shifter_1, // sr = sr or ashift op by op (hi) +AD218X_shifter_2, // sr = ashift op by op (lo) +AD218X_shifter_3, // sr = sr or ashift op by op (lo) +AD218X_shifter_4, // sr = lshift op by op (hi) +AD218X_shifter_5, // sr = sr or lshift op by op (hi) +AD218X_shifter_6, // sr = lshift op by op (lo) +AD218X_shifter_7, // sr = sr or lshift op by op (lo) +// Data Movement +AD218X_move_0, // op = op Internal Data Move, Instruction Type 17: +AD218X_move_1, // op = op Load Register Immediate, Instruction Type 6: +AD218X_move_2, // op = op Load Non-Data Register Immediate, Instruction Type 7: +AD218X_move_3, // op = dm(op) Data Memory Read (Direct Address), Instruction Type 3: +AD218X_move_4, // op = dm(op, op) Data Memory Read, Instruction Type 4: +AD218X_move_5, // op = pm(op, op) Program Memory Read +AD218X_move_6, // dm(op) = op Data Memory Write (Direct Address) +AD218X_move_7, // dm(op, op) = op Data Memory Write, Instruction Type 4: +AD218X_move_8, // dm(op, op) = op Data Memory Write, Immediate Data, Instruction Type 2: +AD218X_move_9, // pm(op, op) = op Program Memory Write, Instruction Type 5 +AD218X_move_a, // op = io(op) I/O Memory Space Read/Write, Instruction Type 29: +AD218X_move_b, // io(op) = op +// Flow control +AD218X_jump, // Unconditional JUMP Direct, Instruction Type 10: +AD218X_jump_1, // Conditional JUMP Direct, Instruction Type 10: +AD218X_jump_2, // Unconditional JUMP Indirect, Instruction Type 19: +AD218X_jump_3, // Conditional JUMP Indirect, Instruction Type 19: +AD218X_jump_4, // Conditional JUMP on Flag In Direct Instruction Type 27: +AD218X_call, // Conditional CALL Direct, Instruction Type 10: +AD218X_call_1, // Conditional CALL Indirect, Instruction Type 19: +AD218X_call_2, // Conditional CALL on Flag In Direct Instruction Type 27: +AD218X_rts, // Unconditional Return, Instruction Type 20: +AD218X_rts_cond, // Conditional Return, Instruction Type 20: +AD218X_rti, // Unconditional Return, Instruction Type 20: +AD218X_rti_cond, // Conditional Return, Instruction Type 20: +AD218X_nop, // No operation, Instruction Type 30 +AD218X_do, // Do Until, Instruction Type 11: +// Misc +AD218X_idle, // Idle, Instruction Type 31: +AD218X_idle_1, +AD218X_flag_out, // Flag Out Mode Control Instruction Type 28: +AD218X_stack_ctl, // Stack Control, Instruction Type 26: +AD218X_mode_ctl, // Mode Control, Instruction Type 18: +AD218X_tops_w, // toppcstack = op Internal Data Move, Instruction Type 17: +AD218X_tops_r, // op = toppcstack +AD218X_ints_dis, // dis ints +AD218X_ints_ena, // ena ints +AD218X_modify, // modify(op, op) Modify Address Register, Instruction Type 21: +// ALU/MAC operation with Data Memory Read/Write +AD218X_double_move, // op = dm(op, op), op = pm(op, op) Data & Program Memory Read, Instruction Type 1: +AD218X_amf_move_0, // amf_cmd, op = dm(op, op) ALU/MAC operation with Data Memory Read, Instruction Type 4: +AD218X_amf_move_1, // amf_cmd, op = pm(op, op) ALU/MAC operation with Program Memory Read, Instruction Type 5: +AD218X_amf_move_2, // amf_cmd, op = dm(op, op) Shift operation with Data Memory Read, Instruction Type 12: +AD218X_amf_move_3, // amf_cmd, op = pm(op, op) Shift operation with Program Memory Read, Instruction Type 13: +AD218X_amf_move_4, // amf_cmd, op = op ALU/MAC operation with Data Register Move, Instruction Type 8: +AD218X_amf_move_5, // amf_cmd, op = op Shift operation with Data Register Move, Instruction Type 14: +AD218X_amf_move_6, // dm(op, op) = op, amf_cmd ALU/MAC operation with Data Memory Write, Instruction Type 4: +AD218X_amf_move_7, // pm(op, op) = op, amf_cmd ALU/MAC operation with Program Memory Write, Instruction Type 5: +AD218X_amf_move_8, // dm(op, op) = op, amf_cmd Shift operation with Data Memory Write, Instruction Type 12: +AD218X_amf_move_9, // pm(op, op) = op, amf_cmd Shift operation with Program Memory Write, Instruction Type 13: +AD218X_amf_move_a, // amf_cmd, op = dm(op, op), op = pm(op, op) ALU/MAC with Data and Program Memory Read, Instruction Type 1: +AD218X_last, + +}; + + + + +enum +{ +OAK_Dsp_null = 0, // Unknown Operation +OAK_Dsp_proc, // cmd need further process +// ALU-ALM subcodes +OAK_Dsp_or, // 000 Logical Or +OAK_Dsp_and, // 001 And +OAK_Dsp_xor, // 010 Exclusive Or +OAK_Dsp_add, // 011 Add +OAK_Dsp_alm_tst0, // 100 Test Bit-field for Zeros +OAK_Dsp_alm_tst1, // 101 Test Bit-field for Ones +OAK_Dsp_cmp, // 110 Compare +OAK_Dsp_sub, // 111 Subtract +// ALM subcodes +OAK_Dsp_alm_msu, // 1000 Multiply and Subtract Previous Product +OAK_Dsp_addh, // 1001 Add to High Accumulator +OAK_Dsp_addl, // 1010 Add to Low Accumulator +OAK_Dsp_subh, // 1011 Subtract from High Accumulator +OAK_Dsp_subl, // 1100 Subtract from Low Accumulator +OAK_Dsp_sqr, // 1101 Square +OAK_Dsp_sqra, // 1110 Square and Accumulate Previous Product +OAK_Dsp_cmpu, // 1111 Compare Unsigned +// MODA-MODB subcodes conditional +OAK_Dsp_shr, // 000 Shift Accumulator Right +OAK_Dsp_shr4, // 001 Shift Accumulator Right by 4 Bits +OAK_Dsp_shl, // 010 Shift Accumulator Left +OAK_Dsp_shl4, // 011 Shift Accumulator Left by 4 Bits +OAK_Dsp_ror, // 100 Rotate Accumulator Right through Carry +OAK_Dsp_rol, // 101 Rotate Accumulator Left through Carry +OAK_Dsp_clr, // 110 Clear Accumulator +OAK_Dsp_mod_reserved, // 111 Mod Reserved +// MODA subcodes conditional +OAK_Dsp_not, // 1000 Logical Not +OAK_Dsp_neg, // 1001 2's Complement of aX-accumulator +OAK_Dsp_rnd, // 1010 Round Upper 20 Bits of aX-accumulator +OAK_Dsp_pacr, // 1011 Product Move and Round to aX-accumulator +OAK_Dsp_clrr, // 1100 Clear and Round aX-accumulator +OAK_Dsp_inc, // 1101 Increment Accumulator by One +OAK_Dsp_dec, // 1110 Decrement aX-accumulator by One +OAK_Dsp_copy, // 1111 Copy aX-accumulator +// --- +OAK_Dsp_norm, // Normalize +OAK_Dsp_divs, // Division Step +// ALB subcodes +OAK_Dsp_set, // 000 Set Bit-field +OAK_Dsp_rst, // 001 Reset Bit-field +OAK_Dsp_chng, // 010 Change Bit-field +OAK_Dsp_addv, // 011 Add Long Immediate Value or Data Memory Location +OAK_Dsp_alb_tst0, // 100 Test Bit-field for Zeros +OAK_Dsp_alb_tst1, // 101 Test Bit-field for Ones +OAK_Dsp_cmpv, // 110 Compare Long Immediate Value to Register or Data Memory Location +OAK_Dsp_subv, // 111 Subtract Long Immediate Value from a Register or a Data Memory Location +// --- +OAK_Dsp_maxd, // Maximum between Data Memory Location and Accumulator +OAK_Dsp_max, // Maximum between Two Accumulators +OAK_Dsp_min, // Minimum between Two Accumulators +OAK_Dsp_lim, // Limit Accumulator (lim aX[, aX]) +// MUL subcodes +OAK_Dsp_mpy, // 000 Multiply +OAK_Dsp_mpysu, // 001 Multiply Signed by Unsigned +OAK_Dsp_mac, // 010 Multiply and Accumulate Previous Product +OAK_Dsp_macus, // 011 Multiply Unsigned by Signed and Accumulate Previous Product +OAK_Dsp_maa, // 100 Multiply and Accumulate Aligned Previous Product +OAK_Dsp_macuu, // 101 Multiply Unsigned by Unsigned and Accumulate Previous Product +OAK_Dsp_macsu, // 110 Multiply Signed by Unsigned and Accumulate Previous Product +OAK_Dsp_maasu, // 111 Multiply Signed by Unsigned and Accumulate Aligned Previous Product +//--- +OAK_Dsp_mpyi, // Multiply Signed Short Immediate +OAK_Dsp_msu, // Multiply and Subtract Previous Product +OAK_Dsp_tstb, // Test Specific Bit +OAK_Dsp_shfc, // Shift Accumulators according to Shift Value Register +OAK_Dsp_shfi, // Shift Accumulators by an Immediate Shift Value +OAK_Dsp_exp, // Evaluate the Exponent Value +//--- +OAK_Dsp_mov, // Move Data +OAK_Dsp_movp, // Move from Program Memory into Data Memory +OAK_Dsp_movs, // Move and Shift According to Shift Value Register +OAK_Dsp_movsi, // Move and Shift According to an Immediate Shift Value +OAK_Dsp_movr, // Move and Round +OAK_Dsp_movd, // Move from Data Memory into Program Memory +//--- +OAK_Dsp_push, // Push Register or Long Immediate Value onto Stack +OAK_Dsp_pop, // Pop from Stack into Register +//--- +OAK_Dsp_swap, // Swap aX- and bX-accumulators +OAK_Dsp_banke, // Bank Exchange +OAK_Dsp_rep, // Repeat Next Instruction +OAK_Dsp_bkrep, // Block-Repeat +OAK_Dsp_break, // Break from Block-repeat +//--- +OAK_Dsp_br, // Conditional Branch +OAK_Dsp_brr, // Relative Conditional Branch +OAK_Dsp_br_u, // UnConditional Branch +OAK_Dsp_brr_u, // Relative UnConditional Branch +OAK_Dsp_call, // Conditional Call Subroutine +OAK_Dsp_callr, // Relative Conditional Call Subroutine +OAK_Dsp_calla, // Call Subroutine at Location Specified by Accumulator +//--- +OAK_Dsp_ret, // Return Conditionally +OAK_Dsp_ret_u, // Return UnConditionally +OAK_Dsp_retd, // Delayed Return +OAK_Dsp_reti, // Return from Interrupt Conditionally +OAK_Dsp_reti_u, // Return from Interrupt UnConditionally +OAK_Dsp_retid, // Delayed Return from Interrupt +OAK_Dsp_rets, // Return with Short Immediate Parameter +//--- +OAK_Dsp_cntx, // Context Switching Store or Restore +OAK_Dsp_nop, // No operation +OAK_Dsp_modr, // Modify rN +OAK_Dsp_dint, // Disable Interrupt +OAK_Dsp_eint, // Enable Interrupt +//--- +OAK_Dsp_trap, // Software Interrupt +//--- +OAK_Dsp_lpg, // Load the Page Bits +OAK_Dsp_load, // Load Specific Fields into Registers +OAK_Dsp_mov_eu, // Move Data, eu +OAK_Dsp_last, + +}; + +/* + * TLCS900 processor module for IDA. + * Copyright (c) 1998-2006 Konstantin Norvatoff, + * Freeware. + */ + + +// List of instructions + +// +enum +{ +T900_null = 0, // Unknown Operation +T900_ld, +T900_ldw, +T900_push, +T900_pushw, +T900_pop, +T900_popw, +T900_lda, +T900_ldar, +T900_ex, +T900_mirr, +T900_ldi, +T900_ldiw, +T900_ldir, +T900_ldirw, +T900_ldd, +T900_lddw, +T900_lddr, +T900_lddrw, +T900_cpi, +T900_cpir, +T900_cpd, +T900_cpdr, +T900_add, +T900_addw, +T900_adc, +T900_adcw, +T900_sub, +T900_subw, +T900_sbc, +T900_sbcw, +T900_cp, +T900_cpw, +T900_inc, +T900_incw, +T900_dec, +T900_decw, +T900_neg, +T900_extz, +T900_exts, +T900_daa, +T900_paa, +T900_cpl, +T900_mul, +T900_muls, +T900_div, +T900_divs, +T900_mula, +T900_minc1, +T900_minc2, +T900_minc4, +T900_mdec1, +T900_mdec2, +T900_mdec4, +T900_and, +T900_andw, +T900_or, +T900_orw, +T900_xor, +T900_xorw, +T900_ldcf, +T900_stcf, +T900_andcf, +T900_orcf, +T900_xorcf, +T900_rcf, +T900_scf, +T900_ccf, +T900_zcf, +T900_bit, +T900_res, +T900_set, +T900_chg, +T900_tset, +T900_bs1f, +T900_bs1b, +T900_nop, +T900_ei, +T900_di, +T900_swi, +T900_halt, +T900_ldc, +T900_ldx, +T900_link, +T900_unlk, +T900_ldf, +T900_incf, +T900_decf, +T900_scc, +T900_rlc, +T900_rlc_mem, +T900_rlcw_mem, +T900_rrc, +T900_rrc_mem, +T900_rrcw_mem, +T900_rl, +T900_rl_mem, +T900_rlw_mem, +T900_rr, +T900_rr_mem, +T900_rrw_mem, +T900_sla, +T900_sla_mem, +T900_slaw_mem, +T900_sra, +T900_sra_mem, +T900_sraw_mem, +T900_sll, +T900_sll_mem, +T900_sllw_mem, +T900_srl, +T900_srl_mem, +T900_srlw_mem, +T900_rld, +T900_rrd, +T900_jp, +T900_jp_cond, +T900_jr, +T900_jr_cond, +T900_jrl, +T900_jrl_cond, +T900_call, +T900_calr, +T900_djnz, +T900_ret, +T900_ret_cond, +T900_retd, +T900_reti, +T900_max, +T900_normal, +T900_last +}; + +/* + * Rockwell C39 processor module for IDA. + * Copyright (c) 2000-2006 Konstantin Norvatoff, + * Freeware. + */ + + +// List of instructions + +// +enum +{ +C39_null = 0, // Unknown Operation +C39_adc, +C39_add, +C39_anc, +C39_and, +C39_ane, +C39_arr, +C39_asl, +C39_asr, +C39_bar, +C39_bas, +C39_bbr, +C39_bbs, +C39_bcc, +C39_bcs, +C39_beq, +C39_bit, +C39_bmi, +C39_bne, +C39_bpl, +C39_bra, +C39_brk, +C39_bvc, +C39_bvs, +C39_clc, +C39_cld, +C39_cli, +C39_clv, +C39_clw, +C39_cmp, +C39_cpx, +C39_cpy, +C39_dcp, +C39_dec, +C39_dex, +C39_dey, +C39_eor, +C39_exc, +C39_inc, +C39_ini, +C39_inx, +C39_iny, +C39_isb, +C39_jmp, +C39_jpi, +C39_jsb, +C39_jsr, +C39_lab, +C39_lae, +C39_lai, +C39_lan, +C39_lax, +C39_lda, +C39_ldx, +C39_ldy, +C39_lii, +C39_lsr, +C39_lxa, +C39_mpa, +C39_mpy, +C39_neg, +C39_nop, +C39_nxt, +C39_ora, +C39_pha, +C39_phi, +C39_php, +C39_phw, +C39_phx, +C39_phy, +C39_pia, +C39_pla, +C39_pli, +C39_plp, +C39_plw, +C39_plx, +C39_ply, +C39_psh, +C39_pul, +C39_rba, +C39_rla, +C39_rmb, +C39_rnd, +C39_rol, +C39_ror, +C39_rra, +C39_rti, +C39_rts, +C39_sax, +C39_sba, +C39_sbc, +C39_sbx, +C39_sec, +C39_sed, +C39_sei, +C39_sha, +C39_shs, +C39_shx, +C39_shy, +C39_slo, +C39_smb, +C39_sre, +C39_sta, +C39_sti, +C39_stx, +C39_sty, +C39_tax, +C39_tay, +C39_taw, +C39_tip, +C39_tsx, +C39_twa, +C39_txa, +C39_txs, +C39_tya, +C39_last +}; + +/* + * National Semiconductor Corporation CR16 processor module for IDA. + * Copyright (c) 2002-2006 Konstantin Norvatoff, + * Freeware. + */ + + +// list of instructions + +enum +{ +CR16_null = 0, // Unknown Operation +CR16_addb, +CR16_addw, +CR16_addub, +CR16_adduw, +CR16_addcb, +CR16_addcw, +CR16_andb, +CR16_andw, +CR16_ashub, +CR16_ashuw, +// !!! don't change sequence !!! +CR16_beq, +CR16_bne, +CR16_bcs, +CR16_bcc, +CR16_bhi, +CR16_bls, +CR16_bgt, +CR16_ble, +CR16_bfs, +CR16_bfc, +CR16_blo, +CR16_bhs, +CR16_blt, +CR16_bge, +CR16_br, +//---------------------------- +CR16_bal, +CR16_cmpb, +CR16_cmpw, +CR16_beq1b, +CR16_beq1w, +CR16_beq0b, +CR16_beq0w, +CR16_bne1b, +CR16_bne1w, +CR16_bne0b, +CR16_bne0w, +CR16_di, +CR16_ei, +CR16_excp, +// !!! don't change sequence !!! +CR16_jeq, +CR16_jne, +CR16_jcs, +CR16_jcc, +CR16_jhi, +CR16_jls, +CR16_jgt, +CR16_jle, +CR16_jfs, +CR16_jfc, +CR16_jlo, +CR16_jhs, +CR16_jlt, +CR16_jge, +CR16_jump, +//---------------------------- +CR16_jal, +CR16_loadb, +CR16_loadw, +CR16_loadm, +CR16_lpr, +CR16_lshb, +CR16_lshw, +CR16_movb, +CR16_movw, +CR16_movxb, +CR16_movzb, +CR16_movd, +CR16_mulb, +CR16_mulw, +CR16_mulsb, +CR16_mulsw, +CR16_muluw, +CR16_nop, +CR16_orb, +CR16_orw, +CR16_push, +CR16_pop, +CR16_popret, +CR16_retx, +// !!! don't change sequence !!! +CR16_seq, +CR16_sne, +CR16_scs, +CR16_scc, +CR16_shi, +CR16_sls, +CR16_sgt, +CR16_sle, +CR16_sfs, +CR16_sfc, +CR16_slo, +CR16_shs, +CR16_slt, +CR16_sge, +//---------------------------- +CR16_spr, +CR16_storb, +CR16_storw, +CR16_storm, +CR16_subb, +CR16_subw, +CR16_subcb, +CR16_subcw, +CR16_tbit, +CR16_tbitb, +CR16_tbitw, +CR16_sbitb, +CR16_sbitw, +CR16_cbitb, +CR16_cbitw, +CR16_wait, +CR16_eiwait, +CR16_xorb, +CR16_xorw, +CR16_last +}; + +/* + * Panasonic MN102 (PanaXSeries) processor module for IDA. + * Copyright (c) 2000-2006 Konstantin Norvatoff, + * Freeware. + */ + + +// List of instructions + +enum +{ +mn102_null = 0, // Unknown Operation +mn102_add, +mn102_addc, +mn102_addnf, +mn102_and, +mn102_asr, +mn102_bcc, +mn102_bccx, +mn102_bclr, +mn102_bcs, +mn102_bcsx, +mn102_beq, +mn102_beqx, +mn102_bge, +mn102_bgex, +mn102_bgt, +mn102_bgtx, +mn102_bhi, +mn102_bhix, +mn102_ble, +mn102_blex, +mn102_bls, +mn102_blsx, +mn102_blt, +mn102_bltx, +mn102_bnc, +mn102_bncx, +mn102_bne, +mn102_bnex, +mn102_bns, +mn102_bnsx, +mn102_bra, +mn102_bset, +mn102_btst, +mn102_bvc, +mn102_bvcx, +mn102_bvs, +mn102_bvsx, +mn102_cmp, +mn102_divu, +mn102_ext, +mn102_extx, +mn102_extxb, +mn102_extxbu, +mn102_extxu, +mn102_jmp, +mn102_jsr, +mn102_lsr, +mn102_mov, +mn102_movb, +mn102_movbu, +mn102_movx, +mn102_mul, +mn102_mulq, +mn102_mulqh, +mn102_mulql, +mn102_mulu, +mn102_nop, +mn102_not, +mn102_or, +mn102_pxst, +mn102_rol, +mn102_ror, +mn102_rti, +mn102_rts, +mn102_sub, +mn102_subc, +mn102_tbnz, +mn102_tbz, +mn102_xor, +mn102_last +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + * PowerPC - RISC with very complex instructions + */ + + + +enum +{ +PPC_null = 0, // Unknown Operation +PPC_add, // Add +PPC_addc, // Add Carrying +PPC_adde, // Add Extended +PPC_addi, // Add Immediate +PPC_addic, // Add Immediate Carrying +PPC_addis, // Add Immediate Shifted +PPC_addme, // Add to Minus One Extended +PPC_addze, // Add to Zero Extended +PPC_and, // AND +PPC_andc, // AND with Complement +PPC_andi, // AND Immediate +PPC_andis, // AND Immediate Shifted +PPC_b, // Branch +PPC_bc, // Branch Conditional +PPC_bcctr, // Branch Conditional to Count Register +PPC_bclr, // Branch Conditional to Link Register +PPC_cmp, // Compare +PPC_cmpi, // Compare Immediate +PPC_cmpl, // Compare Logical +PPC_cmpli, // Compare Logical Immediate +PPC_cntlzd, // Count Leading Zeros Double Word +PPC_cntlzw, // Count Leading Zeros Word +PPC_crand, // Condition Register AND +PPC_crandc, // Condition Register AND with Complement +PPC_creqv, // Condition Register Equivalent +PPC_crnand, // Condition Register NAND +PPC_crnor, // Condition Register NOR +PPC_cror, // Condiiton Register OR +PPC_crorc, // Condition Register OR with Comlement +PPC_crxor, // Condition Register XOR +PPC_dcba, // +PPC_dcbf, // Data Cache Block Flush +PPC_dcbi, // Data Cache Block Invalidate +PPC_dcbst, // Data Cache Block Store +PPC_dcbt, // Data Cache Block Touch +PPC_dcbtst, // Data Cache Block Touch for Store +PPC_dcbz, // Data Cache Block Set to Zero +PPC_divd, // Divide Double Word +PPC_divdu, // Divide Double Word Unsigned +PPC_divw, // Divide Word +PPC_divwu, // Divide Word Unsigned +PPC_eciwx, // External Control In Word Indexed +PPC_ecowx, // External Control Out Word Indexed +PPC_eieio, // Enforce In-Order Execution of I/O +PPC_eqv, // Equivalent +PPC_extsb, // Extend Sign Byte +PPC_extsh, // Extend Sign Half Word +PPC_extsw, // Extend Sign Word +PPC_fabs, // Floating-Point Absolute Value +PPC_fadd, // Floating-Point Add +PPC_fadds, // Floating-Point Add (Single-Precision) +PPC_fcfid, // Floating-Point Convert from Integer Double Word +PPC_fcmpo, // Floating-Point Compare Ordered +PPC_fcmpu, // Floating-Point Compare Unordered +PPC_fctid, // Floating-Point Convert to Integer Double Word +PPC_fctidz, // Floating-Point Convert to Integer Double Word with Round toward Zero +PPC_fctiw, // Floating-Point Convert to Integer Word +PPC_fctiwz, // Floating-Point Convert to Integer Word with Round toward Zero +PPC_fdiv, // Floating-Point Divide +PPC_fdivs, // Floating-Point Divide Single-Precision +PPC_fmadd, // Floating-Point Multiply-Add +PPC_fmadds, // Floating-Point Multiply-Add Single-Precision +PPC_fmr, // Floating-Point Move Register +PPC_fmsub, // Floating-Point Multiply-Subtract +PPC_fmsubs, // Floating-Point Multiply-Subtract (Single-Precision) +PPC_fmul, // Floating-Point Multiply +PPC_fmuls, // Floating-Point Multiply Single-Precision +PPC_fnabs, // Floating-Point Negative Absolute Value +PPC_fneg, // Floating-Point Negate +PPC_fnmadd, // Floating-Point Negative Multiply-Add +PPC_fnmadds, // Floating-Point Negative Multiply-Add Single-Precision +PPC_fnmsub, // Floating-Point Negative Multiply-Subtract +PPC_fnmsubs, // Floating-Point Negative Multiply-Subtract Single-Precision +PPC_fres, // Floating-Point Reciprocal Estimate Single-Precision +PPC_frsp, // Floating-Point Round to Single-Precision +PPC_frsqrte, // Floating-Point Reciprocal Square Root Estimate +PPC_fsel, // Floating-Point Select +PPC_fsqrt, // Floating-Point Square Root +PPC_fsqrts, // Floating-Point Square Root Single-Precision +PPC_fsub, // Floating-Point Subtract +PPC_fsubs, // Floating-Point Subtract Single-Precision +PPC_icbi, // Instruction Cache Block Invalidate +PPC_isync, // Instruction Synchronize +PPC_lbz, // Load Byte and Zero +PPC_lbzu, // Load Byte and Zero with Update +PPC_lbzux, // Load Byte and Zero with Update Indexed +PPC_lbzx, // Load Byte and Zero Indexed +PPC_ld, // Load Double Word +PPC_ldarx, // Load Double Word and Reserve Indexed +PPC_ldu, // Load Double Word with Update +PPC_ldux, // Load Double Word with Update Indexed +PPC_ldx, // Load Double Word Indexed +PPC_lfd, // Load Floating-Point Double-Precision +PPC_lfdu, // Load Floating-Point Double-Precision with Update +PPC_lfdux, // Load Floating-Point Double-Precision with Update Indexed +PPC_lfdx, // Load Floating-Point Double-Precision Indexed +PPC_lfs, // Load Floating-Point Single-Precision +PPC_lfsu, // Load Floating-Point Single-Precision with Update +PPC_lfsux, // Load Floating-Point Single-Precision with Update Indexed +PPC_lfsx, // Load Floating-Point Single-Precision Indexed +PPC_lha, // Load Half Word Algebraic +PPC_lhau, // Load Half Word Algebraic with Update +PPC_lhaux, // Load Half Word Algebraic with Update Indexed +PPC_lhax, // Load Half Word Algebraic Indexed +PPC_lhbrx, // Load Half Word Byte-reverse Indexed +PPC_lhz, // Load Half Word and Zero +PPC_lhzu, // Load Half Word and Zero with Update +PPC_lhzux, // Load Half Word and Zero with Update Indexed +PPC_lhzx, // Load Half Word and Zero Indexed +PPC_lmw, // Load Multiple Word +PPC_lswi, // Load String Word Immediate +PPC_lswx, // Load String Word Indexed +PPC_lwa, // Load Word Algebraic +PPC_lwarx, // Load Word and Reserve Indexed +PPC_lwaux, // Load Word Algebraic with Update Indexed +PPC_lwax, // Load Word Algebraic Indexed +PPC_lwbrx, // Load Word Byte-Reverse Indexed +PPC_lwz, // Load Word and Zero +PPC_lwzu, // Load Word and Zero with Update +PPC_lwzux, // Load Word and Zero with Update Indexed +PPC_lwzx, // Load Word and Zero Indexed +PPC_mcrf, // Move Condition register Field +PPC_mcrfs, // Move to Condition Register from FPSCR +PPC_mcrxr, // Move to Condition Register from XER +PPC_mfcr, // Move from Condition Register +PPC_mffs, // Move from FPSCR +PPC_mfmsr, // Move from Machine State Register +PPC_mfspr, // Move from Special Purpose Register +PPC_mfsr, // Move from Segment Register +PPC_mfsrin, // Move from Segment Register Indexed +PPC_mftb, // Move from Time Base +PPC_mtcrf, // Move to Condition Register Fields +PPC_mtfsb0, // Move to FPSCR Bit 0 +PPC_mtfsb1, // Move to FPSCR Bit 1 +PPC_mtfsf, // Move to FPSCR Fields +PPC_mtfsfi, // Move to FPSCR Field Immediate +PPC_mtmsr, // Move to Machine State Register +PPC_mtmsrd, // Move to Machine State Register Double Word +PPC_mtspr, // Move to Special Purpose Register +PPC_mtsr, // Move to Segment Register +PPC_mtsrd, // Move to Segment Register Double Word +PPC_mtsrdin, // Move to Segment Register Indirect Double +PPC_mtsrin, // Move to Segment Register Indirect +PPC_mulhd, // Multiply High Double Word +PPC_mulhdu, // Multiply High Double Word Unsigned +PPC_mulhw, // Multiply High Word +PPC_mulhwu, // Multiply High Word Unsigned +PPC_mulld, // Multiply Low Double Word +PPC_mulli, // Multiply Low Immediate +PPC_mullw, // Multiply Low +PPC_nand, // NAND (not AND) +PPC_neg, // Negate +PPC_nor, // NOR (not OR) +PPC_or, // OR +PPC_orc, // OR with Complement +PPC_ori, // OR Immediate +PPC_oris, // OR Immediate Shifted +PPC_rfi, // Return from Interrupt +PPC_rfid, // Return from Interrupt Double Word +PPC_rldcl, // Rotate Left Double Word then Clear Left +PPC_rldcr, // Rotate Left Double Word then Clear Right +PPC_rldic, // Rotate Left Double Word Immediate then Clear +PPC_rldicl, // Rotate Left Double Word Immediate then Clear Left +PPC_rldicr, // Rotate Left Double Word Immediate then Clear Right +PPC_rldimi, // Rotate Left Double Word Immediate then Mask Insert +PPC_rlwimi, // Rotate Left Word Immediate then Mask Insert +PPC_rlwinm, // Rotate Left Word Immediate then AND with Mask +PPC_rlwnm, // Rotate Left Word then AND with Mask +PPC_sc, // System Call +PPC_slbia, // SLB Invalidate All, ISA 3.0 format +PPC_slbie, // SLB Invalidate Entry +PPC_sld, // Shift Left Double Word +PPC_slw, // Shift Left Word +PPC_srad, // Shift Right Algebraic Double Word +PPC_sradi, // Shift Right Algebraic Double Word Immediate +PPC_sraw, // Shift Right Algebraic Word +PPC_srawi, // Shift Right Algebraic Word Immediate +PPC_srd, // Shift Right Double Word +PPC_srw, // Shift Right Word +PPC_stb, // Store Byte +PPC_stbu, // Store Byte with Update +PPC_stbux, // Store Byte with Update Indexed +PPC_stbx, // Store Byte Indexed +PPC_std, // Store Double Word +PPC_stdcx, // Store Double Word Conditional Indexed +PPC_stdu, // Store Double Word with Update +PPC_stdux, // Store Double Word with Update Indexed +PPC_stdx, // Store Double Word Indexed +PPC_stfd, // Store Floating-Point Double-Precision +PPC_stfdu, // Store Floating-Point Double-Precision wiht Update +PPC_stfdux, // Store Floating-Point Double-Precision wiht Update Indexed +PPC_stfdx, // Store Floating-Point Double-Precision Indexed +PPC_stfiwx, // Store Floating-Point as Integer Word Indexed +PPC_stfs, // Store Floating-Point Single-Precision +PPC_stfsu, // Store Floating-Point Single-Precision with Update +PPC_stfsux, // Store Floating-Point Single-Precision with Update Indexed +PPC_stfsx, // Store Floating-Point Single-Precision Indexed +PPC_sth, // Store Half Word +PPC_sthbrx, // Store Half Word Byte-Reverse Indexed +PPC_sthu, // Store Half Word with Update +PPC_sthux, // Store Half Word with Update Indexed +PPC_sthx, // Store Half Word Indexed +PPC_stmw, // Store Multiple Word +PPC_stswi, // Store String Word Immediate +PPC_stswx, // Store String Word Indexed +PPC_stw, // Store Word +PPC_stwbrx, // Store Word Byte-Reverse Indexed +PPC_stwcx, // Store Word Conditional Indexed +PPC_stwu, // Store Word with Update +PPC_stwux, // Store Word with Update Indexed +PPC_stwx, // Store Word Indexed +PPC_subf, // Subtract from +PPC_subfc, // Subtract from Carrying +PPC_subfe, // Subtract from Extended +PPC_subfic, // Subtract from Immediate Carrying +PPC_subfme, // Subtract from Minus One Extended +PPC_subfze, // Subtract from Zero Extended +PPC_sync, // Synchronize +PPC_td, // Trap Double Word +PPC_tdi, // Trap Double Word Immediate +PPC_tlbia, // TLB Invalidate All +PPC_tlbie, // TLB Invalidate Entry, ISA 3.0 format +PPC_tlbsync, // TLB Synchronize +PPC_tw, // Trap Word +PPC_twi, // Trap Word Immediate +PPC_xor, // XOR +PPC_xori, // XOR Immediate +PPC_xoris, // XOR Immediate Shifted + +PPC_last_basic = PPC_xoris, + +// +// Simplified mnemonics +// + +PPC_cmpwi, // Compare Word Immediate +PPC_cmpw, // Compare Word +PPC_cmplwi, // Compare Logical Word Immediate +PPC_cmplw, // Compare Logical Word +PPC_cmpdi, // Compare Double Word Immediate +PPC_cmpd, // Compare Double Word +PPC_cmpldi, // Compare Logical Double Word Immediate +PPC_cmpld, // Compare Logical Double Word + +PPC_trap, // Trap Word Unconditionally +// "trapd" seems like a bug; no such mnemonic appears to exist as just plain +// "trap" is used regardless of word/doubleword, but we have to leave it here +// to not mess up existing databases +PPC_trapd, // Trap Double Word Unconditionally +PPC_twlgt, // Trap Word if Logically Greater Than +PPC_twllt, // Trap Word if Logically Less Than +PPC_tweq, // Trap Word if Equal +PPC_twlge, // Trap Word if Logically Greater Than or Equal +PPC_twlle, // Trap Word if Logically Less Than or Equal +PPC_twgt, // Trap Word if Greater Than +PPC_twge, // Trap Word if Greater Than or Equal +PPC_twlt, // Trap Word if Less Than +PPC_twle, // Trap Word if Less Than or Equal +PPC_twne, // Trap Word if Not Equal +PPC_twlgti, // Trap Word Immediate if Logically Greater Than +PPC_twllti, // Trap Word Immediate if Logically Less Than +PPC_tweqi, // Trap Word Immediate if Equal +PPC_twlgei, // Trap Word Immediate if Logically Greater Than or Equal +PPC_twllei, // Trap Word Immediate if Logically Less Than or Equal +PPC_twgti, // Trap Word Immediate if Greater Than +PPC_twgei, // Trap Word Immediate if Greater Than or Equal +PPC_twlti, // Trap Word Immediate if Less Than +PPC_twlei, // Trap Word Immediate if Less Than or Equal +PPC_twnei, // Trap Word Immediate if Not Equal +PPC_tdlgt, // Trap Double Word if Logically Greater Than +PPC_tdllt, // Trap Double Word if Logically Less Than +PPC_tdeq, // Trap Double Word if Equal +PPC_tdlge, // Trap Double Word if Logically Greater Than or Equal +PPC_tdlle, // Trap Double Word if Logically Less Than or Equal +PPC_tdgt, // Trap Double Word if Greater Than +PPC_tdge, // Trap Double Word if Greater Than or Equal +PPC_tdlt, // Trap Double Word if Less Than +PPC_tdle, // Trap Double Word if Less Than or Equal +PPC_tdne, // Trap Double Word if Not Equal +PPC_tdlgti, // Trap Double Word Immediate if Logically Greater Than +PPC_tdllti, // Trap Double Word Immediate if Logically Less Than +PPC_tdeqi, // Trap Double Word Immediate if Equal +PPC_tdlgei, // Trap Double Word Immediate if Logically Greater Than or Equal +PPC_tdllei, // Trap Double Word Immediate if Logically Less Than or Equal +PPC_tdgti, // Trap Double Word Immediate if Greater Than +PPC_tdgei, // Trap Double Word Immediate if Greater Than or Equal +PPC_tdlti, // Trap Double Word Immediate if Less Than +PPC_tdlei, // Trap Double Word Immediate if Less Than or Equal +PPC_tdnei, // Trap Double Word Immediate if Not Equal +// all trap mnemonics are covered as of Power ISA 2.07; t[dw]nl*, t[dw]lnl*, +// and t[dw]lng* are intentionally missing since they are redundant. in other +// words, the assembler supports (e.g.) both "logically less than or equal" +// (tdlei) in addition to "logically not greater than" (tdlngi)--a disassembler +// need not support both + +PPC_nop, // No Operation +PPC_not, // Complement Register +PPC_mr, // Move Register + +PPC_subi, // Subtract Immediate +PPC_subic, // Subtract Immediate Carrying +PPC_subis, // Subtract Immediate Shifted +PPC_li, // Load Immediate +PPC_lis, // Load Immediate Shifted + +PPC_crset, // Condition Register Set +PPC_crnot, // Condition Register NOT +PPC_crmove, // Condition Register Move +PPC_crclr, // Condition Register Clear + +PPC_mtxer, // Move to integer unit exception register +PPC_mtlr, // Move to link register +PPC_mtctr, // Move to count register +PPC_mtdsisr, // Move to DAE/source instruction service register +PPC_mtdar, // Move to data address register +PPC_mtdec, // Move to decrementer register +PPC_mtsrr0, // Move to status save/restore register 0 +PPC_mtsrr1, // Move to status save/restore register 1 +PPC_mtsprg0, // Move to general special purpose register 0 +PPC_mtsprg1, // Move to general special purpose register 1 +PPC_mtsprg2, // Move to general special purpose register 2 +PPC_mtsprg3, // Move to general special purpose register 3 +PPC_mttbl, // Move to time base register (lower) +PPC_mttbu, // Move to time base register (upper) +PPC_mfxer, // Move from integer unit exception register +PPC_mflr, // Move from link register +PPC_mfctr, // Move from count register +PPC_mfdsisr, // Move from DAE/source instruction service register +PPC_mfdar, // Move from data address register +PPC_mfdec, // Move from decrementer register +PPC_mfsrr0, // Move from status save/restore register 0 +PPC_mfsrr1, // Move from status save/restore register 1 +PPC_mfsprg0, // Move from general special purpose register 0 +PPC_mfsprg1, // Move from general special purpose register 1 +PPC_mfsprg2, // Move from general special purpose register 2 +PPC_mfsprg3, // Move from general special purpose register 3 +PPC_mftbl, // Move from time base register (lower) +PPC_mftbu, // Move from time base register (upper) +PPC_mfpvr, // Move from processor version register + +// suffixes: +// lr - goto lr +// ctr - goto ctr +// l - update lr +// a - absolute + +// branch forms not listed here or elsewhere in the file (e.g. bltlr) are +// being handled by emitting extra characters when printing the instructions +// instead of having separate enums + +PPC_balways, // Branch unconditionally +PPC_bt, // Branch if true +PPC_bf, // Branch if false +PPC_bdnz, // CTR--; branch if CTR non-zero +PPC_bdnzt, // CTR--; branch if CTR non-zero and condition is true +PPC_bdnzf, // CTR--; branch if CTR non-zero and condition is false +PPC_bdz, // CTR--; branch if CTR zero +PPC_bdzt, // CTR--; branch if CTR zero and condition is true +PPC_bdzf, // CTR--; branch if CTR zero and condition is false + +PPC_blt, // Branch if less than +PPC_ble, // Branch if less than or equal +PPC_beq, // Branch if equal +PPC_bge, // Branch if greater than or equal +PPC_bgt, // Branch if greater than +PPC_bne, // Branch if not equal +PPC_bso, // Branch if summary overflow +PPC_bns, // Branch if not summary overflow +// bun and bnu are intentionally missing from this list; they are handled by +// bso/bns (this is the result of historical differences between POWER and PPC +// mnemonics) + +PPC_extlwi, // Extract and Left Justify Immediate +PPC_extrwi, // Extract and Right Justify Immediate +PPC_inslwi, // Insert from Left Immediate +PPC_insrwi, // Insert from Right Immediate +PPC_rotlwi, // Rotate Left Immediate +PPC_rotrwi, // Rotate Right Immediate +PPC_rotlw, // Rotate Left +PPC_slwi, // Shift Left Immediate +PPC_srwi, // Shift Right Immediate +PPC_clrlwi, // Clear Left Immediate +PPC_clrrwi, // Clear Right Immediate +PPC_clrlslwi, // Clear Left and Shift Left Immediate + +// +// PowerPC Embedded Controller Instructions +// + +PPC_dccci, // Data cache congruence class invalidate (p.438-439) +PPC_dcread, // Data cache read (p.440-441) +PPC_icbt, // Instruction cache block touch (p.450-451) +PPC_iccci, // Instruction cache congruence class invalidate (p.452-453) +PPC_icread, // Instruction cache read (p.454-455) +PPC_mfdcr, // Move from device control register (p.484-485) +PPC_mtdcr, // Move to device control register (p.491-492) +PPC_rfci, // Return from critical interrupt (p.507) +PPC_tlbre, // TLB read entry (p.548-549) +PPC_tlbsx, // TLB search indexed (p.550) +PPC_tlbwe, // TLB write entry (p.552-553) +PPC_wrtee, // Write external enable (p.560) +PPC_wrteei, // Write external enable immediate (p.561) + +// +// New PowerPC instructions +// + +PPC_abs, // Absolute +PPC_clcs, // Cache Lines Compute Size +PPC_clf, // Cache Line Flush +PPC_cli, // Cache Line Invalidate +PPC_dclst, // Data Cache Line Store +PPC_div, // Divide +PPC_divs, // Divide Short +PPC_doz, // Different Or Zero +PPC_dozi, // Different Or Zero Immediate +PPC_frsqrtes, // Floating Reciprocal Square Root Estimate Single +PPC_hrfid, // Hypervisor Return from Interrupt Doubleword +PPC_lscbx, // Load String And Compare Byte Indexed +PPC_maskg, // Mask Generate +PPC_maskir, // Mask Insert From Register +PPC_mfsri, // Move From Segment Register Indirect +PPC_mul, // Multiply +PPC_nabs, // Negative Absolute +PPC_popcntb, // Population Count Bytes +PPC_rac, // Real Address Compute +PPC_rfsvc, // Return From SVC, obsolete, see ana.cpp +PPC_rlmi, // Rotate Left Then Mask Insert +PPC_rrib, // Rotate Right And Insert Bit +PPC_slbmfee, // SLB Move From Entry ESID +PPC_slbmfev, // SLB Move From Entry VSID +PPC_slbmte, // SLB Move To Entry +PPC_sle, // Shift Left Extended +PPC_sleq, // Shift Left Extended With MQ +PPC_sliq, // Shift Left Immediate With MQ +PPC_slliq, // Shift Left Long Immediate With MQ +PPC_sllq, // Shift Left Long With MQ +PPC_slq, // Shift Left With MQ +PPC_sraiq, // Shift Right Algebraic Immediate With MQ +PPC_sraq, // Shift Right Algebraic With MQ +PPC_sre, // Shift Right Extended +PPC_srea, // Shift Right Extended Algebraic +PPC_sreq, // Shift Right Extended With MQ +PPC_sriq, // Shift Right Immediate With MQ +PPC_srliq, // Shift Right Long Immediate With MQ +PPC_srlq, // Shift Right Long With MQ +PPC_srq, // Shift Right With MQ + +// New instructions + +PPC_mtocrf, // Move To One Condition Register Field +PPC_mfocrf, // Move From One Condition Register Field + +// Freescale e200z6 instructions +PPC_isel, // Integer Select +PPC_isellt, // Integer Select Less Than +PPC_iselgt, // Integer Select Greater Than +PPC_iseleq, // Integer Select Equal +PPC_dcblc, // Data Cache Block Lock Clear +PPC_dcbtls, // Data Cache Block Touch and Lock Set +PPC_dcbtstls, // Data Cache Block Touch for Store and Lock Set +PPC_icblc, // Instruction Cache Block Lock Clear +PPC_icbtls, // Instruction Cache Block Touch and Lock Set +PPC_tlbivax, // TLB Invalidate Virtual Address Indexed +PPC_rfdi, // Return from Debug Interrupt + +// Freescale MPC603e instructions (G2 core) + +PPC_tlbld, // Load Data TLB Entry +PPC_tlbli, // Load Instruction TLB Entry + +// SPE (Signal Processing Engine) instructions + +PPC_brinc, // Bit Reversed Increment +PPC_evabs, // Vector Absolute Value +PPC_evaddiw, // Vector Add Immediate Word +PPC_evaddsmiaaw, // Vector Add Signed, Modulo, Integer to Accumulator Word +PPC_evaddssiaaw, // Vector Add Signed, Saturate, Integer to Accumulator Word +PPC_evaddumiaaw, // Vector Add Unsigned, Modulo, Integer to Accumulator Word +PPC_evaddusiaaw, // Vector Add Unsigned, Saturate, Integer to Accumulator Word +PPC_evaddw, // Vector Add Word +PPC_evand, // Vector AND +PPC_evandc, // Vector AND with Complement +PPC_evcmpeq, // Vector Compare Equal +PPC_evcmpgts, // Vector Compare Greater Than Signed +PPC_evcmpgtu, // Vector Compare Greater Than Unsigned +PPC_evcmplts, // Vector Compare Less Than Signed +PPC_evcmpltu, // Vector Compare Less Than Unsigned +PPC_evcntlsw, // Vector Count Leading Signed Bits Word +PPC_evcntlzw, // Vector Count Leading Zeros Word +PPC_evdivws, // Vector Divide Word Signed +PPC_evdivwu, // Vector Divide Word Unsigned +PPC_eveqv, // Vector Equivalent +PPC_evextsb, // Vector Extend Sign Byte +PPC_evextsh, // Vector Extend Sign Halfword +PPC_evldd, // Vector Load Double Word into Double Word +PPC_evlddx, // Vector Load Double Word into Double Word Indexed +PPC_evldh, // Vector Load Double into Four Halfwords +PPC_evldhx, // Vector Load Double into Four Halfwords Indexed +PPC_evldw, // Vector Load Double into Two Words +PPC_evldwx, // Vector Load Double into Two Words Indexed +PPC_evlhhesplat, // Vector Load Halfword into Halfwords Even and Splat +PPC_evlhhesplatx, // Vector Load Halfword into Halfwords Even and Splat Indexed +PPC_evlhhossplat, // Vector Load Halfword into Halfword Odd Signed and Splat +PPC_evlhhossplatx, // Vector Load Halfword into Halfword Odd Signed and Splat Indexed +PPC_evlhhousplat, // Vector Load Halfword into Halfword Odd Unsigned and Splat +PPC_evlhhousplatx, // Vector Load Halfword into Halfword Odd Unsigned and Splat Indexed +PPC_evlwhe, // Vector Load Word into Two Halfwords Even +PPC_evlwhex, // Vector Load Word into Two Halfwords Even Indexed +PPC_evlwhos, // Vector Load Word into Two Halfwords Odd Signed (with sign extension) +PPC_evlwhosx, // Vector Load Word into Two Halfwords Odd Signed Indexed (with sign extension) +PPC_evlwhou, // Vector Load Word into Two Halfwords Odd Unsigned (zero-extended) +PPC_evlwhoux, // Vector Load Word into Two Halfwords Odd Unsigned Indexed (zero-extended) +PPC_evlwhsplat, // Vector Load Word into Two Halfwords and Splat +PPC_evlwhsplatx, // Vector Load Word into Two Halfwords and Splat Indexed +PPC_evlwwsplat, // Vector Load Word into Word and Splat +PPC_evlwwsplatx, // Vector Load Word into Word and Splat Indexed +PPC_evmergehi, // Vector Merge High +PPC_evmergehilo, // Vector Merge High/Low +PPC_evmergelo, // Vector Merge Low +PPC_evmergelohi, // Vector Merge Low/High +PPC_evmhegsmfaa, // Vector Multiply Halfwords, Even, Guarded, Signed, Modulo, Fractional and Accumulate +PPC_evmhegsmfan, // Vector Multiply Halfwords, Even, Guarded, Signed, Modulo, Fractional and Accumulate Negative +PPC_evmhegsmiaa, // Vector Multiply Halfwords, Even, Guarded, Signed, Modulo, Integer and Accumulate +PPC_evmhegsmian, // Vector Multiply Halfwords, Even, Guarded, Signed, Modulo, Integer and Accumulate Negative +PPC_evmhegumiaa, // Vector Multiply Halfwords, Even, Guarded, Unsigned, Modulo, Integer and Accumulate +PPC_evmhegumian, // Vector Multiply Halfwords, Even, Guarded, Unsigned, Modulo, Integer and Accumulate Negative +PPC_evmhesmf, // Vector Multiply Halfwords, Even, Signed, Modulo, Fractional +PPC_evmhesmfa, // Vector Multiply Halfwords, Even, Signed, Modulo, Fractional to Accumulator +PPC_evmhesmfaaw, // Vector Multiply Halfwords, Even, Signed, Modulo, Fractional and Accumulate into Words +PPC_evmhesmfanw, // Vector Multiply Halfwords, Even, Signed, Modulo, Fractional and Accumulate Negative into Words +PPC_evmhesmi, // Vector Multiply Halfwords, Even, Signed, Modulo, Integer +PPC_evmhesmia, // Vector Multiply Halfwords, Even, Signed, Modulo, Integer to Accumulator +PPC_evmhesmiaaw, // Vector Multiply Halfwords, Even, Signed, Modulo, Integer and Accumulate into Words +PPC_evmhesmianw, // Vector Multiply Halfwords, Even, Signed, Modulo, Integer and Accumulate Negative into Words +PPC_evmhessf, // Vector Multiply Halfwords, Even, Signed, Saturate, Fractional +PPC_evmhessfa, // Vector Multiply Halfwords, Even, Signed, Saturate, Fractional to Accumulator +PPC_evmhessfaaw, // Vector Multiply Halfwords, Even, Signed, Saturate, Fractional and Accumulate into Words +PPC_evmhessfanw, // Vector Multiply Halfwords, Even, Signed, Saturate, Fractional and Accumulate Negative into Words +PPC_evmhessiaaw, // Vector Multiply Halfwords, Even, Signed, Saturate, Integer and Accumulate into Words +PPC_evmhessianw, // Vector Multiply Halfwords, Even, Signed, Saturate, Integer and Accumulate Negative into Words +PPC_evmheumi, // Vector Multiply Halfwords, Even, Unsigned, Modulo, Integer +PPC_evmheumia, // Vector Multiply Halfwords, Even, Unsigned, Modulo, Integer to Accumulator +PPC_evmheumiaaw, // Vector Multiply Halfwords, Even, Unsigned, Modulo, Integer and Accumulate into Words +PPC_evmheumianw, // Vector Multiply Halfwords, Even, Unsigned, Modulo, Integer and Accumulate Negative into Words +PPC_evmheusiaaw, // Vector Multiply Halfwords, Even, Unsigned, Saturate, Integer and Accumulate into Words +PPC_evmheusianw, // Vector Multiply Halfwords, Even, Unsigned, Saturate, Integer and Accumulate Negative into Words +PPC_evmhogsmfaa, // Vector Multiply Halfwords, Odd, Guarded, Signed, Modulo, Fractional and Accumulate +PPC_evmhogsmfan, // Vector Multiply Halfwords, Odd, Guarded, Signed, Modulo, Fractional and Accumulate Negative +PPC_evmhogsmiaa, // Vector Multiply Halfwords, Odd, Guarded, Signed, Modulo, Integer and Accumulate +PPC_evmhogsmian, // Vector Multiply Halfwords, Odd, Guarded, Signed, Modulo, Integer and Accumulate Negative +PPC_evmhogumiaa, // Vector Multiply Halfwords, Odd, Guarded, Unsigned, Modulo, Integer and Accumulate +PPC_evmhogumian, // Vector Multiply Halfwords, Odd, Guarded, Unsigned, Modulo, Integer and Accumulate Negative +PPC_evmhosmf, // Vector Multiply Halfwords, Odd, Signed, Modulo, Fractional +PPC_evmhosmfa, // Vector Multiply Halfwords, Odd, Signed, Modulo, Fractional to Accumulator +PPC_evmhosmfaaw, // Vector Multiply Halfwords, Odd, Signed, Modulo, Fractional and Accumulate into Words +PPC_evmhosmfanw, // Vector Multiply Halfwords, Odd, Signed, Modulo, Fractional and Accumulate Negative into Words +PPC_evmhosmi, // Vector Multiply Halfwords, Odd, Signed, Modulo, Integer +PPC_evmhosmia, // Vector Multiply Halfwords, Odd, Signed, Modulo, Integer to Accumulator +PPC_evmhosmiaaw, // Vector Multiply Halfwords, Odd, Signed, Modulo, Integer and Accumulate into Words +PPC_evmhosmianw, // Vector Multiply Halfwords, Odd, Signed, Modulo, Integer and Accumulate Negative into Words +PPC_evmhossf, // Vector Multiply Halfwords, Odd, Signed, Saturate, Fractional +PPC_evmhossfa, // Vector Multiply Halfwords, Odd, Signed, Saturate, Fractional to Accumulator +PPC_evmhossfaaw, // Vector Multiply Halfwords, Odd, Signed, Saturate, Fractional and Accumulate into Words +PPC_evmhossfanw, // Vector Multiply Halfwords, Odd, Signed, Saturate, Fractional and Accumulate Negative into Words +PPC_evmhossiaaw, // Vector Multiply Halfwords, Odd, Signed, Saturate, Integer and Accumulate into Words +PPC_evmhossianw, // Vector Multiply Halfwords, Odd, Signed, Saturate, Integer and Accumulate Negative into Words +PPC_evmhoumi, // Vector Multiply Halfwords, Odd, Unsigned, Modulo, Integer +PPC_evmhoumia, // Vector Multiply Halfwords, Odd, Unsigned, Modulo, Integer to Accumulator +PPC_evmhoumiaaw, // Vector Multiply Halfwords, Odd, Unsigned, Modulo, Integer and Accumulate into Words +PPC_evmhoumianw, // Vector Multiply Halfwords, Odd, Unsigned, Modulo, Integer and Accumulate Negative into Words +PPC_evmhousiaaw, // Vector Multiply Halfwords, Odd, Unsigned, Saturate, Integer and Accumulate into Words +PPC_evmhousianw, // Vector Multiply Halfwords, Odd, Unsigned, Saturate, Integer and Accumulate Negative into Words +PPC_evmra, // Initialize Accumulator +PPC_evmwhsmf, // Vector Multiply Word High Signed, Modulo, Fractional +PPC_evmwhsmfa, // Vector Multiply Word High Signed, Modulo, Fractional to Accumulator +PPC_evmwhsmi, // Vector Multiply Word High Signed, Modulo, Integer +PPC_evmwhsmia, // Vector Multiply Word High Signed, Modulo, Integer to Accumulator +PPC_evmwhssf, // Vector Multiply Word High Signed, Saturate, Fractional +PPC_evmwhssfa, // Vector Multiply Word High Signed, Saturate, Fractional to Accumulator +PPC_evmwhumi, // Vector Multiply Word High Unsigned, Modulo, Integer +PPC_evmwhumia, // Vector Multiply Word High Unsigned, Modulo, Integer to Accumulator +PPC_evmwlsmiaaw, // Vector Multiply Word Low Signed, Modulo, Integer and Accumulate into Words +PPC_evmwlsmianw, // Vector Multiply Word Low Signed, Modulo, Integer and Accumulate Negative in Words +PPC_evmwlssiaaw, // Vector Multiply Word Low Signed, Saturate, Integer and Accumulate into Words +PPC_evmwlssianw, // Vector Multiply Word Low Signed, Saturate, Integer and Accumulate Negative in Words +PPC_evmwlumi, // Vector Multiply Word Low Unsigned, Modulo, Integer +PPC_evmwlumia, // Vector Multiply Word Low Unsigned, Modulo, Integer to Accumulator +PPC_evmwlumiaaw, // Vector Multiply Word Low Unsigned, Modulo, Integer and Accumulate into Words +PPC_evmwlumianw, // Vector Multiply Word Low Unsigned, Modulo, Integer and Accumulate Negative in Words +PPC_evmwlusiaaw, // Vector Multiply Word Low Unsigned, Saturate, Integer and Accumulate into Words +PPC_evmwlusianw, // Vector Multiply Word Low Unsigned, Saturate, Integer and Accumulate Negative in Words +PPC_evmwsmf, // Vector Multiply Word Signed, Modulo, Fractional +PPC_evmwsmfa, // Vector Multiply Word Signed, Modulo, Fractional to Accumulator +PPC_evmwsmfaa, // Vector Multiply Word Signed, Modulo, Fractional and Accumulate +PPC_evmwsmfan, // Vector Multiply Word Signed, Modulo, Fractional and Accumulate Negative +PPC_evmwsmi, // Vector Multiply Word Signed, Modulo, Integer +PPC_evmwsmia, // Vector Multiply Word Signed, Modulo, Integer to Accumulator +PPC_evmwsmiaa, // Vector Multiply Word Signed, Modulo, Integer and Accumulate +PPC_evmwsmian, // Vector Multiply Word Signed, Modulo, Integer and Accumulate Negative +PPC_evmwssf, // Vector Multiply Word Signed, Saturate, Fractional +PPC_evmwssfa, // Vector Multiply Word Signed, Saturate, Fractional to Accumulator +PPC_evmwssfaa, // Vector Multiply Word Signed, Saturate, Fractional and Accumulate +PPC_evmwssfan, // Vector Multiply Word Signed, Saturate, Fractional and Accumulate Negative +PPC_evmwumi, // Vector Multiply Word Unsigned, Modulo, Integer +PPC_evmwumia, // Vector Multiply Word Unsigned, Modulo, Integer to Accumulator +PPC_evmwumiaa, // Vector Multiply Word Unsigned, Modulo, Integer and Accumulate +PPC_evmwumian, // Vector Multiply Word Unsigned, Modulo, Integer and Accumulate Negative +PPC_evnand, // Vector NAND +PPC_evneg, // Vector Negate +PPC_evnor, // Vector NOR +PPC_evor, // Vector OR +PPC_evorc, // Vector OR with Complement Form +PPC_evrlw, // Vector Rotate Left Word +PPC_evrlwi, // Vector Rotate Left Word Immediate +PPC_evrndw, // Vector Round Word +PPC_evsel, // Vector Select +PPC_evslw, // Vector Shift Left Word +PPC_evslwi, // Vector Shift Left Word Immediate +PPC_evsplatfi, // Vector Splat Fractional Immediate +PPC_evsplati, // Vector Splat Immediate +PPC_evsrwis, // Vector Shift Right Word Immediate Signed +PPC_evsrwiu, // Vector Shift Right Word Immediate Unsigned +PPC_evsrws, // Vector Shift Right Word Signed +PPC_evsrwu, // Vector Shift Right Word Unsigned +PPC_evstdd, // Vector Store Double of Double +PPC_evstddx, // Vector Store Double of Double Indexed +PPC_evstdh, // Vector Store Double of Four Halfwords +PPC_evstdhx, // Vector Store Double of Four Halfwords Indexed +PPC_evstdw, // Vector Store Double of Two Words +PPC_evstdwx, // Vector Store Double of Two Words Indexed +PPC_evstwhe, // Vector Store Word of Two Halfwords from Even +PPC_evstwhex, // Vector Store Word of Two Halfwords from Even Indexed +PPC_evstwho, // Vector Store Word of Two Halfwords from Odd +PPC_evstwhox, // Vector Store Word of Two Halfwords from Odd Indexed +PPC_evstwwe, // Vector Store Word of Word from Even +PPC_evstwwex, // Vector Store Word of Word from Even Indexed +PPC_evstwwo, // Vector Store Word of Word from Odd +PPC_evstwwox, // Vector Store Word of Word from Odd Indexed +PPC_evsubfsmiaaw, // Vector Subtract Signed, Modulo, Integer to Accumulator Word +PPC_evsubfssiaaw, // Vector Subtract Signed, Saturate, Integer to Accumulator Word +PPC_evsubfumiaaw, // Vector Subtract Unsigned, Modulo, Integer to Accumulator Word +PPC_evsubfusiaaw, // Vector Subtract Unsigned, Saturate, Integer to Accumulator Word +PPC_evsubfw, // Vector Subtract from Word +PPC_evsubifw, // Vector Subtract Immediate from Word +PPC_evxor, // Vector XOR + +// SPE.Embedded Float Scalar Double + +PPC_efdabs, // Floating-Point Double-Precision Absolute Value +PPC_efdadd, // Floating-Point Double-Precision Add +PPC_efdcfs, // Floating-Point Double-Precision Convert from Single-Precision +PPC_efdcfsf, // Convert Floating-Point Double-Precision from Signed Fraction +PPC_efdcfsi, // Convert Floating-Point Double-Precision from Signed Integer +PPC_efdcfsid, // Convert Floating-Point Double-Precision from Signed Integer Doubleword +PPC_efdcfuf, // Convert Floating-Point Double-Precision from Unsigned Fraction +PPC_efdcfui, // Convert Floating-Point Double-Precision from Unsigned Integer +PPC_efdcfuid, // Convert Floating-Point Double-Precision from Unsigned Integer Doubleword +PPC_efdcmpeq, // Floating-Point Double-Precision Compare Equal +PPC_efdcmpgt, // Floating-Point Double-Precision Compare Greater Than +PPC_efdcmplt, // Floating-Point Double-Precision Compare Less Than +PPC_efdctsf, // Convert Floating-Point Double-Precision to Signed Fraction +PPC_efdctsi, // Convert Floating-Point Double-Precision to Signed Integer +PPC_efdctsidz, // Convert Floating-Point Double-Precision to Signed Integer Doubleword with Round toward Zero +PPC_efdctsiz, // Convert Floating-Point Double-Precision to Signed Integer with Round toward Zero +PPC_efdctuf, // Convert Floating-Point Double-Precision to Unsigned Fraction +PPC_efdctui, // Convert Floating-Point Double-Precision to Unsigned Integer +PPC_efdctuidz, // Convert Floating-Point Double-Precision to Unsigned Integer Doubleword with Round toward Zero +PPC_efdctuiz, // Convert Floating-Point Double-Precision to Unsigned Integer with Round toward Zero +PPC_efddiv, // Floating-Point Double-Precision Divide +PPC_efdmul, // Floating-Point Double-Precision Multiply +PPC_efdnabs, // Floating-Point Double-Precision Negative Absolute Value +PPC_efdneg, // Floating-Point Double-Precision Negate +PPC_efdsub, // Floating-Point Double-Precision Subtract +PPC_efdtsteq, // Floating-Point Double-Precision Test Equal +PPC_efdtstgt, // Floating-Point Double-Precision Test Greater Than +PPC_efdtstlt, // Floating-Point Double-Precision Test Less Than +PPC_efscfd, // Floating-Point Single-Precision Convert from Double-Precision + +// SPE.Embedded Float Scalar Single + +PPC_efsabs, // Floating-Point Single-Precision Absolute Value +PPC_efsadd, // Floating-Point Single-Precision Add +PPC_efscfsf, // Convert Floating-Point Single-Precision from Signed Fraction +PPC_efscfsi, // Convert Floating-Point Single-Precision from Signed Integer +PPC_efscfuf, // Convert Floating-Point Single-Precision from Unsigned Fraction +PPC_efscfui, // Convert Floating-Point Single-Precision from Unsigned Integer +PPC_efscmpeq, // Floating-Point Single-Precision Compare Equal +PPC_efscmpgt, // Floating-Point Single-Precision Compare Greater Than +PPC_efscmplt, // Floating-Point Single-Precision Compare Less Than +PPC_efsctsf, // Convert Floating-Point Single-Precision to Signed Fraction +PPC_efsctsi, // Convert Floating-Point Single-Precision to Signed Integer +PPC_efsctsiz, // Convert Floating-Point Single-Precision to Signed Integer with Round toward Zero +PPC_efsctuf, // Convert Floating-Point Single-Precision to Unsigned Fraction +PPC_efsctui, // Convert Floating-Point Single-Precision to Unsigned Integer +PPC_efsctuiz, // Convert Floating-Point Single-Precision to Unsigned Integer with Round toward Zero +PPC_efsdiv, // Floating-Point Single-Precision Divide +PPC_efsmul, // Floating-Point Single-Precision Multiply +PPC_efsnabs, // Floating-Point Single-Precision Negative Absolute Value +PPC_efsneg, // Floating-Point Single-Precision Negate +PPC_efssub, // Floating-Point Single-Precision Subtract +PPC_efststeq, // Floating-Point Single-Precision Test Equal +PPC_efststgt, // Floating-Point Single-Precision Test Greater Than +PPC_efststlt, // Floating-Point Single-Precision Test Less Than + +// SPE.Embedded Float Vector + +PPC_evfsabs, // Vector Floating-Point Single-Precision Absolute Value +PPC_evfsadd, // Vector Floating-Point Single-Precision Add +PPC_evfscfsf, // Vector Convert Floating-Point Single-Precision from Signed Fraction +PPC_evfscfsi, // Vector Convert Floating-Point Single-Precision from Signed Integer +PPC_evfscfuf, // Vector Convert Floating-Point Single-Precision from Unsigned Fraction +PPC_evfscfui, // Vector Convert Floating-Point Single-Precision from Unsigned Integer +PPC_evfscmpeq, // Vector Floating-Point Single-Precision Compare Equal +PPC_evfscmpgt, // Vector Floating-Point Single-Precision Compare Greater Than +PPC_evfscmplt, // Vector Floating-Point Single-Precision Compare Less Than +PPC_evfsctsf, // Vector Convert Floating-Point Single-Precision to Signed Fraction +PPC_evfsctsi, // Vector Convert Floating-Point Single-Precision to Signed Integer +PPC_evfsctsiz, // Vector Convert Floating-Point Single-Precision to Signed Integer with Round toward Zero +PPC_evfsctuf, // Vector Convert Floating-Point Single-Precision to Unsigned Fraction +PPC_evfsctui, // Vector Convert Floating-Point Single-Precision to Unsigned Integer +PPC_evfsctuiz, // Vector Convert Floating-Point Single-Precision to Unsigned Integer with Round toward Zero +PPC_evfsdiv, // Vector Floating-Point Single-Precision Divide +PPC_evfsmul, // Vector Floating-Point Single-Precision Multiply +PPC_evfsnabs, // Vector Floating-Point Single-Precision Negative Absolute Value +PPC_evfsneg, // Vector Floating-Point Single-Precision Negate +PPC_evfssub, // Vector Floating-Point Single-Precision Subtract +PPC_evfststeq, // Vector Floating-Point Single-Precision Test Equal +PPC_evfststgt, // Vector Floating-Point Single-Precision Test Greater Than +PPC_evfststlt, // Vector Floating-Point Single-Precision Test Less Than + +// Missing instructions from Power ISA 2.06 + +// category: 64-bit +PPC_bpermd, // Bit Permute Doubleword +PPC_divde, // Divide Doubleword Extended +PPC_divdeu, // Divide Doubleword Extended Unsigned +PPC_ldbrx, // Load Doubleword Byte-Reverse Indexed +PPC_prtyd, // Parity Doubleword +PPC_stdbrx, // Store Doubleword Byte-Reverse Indexed +// category: Base +PPC_cmpb, // Compare Bytes +PPC_divwe, // Divide Word Extended +PPC_divweu, // Divide Word Extended Unsigned +PPC_lbarx, // Load Byte and Reserve Indexed +PPC_lharx, // Load Halfword and Reserve Indexed +PPC_popcntd, // Population Count Doubleword +PPC_popcntw, // Population Count Word +PPC_prtyw, // Parity Word +PPC_stbcx, // Store Byte Conditional Indexed +PPC_sthcx, // Store Halfword Conditional Indexed +// category: Binary Coded Decimal Assist +PPC_addg6s, // Add and Generate Sixes +PPC_cbcdtd, // Convert Binary Coded Decimal to Declets +PPC_cdtbcd, // Convert Declets To Binary Coded Decimal +// category: Decimal Floating-Point +PPC_dadd, // DFP Add +PPC_daddq, // DFP Add Quad +PPC_dcffix, // DFP Convert From Fixed +PPC_dcffixq, // DFP Convert From Fixed Quad +PPC_dcmpo, // DFP Compare Ordered +PPC_dcmpoq, // DFP Compare Ordered Quad +PPC_dcmpu, // DFP Compare Unordered +PPC_dcmpuq, // DFP Compare Unordered Quad +PPC_dctdp, // DFP Convert To DFP Long +PPC_dctfix, // DFP Convert To Fixed +PPC_dctfixq, // DFP Convert To Fixed Quad +PPC_dctqpq, // DFP Convert To DFP Extended +PPC_ddedpd, // DFP Decode DPD To BCD +PPC_ddedpdq, // DFP Decode DPD To BCD Quad +PPC_ddiv, // DFP Divide +PPC_ddivq, // DFP Divide Quad +PPC_denbcd, // DFP Encode BCD To DPD +PPC_denbcdq, // DFP Encode BCD To DPD Quad +PPC_diex, // DFP Insert Biased Exponent +PPC_diexq, // DFP Insert Biased Exponent Quad +PPC_dmul, // DFP Multiply +PPC_dmulq, // DFP Multiply Quad +PPC_dqua, // DFP Quantize +PPC_dquai, // DFP Quantize Immediate +PPC_dquaiq, // DFP Quantize Immediate Quad +PPC_dquaq, // DFP Quantize Quad +PPC_drdpq, // DFP Round To DFP Long +PPC_drintn, // DFP Round To FP Integer Without Inexact +PPC_drintnq, // DFP Round To FP Integer Without Inexact Quad +PPC_drintx, // DFP Round To FP Integer With Inexact +PPC_drintxq, // DFP Round To FP Integer With Inexact Quad +PPC_drrnd, // DFP Reround +PPC_drrndq, // DFP Reround Quad +PPC_drsp, // DFP Round To DFP Short +PPC_dscli, // DFP Shift Significand Left Immediate +PPC_dscliq, // DFP Shift Significand Left Immediate Quad +PPC_dscri, // DFP Shift Significand Right Immediate +PPC_dscriq, // DFP Shift Significand Right Immediate Quad +PPC_dsub, // DFP Subtract +PPC_dsubq, // DFP Subtract Quad +PPC_dtstdc, // DFP Test Data Class +PPC_dtstdcq, // DFP Test Data Class Quad +PPC_dtstdg, // DFP Test Data Group +PPC_dtstdgq, // DFP Test Data Group Quad +PPC_dtstex, // DFP Test Exponent +PPC_dtstexq, // DFP Test Exponent Quad +PPC_dtstsf, // DFP Test Significance +PPC_dtstsfq, // DFP Test Significance Quad +PPC_dxex, // DFP Extract Biased Exponent +PPC_dxexq, // DFP Extract Biased Exponent Quad +// category: Decorated Storage +PPC_dsn, // Decorated Storage Notify +PPC_lbdx, // Load Byte with Decoration Indexed +PPC_lddx, // Load Doubleword with Decoration Indexed +PPC_lfddx, // Load Floating Doubleword with Decoration Indexed +PPC_lhdx, // Load Halfword with Decoration Indexed +PPC_lwdx, // Load Word with Decoration Indexed +PPC_stbdx, // Store Byte with Decoration Indexed +PPC_stddx, // Store Doubleword with Decoration Indexed +PPC_stfddx, // Store Floating Doubleword with Decoration Indexed +PPC_sthdx, // Store Halfword with Decoration Indexed +PPC_stwdx, // Store Word with Decoration Indexed +// category: Embedded +PPC_mbar, // Memory Barrier +PPC_rfmci, // Return From Machine Check Interrupt +PPC_tlbilx, // TLB Invalidate Local +PPC_dci, // Data Cache Invalidate +PPC_ici, // Instruction Cache Invalidate +PPC_mfdcrux, // Move From Device Control Register User-mode Indexed +PPC_mfdcrx, // Move From Device Control Register Indexed +PPC_mtdcrux, // Move To Device Control Register User-mode Indexed +PPC_mtdcrx, // Move To Device Control Register Indexed +// category: Embedded.Enhanced Debug +PPC_dnh, // Debugger Notify Halt +// category: Embedded.Hypervisor +PPC_ehpriv, // Embedded Hypervisor Privilege +PPC_rfgi, // Return From Guest Interrupt +// category: Embedded.Processor Control +PPC_msgclr, // Message Clear +PPC_msgsnd, // Message Send +// category: Embedded.External PID +PPC_dcbfep, // Data Cache Block Flush by External PID +PPC_dcbstep, // Data Cache Block Store by External PID +PPC_dcbtep, // Data Cache Block Touch by External PID +PPC_dcbtstep, // Data Cache Block Touch for Store by External PID +PPC_dcbzep, // Data Cache Block set to Zero by External PID +PPC_evlddepx, // Vector Load Doubleword into Doubleword by External Process ID Indexed +PPC_evstddepx, // Vector Store Doubleword into Doubleword by External Process ID Indexed +PPC_icbiep, // Instruction Cache Block Invalidate by External PID +PPC_lbepx, // Load Byte by External Process ID Indexed +PPC_lfdepx, // Load Floating-Point Double by External Process ID Indexed +PPC_lhepx, // Load Halfword by External Process ID Indexed +PPC_lvepx, // Load Vector by External Process ID Indexed +PPC_lvepxl, // Load Vector by External Process ID Indexed LRU +PPC_lwepx, // Load Word by External Process ID Indexed +PPC_stbepx, // Store Byte by External Process ID Indexed +PPC_stfdepx, // Store Floating-Point Double by External Process ID Indexed +PPC_sthepx, // Store Halfword by External Process ID Indexed +PPC_stvepx, // Store Vector by External Process ID Indexed +PPC_stvepxl, // Store Vector by External Process ID Indexed LRU +PPC_stwepx, // Store Word by External Process ID Indexed +PPC_ldepx, // Load Doubleword by External Process ID Indexed +PPC_stdepx, // Store Doubleword by External Process ID Indexed +// category: Embedded.Performance Monitor +PPC_mfpmr, // Move From Performance Monitor Register +PPC_mtpmr, // Move To Performance Monitor Register +// category: Embedded.Thread Management +PPC_mftmr, // Move From Thread Management Register +PPC_mttmr, // Move To Thread Management Register +// category: Embedded.TLB Write Conditional +PPC_tlbsrx, // TLB Search and Reserve +// category: Floating-point +PPC_fcfids, // Floating Convert From Integer Doubleword Single +PPC_fcfidu, // Floating Convert From Integer Doubleword Unsigned +PPC_fcfidus, // Floating Convert From Integer Doubleword Unsigned Single +PPC_fctidu, // Floating Convert To Integer Doubleword Unsigned +PPC_fctiduz, // Floating Convert To Integer Doubleword Unsigned with round toward Zero +PPC_fctiwu, // Floating Convert To Integer Word Unsigned +PPC_fctiwuz, // Floating Convert To Integer Word Unsigned with round toward Zero +PPC_ftdiv, // Floating Test for software Divide +PPC_ftsqrt, // Floating Test for software Square Root +PPC_lfiwax, // Load Floating-Point as Integer Word Algebraic Indexed +PPC_lfiwzx, // Load Floating-Point as Integer Word and Zero Indexed +PPC_lfdp, // Load Floating-Point Double Pair +PPC_lfdpx, // Load Floating-Point Double Pair Indexed +PPC_stfdp, // Store Floating-Point Double Pair +PPC_stfdpx, // Store Floating-Point Double Pair Indexed +PPC_fcpsgn, // Floating Copy Sign +PPC_fre, // Floating Reciprocal Estimate +PPC_frim, // Floating Round to Integer Minus +PPC_frin, // Floating Round to Integer Nearest +PPC_frip, // Floating Round to Integer Plus +PPC_friz, // Floating Round to Integer Toward Zero +// category: Legacy Integer Multiply-Accumulate +PPC_macchw, // Multiply Accumulate Cross Halfword to Word Modulo Signed +PPC_macchws, // Multiply Accumulate Cross Halfword to Word Saturate Signed +PPC_macchwsu, // Multiply Accumulate Cross Halfword to Word Saturate Unsigned +PPC_macchwu, // Multiply Accumulate Cross Halfword to Word Modulo Unsigned +PPC_machhw, // Multiply Accumulate High Halfword to Word Modulo Signed +PPC_machhws, // Multiply Accumulate High Halfword to Word Saturate Signed +PPC_machhwsu, // Multiply Accumulate High Halfword to Word Saturate Unsigned +PPC_machhwu, // Multiply Accumulate High Halfword to Word Modulo Unsigned +PPC_maclhw, // Multiply Accumulate Low Halfword to Word Modulo Signed +PPC_maclhws, // Multiply Accumulate Low Halfword to Word Saturate Signed +PPC_maclhwsu, // Multiply Accumulate Low Halfword to Word Saturate Unsigned +PPC_maclhwu, // Multiply Accumulate Low Halfword to Word Modulo Unsigned +PPC_mulchw, // Multiply Cross Halfword to Word Signed +PPC_mulchwu, // Multiply Cross Halfword to Word Unsigned +PPC_mulhhw, // Multiply High Halfword to Word Signed +PPC_mulhhwu, // Multiply High Halfword to Word Unsigned +PPC_mullhw, // Multiply Low Halfword to Word Signed +PPC_mullhwu, // Multiply Low Halfword to Word Unsigned +PPC_nmacchw, // Negative Multiply Accumulate Cross Halfword to Word Modulo Signed +PPC_nmacchws, // Negative Multiply Accumulate Cross Halfword to Word Saturate Signed +PPC_nmachhw, // Negative Multiply Accumulate High Halfword to Word Modulo Signed +PPC_nmachhws, // Negative Multiply Accumulate High Halfword to Word Saturate Signed +PPC_nmaclhw, // Negative Multiply Accumulate Low Halfword to Word Modulo Signed +PPC_nmaclhws, // Negative Multiply Accumulate Low Halfword to Word Saturate Signed +// category: Legacy Move Assist +PPC_dlmzb, // Determine Leftmost Zero Byte +// category: Load/Store Quadword +PPC_lq, // Load Quadword +PPC_stq, // Store Quadword +// category: Server +PPC_doze, // Doze +PPC_lbzcix, // Load Byte and Zero Caching Inhibited Indexed +PPC_ldcix, // Load Doubleword Caching Inhibited Indexed +PPC_lhzcix, // Load Halfword and Zero Caching Inhibited Indexed +PPC_lwzcix, // Load Word and Zero Caching Inhibited Indexed +PPC_nap, // Nap +PPC_rvwinkle, // Rip Van Winkle +PPC_slbfee, // SLB Find Entry ESID +PPC_sleep, // Sleep +PPC_stbcix, // Store Byte Caching Inhibited Indexed +PPC_stdcix, // Store Doubleword Caching Inhibited Indexed +PPC_sthcix, // Store Halfword Caching Inhibited Indexed +PPC_stwcix, // Store Word Caching Inhibited Indexed +PPC_tlbiel, // TLB Invalidate Entry Local, ISA 3.0 format +// category: Vector +PPC_lvebx, // Load Vector Element Byte Indexed +PPC_lvehx, // Load Vector Element Halfword Indexed +PPC_lvewx, // Load Vector Element Word Indexed +PPC_lvsl, // Load Vector for Shift Left Indexed +PPC_lvsr, // Load Vector for Shift Right Indexed +PPC_lvx, // Load Vector Indexed +PPC_lvxl, // Load Vector Indexed LRU +PPC_mfvscr, // Move From Vector Status and Control Register +PPC_mtvscr, // Move To Vector Status and Control Register +PPC_stvebx, // Store Vector Element Byte Indexed +PPC_stvehx, // Store Vector Element Halfword Indexed +PPC_stvewx, // Store Vector Element Word Indexed +PPC_stvx, // Store Vector Indexed +PPC_stvxl, // Store Vector Indexed LRU +PPC_vaddcuw, // Vector Add and Write Carry-Out Unsigned Word +PPC_vaddfp, // Vector Add Single-Precision +PPC_vaddsbs, // Vector Add Signed Byte Saturate +PPC_vaddshs, // Vector Add Signed Halfword Saturate +PPC_vaddsws, // Vector Add Signed Word Saturate +PPC_vaddubm, // Vector Add Unsigned Byte Modulo +PPC_vaddubs, // Vector Add Unsigned Byte Saturate +PPC_vadduhm, // Vector Add Unsigned Halfword Modulo +PPC_vadduhs, // Vector Add Unsigned Halfword Saturate +PPC_vadduwm, // Vector Add Unsigned Word Modulo +PPC_vadduws, // Vector Add Unsigned Word Saturate +PPC_vand, // Vector Logical AND +PPC_vandc, // Vector Logical AND with Complement +PPC_vavgsb, // Vector Average Signed Byte +PPC_vavgsh, // Vector Average Signed Halfword +PPC_vavgsw, // Vector Average Signed Word +PPC_vavgub, // Vector Average Unsigned Byte +PPC_vavguh, // Vector Average Unsigned Halfword +PPC_vavguw, // Vector Average Unsigned Word +PPC_vcfsx, // Vector Convert From Signed Fixed-Point Word +PPC_vcfux, // Vector Convert From Unsigned Fixed-Point Word +PPC_vcmpbfp, // Vector Compare Bounds Single-Precision +PPC_vcmpeqfp, // Vector Compare Equal To Single-Precision +PPC_vcmpequb, // Vector Compare Equal To Unsigned Byte +PPC_vcmpequh, // Vector Compare Equal To Unsigned Halfword +PPC_vcmpequw, // Vector Compare Equal To Unsigned Word +PPC_vcmpgefp, // Vector Compare Greater Than or Equal To Single-Precision +PPC_vcmpgtfp, // Vector Compare Greater Than Single-Precision +PPC_vcmpgtsb, // Vector Compare Greater Than Signed Byte +PPC_vcmpgtsh, // Vector Compare Greater Than Signed Halfword +PPC_vcmpgtsw, // Vector Compare Greater Than Signed Word +PPC_vcmpgtub, // Vector Compare Greater Than Unsigned Byte +PPC_vcmpgtuh, // Vector Compare Greater Than Unsigned Halfword +PPC_vcmpgtuw, // Vector Compare Greater Than Unsigned Word +PPC_vctsxs, // Vector Convert To Signed Fixed-Point Word Saturate +PPC_vctuxs, // Vector Convert To Unsigned Fixed-Point Word Saturate +PPC_vexptefp, // Vector 2 Raised to the Exponent Estimate FloatingPoint +PPC_vlogefp, // Vector Log Base 2 Estimate Floating-Point +PPC_vmaddfp, // Vector Multiply-Add Single-Precision +PPC_vmaxfp, // Vector Maximum Single-Precision +PPC_vmaxsb, // Vector Maximum Signed Byte +PPC_vmaxsh, // Vector Maximum Signed Halfword +PPC_vmaxsw, // Vector Maximum Signed Word +PPC_vmaxub, // Vector Maximum Unsigned Byte +PPC_vmaxuh, // Vector Maximum Unsigned Halfword +PPC_vmaxuw, // Vector Maximum Unsigned Word +PPC_vmhaddshs, // Vector Multiply-High-Add Signed Halfword Saturate +PPC_vmhraddshs, // Vector Multiply-High-Round-Add Signed Halfword Saturate +PPC_vminfp, // Vector Minimum Single-Precision +PPC_vminsb, // Vector Minimum Signed Byte +PPC_vminsh, // Vector Minimum Signed Halfword +PPC_vminsw, // Vector Minimum Signed Word +PPC_vminub, // Vector Minimum Unsigned Byte +PPC_vminuh, // Vector Minimum Unsigned Halfword +PPC_vminuw, // Vector Minimum Unsigned Word +PPC_vmladduhm, // Vector Multiply-Low-Add Unsigned Halfword Modulo +PPC_vmrghb, // Vector Merge High Byte +PPC_vmrghh, // Vector Merge High Halfword +PPC_vmrghw, // Vector Merge High Word +PPC_vmrglb, // Vector Merge Low Byte +PPC_vmrglh, // Vector Merge Low Halfword +PPC_vmrglw, // Vector Merge Low Word +PPC_vmsummbm, // Vector Multiply-Sum Mixed Byte Modulo +PPC_vmsumshm, // Vector Multiply-Sum Signed Halfword Modulo +PPC_vmsumshs, // Vector Multiply-Sum Signed Halfword Saturate +PPC_vmsumubm, // Vector Multiply-Sum Unsigned Byte Modulo +PPC_vmsumuhm, // Vector Multiply-Sum Unsigned Halfword Modulo +PPC_vmsumuhs, // Vector Multiply-Sum Unsigned Halfword Saturate +PPC_vmulesb, // Vector Multiply Even Signed Byte +PPC_vmulesh, // Vector Multiply Even Signed Halfword +PPC_vmuleub, // Vector Multiply Even Unsigned Byte +PPC_vmuleuh, // Vector Multiply Even Unsigned Halfword +PPC_vmulosb, // Vector Multiply Odd Signed Byte +PPC_vmulosh, // Vector Multiply Odd Signed Halfword +PPC_vmuloub, // Vector Multiply Odd Unsigned Byte +PPC_vmulouh, // Vector Multiply Odd Unsigned Halfword +PPC_vnmsubfp, // Vector Negative Multiply-Subtract Single-Precision +PPC_vnor, // Vector Logical NOR +PPC_vor, // Vector Logical OR +PPC_vperm, // Vector Permute +PPC_vpkpx, // Vector Pack Pixel +PPC_vpkshss, // Vector Pack Signed Halfword Signed Saturate +PPC_vpkshus, // Vector Pack Signed Halfword Unsigned Saturate +PPC_vpkswss, // Vector Pack Signed Word Signed Saturate +PPC_vpkswus, // Vector Pack Signed Word Unsigned Saturate +PPC_vpkuhum, // Vector Pack Unsigned Halfword Unsigned Modulo +PPC_vpkuhus, // Vector Pack Unsigned Halfword Unsigned Saturate +PPC_vpkuwum, // Vector Pack Unsigned Word Unsigned Modulo +PPC_vpkuwus, // Vector Pack Unsigned Word Unsigned Saturate +PPC_vrefp, // Vector Reciprocal Estimate Single-Precision +PPC_vrfim, // Vector Round to Single-Precision Integer toward -Infinity +PPC_vrfin, // Vector Round to Single-Precision Integer Nearest +PPC_vrfip, // Vector Round to Single-Precision Integer toward +Infinity +PPC_vrfiz, // Vector Round to Single-Precision Integer toward Zero +PPC_vrlb, // Vector Rotate Left Byte +PPC_vrlh, // Vector Rotate Left Halfword +PPC_vrlw, // Vector Rotate Left Word +PPC_vrsqrtefp, // Vector Reciprocal Square Root Estimate Single-Precision +PPC_vsel, // Vector Select +PPC_vsl, // Vector Shift Left +PPC_vslb, // Vector Shift Left Byte +PPC_vsldoi, // Vector Shift Left Double by Octet Immediate +PPC_vslh, // Vector Shift Left Halfword +PPC_vslo, // Vector Shift Left by Octet +PPC_vslw, // Vector Shift Left Word +PPC_vspltb, // Vector Splat Byte +PPC_vsplth, // Vector Splat Halfword +PPC_vspltisb, // Vector Splat Immediate Signed Byte +PPC_vspltish, // Vector Splat Immediate Signed Halfword +PPC_vspltisw, // Vector Splat Immediate Signed Word +PPC_vspltw, // Vector Splat Word +PPC_vsr, // Vector Shift Right +PPC_vsrab, // Vector Shift Right Algebraic Byte +PPC_vsrah, // Vector Shift Right Algebraic Halfword +PPC_vsraw, // Vector Shift Right Algebraic Word +PPC_vsrb, // Vector Shift Right Byte +PPC_vsrh, // Vector Shift Right Halfword +PPC_vsro, // Vector Shift Right by Octet +PPC_vsrw, // Vector Shift Right Word +PPC_vsubcuw, // Vector Subtract and Write Carry-Out Unsigned Word +PPC_vsubfp, // Vector Subtract Single-Precision +PPC_vsubsbs, // Vector Subtract Signed Byte Saturate +PPC_vsubshs, // Vector Subtract Signed Halfword Saturate +PPC_vsubsws, // Vector Subtract Signed Word Saturate +PPC_vsububm, // Vector Subtract Unsigned Byte Modulo +PPC_vsububs, // Vector Subtract Unsigned Byte Saturate +PPC_vsubuhm, // Vector Subtract Unsigned Halfword Modulo +PPC_vsubuhs, // Vector Subtract Unsigned Halfword Saturate +PPC_vsubuwm, // Vector Subtract Unsigned Word Modulo +PPC_vsubuws, // Vector Subtract Unsigned Word Saturate +PPC_vsum2sws, // Vector Sum across Half Signed Word Saturate +PPC_vsum4sbs, // Vector Sum across Quarter Signed Byte Saturate +PPC_vsum4shs, // Vector Sum across Quarter Signed Halfword Saturate +PPC_vsum4ubs, // Vector Sum across Quarter Unsigned Byte Saturate +PPC_vsumsws, // Vector Sum across Signed Word Saturate +PPC_vupkhpx, // Vector Unpack High Pixel +PPC_vupkhsb, // Vector Unpack High Signed Byte +PPC_vupkhsh, // Vector Unpack High Signed Halfword +PPC_vupklpx, // Vector Unpack Low Pixel +PPC_vupklsb, // Vector Unpack Low Signed Byte +PPC_vupklsh, // Vector Unpack Low Signed Halfword +PPC_vxor, // Vector Logical XOR +// category: Vector-Scalar Floating-Point +PPC_lxsdx, // Load VSR Scalar Doubleword Indexed +PPC_lxvd2x, // Load VSR Vector Doubleword*2 Indexed +PPC_lxvdsx, // Load VSR Vector Doubleword & Splat Indexed +PPC_lxvw4x, // Load VSR Vector Word*4 Indexed +PPC_stxsdx, // Store VSR Scalar Doubleword Indexed +PPC_stxvd2x, // Store VSR Vector Doubleword*2 Indexed +PPC_stxvw4x, // Store VSR Vector Word*4 Indexed +PPC_xsabsdp, // VSX Scalar Absolute Value Double-Precision +PPC_xsadddp, // VSX Scalar Add Double-Precision +PPC_xscmpodp, // VSX Scalar Compare Ordered Double-Precision +PPC_xscmpudp, // VSX Scalar Compare Unordered Double-Precision +PPC_xscpsgndp, // VSX Scalar Copy Sign Double-Precision +PPC_xscvdpsp, // VSX Scalar Convert Double-Precision to SinglePrecision +PPC_xscvdpsxds, // VSX Scalar truncate Double-Precision to integer and Convert to Signed Fixed-Point Doubleword format with Saturate +PPC_xscvdpsxws, // VSX Scalar truncate Double-Precision to integer and Convert to Signed Fixed-Point Word format with Saturate +PPC_xscvdpuxds, // VSX Scalar truncate Double-Precision to integer and Convert to Unsigned Fixed-Point Doubleword format with Saturate +PPC_xscvdpuxws, // VSX Scalar truncate Double-Precision to integer and Convert to Unsigned Fixed-Point Word format with Saturate +PPC_xscvspdp, // VSX Scalar Convert Single-Precision to DoublePrecision format +PPC_xscvsxddp, // VSX Scalar Convert and round Signed Fixed-Point Doubleword to Double-Precision format +PPC_xscvuxddp, // VSX Scalar Convert and round Unsigned Fixed-Point Doubleword to Double-Precision format +PPC_xsdivdp, // VSX Scalar Divide Double-Precision +PPC_xsmaddadp, // VSX Scalar Multiply-Add Type-A Double-Precision +PPC_xsmaddmdp, // VSX Scalar Multiply-Add Type-M Double-Precision +PPC_xsmaxdp, // VSX Scalar Maximum Double-Precision +PPC_xsmindp, // VSX Scalar Minimum Double-Precision +PPC_xsmsubadp, // VSX Scalar Multiply-Subtract Type-A Double-Precision +PPC_xsmsubmdp, // VSX Scalar Multiply-Subtract Type-M Double-Precision +PPC_xsmuldp, // VSX Scalar Multiply Double-Precision +PPC_xsnabsdp, // VSX Scalar Negative Absolute Value Double-Precision +PPC_xsnegdp, // VSX Scalar Negate Double-Precision +PPC_xsnmaddadp, // VSX Scalar Negative Multiply-Add Type-A DoublePrecision +PPC_xsnmaddmdp, // VSX Scalar Negative Multiply-Add Type-M DoublePrecision +PPC_xsnmsubadp, // VSX Scalar Negative Multiply-Subtract Type-A DoublePrecision +PPC_xsnmsubmdp, // VSX Scalar Negative Multiply-Subtract Type-M DoublePrecision +PPC_xsrdpi, // VSX Scalar Round to Double-Precision Integer +PPC_xsrdpic, // VSX Scalar Round to Double-Precision Integer using Current rounding mode +PPC_xsrdpim, // VSX Scalar Round to Double-Precision Integer toward Infinity +PPC_xsrdpip, // VSX Scalar Round to Double-Precision Integer toward + Infinity +PPC_xsrdpiz, // VSX Scalar Round to Double-Precision Integer toward Zero +PPC_xsredp, // VSX Scalar Reciprocal Estimate Double-Precision +PPC_xsrsqrtedp, // VSX Scalar Reciprocal Square Root Estimate DoublePrecision +PPC_xssqrtdp, // VSX Scalar Square Root Double-Precision +PPC_xssubdp, // VSX Scalar Subtract Double-Precision +PPC_xstdivdp, // VSX Scalar Test for software Divide Double-Precision +PPC_xstsqrtdp, // VSX Scalar Test for software Square Root DoublePrecision +PPC_xvabsdp, // VSX Vector Absolute Value Double-Precision +PPC_xvabssp, // VSX Vector Absolute Value Single-Precision +PPC_xvadddp, // VSX Vector Add Double-Precision +PPC_xvaddsp, // VSX Vector Add Single-Precision +PPC_xvcmpeqdp, // VSX Vector Compare Equal To Double-Precision +PPC_xvcmpeqsp, // VSX Vector Compare Equal To Single-Precision +PPC_xvcmpgedp, // VSX Vector Compare Greater Than or Equal To Double-Precision +PPC_xvcmpgesp, // VSX Vector Compare Greater Than or Equal To SinglePrecision +PPC_xvcmpgtdp, // VSX Vector Compare Greater Than Double-Precision +PPC_xvcmpgtsp, // VSX Vector Compare Greater Than Single-Precision +PPC_xvcpsgndp, // VSX Vector Copy Sign Double-Precision +PPC_xvcpsgnsp, // VSX Vector Copy Sign Single-Precision +PPC_xvcvdpsp, // VSX Vector round and Convert Double-Precision to Single-Precision format +PPC_xvcvdpsxds, // VSX Vector truncate Double-Precision to integer and Convert to Signed Fixed-Point Doubleword Saturate +PPC_xvcvdpsxws, // VSX Vector truncate Double-Precision to integer and Convert to Signed Fixed-Point Word Saturate +PPC_xvcvdpuxds, // VSX Vector truncate Double-Precision to integer and Convert to Unsigned Fixed-Point Doubleword format with Saturate +PPC_xvcvdpuxws, // VSX Vector truncate Double-Precision to integer and Convert to Unsigned Fixed-Point Word format with Saturate +PPC_xvcvspdp, // VSX Vector Convert Single-Precision to DoublePrecision +PPC_xvcvspsxds, // VSX Vector truncate Single-Precision to integer and Convert to Signed Fixed-Point Doubleword format with Saturate +PPC_xvcvspsxws, // VSX Vector truncate Single-Precision to integer and Convert to Signed Fixed-Point Word format with Saturate +PPC_xvcvspuxds, // VSX Vector truncate Single-Precision to integer and Convert to Unsigned Fixed-Point Doubleword format with Saturate +PPC_xvcvspuxws, // VSX Vector truncate Single-Precision to integer and Convert to Unsigned Fixed-Point Word Saturate +PPC_xvcvsxddp, // VSX Vector Convert and round Signed Fixed-Point Doubleword to Double-Precision format +PPC_xvcvsxdsp, // VSX Vector Convert and round Signed Fixed-Point Doubleword to Single-Precision format +PPC_xvcvsxwdp, // VSX Vector Convert Signed Fixed-Point Word to Double-Precision format +PPC_xvcvsxwsp, // VSX Vector Convert and round Signed Fixed-Point Word to Single-Precision format +PPC_xvcvuxddp, // VSX Vector Convert and round Unsigned Fixed-Point Doubleword to Double-Precision format +PPC_xvcvuxdsp, // VSX Vector Convert and round Unsigned Fixed-Point Doubleword to Single-Precision format +PPC_xvcvuxwdp, // VSX Vector Convert Unsigned Fixed-Point Word to Double-Precision format +PPC_xvcvuxwsp, // VSX Vector Convert and round Unsigned Fixed-Point Word to Single-Precision format +PPC_xvdivdp, // VSX Vector Divide Double-Precision +PPC_xvdivsp, // VSX Vector Divide Single-Precision +PPC_xvmaddadp, // VSX Vector Multiply-Add Type-A Double-Precision +PPC_xvmaddasp, // VSX Vector Multiply-Add Type-A Single-Precision +PPC_xvmaddmdp, // VSX Vector Multiply-Add Type-M Double-Precision +PPC_xvmaddmsp, // VSX Vector Multiply-Add Type-M Single-Precision +PPC_xvmaxdp, // VSX Vector Maximum Double-Precision +PPC_xvmaxsp, // VSX Vector Maximum Single-Precision +PPC_xvmindp, // VSX Vector Minimum Double-Precision +PPC_xvminsp, // VSX Vector Minimum Single-Precision +PPC_xvmsubadp, // VSX Vector Multiply-Subtract Type-A Double-Precision +PPC_xvmsubasp, // VSX Vector Multiply-Subtract Type-A Single-Precision +PPC_xvmsubmdp, // VSX Vector Multiply-Subtract Type-M Double-Precision +PPC_xvmsubmsp, // VSX Vector Multiply-Subtract Type-M Single-Precision +PPC_xvmuldp, // VSX Vector Multiply Double-Precision +PPC_xvmulsp, // VSX Vector Multiply Single-Precision +PPC_xvnabsdp, // VSX Vector Negative Absolute Value Double-Precision +PPC_xvnabssp, // VSX Vector Negative Absolute Value Single-Precision +PPC_xvnegdp, // VSX Vector Negate Double-Precision +PPC_xvnegsp, // VSX Vector Negate Single-Precision +PPC_xvnmaddadp, // VSX Vector Negative Multiply-Add Type-A DoublePrecision +PPC_xvnmaddasp, // VSX Vector Negative Multiply-Add Type-A SinglePrecision +PPC_xvnmaddmdp, // VSX Vector Negative Multiply-Add Type-M DoublePrecision +PPC_xvnmaddmsp, // VSX Vector Negative Multiply-Add Type-M SinglePrecision +PPC_xvnmsubadp, // VSX Vector Negative Multiply-Subtract Type-A DoublePrecision +PPC_xvnmsubasp, // VSX Vector Negative Multiply-Subtract Type-A SinglePrecision +PPC_xvnmsubmdp, // VSX Vector Negative Multiply-Subtract Type-M DoublePrecision +PPC_xvnmsubmsp, // VSX Vector Negative Multiply-Subtract Type-M SinglePrecision +PPC_xvrdpi, // VSX Vector Round to Double-Precision Integer +PPC_xvrdpic, // VSX Vector Round to Double-Precision Integer using Current rounding mode +PPC_xvrdpim, // VSX Vector Round to Double-Precision Integer toward Infinity +PPC_xvrdpip, // VSX Vector Round to Double-Precision Integer toward + Infinity +PPC_xvrdpiz, // VSX Vector Round to Double-Precision Integer toward Zero +PPC_xvredp, // VSX Vector Reciprocal Estimate Double-Precision +PPC_xvresp, // VSX Vector Reciprocal Estimate Single-Precision +PPC_xvrspi, // VSX Vector Round to Single-Precision Integer +PPC_xvrspic, // VSX Vector Round to Single-Precision Integer using Current rounding mode +PPC_xvrspim, // VSX Vector Round to Single-Precision Integer toward Infinity +PPC_xvrspip, // VSX Vector Round to Single-Precision Integer toward + Infinity +PPC_xvrspiz, // VSX Vector Round to Single-Precision Integer toward Zero +PPC_xvrsqrtedp, // VSX Vector Reciprocal Square Root Estimate DoublePrecision +PPC_xvrsqrtesp, // VSX Vector Reciprocal Square Root Estimate SinglePrecision +PPC_xvsqrtdp, // VSX Vector Square Root Double-Precision +PPC_xvsqrtsp, // VSX Vector Square Root Single-Precision +PPC_xvsubdp, // VSX Vector Subtract Double-Precision +PPC_xvsubsp, // VSX Vector Subtract Single-Precision +PPC_xvtdivdp, // VSX Vector Test for software Divide Double-Precision +PPC_xvtdivsp, // VSX Vector Test for software Divide Single-Precision +PPC_xvtsqrtdp, // VSX Vector Test for software Square Root DoublePrecision +PPC_xvtsqrtsp, // VSX Vector Test for software Square Root SinglePrecision +PPC_xxland, // VSX Logical AND +PPC_xxlandc, // VSX Logical AND with Complement +PPC_xxlnor, // VSX Logical NOR +PPC_xxlor, // VSX Logical OR +PPC_xxlxor, // VSX Logical XOR +PPC_xxmrghw, // VSX Merge High Word +PPC_xxmrglw, // VSX Merge Low Word +PPC_xxpermdi, // VSX Permute Doubleword Immediate +PPC_xxsel, // VSX Select +PPC_xxsldwi, // VSX Shift Left Double by Word Immediate +PPC_xxspltw, // VSX Splat Word +// category: Wait +PPC_wait, // Wait + +// Obsolete AltiVec instructions + +PPC_dss, // Data Stream Stop +PPC_dssall, // Data Stream Stop All +PPC_dst, // Data Stream Touch +PPC_dstt, // Data Stream Touch Transient +PPC_dstst, // Data Stream Touch for Store +PPC_dststt, // Data Stream Touch for Store Transient + +// Cell BE AltiVec extension +PPC_lvlx, // Load Vector Left Indexed +PPC_lvlxl, // Load Vector Left Indexed Last +PPC_lvrx, // Load Vector Right Indexed +PPC_lvrxl, // Load Vector Right Indexed Last +PPC_stvlx, // Store Vector Left Indexed +PPC_stvlxl, // Store Vector Left Indexed Last +PPC_stvrx, // Store Vector Right Indexed +PPC_stvrxl, // Store Vector Right Indexed Last + +// VLE instructions that don't have matching mnemonics +PPC_add16i, // [e_add16i] Add Immediate +PPC_add2i, // [e_add2i] Add (2 operand) Immediate and Record +PPC_add2is, // [e_add2is] Add (2 operand) Immediate Shifted +PPC_and2i, // [e_and2i] AND (two operand) Immediate +PPC_and2is, // [e_and2is] AND (2 operand) Immediate Shifted +PPC_cmp16i, // [e_cmp16i] Compare Immediate Word +PPC_cmph, // [e_cmph] Compare Halfword +PPC_cmph16i, // [e_cmph16i] Compare Halfword Immediate +PPC_cmphl, // [e_cmphl] Compare Halfword Logical +PPC_cmphl16i, // [e_cmphl16i] Compare Halfword Logical Immediate +PPC_cmpl16i, // [e_cmpl16i] Compare Logical Immediate Word +PPC_mull2i, // [e_mull2i] Multiply (2 operand) Low Immediate +PPC_or2i, // [e_or2i] OR (two operand) Immediate +PPC_or2is, // [e_or2is] OR (2 operand) Immediate Shifted +PPC_rlw, // [e_rlw] Rotate Left Word +PPC_rlwi, // [e_rlwi] Rotate Left Word Immediate +PPC_bclri, // [se_bclri] Bit Clear Immediate +// PPC_bctr, // [se_bctr] Branch to Count Register +// PPC_bctrl, // [se_bctrl] Branch to Count Register and Link +PPC_bgeni, // [se_bgeni] Bit Generate Immediate +PPC_bmaski, // [se_bmaski] Bit Mask Generate Immediate +PPC_bseti, // [se_bseti] Bit Set Immediate +PPC_btsti, // [se_btsti] Bit Test Immediate +PPC_extzb, // [se_extzb] Extend Zero Byte +PPC_extzh, // [se_extzh] Extend Zero Halfword +PPC_illegal, // [se_illegal] Illegal +PPC_mfar, // [se_mfar] Move from Alternate Register +PPC_mtar, // [se_mtar] Move To Alternate Register +PPC_sub, // [se_sub] Subtract +// simplified VLE mnemonics +PPC_sub16i, // [e_sub16i] Subtract Immediate +PPC_sub2i, // [e_sub2i] Subtract (2 operand) Immediate +PPC_sub2is, // [e_sub2is] Subtract (2 operand) Immediate Shifted + +// misc extended mnemonics +PPC_extldi, // Extract and Left Justify Immediate +PPC_extrdi, // Extract and Right Justify Immediate +PPC_insrdi, // Insert from Right Immediate +PPC_rotldi, // Rotate Left Immediate +PPC_rotrdi, // Rotate Right Immediate +PPC_rotld, // Rotate Left +PPC_sldi, // Shift Left Immediate +PPC_srdi, // Shift Right Immediate +PPC_clrldi, // Clear Left Immediate +PPC_clrrdi, // Clear Right Immediate +PPC_clrlsldi, // Clear Left and Shift Left Immediate + +PPC_xnop, // Extended No Operation +PPC_hnop, // Reserved no-op hint +PPC_dcbfl, // Data Cache Block Flush Local +PPC_dcbflp, // Data Cache Block Flush Local Primary +PPC_dcbtt, // Data Cache Block Touch +PPC_dcbtstt, // Data Cache Block Touch for Store +PPC_lwsync, // Lightweight Sync +PPC_ptesync, // Page Table Entry Sync +PPC_waitrsv, // Wait For Reservation Loss +PPC_waitimpl, // Wait For Implementation-specific Condition +PPC_evmr, // Vector Move +PPC_evnot, // Vector Not +PPC_mtcr, // Move to Condition Register +PPC_xvmovdp, // VSX Vector Copy Double-Precision +PPC_xvmovsp, // VSX Vector Copy Single-Precision +PPC_xxspltd, // VSX Splat Doubleword +PPC_xxmrghd, // VSX Merge High Doubleword +PPC_xxmrgld, // VSX Merge Low Doubleword +PPC_xxswapd, // VSX Swap Doubleword + +// Xbox 360 specific +PPC_dcbz128, // Data Cache Block Clear to Zero (128 bytes) +PPC_mtmsree, // Move to Machine State Register EE + +// new Xbox 360 instructions (VMX128) +PPC_vcfpsxws, // Vector Convert From Single-Precision to Signed Fixed-point Word Saturate +PPC_vcfpuxws, // Vector Convert From Single-Precision to Unsigned Fixed-point Word Saturate +PPC_vcsxwfp, // Vector Convert from Signed Fixed-point Word to Single-Precision +PPC_vcuxwfp, // Vector Convert from Unsigned Fixed-point Word to Single-Precision +PPC_vmaddcfp, // Vector Convert from Unsigned Fixed-point Word to Single-Precision +PPC_vmsum3fp, // Vector Multiply-Sum 3-way Single-Precision +PPC_vmsum4fp, // Vector Multiply-Sum 4-way Single-Precision +PPC_vmulfp, // Vector Multiply Single-Precision +PPC_vpermwi, // Vector Permute Word Immediate +PPC_vpkd3d, // Vector Pack D3Dtype, Rotate Left Immediate and Mask Insert +PPC_vrlimi, // Vector Rotate Left Immediate and Mask Insert +PPC_vupkd3d, // Vector Unpack D3Dtype + +// Paired Singles (750CL/Gekko/Broadway) +PPC_ps_cmpu0, // Paired Single Compare Unordered High +PPC_psq_lx, // Paired Single Quantized Load Indexed +PPC_psq_stx, // Paired Single Quantized Store Indexed +PPC_ps_sum0, // Paired Single Vector Sum High +PPC_ps_sum1, // Paired Single Vector Sum Low +PPC_ps_muls0, // Paired Single Multiply Scalar High +PPC_ps_muls1, // Paired Single Multiply Scalar Low +PPC_ps_madds0, // Paired Single Multiply-Add Scalar High +PPC_ps_madds1, // Paired Single Multiply-Add Scalar Low +PPC_ps_div, // Paired Single Divide +PPC_ps_sub, // Paired Single Subtract +PPC_ps_add, // Paired Single Add +PPC_ps_sel, // Paired Single Select +PPC_ps_res, // Paired Single Reciprocal Estimate +PPC_ps_mul, // Paired Single Multiply +PPC_ps_rsqrte, // Paired Single Reciprocal Square Root Estimate +PPC_ps_msub, // Paired Single Multiply-Subtract +PPC_ps_madd, // Paired Single Multiply-Add +PPC_ps_nmsub, // Paired Single Negative Multiply-Subtract +PPC_ps_nmadd, // Paired Single Negative Multiply-Add +PPC_ps_cmpo0, // Paired Single Compare Ordered High +PPC_psq_lux, // Paired Single Quantized Load with update Indexed +PPC_psq_stux, // Paired Single Quantized Store with update Indexed +PPC_ps_neg, // Paired Single Negate +PPC_ps_cmpu1, // Paired Single Compare Unordered Low +PPC_ps_mr, // Paired Single Move Register +PPC_ps_cmpo1, // Paired Single Compare Ordered Low +PPC_ps_nabs, // Paired Single Negative Absolute Value +PPC_ps_abs, // Paired Single Absolute Value +PPC_ps_merge00, // Paired Single Merge High +PPC_ps_merge01, // Paired Single Merge Direct +PPC_ps_merge10, // Paired Single Merge Swapped +PPC_ps_merge11, // Paired Single Merge Low +PPC_dcbz_l, // Data Cache Block Set to Zero Locked +PPC_psq_l, // Paired Single Quantized Load +PPC_psq_lu, // Paired Single Quantized Load update +PPC_psq_st, // Paired Single Quantized Store +PPC_psq_stu, // Paired Single Quantized Store update + +// additional SPE/EFPU2 instructions +PPC_evfsmadd, // Vector Floating-Point Single-Precision Multiply-Add +PPC_evfsmsub, // Vector Floating-Point Single-Precision Multiply-Subtract +PPC_evfssqrt, // Vector Floating-Point Single-Precision Square Root +PPC_evfsnmadd, // Vector Floating-Point Single-Precision Negative Multiply-Add +PPC_evfsnmsub, // Vector Floating-Point Single-Precision Negative Multiply-Subtract +PPC_evfsmax, // Vector Floating-Point Single-Precision Maximum +PPC_evfsmin, // Vector Floating-Point Single-Precision Minimum +PPC_evfsaddsub, // Vector Floating-Point Single-Precision Add / Subtract +PPC_evfssubadd, // Vector Floating-Point Single-Precision Subtract / Add +PPC_evfssum, // Vector Floating-Point Single-Precision Sums +PPC_evfsdiff, // Vector Floating-Point Single-Precision Differences +PPC_evfssumdiff,// Vector Floating-Point Single-Precision Sum / Difference +PPC_evfsdiffsum,// Vector Floating-Point Single-Precision Difference / Sum +PPC_evfsaddx, // Vector Floating-Point Single-Precision Add Exchanged +PPC_evfssubx, // Vector Floating-Point Single-Precision Subtract Exchanged +PPC_evfsaddsubx,// Vector Floating-Point Single-Precision Add / Subtract Exchanged +PPC_evfssubaddx,// Vector Floating-Point Single-Precision Subtract / Add Exchanged +PPC_evfsmulx, // Vector Floating-Point Single-Precision Multiply Exchanged +PPC_evfsmule, // Vector Floating-Point Single-Precision Multiply By Even Element +PPC_evfsmulo, // Vector Floating-Point Single-Precision Multiply By Odd Element +PPC_evfscfh, // Vector Convert Floating-Point Single-Precision from Half-Precision +PPC_evfscth, // Vector Convert Floating-Point Single-Precision to Half-Precision +PPC_efsmax, // Floating-Point Single-Precision Maximum +PPC_efsmin, // Floating-Point Single-Precision Minimum +PPC_efsmadd, // Floating-Point Single-Precision Multiply-Add +PPC_efsmsub, // Floating-Point Single-Precision Multiply-Subtract +PPC_efssqrt, // Floating-Point Single-Precision Square Root +PPC_efsnmadd, // Floating-Point Single-Precision Negative Multiply-Add +PPC_efsnmsub, // Floating-Point Single-Precision Negative Multiply-Subtract +PPC_efscfh, // Convert Floating-Point Single-Precision from Half-Precision +PPC_efscth, // Convert Floating-Point Single-Precision to Half-Precision + +// Volatile Context Save/Restore APU +PPC_lmvgprw, // Load Multiple Volatile GPR Word +PPC_stmvgprw, // Store Multiple Volatile GPR Word +PPC_lmvsprw, // Load Multiple Volatile SPR Word +PPC_stmvsprw, // Store Multiple Volatile SPR Word +PPC_lmvsrrw, // Load Multiple Volatile SRR Word +PPC_stmvsrrw, // Store Multiple Volatile SRR Word +PPC_lmvcsrrw, // Load Multiple Volatile CSRR Word +PPC_stmvcsrrw, // Store Multiple Volatile CSRR Word +PPC_lmvdsrrw, // Load Multiple Volatile DSRR Word +PPC_stmvdsrrw, // Store Multiple Volatile DSRR Word +PPC_lmvmcsrrw, // Load Multiple Volatile MCSRR Word +PPC_stmvmcsrrw, // Store Multiple Volatile MCSRR Word + +// SPE2 (Signal Processing Engine 2) instructions +PPC_evdotpwcssi, // Vector Dot Product of Words, Complex, Signed, Saturate, Integer (to Accumulator) +PPC_evdotpwcsmi, // Vector Dot Product of Words, Complex, Signed, Modulo, Integer (to Accumulator) +PPC_evdotpwcssfr, // Vector Dot Product of Words, Complex, Signed, Saturate, Fractional, Round (to Accumulator) +PPC_evdotpwcssf, // Vector Dot Product of Words, Complex, Signed, Saturate, Fractional (to Accumulator) +PPC_evdotpwgasmf, // Vector Dot Product of Words Guarded, Add, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotpwxgasmf, // Vector Dot Product of Words Exchanged, Guarded, Add, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotpwgasmfr, // Vector Dot Product of Words Guarded, Add, Signed, Modulo, Fractional, Round (to Accumulator) +PPC_evdotpwxgasmfr, // Vector Dot Product of Words Exchanged, Guarded, Add, Signed, Modulo, Fractional, Round (to Accumulator) +PPC_evdotpwgssmf, // Vector Dot Product of Words Guarded, Subtract, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotpwxgssmf, // Vector Dot Product of Words Exchanged, Guarded, Subtract, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotpwgssmfr, // Vector Dot Product of Words Guarded, Subtract, Signed, Modulo, Fractional, Round (to Accumulator) +PPC_evdotpwxgssmfr, // Vector Dot Product of Words Exchanged, Guarded, Add, Signed, Modulo, Fractional, Round (to Accumulator) +PPC_evdotpwcssiaaw3, // Vector Dot Product of Words, Complex, Signed, Saturate, Integer and Accumulate into Words 3 operand +PPC_evdotpwcsmiaaw3, // Vector Dot Product of Words, Complex, Signed, Modulo, Integer and Accumulate into Words 3 operand +PPC_evdotpwcssfraaw3, // Vector Dot Product of Words, Complex, Signed, Saturate, Fractional, Round and Accumulate into Words 3 operand +PPC_evdotpwcssfaaw3, // Vector Dot Product of Words, Complex, Signed, Saturate, Fractional and Accumulate into Words 3 operand +PPC_evdotpwgasmfaa3, // Vector Dot Product of Words Guarded, Add, Signed, Modulo, Fractional and Accumulate 3 operand +PPC_evdotpwxgasmfaa3, // Vector Dot Product of Words Exchanged, Guarded, Add, Signed, Modulo, Fractional and Accumulate 3 operand +PPC_evdotpwgasmfraa3, // Vector Dot Product of Words Guarded, Add, Signed, Modulo, Fractional, Round and Accumulate 3 operand +PPC_evdotpwxgasmfraa3, // Vector Dot Product of Words Exchanged, Guarded, Add, Signed, Modulo, Fractional, Round and Accumulate 3 operand +PPC_evdotpwgssmfaa3, // Vector Multiply Word Complex, Real, Guarded, Signed, Modulo, Fractional and Accumulate 3 operand +PPC_evdotpwxgssmfaa3, // Vector Dot Product of Words Exchanged, Guarded, Subtract, Signed, Modulo, Fractional and Accumulate 3 operand +PPC_evdotpwgssmfraa3, // Vector Dot Product of Words Guarded, Subtract, Signed, Modulo, Fractional, Round and Accumulate 3 operand +PPC_evdotpwxgssmfraa3, // Vector Dot Product of Words Exchanged, Guarded, Subtract, Signed, Modulo, Fractional, Round and Accumulate 3 operand +PPC_evdotpwcssia, // Vector Dot Product of Words, Complex, Signed, Saturate, Integer (to Accumulator) +PPC_evdotpwcsmia, // Vector Dot Product of Words, Complex, Signed, Modulo, Integer (to Accumulator) +PPC_evdotpwcssfra, // Vector Dot Product of Words, Complex, Signed, Saturate, Fractional, Round (to Accumulator) +PPC_evdotpwcssfa, // Vector Dot Product of Words, Complex, Signed, Saturate, Fractional (to Accumulator) +PPC_evdotpwgasmfa, // Vector Dot Product of Words Guarded, Add, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotpwxgasmfa, // Vector Dot Product of Words Exchanged, Guarded, Add, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotpwgasmfra, // Vector Dot Product of Words Guarded, Add, Signed, Modulo, Fractional, Round (to Accumulator) +PPC_evdotpwxgasmfra, // Vector Dot Product of Words Exchanged, Guarded, Add, Signed, Modulo, Fractional, Round (to Accumulator) +PPC_evdotpwgssmfa, // Vector Dot Product of Words Guarded, Subtract, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotpwxgssmfa, // Vector Dot Product of Words Exchanged, Guarded, Subtract, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotpwgssmfra, // Vector Dot Product of Words Guarded, Subtract, Signed, Modulo, Fractional, Round (to Accumulator) +PPC_evdotpwxgssmfra, // Vector Dot Product of Words Exchanged, Guarded, Add, Signed, Modulo, Fractional, Round (to Accumulator) +PPC_evdotpwcssiaaw, // Vector Dot Product of Words, Complex, Signed, Saturate, Integer and Accumulate into Words +PPC_evdotpwcsmiaaw, // Vector Dot Product of Words, Complex, Signed, Modulo, Integer and Accumulate into Words +PPC_evdotpwcssfraaw, // Vector Dot Product of Words, Complex, Signed, Saturate, Fractional, Round and Accumulate into Words +PPC_evdotpwcssfaaw, // Vector Dot Product of Words, Complex, Signed, Saturate, Fractional and Accumulate into Words +PPC_evdotpwgasmfaa, // Vector Dot Product of Words Guarded, Add, Signed, Modulo, Fractional and Accumulate +PPC_evdotpwxgasmfaa, // Vector Dot Product of Words Exchanged, Guarded, Add, Signed, Modulo, Fractional and Accumulate +PPC_evdotpwgasmfraa, // Vector Dot Product of Words Guarded, Add, Signed, Modulo, Fractional, Round and Accumulate +PPC_evdotpwxgasmfraa, // Vector Dot Product of Words Exchanged, Guarded, Add, Signed, Modulo, Fractional, Round and Accumulate +PPC_evdotpwgssmfaa, // Vector Multiply Word Complex, Real, Guarded, Signed, Modulo, Fractional and Accumulate +PPC_evdotpwxgssmfaa, // Vector Dot Product of Words Exchanged, Guarded, Subtract, Signed, Modulo, Fractional and Accumulate +PPC_evdotpwgssmfraa, // Vector Dot Product of Words Guarded, Subtract, Signed, Modulo, Fractional, Round and Accumulate +PPC_evdotpwxgssmfraa, // Vector Dot Product of Words Exchanged, Guarded, Subtract, Signed, Modulo, Fractional, Round and Accumulate +PPC_evdotphihcssi, // Vector Dot Product of High Halfwords, Complex, Signed, Saturate, Integer (to Accumulator) +PPC_evdotplohcssi, // Vector Dot Product of Low Halfwords, Complex, Signed, Saturate, Integer (to Accumulator) +PPC_evdotphihcssf, // Vector Dot Product of High Halfwords, Complex, Signed, Saturate, Fractional (to Accumulator) +PPC_evdotplohcssf, // Vector Dot Product of Low Halfwords, Complex, Signed, Saturate, Fractional (to Accumulator) +PPC_evdotphihcsmi, // Vector Dot Product of High Halfwords, Complex, Signed, Modulo, Integer (to Accumulator) +PPC_evdotplohcsmi, // Vector Dot Product of Low Halfwords, Complex, Signed, Modulo, Integer (to Accumulator) +PPC_evdotphihcssfr, // Vector Dot Product of High Halfwords, Complex, Signed, Saturate, Fractional, Round (to Accumulator) +PPC_evdotplohcssfr, // Vector Dot Product of Low Halfwords, Complex, Signed, Saturate, Fractional, Round (to Accumulator) +PPC_evdotphihcssiaaw3, // Vector Dot Product of High Halfwords, Complex, Signed, Saturate, Integer and Accumulate into Words, 3 operand +PPC_evdotplohcssiaaw3, // Vector Dot Product of Low Halfwords, Complex, Signed, Saturate, Integer and Accumulate into Words, 3 operand +PPC_evdotphihcssfaaw3, // Vector Dot Product of High Halfwords, Complex, Signed, Saturate, Fractional and Accumulate into Words, 3 operand +PPC_evdotplohcssfaaw3, // Vector Dot Product of Low Halfwords, Complex, Signed, Saturate, Fractional and Accumulate into Words, 3 operand +PPC_evdotphihcsmiaaw3, // Vector Dot Product of High Halfwords, Complex, Signed, Modulo, Integer and Accumulate into Words, 3 operand +PPC_evdotplohcsmiaaw3, // Vector Dot Product of Low Halfwords, Complex, Signed, Modulo, Integer and Accumulate into Words, 3 operand +PPC_evdotphihcssfraaw3, // Vector Dot Product of High Halfwords, Complex, Signed, Saturate, Fractional, Round and Accumulate into Words, 3 operand +PPC_evdotplohcssfraaw3, // Vector Dot Product of Low Halfwords, Complex, Signed, Saturate, Fractional, Round and Accumulate into Words, 3 operand +PPC_evdotphihcssia, // Vector Dot Product of High Halfwords, Complex, Signed, Saturate, Integer (to Accumulator) +PPC_evdotplohcssia, // Vector Dot Product of Low Halfwords, Complex, Signed, Saturate, Integer (to Accumulator) +PPC_evdotphihcssfa, // Vector Dot Product of High Halfwords, Complex, Signed, Saturate, Fractional (to Accumulator) +PPC_evdotplohcssfa, // Vector Dot Product of Low Halfwords, Complex, Signed, Saturate, Fractional (to Accumulator) +PPC_evdotphihcsmia, // Vector Dot Product of High Halfwords, Complex, Signed, Modulo, Integer (to Accumulator) +PPC_evdotplohcsmia, // Vector Dot Product of Low Halfwords, Complex, Signed, Modulo, Integer (to Accumulator) +PPC_evdotphihcssfra, // Vector Dot Product of High Halfwords, Complex, Signed, Saturate, Fractional, Round (to Accumulator) +PPC_evdotplohcssfra, // Vector Dot Product of Low Halfwords, Complex, Signed, Saturate, Fractional, Round (to Accumulator) +PPC_evdotphihcssiaaw, // Vector Dot Product of High Halfwords, Complex, Signed, Saturate, Integer and Accumulate into Words +PPC_evdotplohcssiaaw, // Vector Dot Product of Low Halfwords, Complex, Signed, Saturate, Integer and Accumulate into Words +PPC_evdotphihcssfaaw, // Vector Dot Product of High Halfwords, Complex, Signed, Saturate, Fractional and Accumulate into Words +PPC_evdotplohcssfaaw, // Vector Dot Product of Low Halfwords, Complex, Signed, Saturate, Fractional and Accumulate into Words +PPC_evdotphihcsmiaaw, // Vector Dot Product of High Halfwords, Complex, Signed, Modulo, Integer and Accumulate into Words +PPC_evdotplohcsmiaaw, // Vector Dot Product of Low Halfwords, Complex, Signed, Modulo, Integer and Accumulate into Words +PPC_evdotphihcssfraaw, // Vector Dot Product of High Halfwords, Complex, Signed, Saturate, Fractional, Round and Accumulate into Words +PPC_evdotplohcssfraaw, // Vector Dot Product of Low Halfwords, Complex, Signed, Saturate, Fractional, Round and Accumulate into Words +PPC_evdotphausi, // Vector Dot Product of Halfwords, Add, Unsigned, Saturate, Integer (to Accumulator) +PPC_evdotphassi, // Vector Dot Product of Halfwords, Add, Signed, Saturate, Integer (to Accumulator) +PPC_evdotphasusi, // Vector Dot Product of Halfwords, Add, Signed by Unsigned, Saturate, Integer (to Accumulator) +PPC_evdotphassf, // Vector Dot Product of Halfwords, Add, Signed, Saturate, Fractional (to Accumulator) +PPC_evdotphsssf, // Vector Dot Product of Halfwords, Subtract, Signed, Saturate, Fractional (to Accumulator) +PPC_evdotphaumi, // Vector Dot Product of Halfwords, Add, Unsigned, Modulo, Integer (to Accumulator) +PPC_evdotphasmi, // Vector Dot Product of Halfwords, Add, Signed, Modulo, Integer (to Accumulator) +PPC_evdotphasumi, // Vector Dot Product of Halfwords, Add, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evdotphassfr, // Vector Dot Product of Halfwords, Add, Signed, Saturate, Fractional, Round (to Accumulator) +PPC_evdotphssmi, // Vector Dot Product of Halfwords, Subtract, Signed, Modulo, Integer (to Accumulator) +PPC_evdotphsssfr, // Vector Dot Product of Halfwords, Subtract, Signed, Saturate, Fractional, Round (to Accumulator) +PPC_evdotphausiaaw3, // Vector Dot Product of Halfwords, Add, Unsigned, Saturate, Integer and Accumulate into Words, 3 operand +PPC_evdotphassiaaw3, // Vector Dot Product of Halfwords, Add, Signed, Saturate, Integer and Accumulate into Words, 3 operand +PPC_evdotphasusiaaw3, // Vector Dot Product of Halfwords, Add, Signed by Unsigned, Saturate, Integer and Accumulate into Words, 3 operand +PPC_evdotphassfaaw3, // Vector Dot Product of Halfwords, Add, Signed, Saturate, Fractional and Accumulate into Words, 3 operand +PPC_evdotphsssiaaw3, // Vector Dot Product of Halfwords, Subtract, Signed, Saturate, Integer and Accumulate into Words, 3 operand +PPC_evdotphsssfaaw3, // Vector Dot Product of Halfwords, Subtract, Signed, Saturate, Fractional and Accumulate into Words, 3 operand +PPC_evdotphaumiaaw3, // Vector Dot Product of Halfwords, Add, Unsigned, Modulo, Integer and Accumulate into Words, 3 operand +PPC_evdotphasmiaaw3, // Vector Dot Product of Halfwords, Add, Signed, Modulo, Integer and Accumulate into Words, 3 operand +PPC_evdotphasumiaaw3, // Vector Dot Product of Halfwords, Add, Signed by Unsigned, Modulo, Integer and Accumulate into Words, 3 operand +PPC_evdotphassfraaw3, // Vector Dot Product of Halfwords, Add, Signed, Saturate, Fractional, Round and Accumulate into Words, 3 operand +PPC_evdotphssmiaaw3, // Vector Dot Product of Halfwords, Subtract, Signed, Modulo, Integer and Accumulate into Words, 3 operand +PPC_evdotphsssfraaw3, // Vector Dot Product of Halfwords, Subtract, Signed, Saturate, Fractional, Round and Accumulate into Words, 3 operand +PPC_evdotphausia, // Vector Dot Product of Halfwords, Add, Unsigned, Saturate, Integer (to Accumulator) +PPC_evdotphassia, // Vector Dot Product of Halfwords, Add, Signed, Saturate, Integer (to Accumulator) +PPC_evdotphasusia, // Vector Dot Product of Halfwords, Add, Signed by Unsigned, Saturate, Integer (to Accumulator) +PPC_evdotphassfa, // Vector Dot Product of Halfwords, Add, Signed, Saturate, Fractional (to Accumulator) +PPC_evdotphsssfa, // Vector Dot Product of Halfwords, Subtract, Signed, Saturate, Fractional (to Accumulator) +PPC_evdotphaumia, // Vector Dot Product of Halfwords, Add, Unsigned, Modulo, Integer (to Accumulator) +PPC_evdotphasmia, // Vector Dot Product of Halfwords, Add, Signed, Modulo, Integer (to Accumulator) +PPC_evdotphasumia, // Vector Dot Product of Halfwords, Add, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evdotphassfra, // Vector Dot Product of Halfwords, Add, Signed, Saturate, Fractional, Round (to Accumulator) +PPC_evdotphssmia, // Vector Dot Product of Halfwords, Subtract, Signed, Modulo, Integer (to Accumulator) +PPC_evdotphsssfra, // Vector Dot Product of Halfwords, Subtract, Signed, Saturate, Fractional, Round (to Accumulator) +PPC_evdotphausiaaw, // Vector Dot Product of Halfwords, Add, Unsigned, Saturate, Integer and Accumulate into Words +PPC_evdotphassiaaw, // Vector Dot Product of Halfwords, Add, Signed, Saturate, Integer and Accumulate into Words +PPC_evdotphasusiaaw, // Vector Dot Product of Halfwords, Add, Signed by Unsigned, Saturate, Integer and Accumulate into Words +PPC_evdotphassfaaw, // Vector Dot Product of Halfwords, Add, Signed, Saturate, Fractional and Accumulate into Words +PPC_evdotphsssiaaw, // Vector Dot Product of Halfwords, Subtract, Signed, Saturate, Integer and Accumulate into Words +PPC_evdotphsssfaaw, // Vector Dot Product of Halfwords, Subtract, Signed, Saturate, Fractional and Accumulate into Words +PPC_evdotphaumiaaw, // Vector Dot Product of Halfwords, Add, Unsigned, Modulo, Integer and Accumulate into Words +PPC_evdotphasmiaaw, // Vector Dot Product of Halfwords, Add, Signed, Modulo, Integer and Accumulate into Words +PPC_evdotphasumiaaw, // Vector Dot Product of Halfwords, Add, Signed by Unsigned, Modulo, Integer and Accumulate into Words +PPC_evdotphassfraaw, // Vector Dot Product of Halfwords, Add, Signed, Saturate, Fractional, Round and Accumulate into Words +PPC_evdotphssmiaaw, // Vector Dot Product of Halfwords, Subtract, Signed, Modulo, Integer and Accumulate into Words +PPC_evdotphsssfraaw, // Vector Dot Product of Halfwords, Subtract, Signed, Saturate, Fractional, Round and Accumulate into Words +PPC_evdotp4hgaumi, // Vector Dot Product of Four Halfwords, Guarded, Add, Unsigned, Modulo, Integer (to Accumulator) +PPC_evdotp4hgasmi, // Vector Dot Product of Four Halfwords, Guarded, Add, Signed, Modulo, Integer (to Accumulator) +PPC_evdotp4hgasumi, // Vector Dot Product of Four Halfwords, Guarded, Add, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evdotp4hgasmf, // Vector Dot Product of Four Halfwords, Guarded, Add, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotp4hgssmi, // Vector Dot Product of Four Halfwords, Guarded, Subtract, Signed, Modulo, Integer (to Accumulator) +PPC_evdotp4hgssmf, // Vector Dot Product of Four Halfwords, Guarded, Subtract, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotp4hxgasmi, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Add, Signed, Modulo, Integer (to Accumulator) +PPC_evdotp4hxgasmf, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Add, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotpbaumi, // Vector Dot Product of Bytes, Add, Unsigned, Modulo, Integer (to Accumulator) +PPC_evdotpbasmi, // Vector Dot Product of Bytes, Add, Signed, Modulo, Integer (to Accumulator) +PPC_evdotpbasumi, // Vector Dot Product of Bytes, Add, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evdotp4hxgssmi, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Subtract, Signed, Modulo, Integer (to Accumulator) +PPC_evdotp4hxgssmf, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Subtract, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotp4hgaumiaa3, // Vector Dot Product of Four Halfwords, Guarded, Add, Unsigned, Modulo, Integer and Accumulate, 3 operand +PPC_evdotp4hgasmiaa3, // Vector Dot Product of Four Halfwords, Guarded, Add, Signed, Modulo, Integer and Accumulate, 3 operand +PPC_evdotp4hgasumiaa3, // Vector Dot Product of Four Halfwords, Guarded, Add, Signed by Unsigned, Modulo, Integer and Accumulate, 3 operand +PPC_evdotp4hgasmfaa3, // Vector Dot Product of Four Halfwords, Guarded, Add, Signed, Modulo, Fractional and Accumulate, 3 operand +PPC_evdotp4hgssmiaa3, // Vector Dot Product of Four Halfwords, Guarded, Subtract, Signed, Modulo, Integer and Accumulate 3 operand +PPC_evdotp4hgssmfaa3, // Vector Dot Product of Four Halfwords, Guarded, Subtract, Signed, Modulo, Fractional and Accumulate 3 operand +PPC_evdotp4hxgasmiaa3, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Add, Signed, Modulo, Integer and Accumulate 3 operand +PPC_evdotp4hxgasmfaa3, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Add, Signed, Modulo, Fractional and Accumulate 3 operand +PPC_evdotpbaumiaaw3, // Vector Dot Product of Bytes, Add, Unsigned, Modulo, Integer and Accumulate into Words, 3 operand +PPC_evdotpbasmiaaw3, // Vector Dot Product of Bytes, Add, Signed, Modulo, Integer and Accumulate into Words, 3 operand +PPC_evdotpbasumiaaw3, // Vector Dot Product of Bytes, Add, Signed by Unsigned, Modulo, Integer and Accumulate into Words, 3 operand +PPC_evdotp4hxgssmiaa3, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Subtract, Signed, Modulo, Integer and Accumulate 3 operand +PPC_evdotp4hxgssmfaa3, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Subtract, Signed, Modulo, Fractional and Accumulate 3 operand +PPC_evdotp4hgaumia, // Vector Dot Product of Four Halfwords, Guarded, Add, Unsigned, Modulo, Integer (to Accumulator) +PPC_evdotp4hgasmia, // Vector Dot Product of Four Halfwords, Guarded, Add, Signed, Modulo, Integer (to Accumulator) +PPC_evdotp4hgasumia, // Vector Dot Product of Four Halfwords, Guarded, Add, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evdotp4hgasmfa, // Vector Dot Product of Four Halfwords, Guarded, Add, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotp4hgssmia, // Vector Dot Product of Four Halfwords, Guarded, Subtract, Signed, Modulo, Integer (to Accumulator) +PPC_evdotp4hgssmfa, // Vector Dot Product of Four Halfwords, Guarded, Subtract, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotp4hxgasmia, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Add, Signed, Modulo, Integer (to Accumulator) +PPC_evdotp4hxgasmfa, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Add, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotpbaumia, // Vector Dot Product of Bytes, Add, Unsigned, Modulo, Integer (to Accumulator) +PPC_evdotpbasmia, // Vector Dot Product of Bytes, Add, Signed, Modulo, Integer (to Accumulator) +PPC_evdotpbasumia, // Vector Dot Product of Bytes, Add, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evdotp4hxgssmia, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Subtract, Signed, Modulo, Integer (to Accumulator) +PPC_evdotp4hxgssmfa, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Subtract, Signed, Modulo, Fractional (to Accumulator) +PPC_evdotp4hgaumiaa, // Vector Dot Product of Four Halfwords, Guarded, Add, Unsigned, Modulo, Integer and Accumulate +PPC_evdotp4hgasmiaa, // Vector Dot Product of Four Halfwords, Guarded, Add, Signed, Modulo, Integer and Accumulate +PPC_evdotp4hgasumiaa, // Vector Dot Product of Four Halfwords, Guarded, Add, Signed by Unsigned, Modulo, Integer and Accumulate +PPC_evdotp4hgasmfaa, // Vector Dot Product of Four Halfwords, Guarded, Add, Signed, Modulo, Fractional and Accumulate +PPC_evdotp4hgssmiaa, // Vector Dot Product of Four Halfwords, Guarded, Subtract, Signed, Modulo, Integer and Accumulate +PPC_evdotp4hgssmfaa, // Vector Dot Product of Four Halfwords, Guarded, Subtract, Signed, Modulo, Fractional and Accumulate +PPC_evdotp4hxgasmiaa, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Add, Signed, Modulo, Integer and Accumulate +PPC_evdotp4hxgasmfaa, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Add, Signed, Modulo, Fractional and Accumulate +PPC_evdotpbaumiaaw, // Vector Dot Product of Bytes, Add, Unsigned, Modulo, Integer and Accumulate into Words +PPC_evdotpbasmiaaw, // Vector Dot Product of Bytes, Add, Signed, Modulo, Integer and Accumulate into Words +PPC_evdotpbasumiaaw, // Vector Dot Product of Bytes, Add, Signed by Unsigned, Modulo, Integer and Accumulate into Words +PPC_evdotp4hxgssmiaa, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Subtract, Signed, Modulo, Integer and Accumulate +PPC_evdotp4hxgssmfaa, // Vector Dot Product of Four Halfwords Exchanged, Guarded, Subtract, Signed, Modulo, Fractional and Accumulate +PPC_evdotpwausi, // Vector Dot Product of Words, Add, Unsigned, Saturate, integer (to Accumulator) +PPC_evdotpwassi, // Vector Dot Product of Words, Add, Signed, Saturate, integer (to Accumulator) +PPC_evdotpwasusi, // Vector Dot Product of Words, Add, Signed by Unsigned, Saturate, integer (to Accumulator) +PPC_evdotpwaumi, // Vector Dot Product of Words, Add, Unsigned, Modulo, integer (to Accumulator) +PPC_evdotpwasmi, // Vector Dot Product of Words, Add, Signed, Modulo, integer (to Accumulator) +PPC_evdotpwasumi, // Vector Dot Product of Words, Add, Signed by Unsigned, Modulo, integer (to Accumulator) +PPC_evdotpwssmi, // Vector Dot Product of Words, Subtract, Signed, Modulo, integer (to Accumulator) +PPC_evdotpwausiaa3, // Vector Dot Product of Words, Add, Unsigned, Saturate, Integer and Accumulate, 3 operand +PPC_evdotpwassiaa3, // Vector Dot Product of Words, Add, Signed, Saturate, Integer and Accumulate, 3 operand +PPC_evdotpwasusiaa3, // Vector Dot Product of Words, Add, Signed by Unsigned, Saturate, Integer and Accumulate, 3 operand +PPC_evdotpwsssiaa3, // Vector Dot Product of Words, Subtract, Signed, Saturate, Integer and Accumulate, 3 operand +PPC_evdotpwaumiaa3, // Vector Dot Product of Words, Add, Unsigned, Modulo, Integer and Accumulate, 3 operand +PPC_evdotpwasmiaa3, // Vector Dot Product of Words, Add, Signed, Modulo, Integer and Accumulate, 3 operand +PPC_evdotpwasumiaa3, // Vector Dot Product of Words, Add, Signed by Unsigned, Modulo, Integer and Accumulate, 3 operand +PPC_evdotpwssmiaa3, // Vector Dot Product of Words, Subtract, Signed, Modulo, Integer and Accumulate, 3 operand +PPC_evdotpwausia, // Vector Dot Product of Words, Add, Unsigned, Saturate, integer (to Accumulator) +PPC_evdotpwassia, // Vector Dot Product of Words, Add, Signed, Saturate, integer (to Accumulator) +PPC_evdotpwasusia, // Vector Dot Product of Words, Add, Signed by Unsigned, Saturate, integer (to Accumulator) +PPC_evdotpwaumia, // Vector Dot Product of Words, Add, Unsigned, Modulo, integer (to Accumulator) +PPC_evdotpwasmia, // Vector Dot Product of Words, Add, Signed, Modulo, integer (to Accumulator) +PPC_evdotpwasumia, // Vector Dot Product of Words, Add, Signed by Unsigned, Modulo, integer (to Accumulator) +PPC_evdotpwssmia, // Vector Dot Product of Words, Subtract, Signed, Modulo, integer (to Accumulator) +PPC_evdotpwausiaa, // Vector Dot Product of Words, Add, Unsigned, Saturate, Integer and Accumulate +PPC_evdotpwassiaa, // Vector Dot Product of Words, Add, Signed, Saturate, Integer and Accumulate +PPC_evdotpwasusiaa, // Vector Dot Product of Words, Add, Signed by Unsigned, Saturate, Integer and Accumulate +PPC_evdotpwsssiaa, // Vector Dot Product of Words, Subtract, Signed, Saturate, Integer and Accumulate +PPC_evdotpwaumiaa, // Vector Dot Product of Words, Add, Unsigned, Modulo, Integer and Accumulate +PPC_evdotpwasmiaa, // Vector Dot Product of Words, Add, Signed, Modulo, Integer and Accumulate +PPC_evdotpwasumiaa, // Vector Dot Product of Words, Add, Signed by Unsigned, Modulo, Integer and Accumulate +PPC_evdotpwssmiaa, // Vector Dot Product of Words, Subtract, Signed, Modulo, Integer and Accumulate +PPC_evaddih, // Vector Add Immediate Halfword +PPC_evaddib, // Vector Add Immediate Byte +PPC_evsubifh, // Vector Subtract Immediate from Halfword +PPC_evsubifb, // Vector Subtract Immediate from Byte +PPC_evabsb, // Vector Absolute Value Byte +PPC_evabsh, // Vector Absolute Value Halfword +PPC_evabsd, // Vector Absolute Value Doubleword +PPC_evabss, // Vector Absolute Value (Word) and Saturate +PPC_evabsbs, // Vector Absolute Value Byte and Saturate +PPC_evabshs, // Vector Absolute Value Halfword and Saturate +PPC_evabsds, // Vector Absolute Value Doubleword and Saturate +PPC_evnegwo, // Vector Negate Word Odd +PPC_evnegb, // Vector Negate Byte +PPC_evnegbo, // Vector Negate Byte Odd +PPC_evnegh, // Vector Negate Halfword +PPC_evnegho, // Vector Negate Halfwords Odd +PPC_evnegd, // Vector Negate Doubleword +PPC_evnegs, // Vector Negate (Word) and Saturate +PPC_evnegwos, // Vector Negate Word Odd and Saturate +PPC_evnegbs, // Vector Negate Byte and Saturate +PPC_evnegbos, // Vector Negate Byte Odd and Saturate +PPC_evneghs, // Vector Negate Halfword and Saturate +PPC_evneghos, // Vector Negate Halfwords Odd and Saturate +PPC_evnegds, // Vector Negate Doubleword and Saturate +PPC_evextzb, // Vector Extend Zero Byte +PPC_evextsbh, // Vector Extend Sign of Bytes (Odd) to Halfwords +PPC_evextsw, // Vector Extend Sign Word +PPC_evrndhb, // Vector Round Halfword to Byte +PPC_evrnddw, // Vector Round Doubleword to Word +PPC_evrndwhus, // Vector Round Word to Halfword Unsigned and Saturate +PPC_evrndwhss, // Vector Round Word to Halfword Signed and Saturate +PPC_evrndhbus, // Vector Round Halfword to Byte Unsigned and Saturate +PPC_evrndhbss, // Vector Round Halfword to Byte Signed and Saturate +PPC_evrnddwus, // Vector Round Doubleword to Word Unsigned and Saturate +PPC_evrnddwss, // Vector Round Doubleword to Word Signed and Saturate +PPC_evrndwnh, // Vector Round Word to Nearest Even Halfword +PPC_evrndhnb, // Vector Round Halfword to Nearest Even Byte +PPC_evrnddnw, // Vector Round Doubleword to Nearest Even Word +PPC_evrndwnhus, // Vector Round Word to Nearest Even Halfword Unsigned and Saturate +PPC_evrndwnhss, // Vector Round Word to Nearest Even Halfword Signed and Saturate +PPC_evrndhnbus, // Vector Round Halfword to Nearest Byte Unsigned and Saturate +PPC_evrndhnbss, // Vector Round Halfword to Nearest Even Byte Signed and Saturate +PPC_evrnddnwus, // Vector Round Doubleword to Nearest Even Word Unsigned and Saturate +PPC_evrnddnwss, // Vector Round Doubleword to Nearest Even Word Signed and Saturate +PPC_evcntlzh, // Vector Count Leading Zeros Halfword +PPC_evcntlsh, // Vector Count Leading Signed Bits Halfword +PPC_evpopcntb, // Vector Population Count Byte +PPC_circinc, // Circular Increment +PPC_evunpkhibui, // Vector Unpack High Bytes as Unsigned Integers +PPC_evunpkhibsi, // Vector Unpack High Bytes as Signed Integers +PPC_evunpkhihui, // Vector Extract High Halfwords as Unsigned Integers +PPC_evunpkhihsi, // Vector Extract High Halfwords as Signed Integers +PPC_evunpklobui, // Vector Unpack Low Bytes Unsigned Integers +PPC_evunpklobsi, // Vector Unpack Low Bytes as Signed Integers +PPC_evunpklohui, // Vector Unpack Low Halfwords as Unsigned Integers +PPC_evunpklohsi, // Vector Unpack Low Halfwords as Signed Integers +PPC_evunpklohf, // Vector Unpack Low Halfwords as Fractional +PPC_evunpkhihf, // Vector Unpack High Halfwords as Fractional +PPC_evunpklowgsf, // Vector Unpack Low Word to Guarded Signed Fraction +PPC_evunpkhiwgsf, // Vector Unpack High Word to Guarded Signed Fraction +PPC_evsatsduw, // Vector Saturate Signed Doubleword to Unsigned Word Range +PPC_evsatsdsw, // Vector Saturate Signed Doubleword to Signed Word Range +PPC_evsatshub, // Vector Saturate Signed Halfwords to Unsigned Byte Range +PPC_evsatshsb, // Vector Saturate Signed Halfwords to Signed Byte Range +PPC_evsatuwuh, // Vector Saturate Unsigned Words to Unsigned Halfword Range +PPC_evsatswsh, // Vector Saturate Signed Words to Signed Halfword Range +PPC_evsatswuh, // Vector Pack Signed Words to Unsigned Halfword Range +PPC_evsatuhub, // Vector Saturate Unsigned Halfwords to Unsigned Byte Range +PPC_evsatuduw, // Vector Saturate Unsigned Doubleword to Unsigned Word Range +PPC_evsatuwsw, // Vector Saturate Unsigned Words to Signed Word Range +PPC_evsatshuh, // Vector Saturate Signed Halfwords to Unsigned Halfword Range +PPC_evsatuhsh, // Vector Saturate Unsigned Halfwords to Signed Halfword Range +PPC_evsatswuw, // Vector Pack Signed Words to Unsigned Word Range +PPC_evsatswgsdf, // Vector Saturate Signed Word Guarded to Signed Doubleword Fractional +PPC_evsatsbub, // Vector Saturate Signed Bytes to Unsigned Byte Range +PPC_evsatubsb, // Vector Saturate Unsigned Bytes to Signed Byte Range +PPC_evmaxhpuw, // Vector Maximum of Halfword Pairs Unsigned to Word +PPC_evmaxhpsw, // Vector Maximum of Halfword Pairs Signed to Word +PPC_evmaxbpuh, // Vector Maximum of Byte Pairs Unsigned to Halfword +PPC_evmaxbpsh, // Vector Maximum of Byte Pairs Signed to Halfword +PPC_evmaxwpud, // Vector Maximum of Word Pair Unsigned to Doubleword +PPC_evmaxwpsd, // Vector Maximum of Word Pair Signed to Doubleword +PPC_evminhpuw, // Vector Minimum of Halfword Pairs Unsigned to Word +PPC_evminhpsw, // Vector Minimum of Halfword Pairs Signed to Word +PPC_evminbpuh, // Vector Minimum of Byte Pairs Unsigned to Halfword +PPC_evminbpsh, // Vector Minimum of Byte Pairs Signed to Halfword +PPC_evminwpud, // Vector Minimum of Word Pair Unsigned to Doubleword +PPC_evminwpsd, // Vector Minimum of Word Pair Signed to Doubleword +PPC_evmaxmagws, // Vector Maximum Magnitude Word Signed +PPC_evsl, // Vector Shift Left +PPC_evsli, // Vector Shift Left Immediate +PPC_evsplatie, // Vector Splat Immediate Even (to Accumulator) +PPC_evsplatib, // Vector Splat Immediate Byte (to Accumulator) +PPC_evsplatibe, // Vector Splat Immediate Byte Even (to Accumulator) +PPC_evsplatih, // Vector Splat Immediate Halfword (to Accumulator) +PPC_evsplatihe, // Vector Splat Immediate Halfword Even (to Accumulator) +PPC_evsplatid, // Vector Splat Immediate Doubleword (to Accumulator) +PPC_evsplatia, // Vector Splat Immediate (to Accumulator) +PPC_evsplatiea, // Vector Splat Immediate Even (to Accumulator) +PPC_evsplatiba, // Vector Splat Immediate Byte (to Accumulator) +PPC_evsplatibea, // Vector Splat Immediate Byte Even (to Accumulator) +PPC_evsplatiha, // Vector Splat Immediate Halfword (to Accumulator) +PPC_evsplatihea, // Vector Splat Immediate Halfword Even (to Accumulator) +PPC_evsplatida, // Vector Splat Immediate Doubleword (to Accumulator) +PPC_evsplatfio, // Vector Splat Fractional Immediate Odd (to Accumulator) +PPC_evsplatfib, // Vector Splat Fractional Immediate Byte (to Accumulator) +PPC_evsplatfibo, // Vector Splat Fractional Immediate Byte Odd (to Accumulator) +PPC_evsplatfih, // Vector Splat Fractional Immediate Halfword (to Accumulator) +PPC_evsplatfiho, // Vector Splat Fractional Immediate Halfword Odd (to Accumulator) +PPC_evsplatfid, // Vector Splat Fractional Immediate Doubleword (to Accumulator) +PPC_evsplatfia, // Vector Splat Fractional Immediate (to Accumulator) +PPC_evsplatfioa, // Vector Splat Fractional Immediate Odd (to Accumulator) +PPC_evsplatfiba, // Vector Splat Fractional Immediate Byte (to Accumulator) +PPC_evsplatfiboa, // Vector Splat Fractional Immediate Byte Odd (to Accumulator) +PPC_evsplatfiha, // Vector Splat Fractional Immediate Halfword (to Accumulator) +PPC_evsplatfihoa, // Vector Splat Fractional Immediate Halfword Odd (to Accumulator) +PPC_evsplatfida, // Vector Splat Fractional Immediate Doubleword (to Accumulator) +PPC_evcmpgtdu, // Vector Compare Greater Than Doubleword Unsigned +PPC_evcmpgtds, // Vector Compare Greater Than Doubleword Signed +PPC_evcmpltdu, // Vector Compare Less Than Doubleword Unsigned +PPC_evcmpltds, // Vector Compare Less Than Doubleword Signed +PPC_evcmpeqd, // Vector Compare Equal Doubleword +PPC_evswapbhilo, // Vector Swap Bytes High/Low +PPC_evswapblohi, // Vector Swap Bytes Low/High +PPC_evswaphhilo, // Vector Swap Halfwords High/Low +PPC_evswaphlohi, // Vector Swap Halfwords Low/High +PPC_evswaphe, // Vector Swap Halfwords Even +PPC_evswaphhi, // Vector Swap Halfwords High +PPC_evswaphlo, // Vector Swap Halfwords Low +PPC_evswapho, // Vector Swap Halfwords Odd +PPC_evinsb, // Vector Insert Byte +PPC_evxtrb, // Vector Extract Byte +PPC_evsplath, // Vector Splat Halfword +PPC_evsplatb, // Vector Splat Byte +PPC_evinsh, // Vector Insert Halfword +PPC_evclrbe, // Vector Clear Bytes Even +PPC_evclrbo, // Vector Clear Bytes Odd +PPC_evxtrh, // Vector Extract Halfword +PPC_evclrh, // Vector Clear Halfwords +PPC_evselbitm0, // Vector Select Bit if Mask is 0 +PPC_evselbitm1, // Vector Select Bit if Mask is 1 +PPC_evselbit, // Vector Select Bit +PPC_evperm, // Vector Permute +PPC_evperm2, // Vector Permute (form 2) +PPC_evperm3, // Vector Permute (form 3) +PPC_evxtrd, // Vector Extract Doubleword +PPC_evsrbu, // Vector Shift Right Byte Unsigned +PPC_evsrbs, // Vector Shift Right Byte Signed +PPC_evsrbiu, // Vector Shift Right Byte Immediate Unsigned +PPC_evsrbis, // Vector Shift Right Byte Immediate Signed +PPC_evslb, // Vector Shift Left Byte +PPC_evrlb, // Vector Rotate Left Byte +PPC_evslbi, // Vector Shift Left Byte Immediate +PPC_evrlbi, // Vector Rotate Left Byte Immediate +PPC_evsrhu, // Vector Shift Right Halfword Unsigned +PPC_evsrhs, // Vector Shift Right Halfword Signed +PPC_evsrhiu, // Vector Shift Right Halfword Immediate Unsigned +PPC_evsrhis, // Vector Shift Right Halfword Immediate Signed +PPC_evslh, // Vector Shift Left Halfword +PPC_evrlh, // Vector Rotate Left Halfword +PPC_evslhi, // Vector Shift Left Halfword Immediate +PPC_evrlhi, // Vector Rotate Left Halfword Immediate +PPC_evsru, // Vector Shift Right Unsigned +PPC_evsrs, // Vector Shift Right Signed +PPC_evsriu, // Vector Shift Right Immediate Unsigned +PPC_evsris, // Vector Shift Right Immediate Signed +PPC_evlvsl, // Load Vector for Shift Left +PPC_evlvsr, // Load Vector for Shift Right +PPC_evsroiu, // Vector Shift Right by Octet Immediate Unsigned +PPC_evsloi, // Vector Shift Left by Octet Immediate +PPC_evsrois, // Vector Shift Right by Octet Immediate Signed +PPC_evldbx, // Vector Load Double into Eight Bytes [with Modify] Indexed +PPC_evldb, // Vector Load Double into Eight Bytes [with Update] +PPC_evlhhsplathx, // Vector Load Halfword into Halfword and Splat Halfwords [with Modify] Indexed +PPC_evlhhsplath, // Vector Load Halfword into Halfword and Splat Halfwords [with Update] +PPC_evlwbsplatwx, // Vector Load Word as Bytes and Splat Words [with Modify] Indexed +PPC_evlwbsplatw, // Vector Load Word as Bytes and Splat Words [with Update] +PPC_evlwhsplatwx, // Vector Load Word as Halfwords and Splat Words [with Modify] Indexed +PPC_evlwhsplatw, // Vector Load Word as Halfwords and Splat Words [with Update] +PPC_evlbbsplatbx, // Vector Load Byte into Byte and Splat Bytes [with Modify] Indexed +PPC_evlbbsplatb, // Vector Load Byte into Byte and Splat Bytes [with Update] +PPC_evstdbx, // Vector Store Double of Eight Bytes [with Modify] Indexed +PPC_evstdb, // Vector Store Double of Eight Bytes [with Update] +PPC_evlwbex, // Vector Load Word into Four Bytes Even [with Modify] Indexed +PPC_evlwbe, // Vector Load Word into Four Bytes Even [with Update] +PPC_evlwboux, // Vector Load Word into Four Bytes Odd Unsigned [with Modify] Indexed +PPC_evlwbou, // Vector Load Word into Four Bytes Odd Unsigned [with Update] +PPC_evlwbosx, // Vector Load Word into Two Halfwords Odd Signed [with Modify] Indexed +PPC_evlwbos, // Vector Load Word into Four Bytes Odd Signed [with Update] +PPC_evstwbex, // Vector Store Word of Four Bytes from Even [with Modify] Indexed +PPC_evstwbe, // Vector Store Word of Four Bytes from Even [with Update] +PPC_evstwbox, // Vector Store Word of Four Bytes from Odd [with Modify] Indexed +PPC_evstwbo, // Vector Store Word of Four Bytes from Odd [with Update] +PPC_evstwbx, // Vector Store Word of Four Bytes [with Modify] Indexed +PPC_evstwb, // Vector Store Word of Four Bytes [with Update] +PPC_evsthbx, // Vector Store Halfword of Two Bytes [with Modify] Indexed +PPC_evsthb, // Vector Store Halfword of Two Bytes [with Update] +PPC_evlddmx, // Vector Load Double Word into Double Word [with Modify] Indexed +PPC_evlddu, // Vector Load Double Word into Double Word [with Update] +PPC_evldwmx, // Vector Load Double into Two Words [with Modify] Indexed +PPC_evldwu, // Vector Load Double into Two Words [with Update] +PPC_evldhmx, // Vector Load Double into Four Halfwords [with Modify] Indexed +PPC_evldhu, // Vector Load Double into Four Halfwords [with Update] +PPC_evldbmx, // Vector Load Double into Eight Bytes [with Modify] Indexed +PPC_evldbu, // Vector Load Double into Eight Bytes [with Update] +PPC_evlhhesplatmx, // Vector Load Halfword into Halfwords Even and Splat [with Modify] Indexed +PPC_evlhhesplatu, // Vector Load Halfword into Halfwords Even and Splat [with Update] +PPC_evlhhsplathmx, // Vector Load Halfword into Halfword and Splat Halfwords [with Modify] Indexed +PPC_evlhhsplathu, // Vector Load Halfword into Halfword and Splat Halfwords [with Update] +PPC_evlhhousplatmx, // Vector Load Halfword into Halfword Odd Unsigned and Splat [with Modify] Indexed +PPC_evlhhousplatu, // Vector Load Halfword into Halfword Odd Unsigned and Splat [with Update] +PPC_evlhhossplatmx, // Vector Load Halfword into Halfword Odd Signed and Splat [with Modify] Indexed +PPC_evlhhossplatu, // Vector Load Halfword into Halfword Odd Signed and Splat [with Update] +PPC_evlwhemx, // Vector Load Word into Two Halfwords Even [with Modify] Indexed +PPC_evlwheu, // Vector Load Word into Two Halfwords Even [with Update] +PPC_evlwbsplatwmx, // Vector Load Word as Bytes and Splat Words [with Modify] Indexed +PPC_evlwbsplatwu, // Vector Load Word as Bytes and Splat Words [with Update] +PPC_evlwhoumx, // Vector Load Word into Two Halfwords Odd Unsigned [with Modify] Indexed (zero-extended) +PPC_evlwhouu, // Vector Load Word into Two Halfwords Odd Unsigned (zero-extended) [with Update] +PPC_evlwhosmx, // Vector Load Word into Two Halfwords Odd Signed [with Modify] Indexed (with sign extension) +PPC_evlwhosu, // Vector Load Word into Two Halfwords Odd Signed (with sign extension) [with Update] +PPC_evlwwsplatmx, // Vector Load Word into Word and Splat [with Modify] Indexed +PPC_evlwwsplatu, // Vector Load Word into Word and Splat [with Update] +PPC_evlwhsplatwmx, // Vector Load Word as Halfwords and Splat Words [with Modify] Indexed +PPC_evlwhsplatwu, // Vector Load Word as Halfwords and Splat Words [with Update] +PPC_evlwhsplatmx, // Vector Load Word into Two Halfwords and Splat [with Modify] Indexed +PPC_evlwhsplatu, // Vector Load Word into Two Halfwords and Splat [with Update] +PPC_evlbbsplatbmx, // Vector Load Byte into Byte and Splat Bytes [with Modify] Indexed +PPC_evlbbsplatbu, // Vector Load Byte into Byte and Splat Bytes [with Update] +PPC_evstddmx, // Vector Store Double of Double [with Modify] Indexed +PPC_evstddu, // Vector Store Double of Double [with Update] +PPC_evstdwmx, // Vector Store Double of Two Words [with Modify] Indexed +PPC_evstdwu, // Vector Store Double of Two Words [with Update] +PPC_evstdhmx, // Vector Store Double of Four Halfwords [with Modify] Indexed +PPC_evstdhu, // Vector Store Double of Four Halfwords [with Update] +PPC_evstdbmx, // Vector Store Double of Eight Bytes [with Modify] Indexed +PPC_evstdbu, // Vector Store Double of Eight Bytes [with Update] +PPC_evlwbemx, // Vector Load Word into Four Bytes Even [with Modify] Indexed +PPC_evlwbeu, // Vector Load Word into Four Bytes Even [with Update] +PPC_evlwboumx, // Vector Load Word into Four Bytes Odd Unsigned [with Modify] Indexed +PPC_evlwbouu, // Vector Load Word into Four Bytes Odd Unsigned [with Update] +PPC_evlwbosmx, // Vector Load Word into Two Halfwords Odd Signed [with Modify] Indexed +PPC_evlwbosu, // Vector Load Word into Four Bytes Odd Signed [with Update] +PPC_evstwhemx, // Vector Store Word of Two Halfwords from Even [with Modify] Indexed +PPC_evstwheu, // Vector Store Word of Two Halfwords from Even [with Update] +PPC_evstwbemx, // Vector Store Word of Four Bytes from Even [with Modify] Indexed +PPC_evstwbeu, // Vector Store Word of Four Bytes from Even [with Update] +PPC_evstwhomx, // Vector Store Word of Two Halfwords from Odd [with Modify] Indexed +PPC_evstwhou, // Vector Store Word of Two Halfwords from Odd [with Update] +PPC_evstwbomx, // Vector Store Word of Four Bytes from Odd [with Modify] Indexed +PPC_evstwbou, // Vector Store Word of Four Bytes from Odd [with Update] +PPC_evstwwemx, // Vector Store Word of Word from Even [with Modify] Indexed +PPC_evstwweu, // Vector Store Word of Word from Even [with Update] +PPC_evstwbmx, // Vector Store Word of Four Bytes [with Modify] Indexed +PPC_evstwbu, // Vector Store Word of Four Bytes [with Update] +PPC_evstwwomx, // Vector Store Word of Word from Odd [with Modify] Indexed +PPC_evstwwou, // Vector Store Word of Word from Odd [with Update] +PPC_evsthbmx, // Vector Store Halfword of Two Bytes [with Modify] Indexed +PPC_evsthbu, // Vector Store Halfword of Two Bytes [with Update] +PPC_evmhusi, // Vector Multiply Halfword Unsigned, Saturate, Integer +PPC_evmhssi, // Vector Multiply Halfword Signed, Saturate, Integer +PPC_evmhsusi, // Vector Multiply Halfword Signed by Unsigned, Saturate, Integer +PPC_evmhssf, // Vector Multiply Halfword Signed, Saturate, Fractional +PPC_evmhumi, // Vector Multiply Halfword Unsigned, Modulo, Integer +PPC_evmhssfr, // Vector Multiply Halfword Signed, Saturate, Fractional and Round +PPC_evmhesumi, // Vector Multiply Halfwords, Even, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evmhosumi, // Vector Multiply Halfwords, Odd, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evmbeumi, // Vector Multiply Bytes Even, Unsigned, Modulo, Integer (to Accumulator) +PPC_evmbesmi, // Vector Multiply Bytes Even, Signed, Modulo, Integer (to Accumulator) +PPC_evmbesumi, // Vector Multiply Bytes Even, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evmboumi, // Vector Multiply Bytes Odd, Unsigned, Modulo, Integer (to Accumulator) +PPC_evmbosmi, // Vector Multiply Bytes Odd, Signed, Modulo, Integer (to Accumulator) +PPC_evmbosumi, // Vector Multiply Bytes Odd, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evmhesumia, // Vector Multiply Halfwords, Even, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evmhosumia, // Vector Multiply Halfwords, Odd, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evmbeumia, // Vector Multiply Bytes Even, Unsigned, Modulo, Integer (to Accumulator) +PPC_evmbesmia, // Vector Multiply Bytes Even, Signed, Modulo, Integer (to Accumulator) +PPC_evmbesumia, // Vector Multiply Bytes Even, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evmboumia, // Vector Multiply Bytes Odd, Unsigned, Modulo, Integer (to Accumulator) +PPC_evmbosmia, // Vector Multiply Bytes Odd, Signed, Modulo, Integer (to Accumulator) +PPC_evmbosumia, // Vector Multiply Bytes Odd, Signed by Unsigned, Modulo, Integer (to Accumulator) +PPC_evmwusiw, // Vector Multiply Word Unsigned, Saturate, Integer Word +PPC_evmwssiw, // Vector Multiply Word Signed, Saturate, Integer Word +PPC_evmwhssfr, // Vector Multiply Word High Signed, Saturate, Fractional and Round (to Accumulator) +PPC_evmwehgsmfr, // Vector Multiply Word Even High Guarded Signed, Modulo, Fractional, Round (to Accumulator) +PPC_evmwehgsmf, // Vector Multiply Word Even High Guarded Signed, Modulo, Fractional (to Accumulator) +PPC_evmwohgsmfr, // Vector Multiply Word Odd, High, Guarded, Signed, Modulo, Fractional, Round (to Accumulator) +PPC_evmwohgsmf, // Vector Multiply Word Odd, High, Guarded, Signed, Modulo, Fractional (to Accumulator) +PPC_evmwhssfra, // Vector Multiply Word High Signed, Saturate, Fractional and Round (to Accumulator) +PPC_evmwehgsmfra, // Vector Multiply Word Even High Guarded Signed, Modulo, Fractional, Round (to Accumulator) +PPC_evmwehgsmfa, // Vector Multiply Word Even High Guarded Signed, Modulo, Fractional (to Accumulator) +PPC_evmwohgsmfra, // Vector Multiply Word Odd, High, Guarded, Signed, Modulo, Fractional, Round (to Accumulator) +PPC_evmwohgsmfa, // Vector Multiply Word Odd, High, Guarded, Signed, Modulo, Fractional (to Accumulator) +PPC_evaddusiaa, // Vector Add Unsigned, Saturate, Integer to Accumulator +PPC_evaddssiaa, // Vector Add Signed, Saturate, Integer to Accumulator +PPC_evsubfusiaa, // Vector Subtract from (Accumulator) Unsigned, Saturate, Integer to Accumulator +PPC_evsubfssiaa, // Vector Subtract from Signed, Saturate, Integer to Accumulator +PPC_evaddsmiaa, // Vector Add to Accumulator +PPC_evsubfsmiaa, // Vector Subtract from Accumulator to Accumulator +PPC_evaddh, // Vector Add Halfwords +PPC_evaddhss, // Vector Add Halfwords Signed and Saturate +PPC_evsubfh, // Vector Subtract from Halfword +PPC_evsubfhss, // Vector Subtract from Halfwords Signed and Saturate +PPC_evaddhx, // Vector Add Halfwords Exchanged +PPC_evaddhxss, // Vector Add Halfwords Exchanged, Signed and Saturate +PPC_evsubfhx, // Vector Subtract from Halfwords Exchanged +PPC_evsubfhxss, // Vector Subtract from Halfwords Exchanged, Signed and Saturate +PPC_evaddd, // Vector Add Doubleword +PPC_evadddss, // Vector Add Doubleword Signed and Saturate +PPC_evsubfd, // Vector Subtract from Doubleword +PPC_evsubfdss, // Vector Subtract from Doubleword Signed and Saturate +PPC_evaddb, // Vector Add Bytes +PPC_evaddbss, // Vector Add Bytes Signed and Saturate +PPC_evsubfb, // Vector Subtract from Byte +PPC_evsubfbss, // Vector Subtract from Byte Signed and Saturate +PPC_evaddsubfh, // Vector Add / Subtract from Halfword +PPC_evaddsubfhss, // Vector Add / Subtract from Halfword Signed and Saturate +PPC_evsubfaddh, // Vector Subtract from / Add Halfwords +PPC_evsubfaddhss, // Vector Subtract from / Add Halfwords Signed and Saturate +PPC_evaddsubfhx, // Vector Add / Subtract from Halfword Exchanged +PPC_evaddsubfhxss, // Vector Add / Subtract from Halfword Exchanged, Signed and Saturate +PPC_evsubfaddhx, // Vector Subtract from / Add Halfwords Exchanged +PPC_evsubfaddhxss, // Vector Subtract from / Add Halfwords Exchanged, Signed and Saturate +PPC_evadddus, // Vector Add Doubleword Unsigned and Saturate +PPC_evaddbus, // Vector Add Bytes Unsigned and Saturate +PPC_evsubfdus, // Vector Subtract from Doubleword Unsigned and Saturate +PPC_evsubfbus, // Vector Subtract from Byte Unsigned and Saturate +PPC_evaddwus, // Vector Add Word Unsigned and Saturate +PPC_evaddwxus, // Vector Add Word Exchanged Unsigned and Saturate +PPC_evsubfwus, // Vector Subtract from Word Unsigned and Saturate +PPC_evsubfwxus, // Vector Subtract from Word Exchanged Unsigned and Saturate +PPC_evadd2subf2h, // Vector Add Upper 2 and Subtract from Lower 2 Halfwords +PPC_evadd2subf2hss, // Vector Add Upper 2 and Subtract from Lower 2 Halfwords Signed and Saturate +PPC_evsubf2add2h, // Vector Subtract from Upper 2 / Add Lower 2 Halfwords +PPC_evsubf2add2hss, // Vector Subtract from Upper 2 / Add Lower 2 Halfwords Signed and Saturate +PPC_evaddhus, // Vector Add Halfwords Unsigned and Saturate +PPC_evaddhxus, // Vector Add Halfwords Exchanged, Unsigned and Saturate +PPC_evsubfhus, // Vector Subtract from Halfwords Unsigned and Saturate +PPC_evsubfhxus, // Vector Subtract from Halfwords Exchanged, Unsigned and Saturate +PPC_evaddwss, // Vector Add Word Signed and Saturate +PPC_evsubfwss, // Vector Subtract from Word Signed and Saturate +PPC_evaddwx, // Vector Add Word Exchanged +PPC_evaddwxss, // Vector Add Word Exchanged Signed and Saturate +PPC_evsubfwx, // Vector Subtract from Word Exchanged +PPC_evsubfwxss, // Vector Subtract from Word Exchanged Signed and Saturate +PPC_evaddsubfw, // Vector Add / Subtract from Word +PPC_evaddsubfwss, // Vector Add / Subtract from Word Signed and Saturate +PPC_evsubfaddw, // Vector Subtract from / Add Word +PPC_evsubfaddwss, // Vector Subtract from / Add Word Signed and Saturate +PPC_evaddsubfwx, // Vector Add / Subtract from Word Exchanged +PPC_evaddsubfwxss, // Vector Add / Subtract from Word Exchanged Signed and Saturate +PPC_evsubfaddwx, // Vector Subtract from / Add Word Exchanged +PPC_evsubfaddwxss, // Vector Subtract from / Add Word Exchanged Signed and Saturate +PPC_evmar, // Store Accumulator +PPC_evsumwu, // Vector Sum of Words Unsigned (to Accumulator) +PPC_evsumws, // Vector Sum of Words Signed (to Accumulator) +PPC_evsum4bu, // Vector Sum of 4 Bytes UnsIgned (to Accumulator) +PPC_evsum4bs, // Vector Sum of 4 Bytes SIgned (to Accumulator) +PPC_evsum2hu, // Vector Sum of 2 Halfwords Unsigned (to Accumulator) +PPC_evsum2hs, // Vector Sum of 2 Halfwords Signed (to Accumulator) +PPC_evdiff2his, // Vector Difference of 2 Halfwords Interleaved Signed (to Accumulator) +PPC_evsum2his, // Vector Sum of 2 Halfwords Interleaved Signed (to Accumulator) +PPC_evsumwua, // Vector Sum of Words Unsigned (to Accumulator) +PPC_evsumwsa, // Vector Sum of Words Signed (to Accumulator) +PPC_evsum4bua, // Vector Sum of 4 Bytes UnsIgned (to Accumulator) +PPC_evsum4bsa, // Vector Sum of 4 Bytes SIgned (to Accumulator) +PPC_evsum2hua, // Vector Sum of 2 Halfwords Unsigned (to Accumulator) +PPC_evsum2hsa, // Vector Sum of 2 Halfwords Signed (to Accumulator) +PPC_evdiff2hisa, // Vector Difference of 2 Halfwords Interleaved Signed (to Accumulator) +PPC_evsum2hisa, // Vector Sum of 2 Halfwords Interleaved Signed (to Accumulator) +PPC_evsumwuaa, // Vector Sum of Words Unsigned and Accumulate +PPC_evsumwsaa, // Vector Sum of Words Signed and Accumulate +PPC_evsum4buaaw, // Vector Sum of 4 Bytes Unsigned and Accumulate into Words +PPC_evsum4bsaaw, // Vector Sum of 4 Bytes Signed and Accumulate into Words +PPC_evsum2huaaw, // Vector Sum of 2 Halfwords Unsigned and Accumulate into Words +PPC_evsum2hsaaw, // Vector Sum of 2 Halfwords Signed and Accumulate into Words +PPC_evdiff2hisaaw, // Vector Difference of 2 Halfwords Interleaved Signed and Accumulate into Words +PPC_evsum2hisaaw, // Vector Sum of 2 Halfwords Interleaved Signed and Accumulate into Words +PPC_evdivwsf, // Vector Divide Word Signed Fractional +PPC_evdivwuf, // Vector Divide Word Unsigned Fractional +PPC_evdivs, // Vector Divide Signed +PPC_evdivu, // Vector Divide Unsigned +PPC_evaddwegsi, // Vector Add Word Even Guarded Signed Integer +PPC_evaddwegsf, // Vector Add Word Even Guarded Signed Fraction +PPC_evsubfwegsi, // Vector Subtract from Word Even Guarded Signed Integer +PPC_evsubfwegsf, // Vector Subtract from Word Even Guarded Signed Fraction +PPC_evaddwogsi, // Vector Add Word Odd Guarded Signed Integer +PPC_evaddwogsf, // Vector Add Word Odd Guarded Signed Fraction +PPC_evsubfwogsi, // Vector Subtract from Word Odd Guarded Signed Integer +PPC_evsubfwogsf, // Vector Subtract from Word Odd Guarded Signed Fraction +PPC_evaddhhiuw, // Vector Add Halfwords High Unsigned to Words +PPC_evaddhhisw, // Vector Add Halfwords High Signed to Words +PPC_evsubfhhiuw, // Vector Subtract from Halfwords High Unsigned to Words +PPC_evsubfhhisw, // Vector Subtract from Halfwords High Signed to Words +PPC_evaddhlouw, // Vector Add Halfwords Low Unsigned to Words +PPC_evaddhlosw, // Vector Add Halfwords Low Signed to Words +PPC_evsubfhlouw, // Vector Subtract from Halfwords Low Unsigned to Words +PPC_evsubfhlosw, // Vector Subtract from Halfwords Low Signed to Words +PPC_evmhesusiaaw, // Vector Multiply Halfwords, Even, Signed by Unsigned, Saturate, Integer and Accumulate into Words +PPC_evmhosusiaaw, // Vector Multiply Halfwords, Odd, Signed by Unsigned, Saturate, Integer and Accumulate into Words +PPC_evmhesumiaaw, // Vector Multiply Halfwords, Even, Signed by Unsigned, Modulo, Integer and Accumulate into Words +PPC_evmhosumiaaw, // Vector Multiply Halfwords, Odd, Signed by Unsigned, Modulo, Integer and Accumulate into Words +PPC_evmbeusiaah, // Vector Multiply Byte Even, Unsigned, Saturate, Integer and Accumulate Halfwords +PPC_evmbessiaah, // Vector Multiply Byte Even, Signed, Saturate, Integer and Accumulate Halfwords +PPC_evmbesusiaah, // Vector Multiply Byte Even, Signed by Unsigned, Saturate, Integer and Accumulate Halfwords +PPC_evmbousiaah, // Vector Multiply Byte Odd, Unsigned, Saturate, Integer and Accumulate Halfwords +PPC_evmbossiaah, // Vector Multiply Byte Odd, Signed, Saturate, Integer and Accumulate Halfwords +PPC_evmbosusiaah, // Vector Multiply Byte Odd, Signed by Unsigned, Saturate, Integer and Accumulate Halfwords +PPC_evmbeumiaah, // Vector Multiply Byte Even, Unsigned, Modulo, Integer and Accumulate Halfwords +PPC_evmbesmiaah, // Vector Multiply Byte Even, Signed, Modulo, Integer and Accumulate Halfwords +PPC_evmbesumiaah, // Vector Multiply Byte Even, Signed by Unsigned, Modulo, Integer and Accumulate Halfwords +PPC_evmboumiaah, // Vector Multiply Byte Odd, Unsigned, Modulo, Integer and Accumulate Halfwords +PPC_evmbosmiaah, // Vector Multiply Byte Odd, Signed, Modulo, Integer and Accumulate Halfwords +PPC_evmbosumiaah, // Vector Multiply Byte Odd, Signed by Unsigned, Modulo, Integer and Accumulate Halfwords +PPC_evmwlusiaaw3, // Vector Multiply Word Low Unsigned, Saturate, Integer and Accumulate in Words 3 operand +PPC_evmwlssiaaw3, // Vector Multiply Word Low Signed, Saturate, Integer and Accumulate in Words +PPC_evmwhssfraaw3, // Vector Multiply Word High Signed, Saturate, Fractional, Round and Accumulate into Words 3 operand +PPC_evmwhssfaaw3, // Vector Multiply Word High Signed, Saturate, Fractional and Accumulate into Words 3 operand +PPC_evmwhssfraaw, // Vector Multiply Word High Signed, Saturate, Fractional, Round and Accumulate into Words +PPC_evmwhssfaaw, // Vector Multiply Word High Signed, Saturate, Fractional and Accumulate into Words +PPC_evmwlumiaaw3, // Vector Multiply Word Low Unsigned, Modulo, Integer and Accumulate in Words 3 operand +PPC_evmwlsmiaaw3, // Vector Multiply Word Low Signed, Modulo, Integer and Accumulate in Words 3 operand +PPC_evmwusiaa, // Vector Multiply Word Unsigned, Saturate, Integer and Accumulate +PPC_evmwssiaa, // Vector Multiply Word Signed, Saturate, Integer and Accumulate +PPC_evmwehgsmfraa, // Vector Multiply Word Even High Guarded Signed, Modulo, Fractional, Round and Accumulate +PPC_evmwehgsmfaa, // Vector Multiply Word Even High Guarded Signed, Modulo, Fractional and Accumulate +PPC_evmwohgsmfraa, // Vector Multiply Word Odd High Guarded Signed, Modulo, Fractional, Round and Accumulate +PPC_evmwohgsmfaa, // Vector Multiply Word Odd High Guarded Signed, Modulo, Fractional and Accumulate +PPC_evmhesusianw, // Vector Multiply Halfwords, Even, Signed by Unsigned, Saturate, Integer and Accumulate Negative into Words +PPC_evmhosusianw, // Vector Multiply Halfwords, Odd, Signed by Unsigned, Saturate, Integer and Accumulate Negative into Words +PPC_evmhesumianw, // Vector Multiply Halfwords, Even, Signed by Unsigned, Modulo, Integer and Accumulate Negative into Words +PPC_evmhosumianw, // Vector Multiply Halfwords, Odd, Signed by Unsigned, Modulo, Integer and Accumulate Negative into Words +PPC_evmbeusianh, // Vector Multiply Byte Even, Unsigned, Saturate, Integer and Accumulate Negative Halfwords +PPC_evmbessianh, // Vector Multiply Byte Even, Signed, Saturate, Integer and Accumulate Negative Halfwords +PPC_evmbesusianh, // Vector Multiply Byte Even, Signed by Unsigned, Saturate, Integer and Accumulate Negative Halfwords +PPC_evmbousianh, // Vector Multiply Byte Odd, Unsigned, Saturate, Integer and Accumulate Negative Halfwords +PPC_evmbossianh, // Vector Multiply Byte Odd, Signed, Saturate, Integer and Accumulate Negative Halfwords +PPC_evmbosusianh, // Vector Multiply Byte Odd, Signed by Unsigned, Saturate, Integer and Accumulate Negative Halfwords +PPC_evmbeumianh, // Vector Multiply Byte Even, Unsigned, Modulo, Integer and Accumulate Negative Halfwords +PPC_evmbesmianh, // Vector Multiply Byte Even, Signed, Modulo, Integer and Accumulate Negative Halfwords +PPC_evmbesumianh, // Vector Multiply Byte Even, Signed by Unsigned, Modulo, Integer and Accumulate Negative Halfwords +PPC_evmboumianh, // Vector Multiply Byte Odd, Unsigned, Modulo, Integer and Accumulate Negative Halfwords +PPC_evmbosmianh, // Vector Multiply Byte Odd, Signed, Modulo, Integer and Accumulate Negative Halfwords +PPC_evmbosumianh, // Vector Multiply Byte Odd, Signed by Unsigned, Modulo, Integer and Accumulate Negative Halfwords +PPC_evmwlusianw3, // Vector Multiply Word Low Unsigned, Saturate, Integer and Accumulate Negative in Words 3 operand +PPC_evmwlssianw3, // Vector Multiply Word Low Signed, Saturate, Integer and Accumulate Negative in Words 3 operand +PPC_evmwhssfranw3, // Vector Multiply Word High Signed, Saturate, Fractional, Round, and Accumulate Negative into Words 3 operand +PPC_evmwhssfanw3, // Vector Multiply Word High Signed, Saturate, Fractional and Accumulate Negative into Words 3 operand +PPC_evmwhssfranw, // Vector Multiply Word High Signed, Saturate, Fractional, Round, and Accumulate Negative into Words +PPC_evmwhssfanw, // Vector Multiply Word High Signed, Saturate, Fractional and Accumulate Negative into Words +PPC_evmwlumianw3, // Vector Multiply Word Low Unsigned, Modulo, Integer and Accumulate Negative in Words 3 operand +PPC_evmwlsmianw3, // Vector Multiply Word Low Signed, Modulo, Integer and Accumulate Negative in Words 3 operand +PPC_evmwusian, // Vector Multiply Word Unsigned, Saturate, Integer and Accumulate Negative +PPC_evmwssian, // Vector Multiply Word Signed, Saturate, Integer and Accumulate Negative +PPC_evmwehgsmfran, // Vector Multiply Word Even High, Guarded, Signed, Modulo, Fractional, Round and Accumulate Negative +PPC_evmwehgsmfan, // Vector Multiply Word Even High, Guarded, Signed, Modulo, Fractional and Accumulate Negative +PPC_evmwohgsmfran, // Vector Multiply Word Odd, High, Guarded, Signed, Modulo, Fractional, Round and Accumulate Negative +PPC_evmwohgsmfan, // Vector Multiply Word Odd, High, Guarded, Signed, Modulo, Fractional and Accumulate Negative +PPC_evseteqb, // Vector Set if Equal Byte [and Record] +PPC_evseteqh, // Vector Set if Equal Halfword [and Record] +PPC_evseteqw, // Vector Set if Equal Word [and Record] +PPC_evsetgthu, // Vector Set if Greater Than Halfword UnsIgned [and Record] +PPC_evsetgths, // Vector Set if Greater Than Halfword SIgned [and Record] +PPC_evsetgtwu, // Vector Set if Greater Than Word Unsigned [and Record] +PPC_evsetgtws, // Vector Set if Greater Than Word Signed [and Record] +PPC_evsetgtbu, // Vector Set if Greater Than Byte Unsigned [and Record] +PPC_evsetgtbs, // Vector Set if Greater Than Byte SIgned [and Record] +PPC_evsetltbu, // Vector Set if Less Than Byte Unsigned [and Record] +PPC_evsetltbs, // Vector Set if Less Than Byte SIgned [and Record] +PPC_evsetlthu, // Vector Set if Less Than Halfword UnsIgned [and Record] +PPC_evsetlths, // Vector Set if Less Than Halfword SIgned [and Record] +PPC_evsetltwu, // Vector Set if Less Than Word Unsigned [and Record] +PPC_evsetltws, // Vector Set if Less Than Word Signed [and Record] +PPC_evsaduw, // Vector Sum of Absolute Differences of Unsigned Words (to Accumulator) +PPC_evsadsw, // Vector Sum of Absolute Differences of Signed Words (to Accumulator) +PPC_evsad4ub, // Vector Sum of Absolute Differences of 4 Unsigned Bytes (to Accumulator) +PPC_evsad4sb, // Vector Sum of Absolute Differences of 4 Signed Bytes (to Accumulator) +PPC_evsad2uh, // Vector Sum of Absolute Differences of 2 Unsigned Halfwords (to Accumulator) +PPC_evsad2sh, // Vector Sum of Absolute Differences of 2 Signed Halfwords (to Accumulator) +PPC_evsaduwa, // Vector Sum of Absolute Differences of Unsigned Words (to Accumulator) +PPC_evsadswa, // Vector Sum of Absolute Differences of Signed Words (to Accumulator) +PPC_evsad4uba, // Vector Sum of Absolute Differences of 4 Unsigned Bytes (to Accumulator) +PPC_evsad4sba, // Vector Sum of Absolute Differences of 4 Signed Bytes (to Accumulator) +PPC_evsad2uha, // Vector Sum of Absolute Differences of 2 Unsigned Halfwords (to Accumulator) +PPC_evsad2sha, // Vector Sum of Absolute Differences of 2 Signed Halfwords (to Accumulator) +PPC_evabsdifuw, // Vector Absolute Difference of Unsigned Words +PPC_evabsdifsw, // Vector Absolute Difference of Signed Words +PPC_evabsdifub, // Vector Absolute Difference of Unsigned Bytes +PPC_evabsdifsb, // Vector Absolute Difference of Signed Bytes +PPC_evabsdifuh, // Vector Absolute Difference of Unsigned Halfwords +PPC_evabsdifsh, // Vector Absolute Difference of Signed Halfwords +PPC_evsaduwaa, // Vector Sum of Absolute Differences of Unsigned Words and Accumulate +PPC_evsadswaa, // Vector Sum of Absolute Differences of Signed Words and Accumulate +PPC_evsad4ubaaw, // Vector Sum of Absolute Differences of 4 Unsigned Bytes and Accumulate into Words +PPC_evsad4sbaaw, // Vector Sum of Absolute Differences of 4 Signed Bytes and Accumulate into Words +PPC_evsad2uhaaw, // Vector Sum of Absolute Differences of 2 Unsigned Halfwords and Accumulate into Words +PPC_evsad2shaaw, // Vector Sum of Absolute Differences of 2 Signed Halfwords and Accumulate into Words +PPC_evpkshubs, // Vector Pack Signed Halfwords to Unsigned Bytes and Saturate +PPC_evpkshsbs, // Vector Pack Signed Halfwords to Signed Bytes and Saturate +PPC_evpkswuhs, // Vector Pack Signed Words to Unsigned Halfwords and Saturate +PPC_evpkswshs, // Vector Pack Signed Words to Signed Halfwords and Saturate +PPC_evpkuhubs, // Vector Pack Unsigned Halfwords to Unsigned Bytes and Saturate +PPC_evpkuwuhs, // Vector Pack Unsigned Words to Unsigned Halfwords and Saturate +PPC_evpkswshilvs, // Vector Pack Signed Words to Signed Halfwords Interleaved and Saturate +PPC_evpkswgshefrs, // Vector Pack Signed Words Guarded to Signed Halfwords Even Fractional Round and Saturate +PPC_evpkswshfrs, // Vector Pack Signed Words to Signed Halfwords Fractional, Round and Saturate +PPC_evpkswshilvfrs, // Vector Pack Signed Words to Signed Halfwords Interleaved, Fractional, Round and Saturate +PPC_evpksdswfrs, // Vector Pack Signed Doublewords to Signed Words Fractional, Round and Saturate +PPC_evpksdshefrs, // Vector Pack Signed Doublewords to Signed Halfwords Even, Fractional, Round, and Saturate +PPC_evpkuduws, // Vector Pack Unsigned Doublewords to Unsigned Words and Saturate +PPC_evpksdsws, // Vector Pack Signed Doublewords to Signed Words and Saturate +PPC_evpkswgswfrs, // Vector Pack Signed Words Guarded to Signed Words Fractional Round and Saturate +PPC_evilveh, // Vector Interleave Even Halfwords +PPC_evilveoh, // Vector Interleave Even/Odd Halfwords +PPC_evilvhih, // Vector Interleave High Halfwords +PPC_evilvhiloh, // Vector Interleave High/Low Halfwords +PPC_evilvloh, // Vector Interleave Low Halfwords +PPC_evilvlohih, // Vector Interleave Low/High Halfwords +PPC_evilvoeh, // Vector Interleave Odd/Even Halfwords +PPC_evilvoh, // Vector Interleave Odd Halfwords +PPC_evdlveb, // Vector De-interleave Even Bytes +PPC_evdlveh, // Vector De-interleave Even Halfwords +PPC_evdlveob, // Vector De-interleave Even/Odd Bytes +PPC_evdlveoh, // Vector De-interleave Even/Odd Halfwords +PPC_evdlvob, // Vector De-interleave Odd Bytes +PPC_evdlvoh, // Vector De-interleave Odd Halfwords +PPC_evdlvoeb, // Vector De-interleave Odd/Even Bytes +PPC_evdlvoeh, // Vector De-interleave Odd/Even Halfwords +PPC_evmaxbu, // Vector Maximum Byte Unsigned +PPC_evmaxbs, // Vector Maximum Byte Signed +PPC_evmaxhu, // Vector Maximum Halfword Unsigned +PPC_evmaxhs, // Vector Maximum Halfword Signed +PPC_evmaxwu, // Vector Maximum Word Unsigned +PPC_evmaxws, // Vector Maximum Word Signed +PPC_evmaxdu, // Vector Maximum Doubleword Unsigned +PPC_evmaxds, // Vector Maximum Doubleword Signed +PPC_evminbu, // Vector Minimum Byte Unsigned +PPC_evminbs, // Vector Minimum Byte Signed +PPC_evminhu, // Vector Minimum Halfword Unsigned +PPC_evminhs, // Vector Minimum Halfword Signed +PPC_evminwu, // Vector Minimum Word Unsigned +PPC_evminws, // Vector Minimum Word Signed +PPC_evmindu, // Vector Minimum Doubleword Unsigned +PPC_evminds, // Vector Minimum Doubleword Signed +PPC_evavgwu, // Vector Average Word Unsigned +PPC_evavgws, // Vector Average Word Signed +PPC_evavgbu, // Vector Average Byte Unsigned +PPC_evavgbs, // Vector Average Byte Signed +PPC_evavghu, // Vector Average Halfword Unsigned +PPC_evavghs, // Vector Average Halfword Signed +PPC_evavgdu, // Vector Average Doubleword Unsigned +PPC_evavgds, // Vector Average Doubleword Signed +PPC_evavgwur, // Vector Average Word Unsigned with Round +PPC_evavgwsr, // Vector Average Word Signed with Round +PPC_evavgbur, // Vector Average Byte Unsigned with Round +PPC_evavgbsr, // Vector Average Byte Signed with Round +PPC_evavghur, // Vector Average Halfword Unsigned with Round +PPC_evavghsr, // Vector Average Halfword Signed with Round +PPC_evavgdur, // Vector Average Doubleword Unsigned with Round +PPC_evavgdsr, // Vector Average Doubleword Signed with Round + +// Some simplifications that were missed previously +PPC_tdui, // Trap Doubleword Unconditionally with Immediate +PPC_tdu, // Trap Doubleword Unconditionally +PPC_twui, // Trap Word Unconditionally with Immediate +PPC_twu, // Trap Word Unconditionally + +// Power ISA 2.07 +PPC_bctar, // Branch Conditional to Branch Target Address Register +PPC_clrbhrb, // Clear BHRB +PPC_mfbhrbe, // Move From Branch History Rolling Buffer +PPC_mtsle, // Move To Split Little Endian +PPC_mfvsrd, // Move From VSR Doubleword +PPC_mfvsrwz, // Move From VSR Word and Zero +PPC_mtvsrd, // Move To VSR Doubleword +PPC_mtvsrwa, // Move To VSR Word Algebraic +PPC_mtvsrwz, // Move To VSR Word and Zero +PPC_fmrgew, // Floating Merge Even Word +PPC_fmrgow, // Floating Merge Odd Word +PPC_vpksdss, // Vector Pack Signed Doubleword Signed Saturate +PPC_vpksdus, // Vector Pack Signed Doubleword Unsigned Saturate +PPC_vpkudus, // Vector Pack Unsigned Doubleword Unsigned Saturate +PPC_vpkudum, // Vector Pack Unsigned Doubleword Unsigned Modulo +PPC_vupkhsw, // Vector Unpack High Signed Word +PPC_vupklsw, // Vector Unpack Low Signed Word +PPC_vmrgew, // Vector Merge Even Word +PPC_vmrgow, // Vector Merge Odd Word +PPC_vaddudm, // Vector Add Unsigned Doubleword Modulo +PPC_vadduqm, // Vector Add Unsigned Quadword Modulo +PPC_vaddeuqm, // Vector Add Extended Unsigned Quadword Modulo +PPC_vaddcuq, // Vector Add & write Carry Unsigned Quadword +PPC_vaddecuq, // Vector Add Extended & write Carry Unsigned Quadword +PPC_vsubudm, // Vector Subtract Unsigned Doubleword Modulo +PPC_vsubuqm, // Vector Subtract Unsigned Quadword Modulo +PPC_vsubeuqm, // Vector Subtract Extended Unsigned Quadword Modulo +PPC_vsubcuq, // Vector Subtract & write Carry Unsigned Quadword +PPC_vsubecuq, // Vector Subtract Extended & write Carry Unsigned Quadword +PPC_vmulesw, // Vector Multiply Even Signed Word +PPC_vmuleuw, // Vector Multiply Even Unsigned Word +PPC_vmulosw, // Vector Multiply Odd Signed Word +PPC_vmulouw, // Vector Multiply Odd Unsigned Word +PPC_vmuluwm, // Vector Multiply Unsigned Word Modulo +PPC_vmaxsd, // Vector Maximum Signed Doubleword +PPC_vmaxud, // Vector Maximum Unsigned Doubleword +PPC_vminsd, // Vector Minimum Signed Doubleword +PPC_vminud, // Vector Minimum Unsigned Doubleword +PPC_vcmpequd, // Vector Compare Equal To Unsigned Doubleword +PPC_vcmpgtsd, // Vector Compare Greater Than Signed Doubleword +PPC_vcmpgtud, // Vector Compare Greater Than Unsigned Doubleword +PPC_veqv, // Vector Equivalence +PPC_vnand, // Vector NAND +PPC_vorc, // Vector OR with Complement +PPC_vrld, // Vector Rotate Left Doubleword +PPC_vsld, // Vector Shift Left Doubleword +PPC_vsrd, // Vector Shift Right Doubleword +PPC_vsrad, // Vector Shift Right Algebraic Doubleword +PPC_vcipher, // Vector AES Cipher +PPC_vcipherlast, // Vector AES Cipher Last +PPC_vncipher, // Vector AES Inverse Cipher +PPC_vncipherlast, // Vector AES Inverse Cipher Last +PPC_vsbox, // Vector AES S-Box +PPC_vshasigmad, // Vector SHA-512 Sigma Doubleword +PPC_vshasigmaw, // Vector SHA-256 Sigma Word +PPC_vpmsumb, // Vector Polynomial Multiply-Sum Byte +PPC_vpmsumd, // Vector Polynomial Multiply-Sum Doubleword +PPC_vpmsumh, // Vector Polynomial Multiply-Sum Halfword +PPC_vpmsumw, // Vector Polynomial Multiply-Sum Word +PPC_vpermxor, // Vector Permute and Exclusive-OR +PPC_vgbbd, // Vector Gather Bits by Byte by Doubleword +PPC_vclzb, // Vector Count Leading Zeros Byte +PPC_vclzh, // Vector Count Leading Zeros Halfword +PPC_vclzw, // Vector Count Leading Zeros Word +PPC_vclzd, // Vector Count Leading Zeros Doubleword +PPC_vpopcntb, // Vector Population Count Byte +PPC_vpopcntd, // Vector Population Count Doubleword +PPC_vpopcnth, // Vector Population Count Halfword +PPC_vpopcntw, // Vector Population Count Word +PPC_vbpermq, // Vector Bit Permute Quadword +PPC_bcdadd, // Decimal Add Modulo +PPC_bcdsub, // Decimal Subtract Modulo +PPC_lxsiwax, // Load VSX Scalar as Integer Word Algebraic Indexed +PPC_lxsspx, // Load VSX Scalar Single-Precision Indexed +PPC_lxsiwzx, // Load VSX Scalar as Integer Word and Zero Indexed +PPC_stxsiwx, // Store VSX Scalar as Integer Word Indexed +PPC_stxsspx, // Store VSR Scalar Word Indexed +PPC_xsaddsp, // VSX Scalar Add Single-Precision +PPC_xscvdpspn, // VSX Scalar Convert Double-Precision to Single-Precision format Non-signalling +PPC_xscvspdpn, // Scalar Convert Single-Precision to Double-Precision format Non-signalling +PPC_xscvsxdsp, // VSX Scalar Convert Signed Fixed-Point Doubleword to Single-Precision +PPC_xscvuxdsp, // VSX Scalar Convert Unsigned Fixed-Point Doubleword to Single-Precision +PPC_xsdivsp, // VSX Scalar Divide Single-Precision +PPC_xsmaddasp, // VSX Scalar Multiply-Add Type-A Single-Precision +PPC_xsmaddmsp, // VSX Scalar Multiply-Add Type-M Single-Precision +PPC_xsmsubasp, // VSX Scalar Multiply-Subtract Type-A Single-Precision +PPC_xsmsubmsp, // VSX Scalar Multiply-Subtract Type-M Single-Precision +PPC_xsmulsp, // VSX Scalar Multiply Single-Precision +PPC_xsnmaddasp, // VSX Scalar Negative Multiply-Add Type-A Single-Precision +PPC_xsnmaddmsp, // VSX Scalar Negative Multiply-Add Type-M Single-Precision +PPC_xsnmsubasp, // VSX Scalar Negative Multiply-Subtract Type-A Single-Precision +PPC_xsnmsubmsp, // VSX Scalar Negative Multiply-Subtract Type-M Single-Precision +PPC_xsresp, // VSX Scalar Reciprocal Estimate Single-Precision +PPC_xsrsp, // VSX Scalar Round to Single-Precision +PPC_xsrsqrtesp, // VSX Scalar Reciprocal Square Root Estimate Single-Precision +PPC_xssqrtsp, // VSX Scalar Square Root Single-Precision +PPC_xssubsp, // VSX Scalar Subtract Single-Precision +PPC_xxleqv, // VSX Logical Equivalence +PPC_xxlnand, // VSX Logical NAND +PPC_xxlorc, // VSX Logical OR with Complement +PPC_lqarx, // Load Quadword And Reserve Indexed +PPC_stqcx, // Store Quadword Conditional Indexed and record CR0 +PPC_tbegin, // Transaction Begin +PPC_tend, // Transaction End +PPC_tabort, // Transaction Abort +PPC_tabortwc, // Transaction Abort Word Conditional +PPC_tabortwci, // Transaction Abort Word Conditional Immediate +PPC_tabortdc, // Transaction Abort Doubleword Conditional +PPC_tabortdci, // Transaction Abort Doubleword Conditional Immediate +PPC_tsr, // Transaction Suspend or Resume +PPC_tcheck, // Transaction Check +PPC_rfebb, // Return from Event Based Branch +PPC_treclaim, // Transaction Reclaim +PPC_trechkpt, // Transaction Recheckpoint +PPC_msgsndp, // Message Send Privileged +PPC_msgclrp, // Message Clear Privileged +PPC_dcblq, // Data Cache Block Lock Query +PPC_icblq, // Instruction Cache Block Lock Query + +// Simplifications for Power ISA 2.07 +PPC_vmr, // Vector Move Register +PPC_vnot, // Vector Complement Register +PPC_tendall, // Transaction End All +PPC_tsuspend, // Transaction Suspend +PPC_tresume, // Transaction Resume +PPC_mtppr, // Move To Program Priority Register +PPC_mfppr, // Move From Program Priority Register +PPC_mtppr32, // Move To Program Priority Register 32-Bit +PPC_mfppr32, // Move From Program Priority Register 32-Bit +PPC_mtic, // Move To IC +PPC_mfic, // Move From IC +PPC_mtvtb, // Move to VTB +PPC_mfvtb, // Move From VTB +PPC_miso, // "or" Cache Control Hint +PPC_mdoio, // "or" Shared Resource Hint +PPC_mdoom, // "or" Shared Resource Hint +PPC_yield, // "or" Shared Resource Hint +// these are mentioned in the Power ISA 2.07 document but not implemented by +// binutils; since they are just simplifications better to ignore them than to +// emit "weird" mnemonics +// PPC_mtuamr, // Move To Authority Mask Register +// PPC_mfuamr, // Move From Authority Mask Register +// PPC_dcbtct, // Data Cache Block Touch +// PPC_dcbtds, // Data Cache Block Touch +// PPC_dcbna, // Data Cache Block Touch +// PPC_dcbtctep, // Data Cache Block Touch by External PID +// PPC_dcbtdsep, // Data Cache Block Touch by External PID +// PPC_dcbflep, // Data Cache Block Flush by External PID +// PPC_dcbflpep, // Data Cache Block Flush by External PID +// PPC_dcbtstctep, // Data Cache Block Touch for Store by External PID +// +// these are only needed by assemblers +// PPC_lxvx, // Load VSX Vector Doubleword*2 Indexed +// PPC_stxvx, // Store VSX Vector Doubleword*2 Indexed +// PPC_mffprd, // Move From FPR Doubleword +// PPC_mfvrd, // Move From VR Doubleword +// PPC_mffprwz, // Move From FPR Word and Zero +// PPC_mfvrwz, // Move From VR Word and Zero +// PPC_mtfprd, // Move To FPR Doubleword +// PPC_mtvrd, // Move To VR Doubleword +// PPC_mtfprwa, // Move To FPR Word Algebraic +// PPC_mtvrwa, // Move To VR Word Algebraic +// PPC_mtfprwz, // Move To FPR Word and Zero +// PPC_mtvrwz, // Move To VR Word and Zero + +// e500 core family, category ISAT, valid for VLE +// EREF_RM Rev. 1 (EIS 2.1) 06/2014 +PPC_addbss, // Add Byte Signed Saturate +PPC_addhss, // Add Halfword Signed Saturate +PPC_addwss, // Add Word Signed Saturate +PPC_addbus, // Add Byte Unsigned Saturate +PPC_addhus, // Add Halfword Unsigned Saturate +PPC_addwus, // Add Word Unsigned Saturate +PPC_mulhss, // Multiply Halfword SIgned Saturate +PPC_mulwss, // Multiply Word SIgned Saturate +PPC_mulhus, // Multiply Halfword Unsigned Saturate +PPC_mulwus, // Multiply Word Unsigned Saturate +PPC_sat, // Saturate +PPC_subfbss, // Subtract From Byte Signed Saturate +PPC_subfhss, // Subtract From Halfword Signed Saturate +PPC_subfwss, // Subtract From Word Signed Saturate +PPC_subfbus, // Subtract From Byte Unsigned Saturate +PPC_subfhus, // Subtract From Halfword Unsigned Saturate +PPC_subfwus, // Subtract From Word Unsigned Saturate +// simplified mnemonics for PPC_sat +PPC_satsbs, // Saturate +PPC_satubs, // Saturate +PPC_satsbu, // Saturate +PPC_satubu, // Saturate +PPC_abssb, // Saturate +PPC_absub, // Saturate +PPC_satshs, // Saturate +PPC_satuhs, // Saturate +PPC_satshu, // Saturate +PPC_satuhu, // Saturate +PPC_abssh, // Saturate +PPC_absuh, // Saturate +PPC_satsws, // Saturate +PPC_satuws, // Saturate +PPC_satswu, // Saturate +PPC_satuwu, // Saturate +PPC_abssw, // Saturate +PPC_absuw, // Saturate + +// e200 Enhanced Debug extension +PPC_dni, // Debugger Notify Interrupt + +// Power ISA Version 3.0 +PPC_slbieg, // SLB Invalidate Entry Global +PPC_slbiag, // SLB Invalidate All Global +PPC_slbsync, // SLB Synchronize + +PPC_addpcis, // Add PC Immediate Shifted +PPC_lnia, // Add PC Immediate Shifted +PPC_subpcis, // Add PC Immediate Shifted +PPC_cmpeqb, // Compare Equal Byte +PPC_cmprb, // Compare Ranged Byte +PPC_cnttzw, // Count Trailing Zeros Word +PPC_cnttzd, // Count Trailing Zeros Dword +PPC_darn, // Deliver A Random Number +PPC_extswsli, // Extend-Sign Word and Shift Left +PPC_maddhd, // Multiply-Add High Doublewor +PPC_maddhdu, // Multiply-Add High Doubleword Unsigned +PPC_maddld, // Multiply-Add Low Doubleword +PPC_mcrxrx, // Move to CR from XER Extended, PPC_mcrxr disappeared +PPC_setb, // Set Boolean +PPC_modsd, // Modulo Signed Dword +PPC_modud, // Modulo Unsigned Dword +PPC_modsw, // Modulo Signed Word +PPC_moduw, // Modulo Unsigned Word +PPC_mfvsrld, // Move From VSR Lower Doubleword +PPC_mtvsrdd, // Move To VSR Double Dword +PPC_mtvsrws, // Move To VSR Word & Splat +PPC_scv, // System Call Vectored +PPC_rfscv, // Return From System Call Vectored +PPC_stop, // Stop + +PPC_copy, // Copy +PPC_paste, // Paste +PPC_ldat, // Load Dword ATomic +PPC_lwat, // Load Word ATomic +PPC_stdat, // Store Dword ATomic +PPC_stwat, // Store Word ATomic +PPC_cpabort, // Copy-Paste Abort +PPC_wait30, // Wait for Interrupt + +PPC_dtstsfi, // DFP Test Significance Immediate +PPC_dtstsfiq, // DFP Test Significance Immediate Quad + +PPC_bcdcfn, // Decimal Convert From National & record +PPC_bcdcfz, // Decimal Convert From Zoned & record +PPC_bcdctn, // Decimal Convert To National & record +PPC_bcdctz, // Decimal Convert To Zoned & record +PPC_bcdctsq, // Decimal Convert To Signed Qword & record +PPC_bcdcfsq, // Decimal Convert From Signed Qword & record +PPC_bcdsetsgn, // Decimal Set Sign & record +PPC_bcdcpsgn, // Decimal CopySign & record +PPC_bcds, // Decimal Shift & record +PPC_bcdus, // Decimal Unsigned Shift & record +PPC_bcdsr, // Decimal Shift & Round & record +PPC_bcdtrunc, // Decimal Truncate & record +PPC_bcdutrunc, // Decimal Unsigned Truncate & record + +PPC_vabsdub, // Vector Absolute Difference Unsigned Byte +PPC_vabsduh, // Vector Absolute Difference Unsigned Hword +PPC_vabsduw, // Vector Absolute Difference Unsigned Word +PPC_vbpermd, // Vector Bit Permute Dword +PPC_vclzlsbb, // Vector Count Leading Zero Least-Significant Bits Byte +PPC_vctzlsbb, // Vector Count Trailing Zero Least-Significant Bits Byte +PPC_vcmpneb, // Vector Compare Not Equal Byte +PPC_vcmpnezb, // Vector Compare Not Equal or Zero Byte +PPC_vcmpneh, // Vector Compare Not Equal Hword +PPC_vcmpnezh, // Vector Compare Not Equal or Zero Hword +PPC_vcmpnew, // Vector Compare Not Equal Word +PPC_vcmpnezw, // Vector Compare Not Equal or Zero Word +PPC_vctzb, // Vector Count Trailing Zeros Byte +PPC_vctzh, // Vector Count Trailing Zeros Hword +PPC_vctzw, // Vector Count Trailing Zeros Word +PPC_vctzd, // Vector Count Trailing Zeros Dword +PPC_vextractub, // Vector Extract Unsigned Byte +PPC_vextractuh, // Vector Extract Unsigned Hword +PPC_vextractuw, // Vector Extract Unsigned Word +PPC_vextractd, // Vector Extract Dword +PPC_vextsb2w, // Vector Extend Sign Byte to Word +PPC_vextsb2d, // Vector Extend Sign Byte to Dword +PPC_vextsh2w, // Vector Extend Sign Hword to Word +PPC_vextsh2d, // Vector Extend Sign Hword to Dword +PPC_vextsw2d, // Vector Extend Sign Word to Dword +PPC_vextublx, // Vector Extract Unsigned Byte Left-Indexed +PPC_vextubrx, // Vector Extract Unsigned Byte Right-Indexed +PPC_vextuhlx, // Vector Extract Unsigned Hword Left-Indexed +PPC_vextuhrx, // Vector Extract Unsigned Hword Right-Indexed +PPC_vextuwlx, // Vector Extract Unsigned Word Left-Indexed +PPC_vextuwrx, // Vector Extract Unsigned Word Right-Indexed +PPC_vinsertb, // Vector Insert Byte +PPC_vinserth, // Vector Insert Hword +PPC_vinsertw, // Vector Insert Word +PPC_vinsertd, // Vector Insert Dword +PPC_vmul10uq, // Vector Multiply-by-10 Unsigned Qword +PPC_vmul10euq, // Vector Multiply-by-10 Extended Unsigned Qword +PPC_vmul10cuq, // Vector Multiply-by-10 & write Carry Unsigned Qword +PPC_vmul10ecuq, // Vector Multiply-by-10 Extended & write Carry Unsigned Qword +PPC_vnegw, // Vector Negate Word +PPC_vnegd, // Vector Negate Dword +PPC_vpermr, // Vector Permute Right-indexed +PPC_vprtybw, // Vector Parity Byte Word +PPC_vprtybd, // Vector Parity Byte Dword +PPC_vprtybq, // Vector Parity Byte Qword +PPC_vrlwnm, // Vector Rotate Left Word then AND with Mask +PPC_vrlwmi, // Vector Rotate Left Word then Mask Insert +PPC_vrldnm, // Vector Rotate Left Dword then AND with Mask +PPC_vrldmi, // Vector Rotate Left Dword then Mask Insert +PPC_vslv, // Vector Shift Left Variable +PPC_vsrv, // Vector Shift Right Variable + +PPC_lxsd, // Load VSX Scalar Dword +PPC_lxssp, // Load VSX Scalar Single +PPC_lxsibzx, // Load VSX Scalar as Integer Byte & Zero Indexed +PPC_lxsihzx, // Load VSX Scalar as Integer Hword & Zero Indexed +PPC_lxv, // Load VSX Vector +PPC_lxvb16x, // Load VSX Vector Byte*16 Indexed +PPC_lxvh8x, // Load VSX Vector Hword*8 Indexed +PPC_lxvl, // Load VSX Vector with Length +PPC_lxvll, // Load VSX Vector Left-justified with Length +PPC_lxvwsx, // Load VSX Vector Word & Splat Indexed +PPC_lxvx, // Load VSX Vector Indexed +PPC_stxsd, // Store VSX Scalar Dword +PPC_stxsibx, // Store VSX Scalar as Integer Byte Indexed +PPC_stxsihx, // Store VSX Scalar as Integer Hword Indexed +PPC_stxssp, // Store VSX Scalar SP +PPC_stxv, // Store VSX Vector +PPC_stxvb16x, // Store VSX Vector Byte*16 Indexed +PPC_stxvh8x, // Store VSX Vector Hword*8 Indexed +PPC_stxvl, // Store VSX Vector with Length +PPC_stxvll, // Store VSX Vector Left-justified with Length +PPC_stxvx, // Store VSX Vector Indexed + +PPC_xsabsqp, // VSX Scalar Absolute QP +PPC_xsaddqp, // VSX Scalar Add QP +PPC_xscmpexpqp, // VSX Scalar Compare Exponents QP +PPC_xscmpoqp, // VSX Scalar Compare Ordered QP +PPC_xscmpuqp, // VSX Scalar Compare Unordered QP +PPC_xscpsgnqp, // VSX Scalar Copy Sign QP +PPC_xscvdpqp, // VSX Scalar Convert DP to QP +PPC_xscvqpdp, // VSX Scalar Convert QP to DP +PPC_xscvqpsdz, // VSX Scalar Convert QP to Signed Dword truncate +PPC_xscvqpswz, // VSX Scalar Convert QP to Signed Word truncate +PPC_xscvqpudz, // VSX Scalar Convert QP to Unsigned Dword truncate +PPC_xscvqpuwz, // VSX Scalar Convert QP to Unsigned Word truncate +PPC_xscvsdqp, // VSX Scalar Convert Signed Dword to QP +PPC_xscvudqp, // VSX Scalar Convert Unsigned Dword to QP +PPC_xsdivqp, // VSX Scalar Divide QP +PPC_xsiexpqp, // VSX Scalar Insert Exponent QP +PPC_xsmaddqp, // VSX Scalar Multiply-Add QP +PPC_xsmsubqp, // VSX Scalar Multiply-Subtract QP +PPC_xsmulqp, // VSX Scalar Multiply QP +PPC_xsnabsqp, // VSX Scalar Negative Absolute QP +PPC_xsnegqp, // VSX Scalar Negate QP +PPC_xsnmaddqp, // VSX Scalar Negative Multiply-Add QP +PPC_xsnmsubqp, // VSX Scalar Negative Multiply-Subtract QP +PPC_xssqrtqp, // VSX Scalar Square Root QP +PPC_xssubqp, // VSX Scalar Subtract QP +PPC_xsxexpqp, // VSX Scalar Extract Exponent QP +PPC_xsxsigqp, // VSX Scalar Extract Significand QP +PPC_xststdcqp, // VSX Scalar Test Data Class QP +PPC_xsrqpxp, // VSX Scalar Round QP to XP +PPC_xsrqpi, // VSX Scalar Round QP to Integral [with Inexact] +PPC_xscmpeqdp, // VSX Scalar Compare Equal Double-Precision +PPC_xscmpexpdp, // VSX Scalar Compare Exponents DP +PPC_xscmpgedp, // VSX Scalar Compare Greater Than or Equal Double-Precision +PPC_xscmpgtdp, // VSX Scalar Compare Greater Than Double-Precision +PPC_xsiexpdp, // VSX Scalar Insert Exponent DP +PPC_xsmaxcdp, // VSX Scalar Maximum Type-C Double-Precision +PPC_xsmaxjdp, // VSX Scalar Maximum Type-J Double-Precision +PPC_xsmincdp, // VSX Scalar Minimum Type-C Double-Precision +PPC_xsminjdp, // VSX Scalar Minimum Type-J Double-Precision +PPC_xviexpdp, // VSX Vector Insert Exponent DP +PPC_xviexpsp, // VSX Vector Insert Exponent SP +PPC_xxextractuw,// VSX Vector Extract Unsigned Word +PPC_xxinsertw, // VSX Vector Insert Word +PPC_xxperm, // VSX Vector Permute +PPC_xxpermr, // VSX Vector Permute Right-indexed +PPC_xxspltib, // VSX Vector Splat Immediate Byte +PPC_xststdcdp, // VSX Scalar Test Data Class DP +PPC_xststdcsp, // VSX Scalar Test Data Class SP +PPC_xvtstdcdp, // VSX Vector Test Data Class DP +PPC_xvtstdcsp, // VSX Vector Test Data Class SP +PPC_xsxexpdp, // VSX Scalar Extract Exponent DP +PPC_xsxsigdp, // VSX Scalar Extract Significand DP +PPC_xscvdphp, // VSX Scalar Convert DP to HP +PPC_xscvhpdp, // VSX Scalar Convert HP to DP +PPC_xvxexpdp, // VSX Vector Extract Exponent DP +PPC_xvxexpsp, // VSX Vector Extract Exponent SP +PPC_xvxsigdp, // VSX Vector Extract Significand DP +PPC_xvxsigsp, // VSX Vector Extract Significand SP +PPC_xxbrd, // VSX Vector Byte-Reverse Dword +PPC_xxbrh, // VSX Vector Byte-Reverse Hword +PPC_xxbrq, // VSX Vector Byte-Reverse Qword +PPC_xxbrw, // VSX Vector Byte-Reverse Word +PPC_xvcvhpsp, // VSX Vector Convert HP to SP +PPC_xvcvsphp, // VSX Vector Convert SP to HP + +PPC_msgsync, // Message Synchronize + +// Instructions introduced in ISA 3.0 and withdrawn in ISA 3.0B : +// ldmx "Load Dword Monitored Indexed" +// xscmpnedp "VSX Scalar Compare Not Equal Double-Precision" +// xvcmpnedp[.] "VSX Vector Compare Not Equal Double-Precision" +// xvcmpnesp[.] "VSX Vector Compare Not Equal Single-Precision" + +// Power ISA Version 3.0 B +PPC_addex, // Add Extended using alternate carry +PPC_vmsumudm, // Vector Multiply-Sum Unsigned Doubleword Modulo +PPC_mffsce, // Move From FPSCR & Clear Enables +PPC_mffscdrn, // Move From FPSCR Control & set DRN +PPC_mffscdrni, // Move From FPSCR Control & set DRN Immediate +PPC_mffscrn, // Move From FPSCR Control & set RN +PPC_mffscrni, // Move From FPSCR Control & set RN Immediate +PPC_mffsl, // Move From FPSCR Lightweight + +// Cache Bypass Storage APU +PPC_lbdcbx, // Load Byte with Decoration Indexed Cache Bypass +PPC_lhdcbx, // Load Halfword with Decoration Indexed Cache Bypass +PPC_lwdcbx, // Load Word with Decoration Indexed Cache Bypass +PPC_stbdcbx, // Store Byte with Decoration Indexed Cache Bypass +PPC_sthdcbx, // Store Halfword with Decoration Indexed Cache Bypass +PPC_stwdcbx, // Store Word with Decoration Indexed Cache Bypass + +PPC_lbcbx, // Load Byte Indexed Cache Bypass +PPC_lhcbx, // Load Halfword Indexed Cache Bypass +PPC_lwcbx, // Load Word Indexed Cache Bypass +PPC_stbwtx, // Store Byte Indexed Cache Bypass +PPC_sthwtx, // Store Halfword Indexed Cache Bypass +PPC_stwwtx, // Store Word Indexed Cache Bypass +PPC_dsncb, // Decorated Storage Notify Cache Bypass + +// AIOP (E200Z490) +PPC_ldw, // Load Doubleword +PPC_stdw, // Store Doubleword +PPC_lqw, // Load Quadword +PPC_stqw, // Store Quadword +PPC_ldwcb, // Load Doubleword Cache Bypassed +PPC_ldbrw, // Load Byte Reversed Doubleword +PPC_ldwbrw, // Load Two Byte Reversed Words +PPC_stdwwt, // Store Doubleword with Write-Through +PPC_stdbrw, // Store Doubleword Bytes Reversed +PPC_stdwbrw, // Store Doubleword Byte Reversed in Word +PPC_lqdbrw, // Load Quadword Byte Reversed +PPC_stqdbrw, // Store Quadword Byte Reversed +PPC_lwbr, // Load Word Byte Reversed +PPC_lhbr, // Load Halfword Byte Reversed +PPC_stwbr, // Store Word Byte Reversed +PPC_sthbr, // Store Halfword Byte Reversed +PPC_ldwar, // Load Doubleword and reserve +PPC_stdwc, // Store Doubleword conditional on reservation +PPC_addb, // Add Two Bytes +PPC_addbu, // Add Two Unsigned Bytes +PPC_addh, // Add Two Halfwords +PPC_addhu, // Add Two Unsigned Halfwords +PPC_subfb, // Signed Byte One's Complement Add +PPC_subfbu, // Unsigned Byte One's Complement Add +PPC_subfh, // Signed Halfword One's Complement Subtract +PPC_subfhu, // Unsigned Halfword One's Complement Subtract +PPC_byterevw, // Reverse bytes in a word +PPC_byterevh, // Reverse bytes in each halfword +PPC_hwaccel, // Hardware Acceleration Request +PPC_hwacceli, // Hardware Acceleration Request +PPC_ordhwaccel, // Ordered Hardware Acceleration Request +PPC_ordhwacceli,// Ordered Hardware Acceleration Request +PPC_osmcmd, // Ordered Scope Management Command Request +PPC_mpure, // MPU Read Entry Instruction +PPC_mpuwe, // MPU Write Entry Instruction +PPC_mpusync, // MPU Synchronize Instruction + +// EFP 2.0 instructions +PPC_efdmax, // Floating-Point Double-Precision Maximum +PPC_efdmin, // Floating-Point Double-Precision Minimum +PPC_efdsqrt, // Floating-Point Double-Precision Square Root +PPC_efdcfh, // Convert Floating-Point Double-Precision from Half-Precision +PPC_efdcth, // Convert Floating-Point Double-Precision to Half-Precision + +// LSP (Lightweight Signal Processing) instructions +PPC_zbrminc, // Bit-reversed masked increment +PPC_zcircinc, // Circular increment +PPC_zvabsh, // Vector Absolute Value Halfword +PPC_zvabshs, // Vector absolute value halfword and saturate +PPC_zabsw, // Absolute value word +PPC_zabsws, // Absolute value word and saturate +PPC_zaddd, // Add doubleword +PPC_zadddss, // Add doubleword signed saturate +PPC_zadddus, // Add doubleword unsigned saturate +PPC_zvaddh, // Vector add halfwords +PPC_zvaddhss, // Vector add halfwords signed and saturate +PPC_zvaddhus, // Vector add halfwords unsigned and saturate +PPC_zvaddhx, // Vector add halfwords exchanged +PPC_zvaddhxss, // Vector add halfwords exchanged, signed and saturate +PPC_zvaddih, // Vector add immediate halfword +PPC_zvaddsubfh, // Vector add / subtract from halfword +PPC_zvaddsubfhss, // Vector add / subtract from halfword signed and saturate +PPC_zvaddsubfhx, // Vector add / subtract from halfword exchanged +PPC_zvaddsubfhxss, // Vector add / subtract from halfword exchanged, signed and saturate +PPC_zaddhesw, // Add halfword even signed to word +PPC_zaddheuw, // Add halfword even unsigned to word +PPC_zaddhosw, // Add halfword odd signed to word +PPC_zaddhouw, // Add halfword odd unsigned to word +PPC_zvaddw, // Vector add word +PPC_zvaddsubfw, // Vector add / subtract from word +PPC_zvaddsubfwss, // Vector add / subtract from word signed and saturate +PPC_zaddwgsf, // Add word guarded signed fraction +PPC_zaddwgsi, // Add word guarded signed integer +PPC_zaddwgui, // Add word guarded unsigned integer +PPC_zaddwss, // Add word signed and saturate +PPC_zvaddwss, // Vector add word signed and saturate +PPC_zaddwus, // Add Word Unsigned And Saturate +PPC_zvaddwus, // Vector add word unsigned and saturate +PPC_zvcmpeqh, // Vector compare equal halfword +PPC_zvcmpgths, // Vector compare greater than halfword signed +PPC_zvcmpgthu, // Vector compare greater than halfword unsigned +PPC_zvcmplths, // Vector compare less than halfword signed +PPC_zvcmplthu, // Vector compare less than halfword unsigned +PPC_zvcntlsh, // Vector count leading signed bits halfword +PPC_zcntlsw, // Count leading signed bits word +PPC_zvcntlzh, // Vector count leading zeros halfword +PPC_zdivwsf, // Divide word signed fractional and saturate +PPC_zvmergehih, // Vector merge high halfwords +PPC_zvmergehiloh, // Vector merge high/low halfwords +PPC_zvmergeloh, // Vector merge low halfwords +PPC_zvmergelohih, // Vector merge low/high halfwords +PPC_zvnegh, // Vector negate halfwords +PPC_zvnegho, // Vector negate halfword odd +PPC_zvneghos, // Vector negate halfwords odd and saturate +PPC_zvneghs, // Vector negate halfword and saturate +PPC_znegws, // Negate word and saturate +PPC_zvpkshgwshfrs, // Vector pack signed halfwords guarded to word to signed halfwords fractional round and saturate +PPC_zpkswgshfrs, // Pack signed word guarded to signed halfword fractional round and saturate +PPC_zpkswgswfrs, // Pack signed word guarded to signed word fractional round and saturate +PPC_zvpkswshfrs, // Vector pack signed words to signed halfwords fractional, round and saturate +PPC_zvpkswshs, // Vector pack signed words to signed halfwords and saturate +PPC_zvpkswuhs, // Vector pack signed words to unsigned halfwords and saturate +PPC_zvpkuwuhs, // Vector pack unsigned words to unsigned halfwords and saturate +PPC_zvrlh, // Vector rotate left halfword +PPC_zvrlhi, // Vector rotate left halfword immediate +PPC_zrndwh, // Round word to halfword +PPC_zrndwhss, // Round word to halfword signed and saturate +PPC_zsatsdsw, // Saturate signed doubleword to signed word range +PPC_zsatsduw, // Saturate signed doubleword to unsigned word range +PPC_zvsatshuh, // Vector saturate signed halfwords to unsigned halfword range +PPC_zsatswsh, // Saturate signed word to signed halfword range +PPC_zsatswuh, // Saturate signed word to unsigned halfword range +PPC_zsatswuw, // Saturate signed word to unsigned word range +PPC_zsatuduw, // Saturate unsigned doubleword to unsigned word range +PPC_zvsatuhsh, // Vector saturate unsigned halfword to signed halfword range +PPC_zsatuwsw, // Saturate unsigned word to signed word range +PPC_zsatuwsh, // Saturate unsigned word to unsigned halfword range +PPC_zsatuwuh, // Saturate unsigned word to signed halfword range +PPC_zvselh, // Vector select halfwords +PPC_zvslh, // Vector shift left halfword +PPC_zvslhi, // Vector shift left halfword immediate +PPC_zvslhss, // Vector shift left halfword signed and saturate +PPC_zvslhiss, // Vector shift left halfword immediate signed and saturate +PPC_zvslhus, // Vector shift left halfword unsigned and saturate +PPC_zvslhius, // Vector shift left halfword immediate unsigned and saturate +PPC_zslwss, // Shift left word signed and saturate +PPC_zslwiss, // Shift left word immediate signed and saturate +PPC_zslwus, // Shift left word unsigned and saturate +PPC_zslwius, // Shift left word immediate unsigned and saturate +PPC_zvsplatfih, // Vector splat fractional immediate halfword +PPC_zvsplatih, // Vector splat immediate halfword +PPC_zvsrhis, // Vector shift right halfword immediate signed +PPC_zvsrhiu, // Vector shift right halfword immediate unsigned +PPC_zvsrhs, // Vector shift right halfword signeds +PPC_zvsrhu, // Vector shift right halfword unsigned +PPC_zvsubfaddh, // Vector subtract from / add halfwords +PPC_zvsubfaddhss, // Vector subtract from / add halfwords signed and saturate +PPC_zvsubfaddhx, // Vector subtract from / add halfwords exchanged +PPC_zvsubfaddhxss, // Vector Subtract from / add halfwords exchanged, signed and saturate +PPC_zsubfd, // Subtract from doubleword +PPC_zsubfdss, // Subtract from doubleword signed saturate +PPC_zsubfdus, // Subtract from doubleword unsigned saturate +PPC_zvsubfh, // Vector subtract from halfword +PPC_zsubfhesw, // Subtract from halfword even signed to word +PPC_zsubfheuw, // Subtract from halfword even unsigned to word +PPC_zsubfhosw, // Subtract from halfword odd signed to word +PPC_zsubfhouw, // Vector subtract from halfword odd unsigned to word +PPC_zvsubfhss, // Vector subtract from halfword signed and saturate +PPC_zvsubfhus, // Vector subtract from halfword unsigned and saturate +PPC_zvsubfhx, // Vector subtract from halfwords exchanged +PPC_zvsubfhxss, // Vector subtract from halfwords exchanged, signed and saturate +PPC_zvsubfw, // Vector subtract from word +PPC_zvsubfaddw, // Vector subtract from / add word +PPC_zvsubfaddwss, // Vector subtract from / add word signed and saturate +PPC_zsubfwgsf, // Subtract from word guarded signed fraction +PPC_zsubfwgsi, // Subtract from word guarded signed integer +PPC_zsubfwgui, // Subtract from word guarded unsigned integer +PPC_zsubfwss, // Subtract from word signed and saturate +PPC_zvsubfwss, // Vector subtract from word signed and saturate +PPC_zsubfwus, // Subtract from word unsigned and saturate +PPC_zvsubfwus, // Vector subtract from word unsigned and saturate +PPC_zvsubifh, // Vector subtract immediate from halfword +PPC_zvunpkhgwsf, // Vector unpack halfwords as guarded word signed fractions +PPC_zvunpkhsf, // Vector unpack halfwords as signed fractions +PPC_zvunpkhsi, // Vector unpack halfwords as signed integers +PPC_zvunpkhui, // Vector unpack halfwords as unsigned integers +PPC_zunpkwgsf, // Unpack word to guarded signed fraction +PPC_zxtrw, // Extract word +PPC_zldd, // Vector load doubleword into doubleword +PPC_zlddu, // Vector load doubleword into doubleword with update +PPC_zlddx, // Vector load doubleword into doubleword indexed +PPC_zlddmx, // Vector load doubleword into doubleword with modify indexed +PPC_zldh, // Vector load doubleword into four halfwords +PPC_zldhu, // Vector load doubleword into four halfwords with update +PPC_zldhx, // Vector load doubleword into four halfwords indexed +PPC_zldhmx, // Vector load doubleword into four halfwords with modify indexed +PPC_zldw, // Vector load doubleword into two words +PPC_zldwu, // Vector load doubleword into two words with update +PPC_zldwx, // Vector load double into two words indexed +PPC_zldwmx, // Vector load double into two words with modify indexed +PPC_zlhgwsf, // Vector load halfword guarded to word signed fraction +PPC_zlhgwsfu, // Vector load halfword guarded to word signed fraction with update +PPC_zlhgwsfx, // Vector load halfword guarded to word signed fraction indexed +PPC_zlhgwsfmx, // Vector load halfword guarded to word signed fraction with modify indexed +PPC_zlhhe, // Vector load halfword into halfword even +PPC_zlhheu, // Vector load halfword into halfword even with update +PPC_zlhhex, // Vector load halfword into halfword even indexed +PPC_zlhhemx, // Vector load halfword into halfword even with modify indexed +PPC_zlhhos, // Vector load halfword into halfword odd signed +PPC_zlhhosu, // Vector load halfword into halfword odd signed with update +PPC_zlhhosx, // Vector load halfword into halfword odd signed indexed +PPC_zlhhosmx, // Vector load halfword into halfword odd signed with modify indexed +PPC_zlhhou, // Vector load halfword into halfword odd unsigned +PPC_zlhhouu, // Vector load halfword into halfword odd unsigned with update +PPC_zlhhoux, // Vector load halfword into halfword odd unsigned indexed +PPC_zlhhoumx, // Vector load halfword into halfword odd unsigned with modify indexed +PPC_zlhhsplat, // Vector load halfword into halfwords and splat +PPC_zlhhsplatu, // Vector load halfword into halfwords and splat with update +PPC_zlhhsplatx, // Vector load halfword into halfwords and splat indexed +PPC_zlhhsplatmx, // Vector load halfword into halfwords and splat with modify indexed +PPC_zlwgsfd, // Vector load word as guarded signed fraction to doubleword +PPC_zlwgsfdu, // Vector load word as guarded signed fraction to doubleword with update +PPC_zlwgsfdx, // Vector load word as guarded signed fraction to doubleword indexed +PPC_zlwgsfdmx, // Vector load word as guarded signed fraction to doubleword with modify indexed +PPC_zlwh, // Vector load word into two halfwords +PPC_zlwhu, // Vector load word into two halfwords with update +PPC_zlwhx, // Vector load word into two halfwords indexed +PPC_zlwhmx, // Vector load word into two halfwords with modify indexed +PPC_zlwhed, // Vector load word into two halfwords even to doubleword +PPC_zlwhedu, // Vector load word into two halfwords even to doubleword with update +PPC_zlwhedx, // Vector load word into two halfwords even to doubleword indexed +PPC_zlwhedmx, // Vector load word into two halfwords even to doubleword with modify indexed +PPC_zlwhgwsfd, // Vector load word as halfwords guarded to word signed fraction to doubleword +PPC_zlwhgwsfdu, // Vector load word as halfwords guarded to word signed fraction to doubleword with update +PPC_zlwhgwsfdx, // Vector load word as halfwords guarded to word signed fraction to doubleword indexed +PPC_zlwhgwsfdmx, // Vector load word as halfwords guarded to word signed fraction to doubleword with modify indexed +PPC_zlwhosd, // Vector load word into two halfwords odd signed (with sign extension) to doubleword +PPC_zlwhosdu, // Vector load word into two halfwords odd signed (with sign extension) to doubleword with update +PPC_zlwhosdx, // Vector load word into two halfwords odd signed (with sign extension) to doubleword indexed +PPC_zlwhosdmx, // Vector load word into two halfwords odd signed (with sign extension) to doubleword with modify indexed +PPC_zlwhoud, // Vector load word into two halfwords odd unsigned (zero-extended) to doubleword +PPC_zlwhoudu, // Vector load word into two halfwords odd unsigned (zero-extended) to doubleword with update +PPC_zlwhoudx, // Vector load word into two halfwords odd unsigned (zero-extended) to doubleword indexed +PPC_zlwhoudmx, // Vector load word into two halfwords odd unsigned (zero-extended) to doubleword with modify indexed +PPC_zlwhsplatd, // Vector load word into two halfwords and splat to doubleword +PPC_zlwhsplatdu, // Vector load word into two halfwords and splat to doubleword with update +PPC_zlwhsplatdx, // Vector load word into two halfwords and splat to doubleword indexed +PPC_zlwhsplatdmx, // Vector load word into two halfwords and splat to doubleword with modify indexed +PPC_zlwhsplatwd, // Vector load word as halfwords and splat words to doubleword +PPC_zlwhsplatwdu, // Vector load word as halfwords and splat words to doubleword with update +PPC_zlwhsplatwdx, // Vector load word as halfwords and splat words to doubleword indexed +PPC_zlwhsplatwdmx, // Vector load word as halfwords and splat words to doubleword with modify indexed +PPC_zlww, // Vector load word into word +PPC_zlwwu, // Vector load word into word with update +PPC_zlwwx, // Vector load word into word indexed +PPC_zlwwmx, // Vector load word into word with modify indexed +PPC_zlwwosd, // Vector load word as word odd signed to doubleword +PPC_zlwwosdu, // Vector load word as word odd signed to doubleword with update +PPC_zlwwosdx, // Vector load word as word odd signed to doubleword indexed +PPC_zlwwosdmx, // Vector load word as word odd signed to doubleword with modify indexed +PPC_zstdd, // Vector store double of double +PPC_zstddu, // Vector store double of double with update +PPC_zstddx, // Vector store double of double indexed +PPC_zstddmx, // Vector store double of double with modify indexed +PPC_zstdh, // Vector store double of four halfwords +PPC_zstdhu, // Vector store double of four halfwords with update +PPC_zstdhx, // Vector store double of four halfwords indexed +PPC_zstdhmx, // Vector store double of four halfwords with modify indexed +PPC_zstdw, // Vector store double of two words +PPC_zstdwu, // Vector store double of two words with update +PPC_zstdwx, // Vector store double of two words indexed +PPC_zstdwmx, // Vector store double of two words with modify indexed +PPC_zsthe, // Vector store halfword even +PPC_zstheu, // Vector store halfword even with update +PPC_zsthex, // Vector store halfword even indexed +PPC_zsthemx, // Vector store halfword even with modify indexed +PPC_zstho, // Vector store halfword odd +PPC_zsthou, // Vector store halfword odd with update +PPC_zsthox, // Vector store halfword odd indexed +PPC_zsthomx, // Vector store halfword odd with modify indexed +PPC_zstwh, // Vector store word of two halfwords +PPC_zstwhu, // Vector store word of two halfwords with update +PPC_zstwhx, // Vector store word of two halfwords indexed +PPC_zstwhmx, // Vector store word of two halfwords with modify indexed +PPC_zstwhed, // Vector store word of two halfwords even from double +PPC_zstwhedu, // Vector store word of two halfwords even from double with update +PPC_zstwhedx, // Vector store word of two halfwords even from double indexed +PPC_zstwhedmx, // Vector store word of two halfwords even from double with modify indexed +PPC_zstwhod, // Vector store word of two halfwords odd from double +PPC_zstwhodu, // Vector store word of two halfwords odd from double with update +PPC_zstwhodx, // Vector store word of two halfwords odd from double indexed +PPC_zstwhodmx, // Vector store word of two halfwords odd from double with modify indexed +PPC_zstww, // Vector store word of word +PPC_zstwwu, // Vector store word of word with update +PPC_zstwwx, // Vector store word of word indexed +PPC_zstwwmx, // Vector store word of word with modify indexed +PPC_zmhegui, // Multiply halfwords, even guarded, unsigned integer +PPC_zmhegsi, // Multiply halfwords, even guarded, signed integer +PPC_zmhegsui, // Multiply halfwords, even guarded, signed by unsigned integer +PPC_zmhegsmf, // Multiply halfwords, even guarded, signed modulo fractional +PPC_zmheogui, // Multiply halfwords, even/odd guarded, unsigned integer +PPC_zmheogsi, // Multiply halfwords, even/odd guarded, signed integer +PPC_zmheogsui, // Multiply halfwords, even/odd guarded, signed by unsigned integer +PPC_zmheogsmf, // Multiply halfwords, even/odd guarded, signed modulo fractional +PPC_zmhogui, // Multiply halfwords, odd guarded, unsigned integer +PPC_zmhogsi, // Multiply halfwords, odd guarded, signed integer +PPC_zmhogsui, // Multiply halfwords, odd guarded, signed by unsigned integer +PPC_zmhogsmf, // Multiply halfwords, odd guarded, signed modulo fractional +PPC_zmheguiaa, // Multiply halfwords, even guarded, unsigned integer and accumulate +PPC_zmheguian, // Multiply halfwords, even guarded, unsigned integer and accumulate negative +PPC_zmhegsiaa, // Multiply halfwords, even guarded, signed integer and accumulate +PPC_zmhegsian, // Multiply halfwords, even guarded, signed integer and accumulate negative +PPC_zmhegsuiaa, // Multiply halfwords, even guarded, signed by unsigned integer and accumulate +PPC_zmhegsuian, // Multiply halfwords, even guarded, signed by unsigned integer and accumulate negative +PPC_zmhegsmfaa, // Multiply halfwords, even guarded, signed modulo fractional and accumulate +PPC_zmhegsmfan, // Multiply halfwords, even guarded, signed modulo fractional and accumulate negative +PPC_zmheoguiaa, // Multiply halfwords, even/odd guarded, unsigned integer and accumulate +PPC_zmheoguian, // Multiply halfwords, even/odd guarded, unsigned integer and accumulate negative +PPC_zmheogsiaa, // Multiply halfwords, even/odd guarded, signed integer and accumulate +PPC_zmheogsian, // Multiply halfwords, even/odd guarded, signed integer and accumulate negative +PPC_zmheogsuiaa, // Multiply halfwords, even/odd guarded, signed by unsigned integer and accumulate +PPC_zmheogsuian, // Multiply halfwords, even/odd guarded, signed by unsigned integer and accumulate negative +PPC_zmheogsmfaa, // Multiply halfwords, even/odd guarded, signed modulo fractional and accumulate +PPC_zmheogsmfan, // Multiply halfwords, even/odd guarded, signed modulo fractional and accumulate negative +PPC_zmhoguiaa, // Multiply halfwords, odd guarded, unsigned integer and accumulate +PPC_zmhoguian, // Multiply halfwords, odd guarded, unsigned integer and accumulate negative +PPC_zmhogsiaa, // Multiply halfwords, odd guarded, signed integer and accumulate +PPC_zmhogsian, // Multiply halfwords, odd guarded, signed integer and accumulate negative +PPC_zmhogsuiaa, // Multiply halfwords, odd guarded, signed by unsigned integer and accumulate +PPC_zmhogsuian, // Multiply halfwords, odd guarded, signed by unsigned integer and accumulate negative +PPC_zmhogsmfaa, // Multiply halfwords, odd guarded, signed modulo fractional and accumulate +PPC_zmhogsmfan, // Multiply halfwords, odd guarded, signed modulo fractional and accumulate negative +PPC_zmhegwsmf, // Multiply halfwords, even guarded to word, signed modulo fractional +PPC_zmheogwsmf, // Multiply halfwords, even/odd guarded to word, signed modulo fractional +PPC_zmhogwsmf, // Multiply halfwords, odd guarded to word, signed modulo fractional +PPC_zmhegwsmfr, // Multiply halfwords, even guarded to word, signed modulo fractional with round +PPC_zmheogwsmfr, // Multiply halfwords, even/odd guarded to word, signed modulo fractional with round +PPC_zmhogwsmfr, // Multiply halfwords, odd guarded to word, signed modulo fractional with round +PPC_zmhegwsmfaa, // Multiply halfwords, even guarded to word, signed modulo fractional and accumulate +PPC_zmheogwsmfaa, // Multiply halfwords, even/odd guarded to word, signed modulo fractional and accumulate +PPC_zmhogwsmfaa, // Multiply halfwords, odd guarded to word, signed modulo fractional and accumulate +PPC_zmhegwsmfan, // Multiply halfwords, even guarded to word, signed modulo fractional and accumulate negative +PPC_zmheogwsmfan, // Multiply halfwords, even/odd guarded to word, signed modulo fractional and accumulate negative +PPC_zmhogwsmfan, // Multiply halfwords, odd guarded to word, signed modulo fractional and accumulate negative +PPC_zmhegwsmfraa, // Multiply halfwords, even guarded to word, signed modulo fractional with round and accumulate +PPC_zmheogwsmfraa, // Multiply halfwords, even/odd guarded to word, signed modulo fractional with round and accumulate +PPC_zmhogwsmfraa, // Multiply halfwords, odd guarded to word, signed modulo fractional with round and accumulate +PPC_zmhegwsmfran, // Multiply halfwords, even guarded to word, signed modulo fractional with round and accumulate negative +PPC_zmheogwsmfran, // Multiply halfwords, even/odd guarded to word, signed modulo fractional with round and accumulate negative +PPC_zmhogwsmfran, // Multiply halfwords, odd guarded to word, signed modulo fractional with round and accumulate negative +PPC_zmhesf, // Multiply halfwords, even, signed fractional +PPC_zmheosf, // Multiply halfwords, even/odd, signed fractional +PPC_zmhosf, // Multiply halfwords, odd, signed fractional +PPC_zmhesfr, // Multiply halfwords, even, signed fractional with round +PPC_zmheosfr, // Multiply halfwords, even/odd, signed fractional with round +PPC_zmhosfr, // Multiply halfwords, odd, signed fractional with round +PPC_zmhesfaas, // Multiply halfwords, even signed fractional and accumulate with saturate +PPC_zmheosfaas, // Multiply halfwords, even/odd signed fractional and accumulate with saturate +PPC_zmhosfaas, // Multiply halfwords, odd signed fractional and accumulate with saturate +PPC_zmhesfans, // Multiply halfwords, even signed fractional and accumulate negative with saturate +PPC_zmheosfans, // Multiply halfwords, even/odd signed fractional and accumulate negative with saturate +PPC_zmhosfans, // Multiply halfwords, odd signed fractional and accumulate negative with saturate +PPC_zmhesfraas, // Multiply halfwords, even signed fractional with round and accumulate with saturate +PPC_zmheosfraas, // Multiply halfwords, even/odd signed fractional with round and accumulate with saturate +PPC_zmhosfraas, // Multiply halfwords, odd signed fractional with round and accumulate with saturate +PPC_zmhesfrans, // Multiply halfwords, even signed fractional with round and accumulate negative with saturate +PPC_zmheosfrans, // Multiply halfwords, even/odd signed fractional with round and accumulate negative with saturate +PPC_zmhosfrans, // Multiply halfwords, odd signed fractional with round and accumulate negative with saturate +PPC_zmhesi, // Multiply halfwords, even, signed integer +PPC_zmheosi, // Multiply halfwords, even/odd, signed integer +PPC_zmhosi, // Multiply halfwords, odd, signed integer +PPC_zmhesui, // Multiply halfwords, even, signed by unsigned integer +PPC_zmheosui, // Multiply halfwords, even/odd, signed by unsigned integer +PPC_zmhosui, // Multiply halfwords, odd, signed by unsigned integer +PPC_zmheui, // Multiply halfwords, even, unsigned integer +PPC_zmheoui, // Multiply halfwords, even/odd, unsigned integer +PPC_zmhoui, // Multiply halfwords, odd, unsigned integer +PPC_zmhesiaa, // Multiply halfwords, even, signed integer and accumulate +PPC_zmheosiaa, // Multiply halfwords, even/odd, signed integer and accumulate +PPC_zmhosiaa, // Multiply halfwords, odd, signed integer and accumulate +PPC_zmhesian, // Multiply halfwords, even, signed integer and accumulate negative +PPC_zmheosian, // Multiply halfwords, even/odd, signed integer and accumulate negative +PPC_zmhosian, // Multiply halfwords, odd, signed integer and accumulate negative +PPC_zmhesuiaa, // Multiply halfwords, even, signed by unsigned integer and accumulate +PPC_zmheosuiaa, // Multiply halfwords, even/odd, signed by unsigned integer and accumulate +PPC_zmhosuiaa, // Multiply halfwords, odd, signed by unsigned integer and accumulate +PPC_zmhesuian, // Multiply halfwords, even, signed by unsigned integer and accumulate negative +PPC_zmheosuian, // Multiply halfwords, even/odd, signed by unsigned integer and accumulate negative +PPC_zmhosuian, // Multiply halfwords, odd, signed by unsigned integer and accumulate negative +PPC_zmheuiaa, // Multiply halfwords, even, unsigned integer and accumulate +PPC_zmheouiaa, // Multiply halfwords, even/odd, unsigned integer and accumulate +PPC_zmhouiaa, // Multiply halfwords, odd, unsigned integer and accumulate +PPC_zmheuian, // Multiply halfwords, even, unsigned integer and accumulate negative +PPC_zmheouian, // Multiply halfwords, even/odd, unsigned integer and accumulate negative +PPC_zmhouian, // Multiply halfwords, odd, unsigned integer and accumulate negative +PPC_zmhesiaas, // Multiply halfwords, even, signed integer and accumulate with saturate +PPC_zmheosiaas, // Multiply halfwords, even/odd, signed integer and accumulate with saturate +PPC_zmhosiaas, // Multiply halfwords, odd, signed integer and accumulate with saturate +PPC_zmhesians, // Multiply halfwords, even, signed integer and accumulate negative with saturate +PPC_zmheosians, // Multiply halfwords, even/odd, signed integer and accumulate negative with saturate +PPC_zmhosians, // Multiply halfwords, odd, signed integer and accumulate negative with saturate +PPC_zmhesuiaas, // Multiply halfwords, even, signed by unsigned integer and accumulate with saturate +PPC_zmheosuiaas, // Multiply halfwords, even/odd, signed by unsigned integer and accumulate with saturate +PPC_zmhosuiaas, // Multiply halfwords, odd, signed by unsigned integer and accumulate with saturate +PPC_zmhesuians, // Multiply halfwords, even, signed by unsigned integer and accumulate negative with saturate +PPC_zmheosuians, // Multiply halfwords, even/odd, signed by unsigned integer and accumulate negative with saturate +PPC_zmhosuians, // Multiply halfwords, odd, signed by unsigned integer and accumulate negative with saturate +PPC_zmheuiaas, // Multiply halfwords, even, unsigned integer and accumulate with saturate +PPC_zmheouiaas, // Multiply halfwords, even/odd, unsigned integer and accumulate with saturate +PPC_zmhouiaas, // Multiply halfwords, odd, unsigned integer and accumulate with saturate +PPC_zmheuians, // Multiply halfwords, even, unsigned integer and accumulate negative with saturate +PPC_zmheouians, // Multiply halfwords, even/odd, unsigned integer and accumulate negative with saturate +PPC_zmhouians, // Multiply halfwords, odd, unsigned integer and accumulate negative with saturate +PPC_zvmhsfh, // Vector multiply halfword signed, fractional, to halfword +PPC_zvmhsfrh, // Vector multiply halfword signed, fractional and round, to halfword +PPC_zvmhsfaahs, // Vector multiply halfword, signed fractional and accumulate to halfword with saturate +PPC_zvmhsfanhs, // Vector multiply halfword, signed fractional and accumulate negative to halfword with saturate +PPC_zvmhsfraahs, // Vector multiply halfword, signed fractional with round and accumulate to halfword with saturate +PPC_zvmhsfranhs, // Vector multiply halfword, signed fractional with round and accumulate negative to halfword with saturate +PPC_zvmhsih, // Vector multiply halfword signed integer to halfword +PPC_zvmhsuih, // Vector multiply halfword signed by unsigned integer to halfword +PPC_zvmhuih, // Vector multiply halfword unsigned integer to halfword +PPC_zvmhsihs, // Vector multiply halfword signed integer to halfword with saturate +PPC_zvmhsuihs, // Vector multiply halfword signed by unsigned integer to halfword with saturate +PPC_zvmhuihs, // Vector multiply halfword unsigned integer to halfword with saturate +PPC_zvmhsiaah, // Vector multiply halfword signed integer and accumulate to halfword +PPC_zvmhsuiaah, // Vector multiply halfword signed by unsigned integer and accumulate to halfword +PPC_zvmhuiaah, // Vector multiply halfword unsigned integer and accumulate to halfword +PPC_zvmhsianh, // Vector multiply halfword signed integer and accumulate negative to halfword +PPC_zvmhsuianh, // Vector multiply halfword signed by unsigned integer and accumulate negative to halfword +PPC_zvmhuianh, // Vector multiply halfword unsigned integer and accumulate negative to halfword +PPC_zvmhsiaahs, // Vector multiply halfword signed integer and accumulate to halfword with saturate +PPC_zvmhsuiaahs, // Vector multiply halfword signed by unsigned integer and accumulate to halfword with saturate +PPC_zvmhuiaahs, // Vector multiply halfword unsigned integer and accumulate to halfword with saturate +PPC_zvmhsianhs, // Vector multiply halfword signed integer and accumulate negative to halfword with saturate +PPC_zvmhsuianhs, // Vector multiply halfword signed by unsigned integer and accumulate negative to halfword with saturate +PPC_zvmhuianhs, // Vector multiply halfword unsigned integer and accumulate negative to halfword with saturate +PPC_zmwgsi, // Multiply word guarded, signed integer +PPC_zmwgsui, // Multiply word guarded, signed by unsigned integer +PPC_zmwgui, // Multiply word guarded, unsigned integer +PPC_zmwgsiaa, // Multiply word guarded, signed integer and accumulate +PPC_zmwgsian, // Multiply word guarded, signed integer and accumulate negative +PPC_zmwgsuiaa, // Multiply word guarded, signed by unsigned integer and accumulate +PPC_zmwgsuian, // Multiply word guarded, signed by unsigned integer and accumulate negative +PPC_zmwguiaa, // Multiply word guarded, unsigned integer and accumulate +PPC_zmwguian, // Multiply word guarded, unsigned integer and accumulate negative +PPC_zmwgsiaas, // Multiply word guarded, signed integer and accumulate with saturate +PPC_zmwgsians, // Multiply word guarded, signed integer and accumulate negative with saturate +PPC_zmwgsuiaas, // Multiply word guarded, signed by unsigned integer and accumulate with saturate +PPC_zmwgsuians, // Multiply word guarded, signed by unsigned integer and accumulate negative with saturate +PPC_zmwguiaas, // Multiply word guarded, unsigned integer and accumulate with saturate +PPC_zmwguians, // Multiply word guarded, unsigned integer and accumulate negative with saturate +PPC_zmwgsmf, // Multiply word guarded signed, modulo, fractional +PPC_zmwgsmfr, // Multiply word guarded signed, modulo, fractional with round +PPC_zmwgsmfaa, // Multiply word guarded signed, modulo, fractional and accumulate +PPC_zmwgsmfan, // Multiply word guarded signed, modulo, fractional and accumulate negative +PPC_zmwgsmfraa, // Multiply word guarded signed, modulo, fractional with round and accumulate +PPC_zmwgsmfran, // Multiply word guarded signed, modulo, fractional with round and accumulate negative +PPC_zmwlsis, // Multiply word low signed integer with saturate +PPC_zmwlsuis, // Multiply word low signed by unsigned integer with saturate +PPC_zmwluis, // Multiply word low unsigned integer with saturate +PPC_zmwlsiaa, // Multiply word low signed integer and and accumulate +PPC_zmwlsuiaa, // Multiply word low signed by unsigned integer and and accumulate +PPC_zmwluiaa, // Multiply word low unsigned integer and and accumulate +PPC_zmwlsian, // Multiply word low signed integer and and accumulate negative +PPC_zmwlsuian, // Multiply word low signed by unsigned integer and and accumulate negative +PPC_zmwluian, // Multiply word low unsigned integer and and accumulate negative +PPC_zmwlsiaas, // Multiply word low signed integer and and accumulate with saturate +PPC_zmwlsuiaas, // Multiply word low signed by unsigned integer and and accumulate with saturate +PPC_zmwluiaas, // Multiply word low unsigned integer and and accumulate with saturate +PPC_zmwlsians, // Multiply word low signed integer and and accumulate negative with saturate +PPC_zmwlsuians, // Multiply word low signed by unsigned integer and and accumulate negative with saturate +PPC_zmwluians, // Multiply word low unsigned integer and and accumulate negative with saturate +PPC_zmwsf, // Multiply word signed, fractional +PPC_zmwsfr, // Multiply word signed, fractional with round +PPC_zmwsfaas, // Multiply word signed, fractional and accumulate with saturate +PPC_zmwsfans, // Multiply word signed, fractional and accumulate negative with saturate +PPC_zmwsfraas, // Multiply word signed, fractional with round and accumulate with saturate +PPC_zmwsfrans, // Multiply word signed, fractional with round and accumulate negative with saturate +PPC_zvmhulgwsmf, // Vector multiply halfwords, upper/lower guarded-word, signed modulo fractional +PPC_zvmhllgwsmf, // Vector multiply halfwords, lower/lower guarded-word, signed modulo fractional +PPC_zvmhuugwsmf, // Vector multiply halfwords, upper/upper guarded-word, signed modulo fractional +PPC_zvmhxlgwsmf, // Vector multiply halfwords, exchanged/lower guarded-word, signed modulo fractional +PPC_zvmhulgwsmfr, // Vector multiply halfwords, upper/lower guarded-word, signed modulo fractional with round +PPC_zvmhllgwsmfr, // Vector multiply halfwords, lower/lower guarded-word, signed modulo fractional with round +PPC_zvmhuugwsmfr, // Vector multiply halfwords, upper/upper guarded-word, signed modulo fractional with round +PPC_zvmhxlgwsmfr, // Vector multiply halfwords, exchanged/lower guarded-word, signed modulo fractional with round +PPC_zvmhulgwsmfaa, // Vector multiply halfwords, upper/lower guarded-word, signed modulo fractional and accumulate +PPC_zvmhllgwsmfaa, // Vector multiply halfwords, lower/lower guarded-word, signed modulo fractional and accumulate +PPC_zvmhuugwsmfaa, // Vector multiply halfwords, upper/upper guarded-word, signed modulo fractional and accumulate +PPC_zvmhxlgwsmfaa, // Vector multiply halfwords, exchanged/lower guarded-word, signed modulo fractional and accumulate +PPC_zvmhulgwsmfraa, // Vector multiply halfwords, upper/lower guarded-word, signed modulo fractional with round and accumulate +PPC_zvmhllgwsmfraa, // Vector multiply halfwords, lower/lower guarded-word, signed modulo fractional with round and accumulate +PPC_zvmhuugwsmfraa, // Vector multiply halfwords, upper/upper guarded-word, signed modulo fractional with round and accumulate +PPC_zvmhxlgwsmfraa, // Vector multiply halfwords, exchanged/lower guarded-word, signed modulo fractional with round and accumulate +PPC_zvmhulgwsmfan, // Vector multiply halfwords, upper/lower guarded-word, signed modulo fractional and accumulate negative +PPC_zvmhllgwsmfan, // Vector multiply halfwords, lower/lower guarded-word, signed modulo fractional and accumulate negative +PPC_zvmhuugwsmfan, // Vector multiply halfwords, upper/upper guarded-word, signed modulo fractional and accumulate negative +PPC_zvmhxlgwsmfan, // Vector multiply halfwords, exchanged/lower guarded-word, signed modulo fractional and accumulate negative +PPC_zvmhulgwsmfran, // Vector multiply halfwords, upper/lower guarded-word, signed modulo fractional with round and accumulate negative +PPC_zvmhllgwsmfran, // Vector multiply halfwords, lower/lower guarded-word, signed modulo fractional with round and accumulate negative +PPC_zvmhuugwsmfran, // Vector multiply halfwords, upper/upper guarded-word, signed modulo fractional with round and accumulate negative +PPC_zvmhxlgwsmfran, // Vector multiply halfwords, exchanged/lower guarded-word, signed modulo fractional with round and accumulate negative +PPC_zvmhulgwsmfanp, // Vector multiply halfwords, upper/lower guarded-word, signed modulo fractional and accumulate negative/positive +PPC_zvmhllgwsmfanp, // Vector multiply halfwords, lower/lower guarded-word, signed modulo fractional and accumulate negative/positive +PPC_zvmhuugwsmfanp, // Vector multiply halfwords, upper/upper guarded-word, signed modulo fractional and accumulate negative/positive +PPC_zvmhxlgwsmfanp, // Vector multiply halfwords, exchanged/lower guarded-word, signed modulo fractional and accumulate negative/positive +PPC_zvmhulgwsmfranp, // Vector multiply halfwords, upper/lower guarded-word, signed modulo fractional with round and accumulate negative/positive +PPC_zvmhllgwsmfranp, // Vector multiply halfwords, lower/lower guarded-word, signed modulo fractional with round and accumulate negative/positive +PPC_zvmhuugwsmfranp, // Vector multiply halfwords, upper/upper guarded-word, signed modulo fractional with round and accumulate negative/positive +PPC_zvmhxlgwsmfranp, // Vector multiply halfwords, exchanged/lower guarded-word, signed modulo fractional with round and accumulate negative/positive +PPC_zvmhulsf, // Vector multiply halfwords, upper/lower, signed fractional +PPC_zvmhllsf, // Vector multiply halfwords, lower/lower, signed fractional +PPC_zvmhuusf, // Vector multiply halfwords, upper/upper, signed fractional +PPC_zvmhxlsf, // Vector multiply halfwords, exchanged/lower, signed fractional +PPC_zvmhulsfr, // Vector multiply halfwords, upper/lower, signed fractional with round +PPC_zvmhllsfr, // Vector multiply halfwords, lower/lower, signed fractional with round +PPC_zvmhuusfr, // Vector multiply halfwords, upper/upper, signed fractional with round +PPC_zvmhxlsfr, // Vector multiply halfwords, exchanged/lower, signed fractional with round +PPC_zvmhulsfaas, // Vector multiply halfwords, upper/lower, signed fractional and accumulate with saturate +PPC_zvmhllsfaas, // Vector multiply halfwords, lower/lower, signed fractional and accumulate with saturate +PPC_zvmhuusfaas, // Vector multiply halfwords, upper/upper, signed fractional and accumulate with saturate +PPC_zvmhxlsfaas, // Vector multiply halfwords, exchanged/lower, signed fractional and accumulate with saturate +PPC_zvmhulsfans, // Vector multiply halfwords, upper/lower, signed fractional and accumulate negative with saturate +PPC_zvmhllsfans, // Vector multiply halfwords, lower/lower, signed fractional and accumulate negative with saturate +PPC_zvmhuusfans, // Vector multiply halfwords, upper/upper, signed fractional and accumulate negative with saturate +PPC_zvmhxlsfans, // Vector multiply halfwords, exchanged/lower, signed fractional and accumulate negative with saturate +PPC_zvmhulsfanps, // Vector multiply halfwords, upper/lower, signed fractional and accumulate negative/positive with saturate +PPC_zvmhllsfanps, // Vector multiply halfwords, lower/lower, signed fractional and accumulate negative/positive with saturate +PPC_zvmhuusfanps, // Vector multiply halfwords, upper/upper, signed fractional and accumulate negative/positive with saturate +PPC_zvmhxlsfanps, // Vector multiply halfwords, exchanged/lower, signed fractional and accumulate negative/positive with saturate +PPC_zvmhulsfraas, // Vector multiply halfwords, upper/lower, signed fractional round (to halfword) and accumulate with saturate +PPC_zvmhllsfraas, // Vector multiply halfwords, lower/lower, signed fractional round (to halfword) and accumulate with saturate +PPC_zvmhuusfraas, // Vector multiply halfwords, upper/upper, signed fractional round (to halfword) and accumulate with saturate +PPC_zvmhxlsfraas, // Vector multiply halfwords, exchanged/lower, signed fractional round (to halfword) and accumulate with saturate +PPC_zvmhulsfrans, // Vector multiply halfwords, upper/lower, signed fractional round (to halfword) and accumulate negative with saturate +PPC_zvmhllsfrans, // Vector multiply halfwords, lower/lower, signed fractional round (to halfword) and accumulate negative with saturate +PPC_zvmhuusfrans, // Vector multiply halfwords, upper/upper, signed fractional round (to halfword) and accumulate negative with saturate +PPC_zvmhxlsfrans, // Vector multiply halfwords, exchanged/lower, signed fractional round (to halfword) and accumulate negative with saturate +PPC_zvmhulsfranps, // Vector multiply halfwords, upper/lower, signed fractional round (to halfword) and accumulate negative/positive with saturate +PPC_zvmhllsfranps, // Vector multiply halfwords, lower/lower, signed fractional round (to halfword) and accumulate negative/positive with saturate +PPC_zvmhuusfranps, // Vector multiply halfwords, upper/upper, signed fractional round (to halfword) and accumulate negative/positive with saturate +PPC_zvmhxlsfranps, // Vector multiply halfwords, exchanged/lower, signed fractional round (to halfword) and accumulate negative/positive with saturate +PPC_zvmhulsi, // Vector multiply halfwords, upper/lower, signed integer +PPC_zvmhllsi, // Vector multiply halfwords, lower/lower, signed integer +PPC_zvmhuusi, // Vector multiply halfwords, upper/upper, signed integer +PPC_zvmhxlsi, // Vector multiply halfwords, exchanged/lower, signed integer +PPC_zvmhulsui, // Vector multiply halfwords, upper/lower, signed by unsigned integer +PPC_zvmhllsui, // Vector multiply halfwords, lower/lower, signed by unsigned integer +PPC_zvmhuusui, // Vector multiply halfwords, upper/upper, signed by unsigned integer +PPC_zvmhxlsui, // Vector multiply halfwords, exchanged/lower, signed by unsigned integer +PPC_zvmhului, // Vector multiply halfwords, upper/lower, unsigned integer +PPC_zvmhllui, // Vector multiply halfwords, lower/lower, unsigned integer +PPC_zvmhuuui, // Vector multiply halfwords, upper/upper, unsigned integer +PPC_zvmhxlui, // Vector multiply halfwords, exchanged/lower, unsigned integer +PPC_zvmhulsiaa, // Vector multiply halfwords, upper/lower, signed integer and accumulate +PPC_zvmhllsiaa, // Vector multiply halfwords, lower/lower, signed integer and accumulate +PPC_zvmhuusiaa, // Vector multiply halfwords, upper/upper, signed integer and accumulate +PPC_zvmhxlsiaa, // Vector multiply halfwords, exchanged/lower, signed integer and accumulate +PPC_zvmhulsian, // Vector multiply halfwords, upper/lower, signed integer and accumulate negative +PPC_zvmhllsian, // Vector multiply halfwords, lower/lower, signed integer and accumulate negative +PPC_zvmhuusian, // Vector multiply halfwords, upper/upper, signed integer and accumulate negative +PPC_zvmhxlsian, // Vector multiply halfwords, exchanged/lower, signed integer and accumulate negative +PPC_zvmhulsianp, // Vector multiply halfwords, upper/lower, signed integer and accumulate negative/positive +PPC_zvmhllsianp, // Vector multiply halfwords, lower/lower, signed integer and accumulate negative/positive +PPC_zvmhuusianp, // Vector multiply halfwords, upper/upper, signed integer and accumulate negative/positive +PPC_zvmhxlsianp, // Vector multiply halfwords, exchanged/lower, signed integer and accumulate negative/positive +PPC_zvmhulsuiaa, // Vector multiply halfwords, upper/lower, signed by unsigned integer and accumulate +PPC_zvmhllsuiaa, // Vector multiply halfwords, lower/lower, signed by unsigned integer and accumulate +PPC_zvmhuusuiaa, // Vector multiply halfwords, upper/upper, signed by unsigned integer and accumulate +PPC_zvmhxlsuiaa, // Vector multiply halfwords, exchanged/lower, signed by unsigned integer and accumulate +PPC_zvmhulsuian, // Vector multiply halfwords, upper/lower, signed by unsigned integer and accumulate negative +PPC_zvmhllsuian, // Vector multiply halfwords, lower/lower, signed by unsigned integer and accumulate negative +PPC_zvmhuusuian, // Vector multiply halfwords, upper/upper, signed by unsigned integer and accumulate negative +PPC_zvmhxlsuian, // Vector multiply halfwords, exchanged/lower, signed by unsigned integer and accumulate negative +PPC_zvmhulsuianp, // Vector multiply halfwords, upper/lower, signed by unsigned integer and accumulate negative/positive +PPC_zvmhllsuianp, // Vector multiply halfwords, lower/lower, signed by unsigned integer and accumulate negative/positive +PPC_zvmhuusuianp, // Vector multiply halfwords, upper/upper, signed by unsigned integer and accumulate negative/positive +PPC_zvmhxlsuianp, // Vector multiply halfwords, exchanged/lower, signed by unsigned integer and accumulate negative/positive +PPC_zvmhuluiaa, // Vector multiply halfwords, upper/lower, unsigned integer and accumulate +PPC_zvmhlluiaa, // Vector multiply halfwords, lower/lower, unsigned integer and accumulate +PPC_zvmhuuuiaa, // Vector multiply halfwords, upper/upper, unsigned integer and accumulate +PPC_zvmhxluiaa, // Vector multiply halfwords, exchanged/lower, unsigned integer and accumulate +PPC_zvmhuluian, // Vector multiply halfwords, upper/lower, unsigned integer and accumulate negative +PPC_zvmhlluian, // Vector multiply halfwords, lower/lower, unsigned integer and accumulate negative +PPC_zvmhuuuian, // Vector multiply halfwords, upper/upper, unsigned integer and accumulate negative +PPC_zvmhxluian, // Vector multiply halfwords, exchanged/lower, unsigned integer and accumulate negative +PPC_zvmhuluianp, // Vector multiply halfwords, upper/lower, unsigned integer and accumulate negative/positive +PPC_zvmhlluianp, // Vector multiply halfwords, lower/lower, unsigned integer and accumulate negative/positive +PPC_zvmhuuuianp, // Vector multiply halfwords, upper/upper, unsigned integer and accumulate negative/positive +PPC_zvmhxluianp, // Vector multiply halfwords, exchanged/lower, unsigned integer and accumulate negative/positive +PPC_zvmhulsiaas, // Vector multiply halfwords, upper/lower, signed integer and accumulate with saturate +PPC_zvmhllsiaas, // Vector multiply halfwords, lower/lower, signed integer and accumulate with saturate +PPC_zvmhuusiaas, // Vector multiply halfwords, upper/upper, signed integer and accumulate with saturate +PPC_zvmhxlsiaas, // Vector multiply halfwords, exchanged/lower, signed integer and accumulate with saturate +PPC_zvmhulsians, // Vector multiply halfwords, upper/lower, signed integer and accumulate negative with saturate +PPC_zvmhllsians, // Vector multiply halfwords, lower/lower, signed integer and accumulate negative with saturate +PPC_zvmhuusians, // Vector multiply halfwords, upper/upper, signed integer and accumulate negative with saturate +PPC_zvmhxlsians, // Vector multiply halfwords, exchanged/lower, signed integer and accumulate negative with saturate +PPC_zvmhulsianps, // Vector multiply halfwords, upper/lower, signed integer and accumulate negative/positive with saturate +PPC_zvmhllsianps, // Vector multiply halfwords, lower/lower, signed integer and accumulate negative/positive with saturate +PPC_zvmhuusianps, // Vector multiply halfwords, upper/upper, signed integer and accumulate negative/positive with saturate +PPC_zvmhxlsianps, // Vector multiply halfwords, exchanged/lower, signed integer and accumulate negative/positive with saturate +PPC_zvmhulsuiaas, // Vector multiply halfwords, upper/lower, signed by unsigned integer and accumulate with saturate +PPC_zvmhllsuiaas, // Vector multiply halfwords, lower/lower, signed by unsigned integer and accumulate with saturate +PPC_zvmhuusuiaas, // Vector multiply halfwords, upper/upper, signed by unsigned integer and accumulate with saturate +PPC_zvmhxlsuiaas, // Vector multiply halfwords, exchanged/lower, signed by unsigned integer and accumulate with saturate +PPC_zvmhulsuians, // Vector multiply halfwords, upper/lower, signed by unsigned integer and accumulate negative with saturate +PPC_zvmhllsuians, // Vector multiply halfwords, lower/lower, signed by unsigned integer and accumulate negative with saturate +PPC_zvmhuusuians, // Vector multiply halfwords, upper/upper, signed by unsigned integer and accumulate negative with saturate +PPC_zvmhxlsuians, // Vector multiply halfwords, exchanged/lower, signed by unsigned integer and accumulate negative with saturate +PPC_zvmhulsuianps, // Vector multiply halfwords, upper/lower, signed by unsigned integer and accumulate negative/positive with saturate +PPC_zvmhllsuianps, // Vector multiply halfwords, lower/lower, signed by unsigned integer and accumulate negative/positive with saturate +PPC_zvmhuusuianps, // Vector multiply halfwords, upper/upper, signed by unsigned integer and accumulate negative/positive with saturate +PPC_zvmhxlsuianps, // Vector multiply halfwords, exchanged/lower, signed by unsigned integer and accumulate negative/positive with saturate +PPC_zvmhuluiaas, // Vector multiply halfwords, upper/lower, unsigned integer and accumulate with saturate +PPC_zvmhlluiaas, // Vector multiply halfwords, lower/lower, unsigned integer and accumulate with saturate +PPC_zvmhuuuiaas, // Vector multiply halfwords, upper/upper, unsigned integer and accumulate with saturate +PPC_zvmhxluiaas, // Vector multiply halfwords, exchanged/lower, unsigned integer and accumulate with saturate +PPC_zvmhuluians, // Vector multiply halfwords, upper/lower, unsigned integer and accumulate negative with saturate +PPC_zvmhlluians, // Vector multiply halfwords, lower/lower, unsigned integer and accumulate negative with saturate +PPC_zvmhuuuians, // Vector multiply halfwords, upper/upper, unsigned integer and accumulate negative with saturate +PPC_zvmhxluians, // Vector multiply halfwords, exchanged/lower, unsigned integer and accumulate negative with saturate +PPC_zvmhuluianps, // Vector multiply halfwords, upper/lower, unsigned integer and accumulate negative/positive with saturate +PPC_zvmhlluianps, // Vector multiply halfwords, lower/lower, unsigned integer and accumulate negative/positive with saturate +PPC_zvmhuuuianps, // Vector multiply halfwords, upper/upper, unsigned integer and accumulate negative/positive with saturate +PPC_zvmhxluianps, // Vector multiply halfwords, exchanged/lower, unsigned integer and accumulate negative/positive with saturate +PPC_zvdotphgaui, // Vector dot product of halfwords, guarded, add, unsigned integer +PPC_zvdotphxgaui, // Vector dot product of halfwords exchanged, guarded, add, unsigned integer +PPC_zvdotphgasi, // Vector dot product of halfwords, guarded, add, signed integer +PPC_zvdotphxgasi, // Vector dot product of halfwords exchanged, guarded, add, signed integer +PPC_zvdotphgasui, // Vector dot product of halfwords, guarded, add, signed by unsigned integer +PPC_zvdotphxgasui, // Vector dot product of halfwords exchanged, guarded, add, signed by unsigned integer +PPC_zvdotphgasmf, // Vector dot product of halfwords, guarded, add, signed modulo fractional +PPC_zvdotphxgasmf, // Vector dot product of halfwords exchanged, guarded, add, signed modulo fractional +PPC_zvdotphgauiaa, // Vector dot product of halfwords, guarded, add, unsigned integer and accumulate +PPC_zvdotphxgauiaa, // Vector dot product of halfwords exchanged, guarded, add, unsigned integer and accumulate +PPC_zvdotphgasiaa, // Vector dot product of halfwords, guarded, add, signed integer and accumulate +PPC_zvdotphxgasiaa, // Vector dot product of halfwords exchanged, guarded, add, signed integer and accumulate +PPC_zvdotphgasuiaa, // Vector dot product of halfwords, guarded, add, signed by unsigned integer and accumulate +PPC_zvdotphxgasuiaa, // Vector dot product of halfwords exchanged, guarded, add, signed by unsigned integer and accumulate +PPC_zvdotphgasmfaa, // Vector dot product of halfwords, guarded, add, signed modulo fractional and accumulate +PPC_zvdotphxgasmfaa, // Vector dot product of halfwords exchanged, guarded, add, signed modulo fractional and accumulate +PPC_zvdotphgauian, // Vector dot product of halfwords, guarded, add, unsigned integer and accumulate negative +PPC_zvdotphxgauian, // Vector dot product of halfwords exchanged, guarded, add, unsigned integer and accumulate negative +PPC_zvdotphgasian, // Vector dot product of halfwords, guarded, add, signed integer and accumulate negative +PPC_zvdotphxgasian, // Vector dot product of halfwords exchanged, guarded, add, signed integer and accumulate negative +PPC_zvdotphgasuian, // Vector dot product of halfwords, guarded, add, signed by unsigned integer and accumulate negative +PPC_zvdotphxgasuian, // Vector dot product of halfwords exchanged, guarded, add, signed by unsigned integer and accumulate negative +PPC_zvdotphgasmfan, // Vector dot product of halfwords, guarded, add, signed modulo fractional and accumulate negative +PPC_zvdotphxgasmfan, // Vector dot product of halfwords exchanged, guarded, add, signed modulo fractional and accumulate negative +PPC_zvdotphgsui, // Vector dot product of halfwords, guarded, subtract, unsigned integer +PPC_zvdotphgssi, // Vector dot product of halfwords, guarded, subtract, signed integer +PPC_zvdotphgssui, // Vector dot product of halfwords, guarded, subtract, signed by unsigned integer +PPC_zvdotphgssmf, // Vector dot product of halfwords, guarded, subtract, signed modulo fractional +PPC_zvdotphgsuiaa, // Vector dot product of halfwords, guarded, subtract, unsigned integer and accumulate +PPC_zvdotphgssiaa, // Vector dot product of halfwords, guarded, subtract, signed integer and accumulate +PPC_zvdotphgssuiaa, // Vector dot product of halfwords, guarded, subtract, signed by unsigned integer and accumulate +PPC_zvdotphgssmfaa, // Vector dot product of halfwords, guarded, subtract, signed modulo fractional and accumulate +PPC_zvdotphgsuian, // Vector dot product of halfwords, guarded, subtract, unsigned integer and accumulate negative +PPC_zvdotphgssian, // Vector dot product of halfwords, guarded, subtract, signed integer and accumulate negative +PPC_zvdotphgssuian, // Vector dot product of halfwords, guarded, subtract, signed by unsigned integer and accumulate negative +PPC_zvdotphgssmfan, // Vector dot product of halfwords, guarded, subtract, signed modulo fractional and accumulate negative +PPC_zvdotphasfs, // Vector dot product of halfwords, add, signed fractional, with saturate +PPC_zvdotphxasfs, // Vector dot product of halfwords exchanged, add, signed fractional, with saturate +PPC_zvdotphasfrs, // Vector dot product of halfwords, add, signed fractional with round, with saturate +PPC_zvdotphxasfrs, // Vector dot product of halfwords exchanged, add, signed fractional with round, with saturate +PPC_zvdotphasfaas, // Vector dot product of halfwords, add, signed fractional, and accumulate with saturate +PPC_zvdotphxasfaas, // Vector dot product of halfwords exchanged, add, signed fractional, and accumulate with saturate +PPC_zvdotphasfans, // Vector dot product of halfwords, add, signed fractional, and accumulate negative with saturate +PPC_zvdotphxasfans, // Vector dot product of halfwords exchanged, add, signed fractional, and accumulate negative with saturate +PPC_zvdotphasfraas, // Vector dot product of halfwords, add, signed fractional with round, and accumulate with saturate +PPC_zvdotphxasfraas, // Vector dot product of halfwords exchanged, add, signed fractional with round, and accumulate with saturate +PPC_zvdotphasfrans, // Vector dot product of halfwords, add, signed fractional with round, and accumulate negative with saturate +PPC_zvdotphxasfrans, // Vector dot product of halfwords exchanged, add, signed fractional with round, and accumulate negative with saturate +PPC_zvdotphaui, // Vector dot product of halfwords, add, unsigned integer +PPC_zvdotphxaui, // Vector dot product of halfwords exchanged, add, unsigned integer +PPC_zvdotphasi, // Vector dot product of halfwords, add, signed integer +PPC_zvdotphxasi, // Vector dot product of halfwords exchanged, add, signed integer +PPC_zvdotphasui, // Vector dot product of halfwords, add, signed by unsigned integer +PPC_zvdotphxasui, // Vector dot product of halfwords exchanged, add, signed by unsigned integer +PPC_zvdotphauiaa, // Vector dot product of halfwords, add, unsigned integer and accumulate +PPC_zvdotphxauiaa, // Vector dot product of halfwords exchanged, add, unsigned integer and accumulate +PPC_zvdotphasiaa, // Vector dot product of halfwords, add, signed integer and accumulate +PPC_zvdotphxasiaa, // Vector dot product of halfwords exchanged, add, signed integer and accumulate +PPC_zvdotphasuiaa, // Vector dot product of halfwords, add, signed by unsigned integer and accumulate +PPC_zvdotphxasuiaa, // Vector dot product of halfwords exchanged, add, signed by unsigned integer and accumulate +PPC_zvdotphauian, // Vector dot product of halfwords, add, unsigned integer and accumulate negative +PPC_zvdotphxauian, // Vector dot product of halfwords exchanged, add, unsigned integer and accumulate negative +PPC_zvdotphasian, // Vector dot product of halfwords, add, signed integer and accumulate negative +PPC_zvdotphxasian, // Vector dot product of halfwords exchanged, add, signed integer and accumulate negative +PPC_zvdotphasuian, // Vector dot product of halfwords, add, signed by unsigned integer and accumulate negative +PPC_zvdotphxasuian, // Vector dot product of halfwords exchanged, add, signed by unsigned integer and accumulate negative +PPC_zvdotphauis, // Vector dot product of halfwords, add, unsigned integer, with saturate +PPC_zvdotphxauis, // Vector dot product of halfwords exchanged, add, unsigned integer, with saturate +PPC_zvdotphasis, // Vector dot product of halfwords, add, signed integer, with saturate +PPC_zvdotphxasis, // Vector dot product of halfwords exchanged, add, signed integer, with saturate +PPC_zvdotphasuis, // Vector dot product of halfwords, add, signed by unsigned integer, with saturate +PPC_zvdotphxasuis, // Vector dot product of halfwords exchanged, add, signed by unsigned integer, with saturate +PPC_zvdotphauiaas, // Vector dot product of halfwords, add, unsigned integer and accumulate with saturate +PPC_zvdotphxauiaas, // Vector dot product of halfwords exchanged, add, unsigned integer and accumulate with saturate +PPC_zvdotphasiaas, // Vector dot product of halfwords, add, signed integer and accumulate with saturate +PPC_zvdotphxasiaas, // Vector dot product of halfwords exchanged, add, signed integer and accumulate with saturate +PPC_zvdotphasuiaas, // Vector dot product of halfwords, add, signed by unsigned integer and accumulate with saturate +PPC_zvdotphxasuiaas, // Vector dot product of halfwords exchanged, add, signed by unsigned integer and accumulate with saturate +PPC_zvdotphauians, // Vector dot product of halfwords, add, unsigned integer and accumulate negative with saturate +PPC_zvdotphxauians, // Vector dot product of halfwords exchanged, add, unsigned integer and accumulate negative with saturate +PPC_zvdotphasians, // Vector dot product of halfwords, add, signed integer and accumulate negative with saturate +PPC_zvdotphxasians, // Vector dot product of halfwords exchanged, add, signed integer and accumulate negative with saturate +PPC_zvdotphasuians, // Vector dot product of halfwords, add, signed by unsigned integer and accumulate negative with saturate +PPC_zvdotphxasuians, // Vector dot product of halfwords exchanged, add, signed by unsigned integer and accumulate negative with saturate +PPC_zvdotphssfs, // Vector dot product of halfwords, subtract, signed fractional, with saturate +PPC_zvdotphssfrs, // Vector dot product of halfwords, subtract, signed fractional with round, with saturate +PPC_zvdotphssfaas, // Vector dot product of halfwords, subtract, signed fractional, and accumulate with saturate +PPC_zvdotphssfans, // Vector dot product of halfwords, subtract, signed fractional, and accumulate negative with saturate +PPC_zvdotphssfraas, // Vector dot product of halfwords, subtract, signed fractional with round, and accumulate with saturate +PPC_zvdotphssfrans, // Vector dot product of halfwords, subtract, signed fractional with round, and accumulate negative with saturate +PPC_zvdotphsui, // Vector dot product of halfwords, subtract, unsigned integer +PPC_zvdotphssi, // Vector dot product of halfwords, subtract, signed integer +PPC_zvdotphssui, // Vector dot product of halfwords, subtract, signed by unsigned integer +PPC_zvdotphsuiaa, // Vector dot product of halfwords, subtract, unsigned integer and accumulate +PPC_zvdotphssiaa, // Vector dot product of halfwords, subtract, signed integer and accumulate +PPC_zvdotphssuiaa, // Vector dot product of halfwords, subtract, signed by unsigned integer and accumulate +PPC_zvdotphsuian, // Vector dot product of halfwords, subtract, unsigned integer and accumulate negative +PPC_zvdotphssian, // Vector dot product of halfwords, subtract, signed integer and accumulate negative +PPC_zvdotphssuian, // Vector dot product of halfwords, subtract, signed by unsigned integer and accumulate negative +PPC_zvdotphsuis, // Vector dot product of halfwords, subtract, unsigned integer, with saturate +PPC_zvdotphssis, // Vector dot product of halfwords, subtract, signed integer, with saturate +PPC_zvdotphssuis, // Vector dot product of halfwords, subtract, signed by unsigned integer, with saturate +PPC_zvdotphsuiaas, // Vector dot product of halfwords, subtract, unsigned integer and accumulate with saturate +PPC_zvdotphssiaas, // Vector dot product of halfwords, subtract, signed integer and accumulate with saturate +PPC_zvdotphssuiaas, // Vector dot product of halfwords, subtract, signed by unsigned integer and accumulate with saturate +PPC_zvdotphsuians, // Vector dot product of halfwords, subtract, unsigned integer and accumulate negative with saturate +PPC_zvdotphssians, // Vector dot product of halfwords, subtract, signed integer and accumulate negative with saturate +PPC_zvdotphssuians, // Vector dot product of halfwords, subtract, signed by unsigned integer and accumulate negative with saturate +PPC_zvdotphgwasmf, // Vector dot product of halfwords, guarded to word, add, signed modulo fractional +PPC_zvdotphxgwasmf, // Vector dot product of halfwords exchanged, guarded to word, add, signed modulo fractional +PPC_zvdotphgwasmfr, // Vector dot product of halfwords, guarded to word, add, signed modulo fractional with round +PPC_zvdotphxgwasmfr, // Vector dot product of halfwords exchanged, guarded to word, add, signed modulo fractional with round +PPC_zvdotphgwasmfaa, // Vector dot product of halfwords, guarded to word, add, signed modulo fractional, and accumulate +PPC_zvdotphxgwasmfaa, // Vector dot product of halfwords exchanged, guarded to word, add, signed modulo fractional, and accumulate +PPC_zvdotphgwasmfan, // Vector dot product of halfwords, guarded to word, add, signed modulo fractional, and accumulate negative +PPC_zvdotphxgwasmfan, // Vector dot product of halfwords exchanged, guarded to word, add, signed modulo fractional, and accumulate negative +PPC_zvdotphgwasmfraa, // Vector dot product of halfwords, guarded to word, add, signed modulo fractional with round, and accumulate +PPC_zvdotphxgwasmfraa,// Vector dot product of halfwords exchanged, guarded to word, add, signed modulo fractional with round, and accumulate +PPC_zvdotphgwasmfran, // Vector dot product of halfwords, guarded to word, add, signed modulo fractional with round, and accumulate negative +PPC_zvdotphxgwasmfran,// Vector dot product of halfwords exchanged, guarded to word, add, signed modulo fractional with round, and accumulate negative +PPC_zvdotphgwssmf, // Vector dot product of halfwords, guarded to word, subtract, signed modulo fractional +PPC_zvdotphgwssmfr, // Vector dot product of halfwords, guarded to word, subtract, signed modulo fractional with round +PPC_zvdotphgwssmfaa, // Vector dot product of halfwords, guarded to word, subtract, signed modulo fractional, and accumulate +PPC_zvdotphgwssmfan, // Vector dot product of halfwords, guarded to word, subtract, signed modulo fractional, and accumulate negative +PPC_zvdotphgwssmfraa, // Vector dot product of halfwords, guarded to word, subtract, signed modulo fractional with round, and accumulate +PPC_zvdotphgwssmfran, // Vector dot product of halfwords, guarded to word, subtract, signed modulo fractional with round, and accumulate negative + +PPC_msgclru, // Message Clear Ultravisor +PPC_msgsndu, // Message Send Ultravisor +PPC_urfid, // Ultravisor Return From Interrupt Doubleword + +// Power ISA Version 3.1 Prefixed Instructions +PPC_paddi, // Prefixed Add Immediate +PPC_plbz, // Prefixed Load Byte and Zero +PPC_pld, // Prefixed Load Doubleword +PPC_plfd, // Prefixed Load Floating-Point Double +PPC_plfs, // Prefixed Load Floating-Point Single +PPC_plha, // Prefixed Load Halfword Algebraic +PPC_plhz, // Prefixed Load Halfword and Zero +PPC_plq, // Prefixed Load Quadword +PPC_plwa, // Prefixed Load Word Algebraic +PPC_plwz, // Prefixed Load Word and Zero +PPC_plxsd, // Prefixed Load VSX Scalar Doubleword +PPC_plxssp, // Prefixed Load VSX Scalar Single-Precision +PPC_plxv, // Prefixed Load VSX Vector +PPC_plxvp, // Prefixed Load VSX Vector Paired +PPC_pmxvbf16ger2, // Prefixed Masked VSX Vector bfloat16 GER (Rank-2 Update) +PPC_pmxvbf16ger2nn, // Prefixed Masked VSX Vector bfloat16 GER (Rank-2 Update) Negative multiply, Negative accumulate +PPC_pmxvbf16ger2np, // Prefixed Masked VSX Vector bfloat16 GER (Rank-2 Update) Negative multiply, Positive accumulate +PPC_pmxvbf16ger2pn, // Prefixed Masked VSX Vector bfloat16 GER (Rank-2 Update) Positive multiply, Negative accumulate +PPC_pmxvbf16ger2pp, // Prefixed Masked VSX Vector bfloat16 GER (Rank-2 Update) Positive multiply, Positive accumulate +PPC_pmxvf16ger2, // Prefixed Masked VSX Vector 16-bit Floating-Point GER (rank-2 update) +PPC_pmxvf16ger2nn, // Prefixed Masked VSX Vector 16-bit Floating-Point GER (rank-2 update) Negative multiply, Negative accumulate +PPC_pmxvf16ger2np, // Prefixed Masked VSX Vector 16-bit Floating-Point GER (rank-2 update) Negative multiply, Positive accumulate +PPC_pmxvf16ger2pn, // Prefixed Masked VSX Vector 16-bit Floating-Point GER (rank-2 update) Positive multiply, Negative accumulate +PPC_pmxvf16ger2pp, // Prefixed Masked VSX Vector 16-bit Floating-Point GER (rank-2 update) Positive multiply, Positive accumulate +PPC_pmxvf32ger, // Prefixed Masked VSX Vector 32-bit Floating-Point GER (rank-1 update) +PPC_pmxvf32gernn, // Prefixed Masked VSX Vector 32-bit Floating-Point GER (rank-1 update) Negative multiply, Negative accumulate +PPC_pmxvf32gernp, // Prefixed Masked VSX Vector 32-bit Floating-Point GER (rank-1 update) Negative multiply, Positive accumulate +PPC_pmxvf32gerpn, // Prefixed Masked VSX Vector 32-bit Floating-Point GER (rank-1 update) Positive multiply, Negative accumulate +PPC_pmxvf32gerpp, // Prefixed Masked VSX Vector 32-bit Floating-Point GER (rank-1 update) Positive multiply, Positive accumulate +PPC_pmxvf64ger, // Prefixed Masked VSX Vector 64-bit Floating-Point GER (rank-1 update) +PPC_pmxvf64gernn, // Prefixed Masked VSX Vector 64-bit Floating-Point GER (rank-1 update) Negative multiply, Negative accumulate +PPC_pmxvf64gernp, // Prefixed Masked VSX Vector 64-bit Floating-Point GER (rank-1 update) Negative multiply, Positive accumulate +PPC_pmxvf64gerpn, // Prefixed Masked VSX Vector 64-bit Floating-Point GER (rank-1 update) Positive multiply, Negative accumulate +PPC_pmxvf64gerpp, // Prefixed Masked VSX Vector 64-bit Floating-Point GER (rank-1 update) Positive multiply, Positive accumulate +PPC_pmxvi16ger2, // Prefixed Masked VSX Vector 16-bit Signed Integer GER (rank-2 update) +PPC_pmxvi16ger2pp, // Prefixed Masked VSX Vector 16-bit Signed Integer GER (rank-2 update) Positive multiply, Positive accumulate +PPC_pmxvi16ger2s, // Prefixed Masked VSX Vector 16-bit Signed Integer GER (rank-2 update) with Saturation +PPC_pmxvi16ger2spp, // Prefixed Masked VSX Vector 16-bit Signed Integer GER (rank-2 update) with Saturation Positive multiply, Positive accumulate +PPC_pmxvi4ger8, // Prefixed Masked VSX Vector 4-bit Signed Integer GER (rank-8 update) +PPC_pmxvi4ger8pp, // Prefixed Masked VSX Vector 4-bit Signed Integer GER (rank-8 update) Positive multiply, Positive accumulate +PPC_pmxvi8ger4, // Prefixed Masked VSX Vector 8-bit Signed/Unsigned Integer GER (rank-4 update) +PPC_pmxvi8ger4pp, // Prefixed Masked VSX Vector 8-bit Signed/Unsigned Integer GER (rank-4 update) Positive multiply, Positive accumulate +PPC_pmxvi8ger4spp, // Prefixed Masked VSX Vector 8-bit Signed/Unsigned Integer GER (rank-4 update) with Saturate Positive multiply, Positive accumulate +PPC_pnop, // Prefixed Nop +PPC_pstb, // Prefixed Store Byte +PPC_pstd, // Prefixed Store Doubleword +PPC_pstfd, // Prefixed Store Floating-Point Double +PPC_pstfs, // Prefixed Store Floating-Point Single +PPC_psth, // Prefixed Store Halfword +PPC_pstq, // Prefixed Store Quadword +PPC_pstw, // Prefixed Store Word +PPC_pstxsd, // Prefixed Store VSX Scalar Doubleword +PPC_pstxssp, // Prefixed Store VSX Scalar Single-Precision +PPC_pstxv, // Prefixed Store VSX Vector +PPC_pstxvp, // Prefixed Store VSX Vector Paired + +// Power ISA Version 3.1 Word Instructions +PPC_brd, // Byte-Reverse Doubleword +PPC_brh, // Byte-Reverse Halfword +PPC_brw, // Byte-Reverse Word +PPC_cfuged, // Centrifuge Doubleword +PPC_cntlzdm, // Count Leading Zeros Doubleword under bit Mask +PPC_cnttzdm, // Count Trailing Zeros Doubleword under bit Mask +PPC_dcffixqq, // DFP Convert From Fixed Quadword Quad +PPC_dctfixqq, // DFP Convert To Fixed Quadword Quad +PPC_lxvkq, // Load VSX Vector Special Value Quadword +PPC_lxvp, // Load VSX Vector Paired +PPC_lxvpx, // Load VSX Vector Paired Indexed +PPC_lxvrbx, // Load VSX Vector Rightmost Byte Indexed +PPC_lxvrdx, // Load VSX Vector Rightmost Doubleword Indexed +PPC_lxvrhx, // Load VSX Vector Rightmost Halfword Indexed +PPC_lxvrwx, // Load VSX Vector Rightmost Word Indexed +PPC_mtvsrbm, // Move to VSR Byte Mask +PPC_mtvsrbmi, // Move To VSR Byte Mask Immediate +PPC_mtvsrdm, // Move to VSR Doubleword Mask +PPC_mtvsrhm, // Move to VSR Halfword Mask +PPC_mtvsrqm, // Move to VSR Quadword Mask +PPC_mtvsrwm, // Move to VSR Word Mask +PPC_pdepd, // Parallel Bits Deposit Doubleword +PPC_pextd, // Parallel Bits Extract Doubleword +PPC_setbc, // Set Boolean Condition +PPC_setbcr, // Set Boolean Condition Reverse +PPC_setnbc, // Set Negative Boolean Condition +PPC_setnbcr, // Set Negative Boolean Condition Reverse +PPC_stxvp, // Store VSX Vector Paired +PPC_stxvpx, // Store VSX Vector Paired Indexed +PPC_stxvrbx, // Store VSX Vector Rightmost Byte Indexed +PPC_stxvrdx, // Store VSX Vector Rightmost Doubleword Indexed +PPC_stxvrhx, // Store VSX Vector Rightmost Halfword Indexed +PPC_stxvrwx, // Store VSX Vector Rightmost Word Indexed +PPC_vcfuged, // Vector Centrifuge Doubleword +PPC_vclrlb, // Vector Clear Leftmost Bytes +PPC_vclrrb, // Vector Clear Rightmost Bytes +PPC_vclzdm, // Vector Count Leading Zeros Doubleword under bit Mask +PPC_vcmpequq, // Vector Compare Equal Quadword +PPC_vcmpgtsq, // Vector Compare Greater Than Signed Quadword +PPC_vcmpgtuq, // Vector Compare Greater Than Unsigned Quadword +PPC_vcmpsq, // Vector Compare Signed Quadword +PPC_vcmpuq, // Vector Compare Unsigned Quadword +PPC_vcntmbb, // Vector Count Mask Bits Byte +PPC_vcntmbd, // Vector Count Mask Bits Doubleword +PPC_vcntmbh, // Vector Count Mask Bits Halfword +PPC_vcntmbw, // Vector Count Mask Bits Word +PPC_vctzdm, // Vector Count Trailing Zeros Doubleword under bit Mask +PPC_vdivesd, // Vector Divide Extended Signed Doubleword +PPC_vdivesq, // Vector Divide Extended Signed Quadword +PPC_vdivesw, // Vector Divide Extended Signed Word +PPC_vdiveud, // Vector Divide Extended Unsigned Doubleword +PPC_vdiveuq, // Vector Divide Extended Unsigned Quadword +PPC_vdiveuw, // Vector Divide Extended Unsigned Word +PPC_vdivsd, // Vector Divide Signed Doubleword +PPC_vdivsq, // Vector Divide Signed Quadword +PPC_vdivsw, // Vector Divide Signed Word +PPC_vdivud, // Vector Divide Unsigned Doubleword +PPC_vdivuq, // Vector Divide Unsigned Quadword +PPC_vdivuw, // Vector Divide Unsigned Word +PPC_vexpandbm, // Vector Expand Byte Mask +PPC_vexpanddm, // Vector Expand Doubleword Mask +PPC_vexpandhm, // Vector Expand Halfword Mask +PPC_vexpandqm, // Vector Expand Quadword Mask +PPC_vexpandwm, // Vector Expand Word Mask +PPC_vextddvlx, // Vector Extract Double Doubleword to VSR using GPR-specified Left-Index +PPC_vextddvrx, // Vector Extract Double Doubleword to VSR using GPR-specified Right-Index +PPC_vextdubvlx, // Vector Extract Double Unsigned Byte to VSR using GPR-specified Left-Index +PPC_vextdubvrx, // Vector Extract Double Unsigned Byte to VSR using GPR-specified Right-Index +PPC_vextduhvlx, // Vector Extract Double Unsigned Halfword to VSR using GPR-specified Left-Index +PPC_vextduhvrx, // Vector Extract Double Unsigned Halfword to VSR using GPR-specified Right-Index +PPC_vextduwvlx, // Vector Extract Double Unsigned Word to VSR using GPR-specified Left-Index +PPC_vextduwvrx, // Vector Extract Double Unsigned Word to VSR using GPR-specified Right-Index +PPC_vextractbm, // Vector Extract Byte Mask +PPC_vextractdm, // Vector Extract Doubleword Mask +PPC_vextracthm, // Vector Extract Halfword Mask +PPC_vextractqm, // Vector Extract Quadword Mask +PPC_vextractwm, // Vector Extract Word Mask +PPC_vextsd2q, // Vector Extend Sign Doubleword to Quadword +PPC_vgnb, // Vector Gather every Nth Bit +PPC_vinsblx, // Vector Insert Byte from GPR using GPR-specified Left-Index +PPC_vinsbrx, // Vector Insert Byte from GPR using GPR-specified Right-Index +PPC_vinsbvlx, // Vector Insert Byte from VSR using GPR-specified Left-Index +PPC_vinsbvrx, // Vector Insert Byte from VSR using GPR-specified Right-Index +PPC_vinsd, // Vector Insert Doubleword from GPR using immediate-specified index +PPC_vinsdlx, // Vector Insert Doubleword from GPR using GPR-specified Left-Index +PPC_vinsdrx, // Vector Insert Doubleword from GPR using GPR-specified Right-Index +PPC_vinshlx, // Vector Insert Halfword from GPR using GPR-specified Left-Index +PPC_vinshrx, // Vector Insert Halfword from GPR using GPR-specified Right-Index +PPC_vinshvlx, // Vector Insert Halfword from VSR using GPR-specified Left-Index +PPC_vinshvrx, // Vector Insert Halfword from VSR using GPR-specified Right-Index +PPC_vinsw, // Vector Insert Word from GPR using immediate-specified index +PPC_vinswlx, // Vector Insert Word from GPR using GPR-specified Left-Index +PPC_vinswrx, // Vector Insert Word from GPR using GPR-specified Right-Index +PPC_vinswvlx, // Vector Insert Word from VSR using GPR-specified Left-Index +PPC_vinswvrx, // Vector Insert Word from VSR using GPR-specified Left-Index +PPC_vmodsd, // Vector Modulo Signed Doubleword +PPC_vmodsq, // Vector Modulo Signed Quadword +PPC_vmodsw, // Vector Modulo Signed Word +PPC_vmodud, // Vector Modulo Unsigned Doubleword +PPC_vmoduq, // Vector Modulo Unsigned Quadword +PPC_vmoduw, // Vector Modulo Unsigned Word +PPC_vmsumcud, // Vector Multiply-Sum & write Carry-out Unsigned Doubleword +PPC_vmulesd, // Vector Multiply Even Signed Doubleword +PPC_vmuleud, // Vector Multiply Even Unsigned Doubleword +PPC_vmulhsd, // Vector Multiply High Signed Doubleword +PPC_vmulhsw, // Vector Multiply High Signed Word +PPC_vmulhud, // Vector Multiply High Unsigned Doubleword +PPC_vmulhuw, // Vector Multiply High Unsigned Word +PPC_vmulld, // Vector Multiply Low Doubleword +PPC_vmulosd, // Vector Multiply Odd Signed Doubleword +PPC_vmuloud, // Vector Multiply Odd Unsigned Doubleword +PPC_vpdepd, // Vector Parallel Bits Deposit Doubleword +PPC_vpextd, // Vector Parallel Bits Extract Doubleword +PPC_vrlq, // Vector Rotate Left Quadword +PPC_vrlqmi, // Vector Rotate Left Quadword then Mask Insert +PPC_vrlqnm, // Vector Rotate Left Quadword then AND with Mask +PPC_vsldbi, // Vector Shift Left Double by Bit Immediate +PPC_vslq, // Vector Shift Left Quadword +PPC_vsraq, // Vector Shift Right Algebraic Quadword +PPC_vsrdbi, // Vector Shift Right Double by Bit Immediate +PPC_vsrq, // Vector Shift Right Quadword +PPC_vstribl, // Vector String Isolate Byte Left-justified +PPC_vstribr, // Vector String Isolate Byte Right-justified +PPC_vstrihl, // Vector String Isolate Halfword Left-justified +PPC_vstrihr, // Vector String Isolate Halfword Right-justified +PPC_xscmpeqqp, // VSX Scalar Compare Equal Quad-Precision +PPC_xscmpgeqp, // VSX Scalar Compare Greater Than or Equal Quad-Precision +PPC_xscmpgtqp, // VSX Scalar Compare Greater Than Quad-Precision +PPC_xscvqpsqz, // VSX Scalar Convert with round to zero Quad-Precision to Signed Quadword +PPC_xscvqpuqz, // VSX Scalar Convert with round to zero Quad-Precision to Unsigned Quadword +PPC_xscvsqqp, // VSX Scalar Convert with round Signed Quadword to Quad-Precision +PPC_xscvuqqp, // VSX Scalar Convert with round Unsigned Quadword to Quad-Precision +PPC_xsmaxcqp, // VSX Scalar Maximum Type-C Quad-Precision +PPC_xsmincqp, // VSX Scalar Minimum Type-C Quad-Precision +PPC_xvbf16ger2, // VSX Vector bfloat16 GER (Rank-2 Update) +PPC_xvbf16ger2nn, // VSX Vector bfloat16 GER (Rank-2 Update) Negative multiply, Negative accumulate +PPC_xvbf16ger2np, // VSX Vector bfloat16 GER (Rank-2 Update) Negative multiply, Positive accumulate +PPC_xvbf16ger2pn, // VSX Vector bfloat16 GER (Rank-2 Update) Positive multiply, Negative accumulate +PPC_xvbf16ger2pp, // VSX Vector bfloat16 GER (Rank-2 Update) Positive multiply, Positive accumulate +PPC_xvcvbf16spn, // VSX Vector Convert bfloat16 to Single-Precision format Non-signaling (renamed from xvcvbf16sp) +PPC_xvcvspbf16, // VSX Vector Convert with round Single-Precision to bfloat16 format +PPC_xvf16ger2, // VSX Vector 16-bit Floating-Point GER (rank-2 update) +PPC_xvf16ger2nn, // VSX Vector 16-bit Floating-Point GER (rank-2 update) Negative multiply, Negative accumulate +PPC_xvf16ger2np, // VSX Vector 16-bit Floating-Point GER (rank-2 update) Negative multiply, Positive accumulate +PPC_xvf16ger2pn, // VSX Vector 16-bit Floating-Point GER (rank-2 update) Positive multiply, Negative accumulate +PPC_xvf16ger2pp, // VSX Vector 16-bit Floating-Point GER (rank-2 update) Positive multiply, Positive accumulate +PPC_xvf32ger, // VSX Vector 32-bit Floating-Point GER (rank-1 update) +PPC_xvf32gernn, // VSX Vector 32-bit Floating-Point GER (rank-1 update) Negative multiply, Negative accumulate +PPC_xvf32gernp, // VSX Vector 32-bit Floating-Point GER (rank-1 update) Negative multiply, Positive accumulate +PPC_xvf32gerpn, // VSX Vector 32-bit Floating-Point GER (rank-1 update) Positive multiply, Negative accumulate +PPC_xvf32gerpp, // VSX Vector 32-bit Floating-Point GER (rank-1 update) Positive multiply, Positive accumulate +PPC_xvf64ger, // VSX Vector 64-bit Floating-Point GER (rank-1 update) +PPC_xvf64gernn, // VSX Vector 64-bit Floating-Point GER (rank-1 update) Negative multiply, Negative accumulate +PPC_xvf64gernp, // VSX Vector 64-bit Floating-Point GER (rank-1 update) Negative multiply, Positive accumulate +PPC_xvf64gerpn, // VSX Vector 64-bit Floating-Point GER (rank-1 update) Positive multiply, Negative accumulate +PPC_xvf64gerpp, // VSX Vector 64-bit Floating-Point GER (rank-1 update) Positive multiply, Positive accumulate +PPC_xvi16ger2, // VSX Vector 16-bit Signed Integer GER (rank-2 update) +PPC_xvi16ger2pp, // VSX Vector 16-bit Signed Integer GER (rank-2 update) Positive multiply, Positive accumulate +PPC_xvi16ger2s, // VSX Vector 16-bit Signed Integer GER (rank-2 update) with Saturation +PPC_xvi16ger2spp, // VSX Vector 16-bit Signed Integer GER (rank-2 update) with Saturation Positive multiply, Positive accumulate +PPC_xvi4ger8, // VSX Vector 4-bit Signed Integer GER (rank-8 update) +PPC_xvi4ger8pp, // VSX Vector 4-bit Signed Integer GER (rank-8 update) Positive multiply, Positive accumulate +PPC_xvi8ger4, // VSX Vector 8-bit Signed/Unsigned Integer GER (rank-4 update) +PPC_xvi8ger4pp, // VSX Vector 8-bit Signed/Unsigned Integer GER (rank-4 update) Positive multiply, Positive accumulate +PPC_xvi8ger4spp, // VSX Vector 8-bit Signed/Unsigned Integer GER (rank-4 update) with Saturate Positive multiply, Positive accumulate +PPC_xvtlsbb, // VSX Vector Test Least-Significant Bit by Byte +PPC_xxblendvb, // VSX Vector Blend Variable Byte +PPC_xxblendvd, // VSX Vector Blend Variable Doubleword +PPC_xxblendvh, // VSX Vector Blend Variable Halfword +PPC_xxblendvw, // VSX Vector Blend Variable Word +PPC_xxeval, // VSX Vector Evaluate +PPC_xxgenpcvbm, // VSX Vector Generate PCV from Byte Mask +PPC_xxgenpcvdm, // VSX Vector Generate PCV from Doubleword Mask +PPC_xxgenpcvhm, // VSX Vector Generate PCV from Halfword Mask +PPC_xxgenpcvwm, // VSX Vector Generate PCV from Word Mask +PPC_xxmfacc, // VSX Move From Accumulator +PPC_xxmtacc, // VSX Move To Accumulator +PPC_xxpermx, // VSX Vector Permute Extended +PPC_xxsetaccz, // VSX Set Accumulator to Zero +PPC_xxsplti32dx, // VSX Vector Splat Immediate32 Doubleword Indexed +PPC_xxspltidp, // VSX Vector Splat Immediate Double-Precision +PPC_xxspltiw, // VSX Vector Splat Immediate Word + +PPC_last, +}; + +/* +* Interactive disassembler (IDA). +* Copyright (c) 1990-2025 Hex-Rays +* ALL RIGHTS RESERVED. +* +*/ + + + +//---------------------------------------------------------------------- + + +enum NEC850_Instructions +{ + NEC850_NULL = 0, + + NEC850_BREAKPOINT, // undefined instruction + NEC850_XORI, // Exclusive Or Immediate + NEC850_XOR, // Exclusive OR + NEC850_TST1, // Test bit + NEC850_TST, // Test + NEC850_TRAP, // Software trap + NEC850_SUBR, // Substract reverse + NEC850_SUB, // Substract + NEC850_STSR, // Store Contents of System Register + NEC850_ST_B, // Store byte + NEC850_ST_H, // Store half-word + NEC850_ST_W, // Store word + NEC850_SST_B, // Store byte (use EP) + NEC850_SST_H, // Store half-word (use EP) + NEC850_SST_W, // Store word (use EP) + NEC850_SLD_B, // Load byte (use EP) + NEC850_SLD_H, // Load half-word (use EP) + NEC850_SLD_W, // Load word (use EP) + NEC850_SHR, // Shift Logical Right + NEC850_SHL, // Shift Logical Left + NEC850_SET1, // Set Bit + NEC850_SETF, // Set register to 1 if condition is satisfied + NEC850_SATSUBR, // Saturated Subtract Reverse + NEC850_SATSUBI, // Saturated Subtract Immediate + NEC850_SATSUB, // Saturated Subtract + NEC850_SATADD, // Saturated Add + NEC850_SAR, // Shift Arithmetic Right + NEC850_RETI, // Return from Trap or Interrupt + NEC850_ORI, // OR immediate + NEC850_OR, // OR + NEC850_NOT1, // Not Bit + NEC850_NOT, // Not + NEC850_NOP, // No Operation + NEC850_MULHI, // Multiply Half-Word Immediate + NEC850_MULH, // Multiply Half-Word + NEC850_MOVHI, // Move High Half-Word + NEC850_MOVEA, // Move Effective Address + NEC850_MOV, // Move + NEC850_LDSR, // Load to system register + NEC850_LD_B, // Load byte + NEC850_LD_H, // Load half-word + NEC850_LD_W, // Load word + NEC850_JR, // Jump Relative + NEC850_JMP, // Jump Register + NEC850_JARL, // Jump and Register Link + NEC850_HALT, // Halt + NEC850_EI, // Enable interrupt + NEC850_DIVH, // Divide Half-Word + NEC850_DI, // Disable Interrupt + NEC850_CMP, // Compare + NEC850_CLR1, // Clear bit + NEC850_BV, // Branch if overflow + NEC850_BL, // Branch if less + NEC850_BZ, // Branch if zero + NEC850_BNH, // Branch if not higher + NEC850_BN, // Branch if negative + NEC850_BR, // Branch if always + NEC850_BLT, // Branch if less than (signed) + NEC850_BLE, // Branch if less than or equal (signed) + NEC850_BNV, // Branch if no overflow + NEC850_BNC, // Branch if no carry + NEC850_BNZ, // Branch if not zero + NEC850_BH, // Branch if higher than + NEC850_BP, // Branch if positive + NEC850_BSA, // Branch if saturated + NEC850_BGE, // Branch if greater than or equal (signed) + NEC850_BGT, // Branch if greater than (signed) + NEC850_ANDI, // And immediate + NEC850_AND, // And + NEC850_ADDI, // Add Immediate + NEC850_ADD, // Add + + // + // V850E/E1/ES + // + NEC850_SWITCH, // Jump with table look up + NEC850_ZXB, // Zero-extend byte + NEC850_SXB, // Sign-extend byte + NEC850_ZXH, // Zero-extend halfword + NEC850_SXH, // Sign-extend halfword + NEC850_DISPOSE_r0, // Function dispose + NEC850_DISPOSE_r, // Function dispose + NEC850_CALLT, // Call with table look up + NEC850_DBTRAP, // Debug trap + NEC850_DBRET, // Return from debug trap or interrupt + NEC850_CTRET, // Return from CALLT + + NEC850_SASF, // Shift and set flag condition + + NEC850_PREPARE_sp, // Function prepare + NEC850_PREPARE_i, // Function prepare + + NEC850_MUL, // Multiply word + NEC850_MULU, // Multiply word unsigned + + NEC850_DIVH_r3, // Divide halfword + NEC850_DIVHU, // Divide halfword unsigned + NEC850_DIV, // Divide word + NEC850_DIVU, // Divide word unsigned + + NEC850_BSW, // Byte swap word + NEC850_BSH, // Byte swap halfword + NEC850_HSW, // Halfword swap word + + NEC850_CMOV, // Conditional move + + NEC850_SLD_BU, // Short format load byte unsigned + NEC850_SLD_HU, // Short format load halfword unsigned + NEC850_LD_BU, // load byte unsigned + NEC850_LD_HU, // load halfword unsigned + + // + // V850E2 + // + NEC850_ADF, // Add on condition flag + + NEC850_HSH, // Halfword swap halfword + NEC850_MAC, // Multiply and add word + NEC850_MACU, // Multiply and add word unsigned + + NEC850_SBF, // Subtract on condition flag + + NEC850_SCH0L, // Search zero from left + NEC850_SCH0R, // Search zero from right + NEC850_SCH1L, // Search one from left + NEC850_SCH1R, // Search one from right + + // + // V850E2M + // + NEC850_CAXI, // Compare and exchange for interlock + NEC850_DIVQ, // Divide word quickly + NEC850_DIVQU, // Divide word unsigned quickly + NEC850_EIRET, // Return from EI level exception + NEC850_FERET, // Return from FE level exception + NEC850_FETRAP, // FE-level Trap + NEC850_RMTRAP, // Runtime monitor trap + NEC850_RIE, // Reserved instruction exception + NEC850_SYNCE, // Synchronize exceptions + NEC850_SYNCM, // Synchronize memory + NEC850_SYNCP, // Synchronize pipeline + NEC850_SYSCALL, // System call + + // floating point (E1F only) + NEC850_CVT_SW, // Real to integer conversion + NEC850_TRNC_SW, // Real to integer conversion + NEC850_CVT_WS, // Integer to real conversion + NEC850_LDFC, // Load to Floating Controls + NEC850_LDFF, // Load to Floating Flags + NEC850_STFC, // Store Floating Controls + NEC850_STFF, // Store Floating Flags + NEC850_TRFF, // Transfer Floating Flags + + // floating point (E2M) + + NEC850_ABSF_D, // Floating-point Absolute Value (Double) + NEC850_ABSF_S, // Floating-point Absolute Value (Single) + NEC850_ADDF_D, // Floating-point Add (Double) + NEC850_ADDF_S, // Floating-point Add (Single) + NEC850_DIVF_D, // Floating-point Divide (Double) + NEC850_DIVF_S, // Floating-point Divide (Single) + NEC850_MAXF_D, // Floating-point Maximum (Double) + NEC850_MAXF_S, // Floating-point Maximum (Single) + NEC850_MINF_D, // Floating-point Minimum (Double) + NEC850_MINF_S, // Floating-point Minimum (Single) + NEC850_MULF_D, // Floating-point Multiply (Double) + NEC850_MULF_S, // Floating-point Multiply (Single) + NEC850_NEGF_D, // Floating-point Negate (Double) + NEC850_NEGF_S, // Floating-point Negate (Single) + NEC850_RECIPF_D, // Reciprocal of a floating-point value (Double) + NEC850_RECIPF_S, // Reciprocal of a floating-point value (Single + + NEC850_RSQRTF_D, // Reciprocal of the square root of a floating-point value (Double) + NEC850_RSQRTF_S, // Reciprocal of the square root of a floating-point value (Single) + NEC850_SQRTF_D, // Floating-point Square Root (Double) + NEC850_SQRTF_S, // Floating-point Square Root (Single) + NEC850_SUBF_D, // Floating-point Subtract (Double) + NEC850_SUBF_S, // Floating-point Subtract (Single) + NEC850_MADDF_S, // Floating-point Multiply-Add (Single) + NEC850_MSUBF_S, // Floating-point Multiply-Subtract (Single) + NEC850_NMADDF_S, // Floating-point Negate Multiply-Add (Single) + NEC850_NMSUBF_S, // Floating-point Negate Multiply-Subtract (Single) + + NEC850_CEILF_DL, // Floating-point Truncate to Long Fixed-point Format, rounded toward +inf (Double) + NEC850_CEILF_DW, // Floating-point Truncate to Single Fixed-point Format, rounded toward +inf (Double) + NEC850_CEILF_SL, // Floating-point Truncate to Long Fixed-point Format, rounded toward +inf (Single) + NEC850_CEILF_SW, // Floating-point Truncate to Single Fixed-point Format, rounded toward +inf (Single) + NEC850_CEILF_DUL, // Floating-point Truncate to Unsigned Long, rounded toward +inf (Double) + NEC850_CEILF_DUW, // Floating-point Truncate to Unsigned Word, rounded toward +inf (Double) + NEC850_CEILF_SUL, // Floating-point Truncate to Unsigned Long, rounded toward +inf (Single) + NEC850_CEILF_SUW, // Floating-point Truncate to Unsigned Word, rounded toward +inf (Single) + NEC850_CVTF_DL, // Floating-point Convert to Long Fixed-point Format (Double) + NEC850_CVTF_DS, // Floating-point Convert to Single Floating-point Format (Double) + NEC850_CVTF_DUL, // Floating-point Convert Double to Unsigned-Long (Double) + NEC850_CVTF_DUW, // Floating-point Convert Double to Unsigned-Word (Double) + NEC850_CVTF_DW, // Floating-point Convert to Single Fixed-point Format (Double) + NEC850_CVTF_LD, // Floating-point Convert to Single Floating-point Format (Double) + NEC850_CVTF_LS, // Floating-point Convert to Single Floating-point Format (Single) + NEC850_CVTF_SD, // Floating-point Convert to Double Floating-point Format (Double) + NEC850_CVTF_SL, // Floating-point Convert to Long Fixed-point Format (Single) + NEC850_CVTF_SUL, // Floating-point Convert Single to Unsigned-Long (Single) + NEC850_CVTF_SUW, // Floating-point Convert Single to Unsigned-Word (Single) + NEC850_CVTF_SW, // Floating-point Convert to Single Fixed-point Format (Single) + NEC850_CVTF_ULD, // Floating-point Convert Unsigned-Long to Double (Double) + NEC850_CVTF_ULS, // Floating-point Convert Unsigned-Long to Single (Single) + NEC850_CVTF_UWD, // Floating-point Convert Unsigned-Word to Double (Double) + NEC850_CVTF_UWS, // Floating-point Convert Unsigned-Word to Single (Single) + NEC850_CVTF_WD, // Floating-point Convert to Single Floating-point Format (Double) + NEC850_CVTF_WS, // Floating-point Convert to Single Floating-point Format (Single) + NEC850_FLOORF_DL, // Floating-point Truncate to Long Fixed-point Format, rounded toward -inf (Double) + NEC850_FLOORF_DW, // Floating-point Truncate to Single Fixed-point Format, rounded toward -inf (Double) + NEC850_FLOORF_SL, // Floating-point Truncate to Long Fixed-point Format, rounded toward -inf (Single) + NEC850_FLOORF_SW, // Floating-point Truncate to Single Fixed-point Format, rounded toward -inf (Single) + NEC850_FLOORF_DUL, // Floating-point Truncate to Unsigned Long, rounded toward -inf (Double) + NEC850_FLOORF_DUW, // Floating-point Truncate to Unsigned Word, rounded toward -inf (Double) + NEC850_FLOORF_SUL, // Floating-point Truncate to Unsigned Long, rounded toward -inf (Single) + NEC850_FLOORF_SUW, // Floating-point Truncate to Unsigned Word, rounded toward -inf (Single) + NEC850_TRNCF_DL, // Floating-point Truncate to Long Fixed-point Format, rounded to zero (Double) + NEC850_TRNCF_DUL, // Floating-point Truncate Double to Unsigned-Long (Double) + NEC850_TRNCF_DUW, // Floating-point Truncate Double to Unsigned-Word (Double) + NEC850_TRNCF_DW, // Floating-point Truncate to Single Fixed-point Format, rounded to zero (Double) + NEC850_TRNCF_SL, // Floating-point Truncate to Long Fixed-point Format, rounded to zero (Single) + NEC850_TRNCF_SUL, // Floating-point Truncate Single to Unsigned-Long (Single) + NEC850_TRNCF_SUW, // Floating-point Truncate Single to Unsigned-Word (Single) + NEC850_TRNCF_SW, // Floating-point Truncate to Single Fixed-point Format, rounded to zero (Single) + NEC850_CMPF_S, // Compares floating-point values (Single) + NEC850_CMPF_D, // Compares floating-point values (Double) + NEC850_CMOVF_S, // Floating-point conditional move (Single) + NEC850_CMOVF_D, // Floating-point conditional move (Double) + NEC850_TRFSR, // Transfers specified CC bit to Zero flag in PSW (Single) + + // + // RH850 + // + NEC850_SYNCI, // Synchronize instruction pipeline + NEC850_SNOOZE, // Snooze + NEC850_BINS, // Bitfield Insert + NEC850_ROTL, // Rotate Left + NEC850_LOOP, // Loop + NEC850_LD_DW, // Load Double Word + NEC850_ST_DW, // Store Double Word + NEC850_LDL_W, // Load Linked + NEC850_STC_W, // Store Conditional + NEC850_CLL, // Clear Load Link + NEC850_CACHE, // Cache operation + NEC850_PREF, // Prefetch + NEC850_PUSHSP, // Push registers to Stack + NEC850_POPSP, // Pop registers from Stack + + // new RH850 FP instructions + NEC850_CVTF_HS, // Floating-point Convert Half to Single (Single) + NEC850_CVTF_SH, // Floating-point Convert Single to Half (Single) + NEC850_FMAF_S, // Floating-point Fused-Multiply-add (Single) + NEC850_FMSF_S, // Floating-point Fused-Multiply-subtract (Single) + NEC850_FNMAF_S, // Floating-point Fused-Negate-Multiply-add (Single) + NEC850_FNMSF_S, // Floating-point Fused-Negate-Multiply-subtract (Single) + + // debug instructions + NEC850_DBPUSH, // Output registers as software trace data + NEC850_DBCP, // Output current PC value as software trace data + NEC850_DBTAG, // Output immediate value as software trace data + NEC850_DBHVTRAP, // Debug hypervisor trap + + // virtualization instructions + NEC850_EST, // Enable Single Thread mode + NEC850_DST, // Disable Single Thread mode + NEC850_HVTRAP, // Debug hypervisor trap + NEC850_HVCALL, // Hypervisor call + NEC850_LDVC_SR, // Load to virtual machine context (SR) + NEC850_STVC_SR, // Store contents of virtual machine context (SR) + NEC850_LDTC_GR, // Load to thread context (GR) + NEC850_STTC_GR, // Store contents of thread context (GR) + NEC850_LDTC_PC, // Load to thread context (PC) + NEC850_STTC_PC, // Store contents of thread context (PC) + NEC850_LDTC_SR, // Load to thread context (SR) + NEC850_STTC_SR, // Store contents of thread context (SR) + NEC850_LDTC_VR, // Load to thread context (VR) + NEC850_STTC_VR, // Store contents of thread context (VR) + + // TLB instructions + NEC850_TLBAI, // TLB ASID Invalidate + NEC850_TLBR, // TLB Read + NEC850_TLBS, // TLB Search + NEC850_TLBVI, // TLB VA Invalidate + NEC850_TLBW, // TLB Write + + // RH850 rounding instructions + NEC850_ROUNDF_DL, // Floating-point Convert Double to Long, round to nearest (Double) + NEC850_ROUNDF_DW, // Floating-point Convert Double to Word, round to nearest (Double) + NEC850_ROUNDF_DUL, // Floating-point Convert Double to Unsigned-Long, round to nearest (Double) + NEC850_ROUNDF_DUW, // Floating-point Convert Double to Unsigned-Word, round to nearest (Double) + NEC850_ROUNDF_SL, // Floating-point Convert Single to Long, round to nearest (Single) + NEC850_ROUNDF_SW, // Floating-point Convert Single to Word, round to nearest (Single) + NEC850_ROUNDF_SUL, // Floating-point Convert Single to Unsigned-Long, round to nearest (Single) + NEC850_ROUNDF_SUW, // Floating-point Convert Single to Unsigned-Word, round to nearest (Single) + + NEC850_LDM_MP, // Load Multiple MPU entries from memory + NEC850_STM_MP, // Store Multiple MPU entries to memory + + NEC850_CLIP_B, // Signed data conversion from word to byte with saturation + NEC850_CLIP_BU, // Unsigned data conversion from word to byte with saturation + NEC850_CLIP_H, // Signed data conversion from word to halfword with saturation + NEC850_CLIP_HU, // Unsigned data conversion from word to halfword with saturation + + NEC850_LDL_BU, // Load to start atomic byte data manipulation + NEC850_LDL_HU, // Load to start atomic halfword data manipulation + + NEC850_RESBANK, // Restore from register bank + + NEC850_STC_B, // Store conditional byte + NEC850_STC_H, // Store conditional halfword + + // RH850 SIMD instructions + NEC850_VAND, // Vector and + NEC850_VOR, // Vector or + NEC850_VXOR, // Vector xor + NEC850_VNOT, // Vector not + + NEC850_VSAR_H, // Vector shift arithmetic right halfword + NEC850_VSAR_W, // Vector shift arithmetic right word + NEC850_VSAR_DW, // Vector shift arithmetic right double-word + NEC850_VSHR_H, // Vector shift logical right halfword + NEC850_VSHR_W, // Vector shift logical right word + NEC850_VSHR_DW, // Vector shift logical right double-word + NEC850_VSHL_H, // Vector shift logical left halfword + NEC850_VSHL_W, // Vector shift logical left word + NEC850_VSHL_DW, // Vector shift logical left double-word + + NEC850_VCONCAT_B, // Concatenate vector byte + NEC850_VITLV_H, // Interleave halfword + NEC850_VITLV_W, // Interleave word + NEC850_VITLVHW_H, // Interleave halfword(halfword to word) + NEC850_VITLVWH_H, // Interleave halfword(word to halfword) + NEC850_VSHUFL_B, // Shuffle byte + NEC850_VBSWAP_DW, // Swap double - word + NEC850_VBSWAP_H, // Swap halfword + NEC850_VBSWAP_W, // Swap word + + NEC850_DUP_H, // Duplicate halfword + NEC850_DUP_W, // Duplicate word + NEC850_MOV_H, // Move register (halfword) + NEC850_MOV_W, // Move register (word) + NEC850_MOV_DW, // Move register (double word) + + NEC850_VLD_B, // Vector Load byte + NEC850_VLD_B_FMT3, // Vector Load byte (3rd instruction format) + NEC850_VLD_B_FMT4, // Vector Load byte (4th instruction format) + + NEC850_VLD_H, // Vector Load halfword + NEC850_VLD_H_FMT4, // Vector Load halfword (4th instruction format) + + NEC850_VLD_W, // Vector Load word + NEC850_VLD_W_FMT4, // Vector Load word (4th instruction format) + + NEC850_VLD_DW, // Vector Load double - word + NEC850_VLD_DW_FMT3, // Vector Load double - word (3rd instruction format) + NEC850_VLD_DW_FMT4, // Vector Load double - word (4th instruction format) + NEC850_VLD_DW_FMT5, // Vector Load double - word (5th instruction format) + + NEC850_VST_B, // Vector Store byte + NEC850_VST_B_FMT4, // Vector Store byte (4th instruction format) + + NEC850_VST_H, // Vector Store halfword + NEC850_VST_H_FMT_4_5, // Vector Store halfword (4th/5th instruction format) + + NEC850_VST_W, // Vector Store word + NEC850_VST_W_FMT_4_5, // Vector Store word (4th/5th instruction format) + + NEC850_VST_DW, // Vector Store double - word + NEC850_VST_DW_FMT_4_5, // Vector Store double - word (4th/5th instruction format) + NEC850_VST_DW_FMT6, // Vector Store double - word (6th instruction format) + + NEC850_VCMOV, // Vector conditional move + NEC850_MODADD, // Modulo Add + + NEC850_VADD_H, // Vector add halfword + NEC850_VADD_W, // Vector add word + NEC850_VADD_DW, // Vector add double - word + NEC850_VSUB_H, // Vector subtract halfword + NEC850_VSUB_W, // Vector subtract word + NEC850_VSUB_DW, // Vector subtract double - word + NEC850_VADDSAT_H, // Vector add halfword with saturation + NEC850_VADDSAT_W, // Vector add word with saturation + NEC850_VSUBSAT_H, // Vector subtract halfword with saturation + NEC850_VSUBSAT_W, // Vector subtract word with saturation + NEC850_VADDS_H, // Vector add halfword with scaling + NEC850_VADDS_W, // Vector add word with scaling + NEC850_VSUBS_H, // Vector subtract halfword with scaling + NEC850_VSUBS_W, // Vector subtract word with scaling + + NEC850_VMUL_H, // Vector multiply halfword + NEC850_VMUL_W, // Vector multiply word + NEC850_VMULT_H, // Vector multiply halfword with truncation + NEC850_VMULT_W, // Vector multiply word with truncation + NEC850_VMULCX_H, // Vector multiply halfword with complex number + NEC850_VMULCX_W, // Vector multiply word with complex number + + NEC850_VCMPEQ_H, // Vector compare equal halfword + NEC850_VCMPEQ_W, // Vector compare equal word + NEC850_VCMPLT_H, // Vector compare less-than halfword + NEC850_VCMPLT_W, // Vector compare less-than word + NEC850_VCMPLE_H, // Vector compare less-than-equal halfword + NEC850_VCMPLE_W, // Vector compare less-than-equal word + NEC850_VCMPNE_H, // Vector compare not-equal halfword + NEC850_VCMPNE_W, // Vector compare not-equal word + + NEC850_VABS_H, // Vector absolute halfword + NEC850_VABS_W, // Vector absolute word + + NEC850_VNEG_H, // Vector negate halfword + NEC850_VNEG_W, // Vector negate word + + NEC850_VMAXGT_H, // Vector maximum greater-than halfword + NEC850_VMAXGE_H, // Vector maximum greater-than-equal halfword + NEC850_VMINLT_H, // Vector minimum less-than halfword + NEC850_VMINLE_H, // Vector minimum less-than-equal halfword + NEC850_VMAXGT_W, // Vector maximum greater-than word + NEC850_VMAXGE_W, // Vector maximum greater-than-equal word + NEC850_VMINLT_W, // Vector minimum less-than word + NEC850_VMINLE_W, // Vector minimum less-than-equal word + + NEC850_VMADSAT_H, // Vector multiply halfword and add with saturation + NEC850_VMADSAT_W, // Vector multiply word and add with saturation + NEC850_VMADRN_H, // Vector multiply halfword and add with rounding + NEC850_VMADRN_W, // Vector multiply word and add with rounding + + NEC850_VMSUM_H, // Vector multiply halfword and sum + NEC850_VMSUM_W, // Vector multiply word and sum + + NEC850_VMSUMAD_H, // Vector multiply halfword and sum and add to vector register + NEC850_VMSUMAD_W, // Vector multiply word and sum and add to vector register + NEC850_VMSUMADRE_H, // Vector multiply halfword and sum and add to vector register for real part result of complex number calculation + NEC850_VMSUMADRE_W, // Vector multiply word and sum and add to vector register for real part result of complex number calculation + NEC850_VMSUMADIM_H, // Vector multiply halfword and sum and add to vector register for imaginary part result of complex number calculation + NEC850_VMSUMADIM_W, // Vector multiply word and sum and add to vector register for imaginary part result of complex number calculation + NEC850_VMSUMADRN_H, // Vector multiply halfword and sum and add to vector register with rounding + NEC850_VBIQ_H, // Vector multiply halfword for biquad calculation + + NEC850_PKUI8I16, // Pack vector unsigned integer byte to vector signed integer halfword + NEC850_PKI16I32, // Pack vector signed integer halfword to vector signed integer word + NEC850_PKQ15Q31, // Pack vector Q15 fractional halfword to vector Q31 fractional word + NEC850_PKI16UI8, // Pack vector signed integer halfword to vector unsigned integer byte + NEC850_PKI32I16, // Pack vector signed integer word to vector signed integer halfword + NEC850_PKQ31Q15, // Pack vector Q31 fractional word to vector Q15 fractional halfword + NEC850_PKQ30Q31, // Pack Q31 fractional double-word to vector Q31 fractional halfword + NEC850_PKI64I32, // Pack signed integer double-word to vector signed integer word + NEC850_CNVQ15Q30, // Convert from Q15 fractional halfword to Q30 fractional double-word + NEC850_CNVQ31Q62, // Convert from Q31 fractional word to Q62 fractional double-word + NEC850_CNVQ30Q15, // Convert from Q30 fractional double-word to Q15 fractional halfword + NEC850_CNVQ62Q31, // Convert from Q62 fractional double-word to Q31 fractional word + + NEC850_EXPQ31, // Extract exponent of Q31 fractional word + NEC850_VCALCH, // Vector calculate with halfword + NEC850_VCALCW, // Vector calculate with word + + NEC850_MOVV_W4, // Move vector register to vector register + + NEC850_FLPV_S4, // Floating-point SIMD Flip (single) + NEC850_SHFLV_W4, // Vector Shuffle + + NEC850_LDV_DW, // Load Vector (Double-Word) + NEC850_LDV_QW, // Load Vector (Quad-Word) + NEC850_LDV_W, // Load Vector (Word) + NEC850_LDVZ_H4, // Load Vector at Even Halfword field + + NEC850_STV_DW, // Store Vector (Double-Word) + NEC850_STV_QW, // Store Vector (Quad-Word) + NEC850_STV_W, // Store Vector (Word) + NEC850_STVZ_H4, // Store Vector at Even Halfword field + + NEC850_CMOVF_W4, // Conditional move of vector register (Single precision) + NEC850_TRFSRV_W4, // Transfers compare result to PSW + + NEC850_ABSF_S4, // Floating-point SIMD Absolute (single) + NEC850_ADDF_S4, // Floating-point SIMD Add (single) + NEC850_DIVF_S4, // Floating-point SIMD Divide (single) + NEC850_MAXF_S4, // Floating-point SIMD Maximum (single) + NEC850_MINF_S4, // Floating-point SIMD Minimum (single) + NEC850_MULF_S4, // Floating-point SIMD Multiply (single) + NEC850_NEGF_S4, // Floating-point SIMD Negative (single) + NEC850_RECIPF_S4, // Floating-point SIMD Reciprocal (single) + NEC850_RSQRTF_S4, // Floating-point SIMD Reciprocal Square-Root (single) + NEC850_SQRTF_S4, // Floating-point SIMD Square-Root (single) + NEC850_SUBF_S4, // Floating-point SIMD Subtract (single) + + NEC850_FMAF_S4, // Floating-point SIMD Fused-Multiply-Add (Single) + NEC850_FMSF_S4, // Floating-point SIMD Fused-Multiply-Subtract (Single) + NEC850_FNMAF_S4, // Floating-point SIMD Fused-Negative-Multiply-Add (Single) + NEC850_FNMSF_S4, // Floating-point SIMD Fused-Negative-Multiply-Subtract (Single) + + NEC850_ADDSUBF_S4, // Floating-point SIMD Add/Subtract (single) + NEC850_ADDSUBNF_S4, // Floating-point SIMD Add/Subtract Negative (single) + NEC850_SUBADDF_S4, // Floating-point SIMD Subtract/Add (single) + NEC850_SUBADDNF_S4, // Floating-point SIMD Subtract/Add Negative (single) + + NEC850_ADDXF_S4, // Floating-point SIMD Add Exchange (single) + NEC850_MULXF_S4, // Floating-point SIMD Multiply Exchange (single) + NEC850_SUBXF_S4, // Floating-point SIMD Subtract Exchange (single) + + NEC850_ADDSUBNXF_S4, // Floating-point SIMD Add/Subtract Negative Exchange (single) + NEC850_ADDSUBXF_S4, // Floating-point SIMD Add/Subtract Exchange (single) + NEC850_SUBADDNXF_S4, // Floating-point SIMD Subtract/Add Negative Exchange (single) + NEC850_SUBADDXF_S4, // Floating-point SIMD Subtract/Add Exchange (single) + + NEC850_ADDRF_S4, // Floating-point SIMD Add Reduction (single) + NEC850_MAXRF_S4, // Floating-point SIMD Maximum Reduction (single) + NEC850_MINRF_S4, // Floating-point SIMD Minimum Reduction (single) + NEC850_MULRF_S4, // Floating-point SIMD Multiply Reduction (single) + NEC850_SUBRF_S4, // Floating-point SIMD Subtract Reduction (single) + + NEC850_CEILF_SUW4, // Floating-point SIMD Convert Single to Unsigned Word, round toward positive (single) + NEC850_CEILF_SW4, // Floating-point SIMD Convert Single to Word, round toward positive (single) + NEC850_CVTF_HS4, // Floating-point SIMD Convert Half to Single (single) + NEC850_CVTF_SH4, // Floating-point SIMD Convert Single to Half (single) + NEC850_CVTF_SUW4, // Floating-point SIMD Convert Single to Unsigned Word (single) + NEC850_CVTF_SW4, // Floating-point SIMD Convert Single to Word (single) + NEC850_CVTF_UWS4, // Floating-point SIMD Convert Unsigned Word to Single (single) + NEC850_CVTF_WS4, // Floating-point SIMD Convert Word to Single (single) + NEC850_FLOORF_SUW4, // Floating-point SIMD Convert Single to Unsigned Word, round toward negative (single) + NEC850_FLOORF_SW4, // Floating-point SIMD Convert Single to Word, round toward negative (single) + NEC850_ROUNDF_SUW4, // Floating-point SIMD Convert Single to Unsigned Word, round to nearest (single) + NEC850_ROUNDF_SW4, // Floating-point SIMD Convert Single to Word, round to nearest (single) + NEC850_TRNCF_SUW4, // Floating-point SIMD Convert Single to Unsigned Word, round toward zero (single) + NEC850_TRNCF_SW4, // Floating-point SIMD Convert Single to Word, round toward zero (single) + + NEC850_CMPF_S4, // Floating-point SIMD Comparison (single) + + NEC850_LAST_INSTRUCTION +}; + +/* + * Tricore Disassembly Module + * Version 1.0 + * Copyright (c) 2000 by Juergen Krumm + * ALL RIGHTS RESERVED + * + * + */ + + + +enum +{ + TRICORE_null = 0, // Unknown Operation + TRICORE_abs, + TRICORE_abs_b, + TRICORE_abs_h, + TRICORE_absdif, + TRICORE_absdif_b, + TRICORE_absdif_h, + TRICORE_absdifs, + TRICORE_absdifs_h, + TRICORE_abss, + TRICORE_abss_h, + TRICORE_add_b, + TRICORE_add_f, + TRICORE_add_h, + TRICORE_add16, + TRICORE_add16_a, + TRICORE_add32, + TRICORE_add32_a, + TRICORE_addc, + TRICORE_addi, + TRICORE_addih, + TRICORE_addih_a, + TRICORE_adds, + TRICORE_adds_h, + TRICORE_adds_hu, + TRICORE_adds_u, + TRICORE_adds16, + TRICORE_addsc_at, + TRICORE_addsc16_a, + TRICORE_addsc32_a, + TRICORE_addx, + TRICORE_and_and_t, + TRICORE_and_andn_t, + TRICORE_and_eq, + TRICORE_and_ge, + TRICORE_and_ge_u, + TRICORE_and_lt, + TRICORE_and_lt_u, + TRICORE_and_ne, + TRICORE_and_nor_t, + TRICORE_and_or_t, + TRICORE_and_t, + TRICORE_and16, + TRICORE_and32, + TRICORE_andn, + TRICORE_andn_t, + TRICORE_bisr16, + TRICORE_bisr32, + TRICORE_bmerge, + TRICORE_bsplit, + TRICORE_cachea_i, + TRICORE_cachea_w, + TRICORE_cachea_wi, + TRICORE_cadd16, + TRICORE_cadd32, + TRICORE_caddn16, + TRICORE_caddn32, + TRICORE_call16, + TRICORE_call32, + TRICORE_calla, + TRICORE_calli, + TRICORE_clo, + TRICORE_clo_h, + TRICORE_cls, + TRICORE_cls_h, + TRICORE_clz, + TRICORE_clz_h, + TRICORE_cmov16, + TRICORE_cmovn16, + TRICORE_cmp_f, + TRICORE_csub, + TRICORE_csubn, + TRICORE_debug16, + TRICORE_debug32, + TRICORE_dextr, + TRICORE_disable, + TRICORE_div_f, + TRICORE_dsync, + TRICORE_dvadj, + TRICORE_dvinit, + TRICORE_dvinit_b, + TRICORE_dvinit_bu, + TRICORE_dvinit_h, + TRICORE_dvinit_hu, + TRICORE_dvinit_u, + TRICORE_dvstep, + TRICORE_dvstep_u, + TRICORE_enable, + TRICORE_eq_a, + TRICORE_eq_b, + TRICORE_eq_h, + TRICORE_eq_w, + TRICORE_eq16, + TRICORE_eq32, + TRICORE_eqany_b, + TRICORE_eqany_h, + TRICORE_eqz_a, + TRICORE_extr, + TRICORE_extr_u, + TRICORE_ftoi, + TRICORE_ftoq31, + TRICORE_ftou, + TRICORE_ge, + TRICORE_ge_a, + TRICORE_ge_u, + TRICORE_imask, + TRICORE_ins_t, + TRICORE_insert, + TRICORE_insn_t, + TRICORE_isync, + TRICORE_itof, + TRICORE_ixmax, + TRICORE_ixmax_u, + TRICORE_ixmin, + TRICORE_ixmin_u, + TRICORE_j16, + TRICORE_j32, + TRICORE_ja, + TRICORE_jeq_a, + TRICORE_jeq16, + TRICORE_jeq32, + TRICORE_jge, + TRICORE_jge_u, + TRICORE_jgez16, + TRICORE_jgtz16, + TRICORE_ji16, + TRICORE_ji32, + TRICORE_jl, + TRICORE_jla, + TRICORE_jlez16, + TRICORE_jli, + TRICORE_jlt, + TRICORE_jlt_u, + TRICORE_jltz16, + TRICORE_jne_a, + TRICORE_jne16, + TRICORE_jne32, + TRICORE_jned, + TRICORE_jnei, + TRICORE_jnz16, + TRICORE_jnz16_a, + TRICORE_jnz16_t, + TRICORE_jnz32_a, + TRICORE_jnz32_t, + TRICORE_jz16, + TRICORE_jz16_a, + TRICORE_jz16_t, + TRICORE_jz32_a, + TRICORE_jz32_t, + TRICORE_ld_b, + TRICORE_ld_d, + TRICORE_ld_da, + TRICORE_ld_hu, + TRICORE_ld_q, + TRICORE_ld16_a, + TRICORE_ld16_bu, + TRICORE_ld16_h, + TRICORE_ld16_w, + TRICORE_ld32_a, + TRICORE_ld32_bu, + TRICORE_ld32_h, + TRICORE_ld32_w, + TRICORE_ldlcx, + TRICORE_ldmst, + TRICORE_lducx, + TRICORE_lea, + TRICORE_loop16, + TRICORE_loop32, + TRICORE_loopu, + TRICORE_lt_a, + TRICORE_lt_b, + TRICORE_lt_bu, + TRICORE_lt_h, + TRICORE_lt_hu, + TRICORE_lt_u, + TRICORE_lt_w, + TRICORE_lt_wu, + TRICORE_lt16, + TRICORE_lt32, + TRICORE_madd, + TRICORE_madd_f, + TRICORE_madd_h, + TRICORE_madd_q, + TRICORE_madd_u, + TRICORE_maddm_h, + TRICORE_maddms_h, + TRICORE_maddr_h, + TRICORE_maddr_q, + TRICORE_maddrs_h, + TRICORE_maddrs_q, + TRICORE_madds, + TRICORE_madds_h, + TRICORE_madds_q, + TRICORE_madds_u, + TRICORE_maddsu_h, + TRICORE_maddsum_h, + TRICORE_maddsums_h, + TRICORE_maddsur_h, + TRICORE_maddsurs_h, + TRICORE_maddsus_h, + TRICORE_max, + TRICORE_max_b, + TRICORE_max_bu, + TRICORE_max_h, + TRICORE_max_hu, + TRICORE_max_u, + TRICORE_mfcr, + TRICORE_min, + TRICORE_min_b, + TRICORE_min_bu, + TRICORE_min_h, + TRICORE_min_hu, + TRICORE_min_u, + TRICORE_mov_u, + TRICORE_mov16, + TRICORE_mov16_a, + TRICORE_mov16_aa, + TRICORE_mov16_d, + TRICORE_mov32, + TRICORE_mov32_a, + TRICORE_mov32_aa, + TRICORE_mov32_d, + TRICORE_movh, + TRICORE_movh_a, + TRICORE_msub, + TRICORE_msub_f, + TRICORE_msub_h, + TRICORE_msub_q, + TRICORE_msub_u, + TRICORE_msubad_h, + TRICORE_msubadm_h, + TRICORE_msubadms_h, + TRICORE_msubadr_h, + TRICORE_msubadrs_h, + TRICORE_msubads_h, + TRICORE_msubm_h, + TRICORE_msubms_h, + TRICORE_msubr_h, + TRICORE_msubr_q, + TRICORE_msubrs_h, + TRICORE_msubrs_q, + TRICORE_msubs, + TRICORE_msubs_h, + TRICORE_msubs_q, + TRICORE_msubs_u, + TRICORE_mtcr, + TRICORE_mul_f, + TRICORE_mul_h, + TRICORE_mul_q, + TRICORE_mul_u, + TRICORE_mul16, + TRICORE_mul32, + TRICORE_mulm_h, + TRICORE_mulms_h, + TRICORE_mulr_h, + TRICORE_mulr_q, + TRICORE_muls, + TRICORE_muls_u, + TRICORE_nand, + TRICORE_nand_t, + TRICORE_ne, + TRICORE_ne_a, + TRICORE_nez_a, + TRICORE_nop16, + TRICORE_nop32, + TRICORE_nor_t, + TRICORE_not16, + TRICORE_nor32, + TRICORE_or_and_t, + TRICORE_or_andn_t, + TRICORE_or_eq, + TRICORE_or_ge, + TRICORE_or_ge_u, + TRICORE_or_lt, + TRICORE_or_lt_u, + TRICORE_or_ne, + TRICORE_or_nor_t, + TRICORE_or_or_t, + TRICORE_or_t, + TRICORE_or16, + TRICORE_or32, + TRICORE_orn, + TRICORE_orn_t, + TRICORE_pack, + TRICORE_parity, + TRICORE_q31tof, + TRICORE_qseed_f, + TRICORE_ret16, + TRICORE_ret32, + TRICORE_rfe16, + TRICORE_rfe32, + TRICORE_rfm, + TRICORE_rslcx, + TRICORE_rstv, + TRICORE_rsub16, + TRICORE_rsub32, + TRICORE_rsubs, + TRICORE_rsubs_u, + TRICORE_sat16_b, + TRICORE_sat16_bu, + TRICORE_sat16_h, + TRICORE_sat16_hu, + TRICORE_sat32_b, + TRICORE_sat32_bu, + TRICORE_sat32_h, + TRICORE_sat32_hu, + TRICORE_sel, + TRICORE_seln, + TRICORE_sh_and_t, + TRICORE_sh_andn_t, + TRICORE_sh_eq, + TRICORE_sh_ge, + TRICORE_sh_ge_u, + TRICORE_sh_h, + TRICORE_sh_lt, + TRICORE_sh_lt_u, + TRICORE_sh_nand_t, + TRICORE_sh_ne, + TRICORE_sh_nor_t, + TRICORE_sh_or_t, + TRICORE_sh_orn_t, + TRICORE_sh_xnor_t, + TRICORE_sh_xor_t, + TRICORE_sh16, + TRICORE_sh32, + TRICORE_sha_h, + TRICORE_sha16, + TRICORE_sha32, + TRICORE_shas, + TRICORE_st_d, + TRICORE_st_da, + TRICORE_st_q, + TRICORE_st_t, + TRICORE_st16_a, + TRICORE_st16_b, + TRICORE_st16_h, + TRICORE_st16_w, + TRICORE_st32_a, + TRICORE_st32_b, + TRICORE_st32_h, + TRICORE_st32_w, + TRICORE_stlcx, + TRICORE_stucx, + TRICORE_sub_b, + TRICORE_sub_f, + TRICORE_sub_h, + TRICORE_sub16, + TRICORE_sub16_a, + TRICORE_sub32, + TRICORE_sub32_a, + TRICORE_subc, + TRICORE_subs_h, + TRICORE_subs_hu, + TRICORE_subs_u, + TRICORE_subs16, + TRICORE_subs32, + TRICORE_subx, + TRICORE_svlcx, + TRICORE_swap_w, + TRICORE_syscall, + TRICORE_tlbdemap, + TRICORE_tlbflush_a, + TRICORE_tlbflush_b, + TRICORE_tlbmap, + TRICORE_tlbprobe_a, + TRICORE_tlbprobe_i, + TRICORE_trapsv, + TRICORE_trapv, + TRICORE_unpack, + TRICORE_updfl, + TRICORE_utof, + TRICORE_xnor, + TRICORE_xnor_t, + TRICORE_xor_eq, + TRICORE_xor_ge, + TRICORE_xor_ge_u, + TRICORE_xor_lt, + TRICORE_xor_lt_u, + TRICORE_xor_ne, + TRICORE_xor_t, + TRICORE_xor16, + TRICORE_xor32, + + // new v1.6 instructions + TRICORE_cachei_i, + TRICORE_cachei_w, + TRICORE_cachei_wi, + TRICORE_div, + TRICORE_div_u, + TRICORE_fcall, + TRICORE_fcalla, + TRICORE_fcalli, + TRICORE_fret16, + TRICORE_fret32, + TRICORE_ftoiz, + TRICORE_ftoq31z, + TRICORE_ftouz, + TRICORE_restore, + + // new v1.6.1 instructions + TRICORE_crc32, // Calculate CRC32 + TRICORE_wait, // Suspend execution until the next enabled interrupt or asynchronous trap event + TRICORE_cmpswap_w, // Compare and swap + TRICORE_swapmsk_w, // Swap under mask + + // new v1.6.2 instructions + TRICORE_crc32_b, // CRC32 for big endian data + TRICORE_crc32l_w, // CRC32 for little endian data + TRICORE_crcn, // Arbitrary width and polynomial CRC calculation + TRICORE_shuffle, // Reorder bytes within word + TRICORE_popcnt_w, // Count number of bits set in word + TRICORE_lha, // Load high bits of address value + TRICORE_ftohp, // Single Precision to Half Precision + TRICORE_hptof, // Half Precision to Single Precision + + // new v1.8 instructions + TRICORE_rfh, // Return from hypervisor + TRICORE_lsync, // Synchronize local data + TRICORE_trapinv, // Trap Invalid Opcode + TRICORE_hvcall, // Hypervisor call + TRICORE_ltodf, // Long integer to double + TRICORE_ftodf, // Single precision to double precision + TRICORE_utodf, // Unsigned to double + TRICORE_ultodf, // Unsigned long integer to double + TRICORE_itodf, // Integer to double + TRICORE_dftoin, // Double to integer, round to nearest + TRICORE_dftoi, // Double to integer + TRICORE_dftoul, // Double to unsigned long + TRICORE_dftol, // Double to long integer + TRICORE_dftof, // Double precision to single precision + TRICORE_dftou, // Double to unsigned + TRICORE_dftolz, // Double to long integer, round towards zero + TRICORE_dftouz, // Double to unsigned, round towards zero + TRICORE_dftoulz, // Double to unsigned long integer, round towards zero + TRICORE_dftoiz, // Double to integer, round towards zero + TRICORE_ftoin, // Float to integer, round to nearest + TRICORE_cachea_i_vm, // Cache address, invalidate virtual machine entry + TRICORE_cachea_w_vm, // Cache address, writeback virtual machine entry + TRICORE_cachea_wi_vm, // Cache address, writeback and invalidate virtual machine entry + TRICORE_cachei_i_vm, // Cache index, invalidate virtual machine entry + TRICORE_cachei_w_vm, // Cache index, writeback virtual machine entry + TRICORE_cachei_wi_vm, // Cache index, writeback and invalidate virtual machine entry + TRICORE_mfdcr, // Move from core register pair + TRICORE_mtdcr, // Move to core register pair + TRICORE_cmp_df, // Compare double + TRICORE_div_df, // Divide double + TRICORE_mul_df, // Multiply double + TRICORE_abs_df, // Absolute value double + TRICORE_sub_df, // Subtract double + TRICORE_add_df, // Add double + TRICORE_max_df, // Maximum value double + TRICORE_madd_df, // Multiply add double + TRICORE_msub_df, // Multiply subtract double + TRICORE_min_df, // Minimum value double + TRICORE_neg_df, // Negate value double + TRICORE_max_f, // Maximum value float + TRICORE_neg_f, // Negate value float + TRICORE_abs_f, // Absolute value float + TRICORE_min_f, // minimum value double + TRICORE_ld_dd, // Load double double word + TRICORE_st_dd, // Store double double word + TRICORE_div64_u, // Divide 64-bit unsigned long + TRICORE_div64, // Divide 64-bit long + TRICORE_rem64, // Remainder 64-bit long + TRICORE_rem64_u, // Remainder 64-bit unsigned long + TRICORE_mulp_b, // Packed carry-less multiplication + TRICORE_qseed_df, // Inverse square root seed + TRICORE_jri, // Jump relative indirect + TRICORE_calli16, // Call Indirect + + TRICORE_last +}; + + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + + ARC_null = 0, // Unknown Operation + + ARC_ld, // Load + ARC_lr, // Load from auxiliary register + ARC_st, // Store + ARC_sr, // Store to auxiliary register + ARC_store_instructions = ARC_sr, + ARC_flag, // Set flags + ARC_asr, // Arithmetic shift right + ARC_lsr, // Logical shift right + ARC_sexb, // Sign extend byte + ARC_sexw, // Sign extend word + ARC_sexh = ARC_sexw, + ARC_extb, // Zero extend byte + ARC_extw, // Zero extend word + ARC_exth = ARC_extw, + ARC_ror, // Rotate right + ARC_rrc, // Rotate right through carry + ARC_b, // Branch + ARC_bl, // Branch and link + ARC_lp, // Zero-overhead loop setup + ARC_j, // Jump + ARC_jl, // Jump and link + ARC_add, // Add + ARC_adc, // Add with carry + ARC_sub, // Subtract + ARC_sbc, // Subtract with carry + ARC_and, // Logical bitwise AND + ARC_or, // Logical bitwise OR + ARC_bic, // Logical bitwise AND with invert + ARC_xor, // Logical bitwise exclusive-OR + + // pseudo instructions + ARC_mov, // Move + ARC_nop, // No operation + ARC_lsl, // Logical shift left + ARC_rlc, // Rotate left through carry + + // arc7 + ARC_brk, // Breakpoint + ARC_sleep, // Sleep until interrupt or restart + + // arc8 + ARC_swi, // Software interrupt + + // extra optional instrutions + ARC_asl, // Arithmetic shift left + ARC_mul64, // Signed 32x32 multiply + ARC_mulu64, // Unsigned 32x32 multiply + ARC_max, // Maximum of two signed integers + ARC_min, // Minimum of two signed integers + ARC_swap, // Exchange upper and lower 16 bits + ARC_norm, // Normalize (find-first-bit) + + // ARCompact instructions + ARC_bbit0, // Branch if bit cleared to 0 + ARC_bbit1, // Branch if bit set to 1 + ARC_br, // Branch on compare + ARC_pop, // Restore register value from stack + ARC_push, // Store register value on stack + + ARC_abs, // Absolute value + ARC_add1, // Add with left shift by 1 bit + ARC_add2, // Add with left shift by 2 bits + ARC_add3, // Add with left shift by 3 bits + ARC_bclr, // Clear specified bit (to 0) + ARC_bmsk, // Bit Mask + ARC_bset, // Set specified bit (to 1) + ARC_btst, // Test value of specified bit + ARC_bxor, // Bit XOR + ARC_cmp, // Compare + ARC_ex, // Atomic Exchange + ARC_mpy, // Signed 32x32 multiply (low) + ARC_mpyh, // Signed 32x32 multiply (high) + ARC_mpym = ARC_mpyh, + ARC_mpyhu, // Unsigned 32x32 multiply (high) + ARC_mpyhm = ARC_mpyhu, + ARC_mpyu, // Unsigned 32x32 multiply (low) + ARC_neg, // Negate + ARC_not, // Logical bit inversion + ARC_rcmp, // Reverse Compare + ARC_rsub, // Reverse Subtraction + ARC_rtie, // Return from Interrupt/Exception + ARC_sub1, // Subtract with left shift by 1 bit + ARC_sub2, // Subtract with left shift by 2 bits + ARC_sub3, // Subtract with left shift by 3 bits + ARC_sync, // Synchronize + ARC_trap, // Raise an exception + ARC_tst, // Test + ARC_unimp, // Unimplemented instruction + + ARC_abss, // Absolute and saturate + ARC_abssw, // Absolute and saturate of word + ARC_abssh = ARC_abssw, + ARC_adds, // Add and saturate + ARC_addsdw, // Add and saturate dual word + ARC_asls, // Arithmetic shift left and saturate + ARC_asrs, // Arithmetic shift right and saturate + ARC_divaw, // Division assist + ARC_negs, // Negate and saturate + ARC_negsw, // Negate and saturate of word + ARC_negsh = ARC_negsw, + ARC_normw, // Normalize to 16 bits + ARC_normh = ARC_normw, + ARC_rnd16, // Round to word + ARC_rndh = ARC_rnd16, + ARC_sat16, // Saturate to word + ARC_sath = ARC_sat16, + ARC_subs, // Subtract and saturate + ARC_subsdw, // Subtract and saturate dual word + + // mac d16 + ARC_muldw, + ARC_muludw, + ARC_mulrdw, + ARC_macdw, + ARC_macudw, + ARC_macrdw, + ARC_msubdw, + + // 32x16 MUL/MAC + ARC_mululw, + ARC_mullw, + ARC_mulflw, + ARC_maclw, + ARC_macflw, + ARC_machulw, + ARC_machlw, + ARC_machflw, + ARC_mulhlw, + ARC_mulhflw, + + // Major 6 compact insns + ARC_acm, + ARC_addqbs, + ARC_avgqb, + ARC_clamp, + ARC_daddh11, + ARC_daddh12, + ARC_daddh21, + ARC_daddh22, + ARC_dexcl1, + ARC_dexcl2, + ARC_dmulh11, + ARC_dmulh12, + ARC_dmulh21, + ARC_dmulh22, + ARC_dsubh11, + ARC_dsubh12, + ARC_dsubh21, + ARC_dsubh22, + ARC_drsubh11, + ARC_drsubh12, + ARC_drsubh21, + ARC_drsubh22, + ARC_fadd, + ARC_fsadd = ARC_fadd, + ARC_fmul, + ARC_fsmul = ARC_fmul, + ARC_fsub, + ARC_fssub = ARC_fsub, + ARC_fxtr, + ARC_iaddr, + ARC_mpyqb, + ARC_sfxtr, + ARC_pkqb, + ARC_upkqb, + ARC_xpkqb, + + // ARCv2 only major 4 instructions + ARC_mpyw, // Signed 16x16 multiply + ARC_mpyuw, // Unsigned 16x16 multiply + ARC_bi, // Branch indexed + ARC_bih, // Branch indexed half-word + ARC_ldi, // Load indexed + ARC_aex, // Exchange with auxiliary register + ARC_bmskn, // Bit mask negated + ARC_seteq, // Set if equal + ARC_setne, // Set if not equal + ARC_setlt, // Set if less than + ARC_setge, // Set if greater or equal + ARC_setlo, // Set if lower than + ARC_seths, // Set if higher or same + ARC_setle, // Set if less than or equal + ARC_setgt, // Set if greater than + + ARC_rol, // Rotate left + ARC_llock, // Load locked + ARC_scond, // Store conditional + + ARC_seti, // Set interrupt enable and priority level + ARC_clri, // Cler and get interrupt enable and priority level + + // ARCv2 compact prolog / epilog instructions + ARC_enter, // Function prologue sequence + ARC_leave, // Function epilogue sequence + + // ARCv2 32-bit extension major 5 DOP instructions + ARC_div, // Signed integer divsion + ARC_divu, // Unsigned integer divsion + ARC_rem, // Signed integer remainder + ARC_remu, // Unsigned integer remainder + ARC_asrsr, // Shift right rounding and saturating + ARC_valgn2h, // Two-way 16-bit vector align + ARC_setacc, // Set the accumulator + ARC_mac, // Signed 32x32 multiply accumulate + ARC_macu, // Unsigned 32x32 multiply accumulate + ARC_dmpyh, // Sum of dual signed 16x16 multiplication + ARC_dmpyhu, // Sum of dual unsigned 16x16 multiplication + ARC_dmach, // Dual signed 16x16 multiply accumulate + ARC_dmachu, // Dual unsigned 16x16 multiply accumulate + ARC_vadd2h, // Dual 16-bit addition + ARC_vadds2h, // Dual 16-bit saturating addition + ARC_vsub2h, // Dual 16-bit subtraction + ARC_vsubs2h, // Dual 16-bit saturating subtraction + ARC_vaddsub2h, // Dual 16-bit addition/subtraction + ARC_vaddsubs2h, // Dual 16-bit saturating addition/subtraction + ARC_vsubadd2h, // Dual 16-bit subtraction/addition + ARC_vsubadds2h, // Dual 16-bit saturating subtraction/addition + ARC_mpyd, // Signed 32x32 multiply (wide) + ARC_mpydu, // Unsigned 32x32 multiply (wide) + ARC_macd, // Signed 32x32 multiply accumulate (wide) + ARC_macdu, // Unsigned 32x32 multiply accumulate (wide) + ARC_vmpy2h, // Dual signed 16x16 multiply (wide) + ARC_vmpy2hf, // Dual 16x16 saturating fractional multiply + ARC_vmpy2hu, // Dual unsigned 16x16 multiply (wide) + ARC_vmpy2hfr, // Dual 16x16 saturating rounded fractional multiply + ARC_vmac2h, // Dual signed 16x16 multiply (wide) + ARC_vmac2hf, // Dual 16x16 saturating fractional multiply + ARC_vmac2hu, // Dual unsigned 16x16 multiply (wide) + ARC_vmac2hfr, // Dual 16x16 saturating rounded fractional multiply + ARC_vmpy2hwf, // Dual 16x16 saturating fractional multiply (wide) + ARC_vasl2h, // Dual 16-bit arithmetic shift left + ARC_vasls2h, // Dual 16-bit saturating arithmetic shift left + ARC_vasr2h, // Dual 16-bit arithmetic shift right + ARC_vasrs2h, // Dual 16-bit saturating arithmetic shift right + ARC_vlsr2h, // Dual 16-bit logical shift right + ARC_vasrsr2h, // Dual 16-bit saturating rounded arithmetic shift right + ARC_vadd4b, // Quad 8-bit addition + ARC_vmax2h, // Dual 16-bit maximum + ARC_vsub4b, // Quad 8-bit subtraction + ARC_vmin2h, // Dual 16-bit minimum + ARC_adcs, // Signed saturating addition with carry in + ARC_sbcs, // Signed saturating subtraction with carry in + ARC_dmpyhwf, // Fractional saturating sum of dual 16x16 signed fractional multiply + ARC_vpack2hl, // Compose lower 16-bits + ARC_vpack2hm, // Compose upper 16-bits + ARC_dmpyhf, // Saturating sum of dual 16x16 signed fractional multiply + ARC_dmpyhfr, // Saturating rounded sum of dual 16x16 signed fractional multiply + ARC_dmachf, // Saturating sum of dual 16x16 signed fractional multiply accumulate + ARC_dmachfr, // Saturating rounded sum of dual 16x16 signed fractional multiply accumulate + ARC_vperm, // Byte permutation with zero or sign extension + ARC_bspush, // Bitstream push + + // ARCv2 32-bit extension major 5 SOP instructions + ARC_swape, // Swap byte ordering + ARC_lsl16, // Logical shift left by 16 bits + ARC_lsr16, // Logical shift right by 16 bits + ARC_asr16, // Arithmetic shift right by 16 bits + ARC_asr8, // Arithmetic shift right by 8 bits + ARC_lsr8, // Logical shift right by 8 bits + ARC_lsl8, // Logical shift left by 8 bits + ARC_rol8, // Rotate left by 8 bits + ARC_ror8, // Rotate right by 8 bits + ARC_ffs, // Find first set bit + ARC_fls, // Find last set bit + + ARC_getacc, // Get accumulator + ARC_normacc, // Normalize accumulator + ARC_satf, // Saturate according to flags + ARC_vpack2hbl, // Pack lower bytes into lower 16 bits + ARC_vpack2hbm, // Pack upper bytes into upper 16 bits + ARC_vpack2hblf, // Pack upper bytes into lower 16 bits + ARC_vpack2hbmf, // Pack lower bytes into upper 16 bits + ARC_vext2bhlf, // Pack lower 2 bytes into upper byte of 16 bits each + ARC_vext2bhmf, // Pack upper 2 bytes into upper byte of 16 bits each + ARC_vrep2hl, // Repeat lower 16 bits + ARC_vrep2hm, // Repeat upper 16 bits + ARC_vext2bhl, // Pack lower 2 bytes into zero extended 16 bits + ARC_vext2bhm, // Pack upper 2 bytes into zero extended 16 bits + ARC_vsext2bhl, // Pack lower 2 bytes into sign extended 16 bits + ARC_vsext2bhm, // Pack upper 2 bytes into sign extended 16 bits + ARC_vabs2h, // Dual 16-bit absolute value + ARC_vabss2h, // Dual saturating 16-bit absolute value + ARC_vneg2h, // Dual 16-bit negation + ARC_vnegs2h, // Dual saturating 16-bit negation + ARC_vnorm2h, // Dual 16-bit normalization + ARC_bspeek, // Bitstream peek + ARC_bspop, // Bitstream pop + ARC_sqrt, // Integer square root + ARC_sqrtf, // Fractional square root + + // ARCv2 32-bit extension major 5 ZOP instructions + ARC_aslacc, // Arithmetic shift of accumulator + ARC_aslsacc, // Saturating arithmetic shift of accumulator + ARC_flagacc, // Copy accumulator flags to status32 register + ARC_modif, // Update address pointer + + // ARCv2 32-bit extension major 6 DOP instructions + ARC_cmpyhnfr, // Fractional 16+16 bit complex saturating rounded unshifted multiply + ARC_cmpyhfr, // Fractional 16+16 bit complex saturating rounded multiply + ARC_cmpychfr, // Fractional 16+16 bit complex saturating rounded conjugated multiply + ARC_vmsub2hf, // Dual 16x16 saturating fractional multiply subtract + ARC_vmsub2hfr, // Dual 16x16 saturating rounded fractional multiply subtract + ARC_cmpychnfr, // Fractional 16+16 bit complex saturating rounded unshifted conjugated multiply + ARC_cmachnfr, // Fractional 16+16 bit complex saturating rounded unshifted multiply accumulate + ARC_cmachfr, // Fractional 16+16 bit complex saturating rounded unshifted accumulate + ARC_cmacchnfr, // Fractional 16+16 bit complex saturating rounded conjugated multiply accumulate + ARC_cmacchfr, // Fractional 16+16 bit complex saturating rounded unshifted conjugated multiply accumulate + ARC_mpyf, // Signed 32-bit fractional saturating multiply + ARC_mpyfr, // Signed 32-bit fractional saturating rounded multiply + ARC_macf, // Signed 32-bit fractional saturating multiply accumulate + ARC_macfr, // Signed 32-bit fractional saturating rounded multiply accumulate + ARC_msubf, // Signed 32-bit fractional saturating multiply subtract + ARC_msubfr, // Signed 32-bit fractional saturating rounded multiply subtract + ARC_divf, // Signed 32-bit fractional division + ARC_vmac2hnfr, // Dual signed 16-bit fractional saturating rounded multiply accumulate + ARC_vmsub2hnfr, // Dual signed 16-bit fractional saturating rounded multiply subtract + ARC_mpydf, // Signed 32-bit fractional multiply (wide) + ARC_macdf, // Signed 32-bit fractional multiply accumulate (wide) + ARC_msubwhfl, // Signed 32 x 16 (lower) fractional saturating multiply subtract + ARC_msubdf, // Signed 32-bit fractional multiply subtract (wide) + ARC_dmpyhbl, // Dual 16x8 signed multiply with lower two bytes + ARC_dmpyhbm, // Dual 16x8 signed multiply with upper two bytes + ARC_dmachbl, // Dual 16x8 signed multiply accumulate with lower two bytes + ARC_dmachbm, // Dual 16x8 signed multiply accumulate with upper two bytes + ARC_msubwhflr, // Signed 32 x 16 (lower) fractional saturating rounded multiply subtract + ARC_cmpyhfmr, // Fractional 16+16 bit complex x 16bit real (upper) saturating rounded multiply + ARC_cbflyhf0r, // Fractional 16+16 bit complex FFT butterfly, first half + ARC_mpywhl, // Signed 32 x 16 (lower) multiply + ARC_macwhl, // Signed 32 x 16 (lower) multiply accumulate + ARC_mpywhul, // Unsigned 32 x 16 (lower) multiply + ARC_macwhul, // Unsigned 32 x 16 (lower) multiply accumulate + ARC_mpywhfm, // Signed 32 x 16 (upper) fractional saturating multiply + ARC_mpywhfmr, // Signed 32 x 16 (upper) fractional saturating rounded multiply + ARC_macwhfm, // Signed 32 x 16 (upper) fractional saturating multiply accumulate + ARC_macwhfmr, // Signed 32 x 16 (upper) fractional saturating rounded multiply accumulate + ARC_mpywhfl, // Signed 32 x 16 (lower) fractional saturating multiply + ARC_mpywhflr, // Signed 32 x 16 (lower) fractional saturating rounded multiply + ARC_macwhfl, // Signed 32 x 16 (lower) fractional saturating multiply accumulate + ARC_macwhflr, // Signed 32 x 16 (lower) fractional saturating rounded multiply accumulate + ARC_macwhkl, // Signed 32 x 16 (lower) 16-bit shifted multiply accumulate + ARC_macwhkul, // Unsigned 32 x 16 (lower) 16-bit shifted multiply accumulate + ARC_mpywhkl, // Signed 32 x 16 (lower) 16-bit shifted multiply + ARC_mpywhkul, // Unsigned 32 x 16 (lower) 16-bit shifted multiply + ARC_msubwhfm, // Signed 32 x 16 (upper) fractional saturating multiply subtract + ARC_msubwhfmr, // Signed 32 x 16 (upper) fractional saturating rounded multiply subtract + + // ARCv2 32-bit extension major 6 SOP instructions + ARC_cbflyhf1r, // Fractional 16+16 bit complex FFT butterfly, second half + + // ARCv2 FPU instructions + ARC_fscmp, // Single precision floating point compare + ARC_fscmpf, // Single precision floating point compare (IEEE 754 flag generation) + ARC_fsmadd, // Single precision floating point fused multiply add + ARC_fsmsub, // Single precision floating point fused multiply subtract + ARC_fsdiv, // Single precision floating point division + ARC_fcvt32, // Single precision floating point / integer conversion + ARC_fssqrt, // Single precision floating point square root + + // ARCv2 jump / execute indexed instructions + ARC_jli, // Jump and link indexed + ARC_ei, // Execute indexed + + ARC_kflag, // Set kernel flags + ARC_wevt, // Enter sleep state + + ARC_last, +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + + + +enum +{ + +TMS28_null = 0, // Unknown Operation + +TMS28_aborti, +TMS28_abs, +TMS28_abstc, +TMS28_add, +TMS28_addb, +TMS28_addcl, +TMS28_addcu, +TMS28_addl, +TMS28_addu, +TMS28_addul, +TMS28_adrk, +TMS28_and, +TMS28_andb, +TMS28_asp, +TMS28_asr, +TMS28_asr64, +TMS28_asrl, +TMS28_b, +TMS28_banz, +TMS28_bar, +TMS28_bf, +TMS28_c27map, +TMS28_c27obj, +TMS28_c28addr, +TMS28_c28map, +TMS28_c28obj, +TMS28_clrc, +TMS28_cmp, +TMS28_cmp64, +TMS28_cmpb, +TMS28_cmpl, +TMS28_cmpr, +TMS28_csb, +TMS28_dec, +TMS28_dint, +TMS28_dmac, +TMS28_dmov, +TMS28_eallow, +TMS28_edis, +TMS28_eint, +TMS28_estop0, +TMS28_estop1, +TMS28_ffc, +TMS28_flip, +TMS28_iack, +TMS28_idle, +TMS28_imacl, +TMS28_impyal, +TMS28_impyl, +TMS28_impysl, +TMS28_impyxul, +TMS28_in, +TMS28_inc, +TMS28_intr, +TMS28_iret, +TMS28_lb, +TMS28_lc, +TMS28_lcr, +TMS28_loopnz, +TMS28_loopz, +TMS28_lpaddr, +TMS28_lret, +TMS28_lrete, +TMS28_lretr, +TMS28_lsl, +TMS28_lsl64, +TMS28_lsll, +TMS28_lsr, +TMS28_lsr64, +TMS28_lsrl, +TMS28_mac, +TMS28_max, +TMS28_maxcul, +TMS28_maxl, +TMS28_min, +TMS28_mincul, +TMS28_minl, +TMS28_mov, +TMS28_mova, +TMS28_movad, +TMS28_movb, +TMS28_movdl, +TMS28_movh, +TMS28_movl, +TMS28_movp, +TMS28_movs, +TMS28_movu, +TMS28_movw, +TMS28_movx, +TMS28_movz, +TMS28_mpy, +TMS28_mpya, +TMS28_mpyb, +TMS28_mpys, +TMS28_mpyu, +TMS28_mpyxu, +TMS28_nasp, +TMS28_neg, +TMS28_neg64, +TMS28_negtc, +TMS28_nop, +TMS28_norm, +TMS28_not, +TMS28_or, +TMS28_orb, +TMS28_out, +TMS28_pop, +TMS28_pread, +TMS28_push, +TMS28_pwrite, +TMS28_qmacl, +TMS28_qmpyal, +TMS28_qmpyl, +TMS28_qmpysl, +TMS28_qmpyul, +TMS28_qmpyxul, +TMS28_rol, +TMS28_ror, +TMS28_rpt, +TMS28_sat, +TMS28_sat64, +TMS28_sb, +TMS28_sbbu, +TMS28_sbf, +TMS28_sbrk, +TMS28_setc, +TMS28_sfr, +TMS28_spm, +TMS28_sqra, +TMS28_sqrs, +TMS28_sub, +TMS28_subb, +TMS28_subbl, +TMS28_subcu, +TMS28_subcul, +TMS28_subl, +TMS28_subr, +TMS28_subrl, +TMS28_subu, +TMS28_subul, +TMS28_sxtb, +TMS28_tbit, +TMS28_tclr, +TMS28_test, +TMS28_trap, +TMS28_tset, +TMS28_uout, +TMS28_xb, +TMS28_xbanz, +TMS28_xcall, +TMS28_xmac, +TMS28_xmacd, +TMS28_xor, +TMS28_xorb, +TMS28_xpread, +TMS28_xpwrite, +TMS28_xret, +TMS28_xretc, +TMS28_zalr, +TMS28_zap, +TMS28_zapa, +// TODO below is WIP +// Floating Point Unit (FPU) +TMS28_absf32, +TMS28_addf32, +TMS28_cmpf32, +TMS28_einvf32, +TMS28_eisqrtf32, +TMS28_f32toi16, +TMS28_f32toi16r, +TMS28_f32toi32, +TMS28_f32toui16, +TMS28_f32toui16r, +TMS28_f32toui32, +TMS28_fracf32, +TMS28_i16tof32, +TMS28_i32tof32, +TMS28_macf32, +TMS28_maxf32, +TMS28_minf32, +TMS28_mov16, +TMS28_mov32, +TMS28_movd32, +TMS28_movf32, +TMS28_moviz, +TMS28_movst0, +TMS28_movxi, +TMS28_mpyf32, +TMS28_negf32, +TMS28_poprb, +TMS28_pushrb, +TMS28_restore, +TMS28_rptb, +TMS28_save, +TMS28_setflg, +TMS28_subf32, +TMS28_swapf, +TMS28_testtf, +TMS28_ui16tof32, +TMS28_ui32tof32, +TMS28_zero, +TMS28_zeroa, + +// Floating Point Unit (FPU64) +TMS28_movdd32, +TMS28_movix, +TMS28_macf64, +TMS28_mpyf64, +TMS28_addf64, +TMS28_subf64, +TMS28_cmpf64, +TMS28_maxf64, +TMS28_minf64, +TMS28_f64toi32, +TMS28_f64toui32, +TMS28_i32tof64, +TMS28_ui32tof64, +TMS28_f64toi64, +TMS28_f64toui64, +TMS28_i64tof64, +TMS28_ui64tof64, +TMS28_fracf64, +TMS28_f64tof32, +TMS28_f32tof64, +TMS28_f32dtof64, +TMS28_absf64, +TMS28_negf64, +TMS28_mov64, +TMS28_eisqrtf64, +TMS28_einvf64, + +// Viterbi, Complex Math and CRC Unit (VCU) +TMS28_vclear, +TMS28_vclearall, +TMS28_vclrovfi, +TMS28_vclrovfr, +TMS28_vmov16, +TMS28_vmov32, +TMS28_vmovd32, +TMS28_vmovix, +TMS28_vmovzi, +TMS28_vmovxi, +TMS28_vrndoff, +TMS28_vrndon, +TMS28_vsatoff, +TMS28_vsaton, +TMS28_vsetshl, +TMS28_vsetshr, +TMS28_vcadd, +TMS28_vcdadd16, +TMS28_vcdsub16, +TMS28_vcmac, +TMS28_vcmpy, +TMS28_vneg, +TMS28_vcsub, +TMS28_vcrc8h_1, +TMS28_vcrc8l_1, +TMS28_vcrc16p1h_1, +TMS28_vcrc16p1l_1, +TMS28_vcrc16p2h_1, +TMS28_vcrc16p2l_1, +TMS28_vcrc32h_1, +TMS28_vcrc32l_1, +TMS28_vcrcclr, +TMS28_vitbm2, +TMS28_vitbm3, +TMS28_vitdhaddsub, +TMS28_vitdhsubadd, +TMS28_vitdladdsub, +TMS28_vitdlsubadd, +TMS28_vithsel, +TMS28_vitlsel, +TMS28_vtclear, +TMS28_vtrace, + +// Cyclic Redundancy Check (VCRC) +TMS28_vnop, +TMS28_vsetcrcmsgflip, +TMS28_vclrcrcmsgflip, +TMS28_vcrc32p2l_1, +TMS28_vcrc32p2h_1, +TMS28_vcrc24l_1, +TMS28_vcrc24h_1, +TMS28_vsetcrcsize, +TMS28_vcrcl, +TMS28_vcrch, +TMS28_vswapcrc, + +// C28 Viterbi, Complex Math and CRC Unit-II (VCU-II) +TMS28_vclrcpack, +TMS28_vclropack, +TMS28_vsetcpack, +TMS28_vsetopack, +TMS28_vswap32, +TMS28_vxormov32, +TMS28_vashl32, +TMS28_vashr32, +TMS28_vbitflip, +TMS28_vlshl32, +TMS28_vlshr32, +TMS28_vccmac, +TMS28_vccmpy, +TMS28_vccon, +TMS28_vcflip, +TMS28_vcmag, +TMS28_vcshl16, +TMS28_vcshr16, +TMS28_vclrdive, +TMS28_vdec, +TMS28_vinc, +TMS28_vmod32, +TMS28_vmpyadd, +TMS28_vcfft1, +TMS28_vcfft2, +TMS28_vcfft3, +TMS28_vcfft4, +TMS28_vcfft5, +TMS28_vcfft6, +TMS28_vcfft7, +TMS28_vcfft8, +TMS28_vcfft9, +TMS28_vcfft10, +TMS28_vgfacc, +TMS28_vgfadd4, +TMS28_vgfinit, +TMS28_vgfmac4, +TMS28_vgfmpy4, +TMS28_vpack4, +TMS28_vrevb, +TMS28_vshlmb, +TMS28_vitstage, +TMS28_vsetk, +TMS28_vsminit, + +// Fast Integer Division Unit (FINTDIV) +TMS28_absi32div32, +TMS28_absi32div32u, +TMS28_absi64div32, +TMS28_absi64div32u, +TMS28_absi64div64, +TMS28_absi64div64u, +TMS28_subc4ui32, +TMS28_subc2ui64, +TMS28_negi32div32, +TMS28_enegi32div32, +TMS28_mnegi32div32, +TMS28_negi64div32, +TMS28_enegi64div32, +TMS28_mnegi64div32, +TMS28_negi64div64, +TMS28_enegi64div64, +TMS28_mnegi64div64, + +// Trigonometric Math Unit (TMU) +TMS28_mpy2pif32, +TMS28_div2pif32, +TMS28_divf32, +TMS28_sqrtf32, +TMS28_sinpuf32, +TMS28_cospuf32, +TMS28_atanpuf32, +TMS28_quadf32, +TMS28_iexp2f32, +TMS28_log2f32, + +TMS28_last, + +}; + + + +enum +{ +UNSP_null = 0, // Unknown Operation + +// ALU +UNSP_add, // Add +UNSP_adc, // Add with Carry +UNSP_sub, // Subtract +UNSP_sbc, // Subtract with Carry +UNSP_cmp, // Compare +UNSP_cmpc, // Compare with Carry? +UNSP_neg, // Negate +UNSP_negc, // Negate with Carry? +UNSP_xor, // Exclusive-OR +UNSP_load, // Load +UNSP_or, // OR +UNSP_and, // AND +UNSP_test, // Test +UNSP_store, // Store + +UNSP_add_s, // Add (Simple) +UNSP_adc_s, // Add with Carry (Simple) +UNSP_sub_s, // Subtract (Simple) +UNSP_sbc_s, // Subtract with Carry (Simple) +UNSP_cmp_s, // Compare (Simple) +UNSP_cmpc_s, // Compare with Carry? (Simple) +UNSP_neg_s, // Negate (Simple) +UNSP_negc_s, // Negate with Carry? (Simple) +UNSP_xor_s, // Exclusive-OR (Simple) +UNSP_load_s, // Load (Simple) +UNSP_or_s, // OR (Simple) +UNSP_and_s, // AND (Simple) +UNSP_test_s, // Test (Simple) +UNSP_store_s, // Store (Simple) + + +// Misc +UNSP_retf, // Return Function +UNSP_reti, // Return Interrupt +UNSP_pop, // Pop from Stack +UNSP_push, // Push to Stack +UNSP_call, // Call +UNSP_goto, // Goto (far jump) +UNSP_nop, // NOP +UNSP_exp, // Effective Exponent + +// Jump +UNSP_jb, // Jump if Below (unsigned) [jcc, jnae] +UNSP_jae, // Jump if Above or Equal (unsigned) [jcs, jnb] +UNSP_jge, // Jump if Greater or Equal (signed) [jsc, jnl] +UNSP_jl, // Jump if Less (signed) [jss, jnge] +UNSP_jne, // Jump if Not Equal [jnz] +UNSP_je, // Jump if Equal [jz] +UNSP_jpl, // Jump if Positive +UNSP_jmi, // Jump if Negative +UNSP_jbe, // Jump if Below or Equal (unsigned) [jna] +UNSP_ja, // Jump if Above (unsigned) [jnbe] +UNSP_jle, // Jump if Less or Equal (signed) [jng] +UNSP_jg, // Jump if Greater (signed) [jnle] +UNSP_jvc, // Jump if Not Overflow +UNSP_jvs, // Jump if Overflow +UNSP_jmp, // Jump + +// Multiplication/Division +UNSP_mulss, // Multiply Signed * Signed +UNSP_mulus, // Multiply Unsigned * Signed +UNSP_muluu, // Multiply Unsigned * Unsigned +UNSP_divs, // Divide (Sign) +UNSP_divq, // Divide (Quotient) + +// Interrupt +UNSP_int1, // Interrupt flags +UNSP_int2, // Interrupt flags +UNSP_fir_mov, // FIR_MOV setting +UNSP_fraction, // FRACTION setting +UNSP_irq, // IRQ setting +UNSP_secbank, // SECBANK setting +UNSP_fiq, // FIQ setting +UNSP_irqnest, // IRQNEST setting +UNSP_break, // BREAK + +// Shift +UNSP_asr, // Arithmetic shift right +UNSP_asror, // Arithmetic shift right OR +UNSP_lsl, // Logical shift left +UNSP_lslor, // Logical shift left OR +UNSP_lsr, // Logical shift right +UNSP_lsror, // Logical shift right OR +UNSP_rol, // Rotate left +UNSP_ror, // Rotate right + +// Bitops +UNSP_tstb, // Test bit +UNSP_setb, // Set bit +UNSP_clrb, // Clear bit +UNSP_invb, // Invert bit + +UNSP_last, + }; + +/* + * Dalvik opcode enumeration. + */ + + +enum +{ + DALVIK_UNUSED = 0, + DALVIK_NOP, + + DALVIK_MOVE, + DALVIK_MOVE_FROM16, + DALVIK_MOVE_16, + DALVIK_MOVE_WIDE, + DALVIK_MOVE_WIDE_FROM16, + DALVIK_MOVE_WIDE_16, + DALVIK_MOVE_OBJECT, + DALVIK_MOVE_OBJECT_FROM16, + DALVIK_MOVE_OBJECT_16, + + DALVIK_MOVE_RESULT, + DALVIK_MOVE_RESULT_WIDE, + DALVIK_MOVE_RESULT_OBJECT, + DALVIK_MOVE_EXCEPTION, + + DALVIK_RETURN_VOID, + DALVIK_RETURN, + DALVIK_RETURN_WIDE, + DALVIK_RETURN_OBJECT, + + DALVIK_CONST_4, + DALVIK_CONST_16, + DALVIK_CONST, + DALVIK_CONST_HIGH16, + DALVIK_CONST_WIDE_16, + DALVIK_CONST_WIDE_32, + DALVIK_CONST_WIDE, + DALVIK_CONST_WIDE_HIGH16, + DALVIK_CONST_STRING, + DALVIK_CONST_STRING_JUMBO, + DALVIK_CONST_CLASS, + + DALVIK_MONITOR_ENTER, + DALVIK_MONITOR_EXIT, + + DALVIK_CHECK_CAST, + DALVIK_INSTANCE_OF, + + DALVIK_ARRAY_LENGTH, + + DALVIK_NEW_INSTANCE, + DALVIK_NEW_ARRAY, + + DALVIK_FILLED_NEW_ARRAY, + DALVIK_FILLED_NEW_ARRAY_RANGE, + DALVIK_FILL_ARRAY_DATA, + + DALVIK_THROW, + DALVIK_GOTO, + DALVIK_GOTO_16, + DALVIK_GOTO_32, + DALVIK_PACKED_SWITCH, + DALVIK_SPARSE_SWITCH, + + DALVIK_CMPL_FLOAT, + DALVIK_CMPG_FLOAT, + DALVIK_CMPL_DOUBLE, + DALVIK_CMPG_DOUBLE, + DALVIK_CMP_LONG, + + DALVIK_IF_EQ, + DALVIK_IF_NE, + DALVIK_IF_LT, + DALVIK_IF_GE, + DALVIK_IF_GT, + DALVIK_IF_LE, + DALVIK_IF_EQZ, + DALVIK_IF_NEZ, + DALVIK_IF_LTZ, + DALVIK_IF_GEZ, + DALVIK_IF_GTZ, + DALVIK_IF_LEZ, + + DALVIK_AGET, + DALVIK_AGET_WIDE, + DALVIK_AGET_OBJECT, + DALVIK_AGET_BOOLEAN, + DALVIK_AGET_BYTE, + DALVIK_AGET_CHAR, + DALVIK_AGET_SHORT, + DALVIK_APUT, + DALVIK_APUT_WIDE, + DALVIK_APUT_OBJECT, + DALVIK_APUT_BOOLEAN, + DALVIK_APUT_BYTE, + DALVIK_APUT_CHAR, + DALVIK_APUT_SHORT, + + DALVIK_IGET, + DALVIK_IGET_WIDE, + DALVIK_IGET_OBJECT, + DALVIK_IGET_BOOLEAN, + DALVIK_IGET_BYTE, + DALVIK_IGET_CHAR, + DALVIK_IGET_SHORT, + DALVIK_IPUT, + DALVIK_IPUT_WIDE, + DALVIK_IPUT_OBJECT, + DALVIK_IPUT_BOOLEAN, + DALVIK_IPUT_BYTE, + DALVIK_IPUT_CHAR, + DALVIK_IPUT_SHORT, + + DALVIK_SGET, + DALVIK_SGET_WIDE, + DALVIK_SGET_OBJECT, + DALVIK_SGET_BOOLEAN, + DALVIK_SGET_BYTE, + DALVIK_SGET_CHAR, + DALVIK_SGET_SHORT, + DALVIK_SPUT, + DALVIK_SPUT_WIDE, + DALVIK_SPUT_OBJECT, + DALVIK_SPUT_BOOLEAN, + DALVIK_SPUT_BYTE, + DALVIK_SPUT_CHAR, + DALVIK_SPUT_SHORT, + + DALVIK_INVOKE_VIRTUAL, + DALVIK_INVOKE_SUPER, + DALVIK_INVOKE_DIRECT, + DALVIK_INVOKE_STATIC, + DALVIK_INVOKE_INTERFACE, + + DALVIK_INVOKE_VIRTUAL_RANGE, + DALVIK_INVOKE_SUPER_RANGE, + DALVIK_INVOKE_DIRECT_RANGE, + DALVIK_INVOKE_STATIC_RANGE, + DALVIK_INVOKE_INTERFACE_RANGE, + + DALVIK_NEG_INT, + DALVIK_NOT_INT, + DALVIK_NEG_LONG, + DALVIK_NOT_LONG, + DALVIK_NEG_FLOAT, + DALVIK_NEG_DOUBLE, + DALVIK_INT_TO_LONG, + DALVIK_INT_TO_FLOAT, + DALVIK_INT_TO_DOUBLE, + DALVIK_LONG_TO_INT, + DALVIK_LONG_TO_FLOAT, + DALVIK_LONG_TO_DOUBLE, + DALVIK_FLOAT_TO_INT, + DALVIK_FLOAT_TO_LONG, + DALVIK_FLOAT_TO_DOUBLE, + DALVIK_DOUBLE_TO_INT, + DALVIK_DOUBLE_TO_LONG, + DALVIK_DOUBLE_TO_FLOAT, + DALVIK_INT_TO_BYTE, + DALVIK_INT_TO_CHAR, + DALVIK_INT_TO_SHORT, + + DALVIK_ADD_INT, + DALVIK_SUB_INT, + DALVIK_MUL_INT, + DALVIK_DIV_INT, + DALVIK_REM_INT, + DALVIK_AND_INT, + DALVIK_OR_INT, + DALVIK_XOR_INT, + DALVIK_SHL_INT, + DALVIK_SHR_INT, + DALVIK_USHR_INT, + + DALVIK_ADD_LONG, + DALVIK_SUB_LONG, + DALVIK_MUL_LONG, + DALVIK_DIV_LONG, + DALVIK_REM_LONG, + DALVIK_AND_LONG, + DALVIK_OR_LONG, + DALVIK_XOR_LONG, + DALVIK_SHL_LONG, + DALVIK_SHR_LONG, + DALVIK_USHR_LONG, + + DALVIK_ADD_FLOAT, + DALVIK_SUB_FLOAT, + DALVIK_MUL_FLOAT, + DALVIK_DIV_FLOAT, + DALVIK_REM_FLOAT, + DALVIK_ADD_DOUBLE, + DALVIK_SUB_DOUBLE, + DALVIK_MUL_DOUBLE, + DALVIK_DIV_DOUBLE, + DALVIK_REM_DOUBLE, + + DALVIK_ADD_INT_2ADDR, + DALVIK_SUB_INT_2ADDR, + DALVIK_MUL_INT_2ADDR, + DALVIK_DIV_INT_2ADDR, + DALVIK_REM_INT_2ADDR, + DALVIK_AND_INT_2ADDR, + DALVIK_OR_INT_2ADDR, + DALVIK_XOR_INT_2ADDR, + DALVIK_SHL_INT_2ADDR, + DALVIK_SHR_INT_2ADDR, + DALVIK_USHR_INT_2ADDR, + + DALVIK_ADD_LONG_2ADDR, + DALVIK_SUB_LONG_2ADDR, + DALVIK_MUL_LONG_2ADDR, + DALVIK_DIV_LONG_2ADDR, + DALVIK_REM_LONG_2ADDR, + DALVIK_AND_LONG_2ADDR, + DALVIK_OR_LONG_2ADDR, + DALVIK_XOR_LONG_2ADDR, + DALVIK_SHL_LONG_2ADDR, + DALVIK_SHR_LONG_2ADDR, + DALVIK_USHR_LONG_2ADDR, + + DALVIK_ADD_FLOAT_2ADDR, + DALVIK_SUB_FLOAT_2ADDR, + DALVIK_MUL_FLOAT_2ADDR, + DALVIK_DIV_FLOAT_2ADDR, + DALVIK_REM_FLOAT_2ADDR, + DALVIK_ADD_DOUBLE_2ADDR, + DALVIK_SUB_DOUBLE_2ADDR, + DALVIK_MUL_DOUBLE_2ADDR, + DALVIK_DIV_DOUBLE_2ADDR, + DALVIK_REM_DOUBLE_2ADDR, + + DALVIK_ADD_INT_LIT16, + DALVIK_RSUB_INT, // no _LIT16 suffix for this + DALVIK_MUL_INT_LIT16, + DALVIK_DIV_INT_LIT16, + DALVIK_REM_INT_LIT16, + DALVIK_AND_INT_LIT16, + DALVIK_OR_INT_LIT16, + DALVIK_XOR_INT_LIT16, + + DALVIK_ADD_INT_LIT8, + DALVIK_RSUB_INT_LIT8, + DALVIK_MUL_INT_LIT8, + DALVIK_DIV_INT_LIT8, + DALVIK_REM_INT_LIT8, + DALVIK_AND_INT_LIT8, + DALVIK_OR_INT_LIT8, + DALVIK_XOR_INT_LIT8, + DALVIK_SHL_INT_LIT8, + DALVIK_SHR_INT_LIT8, + DALVIK_USHR_INT_LIT8, + + DALVIK_IGET_VOLATILE, + DALVIK_IPUT_VOLATILE, + DALVIK_SGET_VOLATILE, + DALVIK_SPUT_VOLATILE, + DALVIK_IGET_OBJECT_VOLATILE, + + DALVIK_IGET_WIDE_VOLATILE, + DALVIK_IPUT_WIDE_VOLATILE, + DALVIK_SGET_WIDE_VOLATILE, + DALVIK_SPUT_WIDE_VOLATILE, + + // The "breakpoint" instruction is special, in that it should never + // be seen by anything but the debug interpreter. During debugging + // it takes the place of an arbitrary opcode, which means operations + // like "tell me the opcode width so I can find the next instruction" + // aren't possible. (This is correctable, but probably not useful.) + DALVIK_BREAKPOINT, + + DALVIK_THROW_VERIFICATION_ERROR, + + DALVIK_EXECUTE_INLINE, + DALVIK_EXECUTE_INLINE_RANGE, + + DALVIK_INVOKE_DIRECT_EMPTY, + DALVIK_RETURN_VOID_BARRIER, + DALVIK_IGET_QUICK, + DALVIK_IGET_WIDE_QUICK, + DALVIK_IGET_OBJECT_QUICK, + DALVIK_IPUT_QUICK, + DALVIK_IPUT_WIDE_QUICK, + DALVIK_IPUT_OBJECT_QUICK, + + DALVIK_INVOKE_VIRTUAL_QUICK, + DALVIK_INVOKE_VIRTUAL_QUICK_RANGE, + DALVIK_INVOKE_SUPER_QUICK, + DALVIK_INVOKE_SUPER_QUICK_RANGE, + DALVIK_IPUT_OBJECT_VOLATILE, + DALVIK_SGET_OBJECT_VOLATILE, + DALVIK_SPUT_OBJECT_VOLATILE, + + // dex-file format 038 + DALVIK_INVOKE_POLYMORPHIC, + DALVIK_INVOKE_POLYMORPHIC_RANGE, + DALVIK_INVOKE_CUSTOM, + DALVIK_INVOKE_CUSTOM_RANGE, + + // dex-file format 039 + DALVIK_CONST_METHOD_HANDLE, + DALVIK_CONST_METHOD_TYPE, + + DALVIK_LAST, +}; + +/* this file was entirely generated by gen_opc.py */ +enum +{ + s39_null = 0, + s39_a, // add (32) + s39_ad, // add normalized (long HFP) + s39_adb, // add (long BFP) + s39_adbr, // add (long BFP) + s39_adr, // add normalized (long HFP) + s39_adtr, // add (long DFP) + s39_adtra, // add (long DFP) + s39_ae, // add normalized (short HFP) + s39_aeb, // add (short BFP) + s39_aebr, // add (short BFP) + s39_aer, // add normalized (short HFP) + s39_afi, // add immediate (32) + s39_ag, // add (64) + s39_agf, // add (64 <- 32) + s39_agfi, // add immediate (64 <- 32) + s39_agfr, // add (64 <- 32) + s39_agh, // add halfword (64 <- 16) + s39_aghi, // add halfword immediate (64 <- 16) + s39_aghik, // add immediate (64 <- 16) + s39_agr, // add (64) + s39_agrk, // add (64) + s39_agsi, // add immediate (64 <- 8) + s39_ah, // add halfword (32 <- 16) + s39_ahhhr, // add high (32) + s39_ahhlr, // add high (32) + s39_ahi, // add halfword immediate (32 <- 16) + s39_ahik, // add immediate (32 <- 16) + s39_ahy, // add halfword (32 <- 16) + s39_aih, // add immediate high (32) + s39_al, // add logical (32) + s39_alc, // add logical with carry (32) + s39_alcg, // add logical with carry (64) + s39_alcgr, // add logical with carry (64) + s39_alcr, // add logical with carry (32) + s39_alfi, // add logical immediate (32) + s39_alg, // add logical (64) + s39_algf, // add logical (64 <- 32) + s39_algfi, // add logical immediate (64 <- 32) + s39_algfr, // add logical (64 <- 32) + s39_alghsik, // add logical with signed immediate (64 <- 16) + s39_algr, // add logical (64) + s39_algrk, // add logical (64) + s39_algsi, // add logical with signed immediate (64 <- 8) + s39_alhhhr, // add logical high (32) + s39_alhhlr, // add logical high (32) + s39_alhsik, // add logical with signed immediate (32 <- 16) + s39_alr, // add logical (32) + s39_alrk, // add logical (32) + s39_alsi, // add logical with signed immediate (32 <- 8) + s39_alsih, // add logical with signed immediate high (32) + s39_alsihn, // add logical with signed immediate high (32) + s39_aly, // add logical (32) + s39_ap, // add decimal + s39_ar, // add (32) + s39_ark, // add (32) + s39_asi, // add immediate (32 <- 8) + s39_au, // add unnormalized (short HFP) + s39_aur, // add unnormalized (short HFP) + s39_aw, // add unnormalized (long HFP) + s39_awr, // add unnormalized (long HFP) + s39_axbr, // add (extended BFP) + s39_axr, // add normalized (extended HFP) + s39_axtr, // add (extended DFP) + s39_axtra, // add (extended DFP) + s39_ay, // add (32) + s39_b, // unconditional branch + s39_bo, // branch on condition, jump on overflow / if ones + s39_bh, // branch on condition, jump on A high + s39_bnle, // branch on condition, jump on not low or equal + s39_bl, // branch on condition, jump on A low + s39_bnhe, // branch on condition, jump on not high or equal + s39_blh, // branch on condition, jump on low or high + s39_bne, // branch on condition, jump on A not equal B + s39_be, // branch on condition, jump on A equal B + s39_bnlh, // branch on condition, jump on not low or high + s39_bhe, // branch on condition, jump on high or equal + s39_bnl, // branch on condition, jump on A not low + s39_ble, // branch on condition, jump on low or equal + s39_bnh, // branch on condition, jump on A not high + s39_bno, // branch on condition, jump on not overflow / if not ones + s39_bor, // branch on condition, jump on overflow / if ones + s39_bhr, // branch on condition, jump on A high + s39_bnler, // branch on condition, jump on not low or equal + s39_blr, // branch on condition, jump on A low + s39_bnher, // branch on condition, jump on not high or equal + s39_blhr, // branch on condition, jump on low or high + s39_bner, // branch on condition, jump on A not equal B + s39_ber, // branch on condition, jump on A equal B + s39_bnlhr, // branch on condition, jump on not low or high + s39_bher, // branch on condition, jump on high or equal + s39_bnlr, // branch on condition, jump on A not low + s39_bler, // branch on condition, jump on low or equal + s39_bnhr, // branch on condition, jump on A not high + s39_bnor, // branch on condition, jump on not overflow / if not ones + s39_bakr, // branch and stack + s39_bal, // branch and link + s39_balr, // branch and link + s39_bas, // branch and save + s39_basr, // branch and save + s39_bassm, // branch and save and set mode + s39_bc, // branch on condition + s39_bcr, // branch on condition + s39_bct, // branch on count (32) + s39_bctg, // branch on count (64) + s39_bctgr, // branch on count (64) + s39_bctr, // branch on count (32) + s39_bi, // unconditional indirect branch + s39_bio, // branch indirect on condition, jump on overflow / if ones + s39_bih, // branch indirect on condition, jump on A high + s39_binle, // branch indirect on condition, jump on not low or equal + s39_bil, // branch indirect on condition, jump on A low + s39_binhe, // branch indirect on condition, jump on not high or equal + s39_bilh, // branch indirect on condition, jump on low or high + s39_bine, // branch indirect on condition, jump on A not equal B + s39_bie, // branch indirect on condition, jump on A equal B + s39_binlh, // branch indirect on condition, jump on not low or high + s39_bihe, // branch indirect on condition, jump on high or equal + s39_binl, // branch indirect on condition, jump on A not low + s39_bile, // branch indirect on condition, jump on low or equal + s39_binh, // branch indirect on condition, jump on A not high + s39_bino, // branch indirect on condition, jump on not overflow / if not ones + s39_bic, // branch indirect on condition + s39_bpp, // branch prediction preload + s39_bprp, // branch prediction relative preload + s39_br, // unconditional branch + s39_bras, // branch relative and save + s39_brasl, // branch relative and save long + s39_brc, // branch relative on condition + s39_brcl, // branch relative on condition long + s39_brct, // branch relative on count (32) + s39_brctg, // branch relative on count (64) + s39_brcth, // branch relative on count high (32) + s39_brxh, // branch relative on index high (32) + s39_brxhg, // branch relative on index high (64) + s39_brxle, // branch relative on index low or equal (32) + s39_brxlg, // branch relative on index low or equal (64) + s39_bsa, // branch and set authority + s39_bsg, // branch in subspace group + s39_bsm, // branch and set mode + s39_bxh, // branch on index high (32) + s39_bxhg, // branch on index high (64) + s39_bxle, // branch on index low or equal (32) + s39_bxleg, // branch on index low or equal (64) + s39_c, // compare (32) + s39_cd, // compare (long HFP) + s39_cdb, // compare (long BFP) + s39_cdbr, // compare (long BFP) + s39_cdfbr, // convert from fixed (32 to long BFP) + s39_cdfbra, // convert from fixed (32 to long BFP) + s39_cdfr, // convert from fixed (32 to long HFP) + s39_cdftr, // convert from fixed (32 to long DFP) + s39_cdgbr, // convert from fixed (64 to long BFP) + s39_cdgbra, // convert from fixed (64 to long BFP) + s39_cdgr, // convert from fixed (64 to long HFP) + s39_cdgtr, // convert from fixed (64 to long DFP) + s39_cdgtra, // convert from fixed (64 to long DFP) + s39_cdlfbr, // convert from logical (32 to long BFP) + s39_cdlftr, // convert from logical (32 to long DFP) + s39_cdlgbr, // convert from logical (64 to long BFP) + s39_cdlgtr, // convert from logical (64 to long DFP) + s39_cdpt, // convert from packed (to long DFP) + s39_cdr, // compare (long HFP) + s39_cds, // compare double and swap (32) + s39_cdsg, // compare double and swap (64) + s39_cdstr, // convert from signed packed (64 to long DFP) + s39_cdsy, // compare double and swap (32) + s39_cdtr, // compare (long DFP) + s39_cdutr, // convert from unsigned packed (64 to long DFP) + s39_cdzt, // convert from zoned (to long DFP) + s39_ce, // compare (short HFP) + s39_ceb, // compare (short BFP) + s39_cebr, // compare (short BFP) + s39_cedtr, // compare biased exponent (long DFP) + s39_cefbr, // convert from fixed (32 to short BFP) + s39_cefbra, // convert from fixed (32 to short BFP) + s39_cefr, // convert from fixed (32 to short HFP) + s39_cegbr, // convert from fixed (64 to short BFP) + s39_cegbra, // convert from fixed (64 to short BFP) + s39_cegr, // convert from fixed (64 to short HFP) + s39_celfbr, // convert from logical (32 to short BFP) + s39_celgbr, // convert from logical (64 to short BFP) + s39_cer, // compare (short HFP) + s39_cextr, // compare biased exponent (extended DFP) + s39_cfc, // compare and form codeword + s39_cfdbr, // convert to fixed (long BFP to 32) + s39_cfdbra, // convert to fixed (long BFP to 32) + s39_cfdr, // convert to fixed (long HFP to 32) + s39_cfdtr, // convert to fixed (long DFP to 32) + s39_cfebr, // convert to fixed (short BFP to 32) + s39_cfebra, // convert to fixed (short BFP to 32) + s39_cfer, // convert to fixed (short HFP to 32) + s39_cfi, // compare immediate (32) + s39_cfxbr, // convert to fixed (extended BFP to 32) + s39_cfxbra, // convert to fixed (extended BFP to 32) + s39_cfxr, // convert to fixed (extended HFP to 32) + s39_cfxtr, // convert to fixed (extended DFP to 32) + s39_cg, // compare (64) + s39_cgdbr, // convert to fixed (long BFP to 64) + s39_cgdbra, // convert to fixed (long BFP to 64) + s39_cgdr, // convert to fixed (long HFP to 64) + s39_cgdtr, // convert to fixed (long DFP to 64) + s39_cgdtra, // convert to fixed (long DFP to 64) + s39_cgebr, // convert to fixed (short BFP to 64) + s39_cgebra, // convert to fixed (short BFP to 64) + s39_cger, // convert to fixed (short HFP to 64) + s39_cgf, // compare (64 <- 32) + s39_cgfi, // compare immediate (64 <- 32) + s39_cgfr, // compare (64 <- 32) + s39_cgfrl, // compare relative long (64 <- 32) + s39_cgh, // compare halfword (64 <- 16) + s39_cghi, // compare halfword immediate (64 <- 16) + s39_cghrl, // compare halfword relative long (64 <- 16) + s39_cghsi, // compare halfword immediate (64 <- 16) + s39_cgib, // compare immediate and branch (64 <- 8) + s39_cgibh, // compare immediate and branch (64 <- 8), jump on A high + s39_cgibnhe, // compare immediate and branch (64 <- 8), jump on not high or equal + s39_cgiblh, // compare immediate and branch (64 <- 8), jump on low or high + s39_cgibnlh, // compare immediate and branch (64 <- 8), jump on not low or high + s39_cgibnl, // compare immediate and branch (64 <- 8), jump on A not low + s39_cgible, // compare immediate and branch (64 <- 8), jump on low or equal + s39_cgij, // compare immediate and branch relative (64 <- 8) + s39_cgijh, // compare immediate and branch relative (64 <- 8), jump on A high + s39_cgijnhe, // compare immediate and branch relative (64 <- 8), jump on not high or equal + s39_cgijlh, // compare immediate and branch relative (64 <- 8), jump on low or high + s39_cgijnlh, // compare immediate and branch relative (64 <- 8), jump on not low or high + s39_cgijnl, // compare immediate and branch relative (64 <- 8), jump on A not low + s39_cgijle, // compare immediate and branch relative (64 <- 8), jump on low or equal + s39_cgit, // compare immediate and trap (64 <- 16) + s39_cgith, // compare immediate and trap (64 <- 16), jump on A high + s39_cgitnhe, // compare immediate and trap (64 <- 16), jump on not high or equal + s39_cgitlh, // compare immediate and trap (64 <- 16), jump on low or high + s39_cgitnlh, // compare immediate and trap (64 <- 16), jump on not low or high + s39_cgitnl, // compare immediate and trap (64 <- 16), jump on A not low + s39_cgitle, // compare immediate and trap (64 <- 16), jump on low or equal + s39_cgr, // compare (64) + s39_cgrb, // compare and branch (64) + s39_cgrbh, // compare and branch (64), jump on A high + s39_cgrbnhe, // compare and branch (64), jump on not high or equal + s39_cgrblh, // compare and branch (64), jump on low or high + s39_cgrbnlh, // compare and branch (64), jump on not low or high + s39_cgrbnl, // compare and branch (64), jump on A not low + s39_cgrble, // compare and branch (64), jump on low or equal + s39_cgrj, // compare and branch relative (64) + s39_cgrjh, // compare and branch relative (64), jump on A high + s39_cgrjnhe, // compare and branch relative (64), jump on not high or equal + s39_cgrjlh, // compare and branch relative (64), jump on low or high + s39_cgrjnlh, // compare and branch relative (64), jump on not low or high + s39_cgrjnl, // compare and branch relative (64), jump on A not low + s39_cgrjle, // compare and branch relative (64), jump on low or equal + s39_cgrl, // compare relative long (64) + s39_cgrt, // compare and trap (64) + s39_cgrth, // compare and trap (64), jump on A high + s39_cgrtnhe, // compare and trap (64), jump on not high or equal + s39_cgrtlh, // compare and trap (64), jump on low or high + s39_cgrtnlh, // compare and trap (64), jump on not low or high + s39_cgrtnl, // compare and trap (64), jump on A not low + s39_cgrtle, // compare and trap (64), jump on low or equal + s39_cgxbr, // convert to fixed (extended BFP to 64) + s39_cgxbra, // convert to fixed (extended BFP to 64) + s39_cgxr, // convert to fixed (extended HFP to 64) + s39_cgxtr, // convert to fixed (extended DFP to 64) + s39_cgxtra, // convert to fixed (extended DFP to 64) + s39_ch, // compare halfword (32 <- 16) + s39_chf, // compare high (32) + s39_chhr, // compare high (32) + s39_chhsi, // compare halfword immediate (16 <- 16) + s39_chi, // compare halfword immediate (32 <- 16) + s39_chlr, // compare high (32) + s39_chrl, // compare halfword relative long (32 <- 16) + s39_chsi, // compare halfword immediate (32 <- 16) + s39_chy, // compare halfword (32 <- 16) + s39_cib, // comapre immediate and branch (32 <- 8) + s39_cibh, // comapre immediate and branch (32 <- 8), jump on A high + s39_cibnhe, // comapre immediate and branch (32 <- 8), jump on not high or equal + s39_ciblh, // comapre immediate and branch (32 <- 8), jump on low or high + s39_cibnlh, // comapre immediate and branch (32 <- 8), jump on not low or high + s39_cibnl, // comapre immediate and branch (32 <- 8), jump on A not low + s39_cible, // comapre immediate and branch (32 <- 8), jump on low or equal + s39_cih, // compare immediate high (32) + s39_cij, // compare immediate and branch relative (32 <- 8) + s39_cijh, // compare immediate and branch relative (32 <- 8), jump on A high + s39_cijnhe, // compare immediate and branch relative (32 <- 8), jump on not high or equal + s39_cijlh, // compare immediate and branch relative (32 <- 8), jump on low or high + s39_cijnlh, // compare immediate and branch relative (32 <- 8), jump on not low or high + s39_cijnl, // compare immediate and branch relative (32 <- 8), jump on A not low + s39_cijle, // compare immediate and branch relative (32 <- 8), jump on low or equal + s39_cit, // compare immediate and trap (32 <- 16) + s39_cith, // compare immediate and trap (32 <- 16), jump on A high + s39_citnhe, // compare immediate and trap (32 <- 16), jump on not high or equal + s39_citlh, // compare immediate and trap (32 <- 16), jump on low or high + s39_citnlh, // compare immediate and trap (32 <- 16), jump on not low or high + s39_citnl, // compare immediate and trap (32 <- 16), jump on A not low + s39_citle, // compare immediate and trap (32 <- 16), jump on low or equal + s39_cksm, // checksum + s39_cl, // compare logical (32) + s39_clc, // compare logical (character) + s39_clcl, // compare logical long + s39_clcle, // compare logical long extended + s39_clclu, // compare logical long unicode + s39_clfdbr, // convert to logical (long BFP to 32) + s39_clfdtr, // convert to logical (long DFP to 32) + s39_clfebr, // convert to logical (short BFP to 32) + s39_clfhsi, // compare logical immediate (32 <- 16) + s39_clfi, // compare logical immediate (32) + s39_clfit, // compare logical immediate and trap (32 <- 16) + s39_clfith, // compare logical immediate and trap (32 <- 16), jump on A high + s39_clfitnhe, // compare logical immediate and trap (32 <- 16), jump on not high or equal + s39_clfitlh, // compare logical immediate and trap (32 <- 16), jump on low or high + s39_clfitnlh, // compare logical immediate and trap (32 <- 16), jump on not low or high + s39_clfitnl, // compare logical immediate and trap (32 <- 16), jump on A not low + s39_clfitle, // compare logical immediate and trap (32 <- 16), jump on low or equal + s39_clfxbr, // convert to logical (extended BFP to 32) + s39_clfxtr, // convert to logical (extended DFP to 32) + s39_clg, // compare logical (64) + s39_clgdbr, // convert to logical (long BFP to 64) + s39_clgdtr, // convert to logical (long DFP to 64) + s39_clgebr, // convert to logical (short BFP to 64) + s39_clgf, // compare logical (64 <- 32) + s39_clgfi, // compare logical immediate (64 <- 32) + s39_clgfr, // compare logical (64 <- 32) + s39_clgfrl, // compare logical relative long (64 <- 32) + s39_clghrl, // compare logical relative long (64 <- 16) + s39_clghsi, // compare logical relative long (64 <- 16) + s39_clgib, // compare logical immediate and branch (64 <- 8) + s39_clgibh, // compare logical immediate and branch (64 <- 8), jump on A high + s39_clgibnhe, // compare logical immediate and branch (64 <- 8), jump on not high or equal + s39_clgiblh, // compare logical immediate and branch (64 <- 8), jump on low or high + s39_clgibnlh, // compare logical immediate and branch (64 <- 8), jump on not low or high + s39_clgibnl, // compare logical immediate and branch (64 <- 8), jump on A not low + s39_clgible, // compare logical immediate and branch (64 <- 8), jump on low or equal + s39_clgij, // compare logical immediate and branch relative (64 <- 8) + s39_clgijh, // compare logical immediate and branch relative (64 <- 8), jump on A high + s39_clgijnhe, // compare logical immediate and branch relative (64 <- 8), jump on not high or equal + s39_clgijlh, // compare logical immediate and branch relative (64 <- 8), jump on low or high + s39_clgijnlh, // compare logical immediate and branch relative (64 <- 8), jump on not low or high + s39_clgijnl, // compare logical immediate and branch relative (64 <- 8), jump on A not low + s39_clgijle, // compare logical immediate and branch relative (64 <- 8), jump on low or equal + s39_clgit, // compare logical immedate and trap (64 <- 16) + s39_clgith, // compare logical immedate and trap (64 <- 16), jump on A high + s39_clgitnhe, // compare logical immedate and trap (64 <- 16), jump on not high or equal + s39_clgitlh, // compare logical immedate and trap (64 <- 16), jump on low or high + s39_clgitnlh, // compare logical immedate and trap (64 <- 16), jump on not low or high + s39_clgitnl, // compare logical immedate and trap (64 <- 16), jump on A not low + s39_clgitle, // compare logical immedate and trap (64 <- 16), jump on low or equal + s39_clgr, // compare logical (64) + s39_clgrb, // compare logical and branch (64) + s39_clgrbh, // compare logical and branch (64), jump on A high + s39_clgrbnhe, // compare logical and branch (64), jump on not high or equal + s39_clgrblh, // compare logical and branch (64), jump on low or high + s39_clgrbnlh, // compare logical and branch (64), jump on not low or high + s39_clgrbnl, // compare logical and branch (64), jump on A not low + s39_clgrble, // compare logical and branch (64), jump on low or equal + s39_clgrj, // compare logical and branch relative (64) + s39_clgrjh, // compare logical and branch relative (64), jump on A high + s39_clgrjnhe, // compare logical and branch relative (64), jump on not high or equal + s39_clgrjlh, // compare logical and branch relative (64), jump on low or high + s39_clgrjnlh, // compare logical and branch relative (64), jump on not low or high + s39_clgrjnl, // compare logical and branch relative (64), jump on A not low + s39_clgrjle, // compare logical and branch relative (64), jump on low or equal + s39_clgrl, // compare logical relative long (64) + s39_clgrt, // compare logical and trap (64) + s39_clgrth, // compare logical and trap (64), jump on A high + s39_clgrtnhe, // compare logical and trap (64), jump on not high or equal + s39_clgrtlh, // compare logical and trap (64), jump on low or high + s39_clgrtnlh, // compare logical and trap (64), jump on not low or high + s39_clgrtnl, // compare logical and trap (64), jump on A not low + s39_clgrtle, // compare logical and trap (64), jump on low or equal + s39_clgt, // compare logical and trap (64) + s39_clgth, // compare logical and trap (64), jump on A high + s39_clgtnhe, // compare logical and trap (64), jump on not high or equal + s39_clgtlh, // compare logical and trap (64), jump on low or high + s39_clgtnlh, // compare logical and trap (64), jump on not low or high + s39_clgtnl, // compare logical and trap (64), jump on A not low + s39_clgtle, // compare logical and trap (64), jump on low or equal + s39_clgxbr, // convert to logical (extended BFP to 64) + s39_clgxtr, // convert to logical (extended DFP to 64) + s39_clhf, // compare logical high (32) + s39_clhhr, // compare logical high (32) + s39_clhhsi, // compare logical immediate (16 <- 16) + s39_clhlr, // compare logical high (32) + s39_clhrl, // compare logical relative long (32 <- 16) + s39_cli, // compare logical (immediate) + s39_clib, // compare logical immediate and branch (32 <- 8) + s39_clibh, // compare logical immediate and branch (32 <- 8), jump on A high + s39_clibnhe, // compare logical immediate and branch (32 <- 8), jump on not high or equal + s39_cliblh, // compare logical immediate and branch (32 <- 8), jump on low or high + s39_clibnlh, // compare logical immediate and branch (32 <- 8), jump on not low or high + s39_clibnl, // compare logical immediate and branch (32 <- 8), jump on A not low + s39_clible, // compare logical immediate and branch (32 <- 8), jump on low or equal + s39_clih, // compare logical immediate high (32) + s39_clij, // compare logical immediate and branch relative (32 <- 8) + s39_clijh, // compare logical immediate and branch relative (32 <- 8), jump on A high + s39_clijnhe, // compare logical immediate and branch relative (32 <- 8), jump on not high or equal + s39_clijlh, // compare logical immediate and branch relative (32 <- 8), jump on low or high + s39_clijnlh, // compare logical immediate and branch relative (32 <- 8), jump on not low or high + s39_clijnl, // compare logical immediate and branch relative (32 <- 8), jump on A not low + s39_clijle, // compare logical immediate and branch relative (32 <- 8), jump on low or equal + s39_cliy, // compare logical (immediate) + s39_clm, // compare logical character under mask (low) + s39_clmh, // compare logical character under mask (high) + s39_clmy, // compare logical (32) + s39_clr, // compare logical (32) + s39_clrb, // compare logical and branch (32) + s39_clrbh, // compare logical and branch (32), jump on A high + s39_clrbnhe, // compare logical and branch (32), jump on not high or equal + s39_clrblh, // compare logical and branch (32), jump on low or high + s39_clrbnlh, // compare logical and branch (32), jump on not low or high + s39_clrbnl, // compare logical and branch (32), jump on A not low + s39_clrble, // compare logical and branch (32), jump on low or equal + s39_clrj, // compare logical and branch relative (32) + s39_clrjh, // compare logical and branch relative (32), jump on A high + s39_clrjnhe, // compare logical and branch relative (32), jump on not high or equal + s39_clrjlh, // compare logical and branch relative (32), jump on low or high + s39_clrjnlh, // compare logical and branch relative (32), jump on not low or high + s39_clrjnl, // compare logical and branch relative (32), jump on A not low + s39_clrjle, // compare logical and branch relative (32), jump on low or equal + s39_clrl, // compare logical relative long (32) + s39_clrt, // compare logical and trap (32) + s39_clrth, // compare logical and trap (32), jump on A high + s39_clrtnhe, // compare logical and trap (32), jump on not high or equal + s39_clrtlh, // compare logical and trap (32), jump on low or high + s39_clrtnlh, // compare logical and trap (32), jump on not low or high + s39_clrtnl, // compare logical and trap (32), jump on A not low + s39_clrtle, // compare logical and trap (32), jump on low or equal + s39_clst, // compare logical string + s39_clt, // compare logical and trap (32) + s39_clth, // compare logical and trap (32), jump on A high + s39_cltnhe, // compare logical and trap (32), jump on not high or equal + s39_cltlh, // compare logical and trap (32), jump on low or high + s39_cltnlh, // compare logical and trap (32), jump on not low or high + s39_cltnl, // compare logical and trap (32), jump on A not low + s39_cltle, // compare logical and trap (32), jump on low or equal + s39_cly, // compare logical (32) + s39_cmpsc, // compression call + s39_cp, // compare decimal + s39_cpdt, // convert to packed (from long DFP) + s39_cpsdr, // copy sign (long) + s39_cpxt, // convert to packed (from extended DFP) + s39_cpya, // copy access + s39_cr, // compare (32) + s39_crb, // compare and branch (32) + s39_crbh, // compare and branch (32), jump on A high + s39_crbnhe, // compare and branch (32), jump on not high or equal + s39_crblh, // compare and branch (32), jump on low or high + s39_crbnlh, // compare and branch (32), jump on not low or high + s39_crbnl, // compare and branch (32), jump on A not low + s39_crble, // compare and branch (32), jump on low or equal + s39_crdte, // compare and replace DAT table entry + s39_crj, // compare and branch relative (32) + s39_crjh, // compare and branch relative (32), jump on A high + s39_crjnhe, // compare and branch relative (32), jump on not high or equal + s39_crjlh, // compare and branch relative (32), jump on low or high + s39_crjnlh, // compare and branch relative (32), jump on not low or high + s39_crjnl, // compare and branch relative (32), jump on A not low + s39_crjle, // compare and branch relative (32), jump on low or equal + s39_crl, // compare relative long (32) + s39_crt, // compare and trap (32) + s39_crth, // compare and trap (32), jump on A high + s39_crtnhe, // compare and trap (32), jump on not high or equal + s39_crtlh, // compare and trap (32), jump on low or high + s39_crtnlh, // compare and trap (32), jump on not low or high + s39_crtnl, // compare and trap (32), jump on A not low + s39_crtle, // compare and trap (32), jump on low or equal + s39_cs, // compare and swap (32) + s39_csch, // clear subchannel + s39_csdtr, // convert to signed packed (long DFP to 64) + s39_csg, // compare and swap (64) + s39_csp, // compare and swap and purge (32) + s39_cspg, // compare and swap and purge (64) + s39_csst, // compare and swap and store + s39_csxtr, // convert to signed packed (extended DFP to 128) + s39_csy, // compare and swap (32) + s39_cu12, // convert utf-8 to utf-16 + s39_cu14, // convert utf-8 to utf-32 + s39_cu21, // convert utf-16 to utf-8 + s39_cu24, // convert utf-16 to utf-32 + s39_cu41, // convert utf-32 to utf-8 + s39_cu42, // convert utf-32 to utf-16 + s39_cudtr, // convert to unsigned packed (long DFP to 64) + s39_cuse, // compare until substring equal + s39_cutfu, // convert utf-8 to unicode + s39_cuutf, // convert unicode to utf-8 + s39_cuxtr, // convert to unsigned packed (extended DFP to 128) + s39_cvb, // convert to binary (32) + s39_cvbg, // convert to binary (64) + s39_cvby, // convert to binary (32) + s39_cvd, // convert to decimal (32) + s39_cvdg, // convert to decimal (64) + s39_cvdy, // convert to decimal (32) + s39_cxbr, // compare (extended BFP) + s39_cxfbr, // convert from fixed (32 to extended BFP) + s39_cxfbra, // convert from fixed (32 to extended BFP) + s39_cxfr, // convert from fixed (32 to extended HFP) + s39_cxftr, // convert from fixed (32 to extended DFP) + s39_cxgbr, // convert from fixed (64 to extended BFP) + s39_cxgbra, // convert from fixed (64 to extended BFP) + s39_cxgr, // convert from fixed (64 to extended HFP) + s39_cxgtr, // convert from fixed (64 to extended DFP) + s39_cxgtra, // convert from fixed (64 to extended DFP) + s39_cxlfbr, // convert from logical (32 to extended BFP) + s39_cxlftr, // convert from logical (32 to extended DFP) + s39_cxlgbr, // convert from logical (64 to extended BFP) + s39_cxlgtr, // convert from logical (64 to extended DFP) + s39_cxpt, // convert from packed (to extended DFP) + s39_cxr, // compare (extended HFP) + s39_cxstr, // convert from singed packed (128 to extended DFP) + s39_cxtr, // compare (extended DFP) + s39_cxutr, // convert from unsigned packed (128 to extended DFP) + s39_cxzt, // convert from zoned (to extended DFP) + s39_cy, // compare (32) + s39_czdt, // convert to zoned (from long DFP) + s39_czxt, // convert to zoned (from extended DFP) + s39_d, // divide (32 <- 64) + s39_dd, // divide (long HFP) + s39_ddb, // divide (long BFP) + s39_ddbr, // divide (long BFP) + s39_ddr, // divide (long HFP) + s39_ddtr, // divide (long DFP) + s39_ddtra, // divide (long DFP) + s39_de, // divide (short HFP) + s39_deb, // divide (short BFP) + s39_debr, // divide (short BFP) + s39_der, // divide (short HFP) + s39_dfltcc, // + s39_diag, // diagnose + s39_didbr, // divide to integer (long BFP) + s39_diebr, // divide to integer (short BFP) + s39_dl, // divide logical (32 <- 64) + s39_dlg, // divide logical (64 <- 128) + s39_dlgr, // divide logical (64 <- 128) + s39_dlr, // divide logical (32 <- 64) + s39_dp, // divide decimal + s39_dr, // divide (32 <- 64) + s39_dsg, // divide single (64) + s39_dsgf, // divide single (64 <- 32) + s39_dsgfr, // divide single (64 <- 32) + s39_dsgr, // divide single (64) + s39_dxbr, // divide (extended BFP) + s39_dxr, // divide (extended HFP) + s39_dxtr, // divide (extended DFP) + s39_dxtra, // divide (extended DFP) + s39_ear, // extract access + s39_ecag, // extract cache attribute + s39_ecctr, // extract cpu counter + s39_ecpga, // extract coprocessor-group address + s39_ectg, // extract cpu time + s39_ed, // edit + s39_edmk, // edit and mark + s39_eedtr, // extract biased exponent (long DFP to 64) + s39_eextr, // extract biased exponent (extended DFP to 64) + s39_efpc, // extract fpc + s39_epair, // extract primary ASN and instance + s39_epar, // extract primary ASN + s39_epctr, // extract peripheral counter + s39_epsw, // extract psw + s39_ereg, // extract stacked registers (32) + s39_eregg, // extract stacked registers (64) + s39_esair, // extract secondary ASN and instance + s39_esar, // extract secondary ASN + s39_esdtr, // extract significance (long DFP to 64) + s39_esea, // extract and set extended authority + s39_esta, // extract stacked state + s39_esxtr, // extract significance (extended DFP to 64) + s39_etnd, // extract transaction nesting depth + s39_ex, // execute + s39_exrl, // execute relative long + s39_fidbr, // load fp integer (long BFP) + s39_fidbra, // load fp integer (long BFP) + s39_fidr, // load fp integer (long HFP) + s39_fidtr, // load fp integer (long DFP) + s39_fiebr, // load fp integer (short BFP) + s39_fiebra, // load fp integer (short BFP) + s39_fier, // load fp integer (short HFP) + s39_fixbr, // load fp integer (extended BFP) + s39_fixbra, // load fp integer (extended BFP) + s39_fixr, // load fp integer (extended HFP) + s39_fixtr, // load fp integer (extended DFP) + s39_flogr, // find leftmost one + s39_hdr, // halve (long HFP) + s39_her, // halve (short HFP) + s39_hsch, // halt subchannel + s39_iac, // insert address space control + s39_ic, // insert character + s39_icm, // insert characters under mask (low) + s39_icmh, // insert characters under mask (high) + s39_icmy, // insert characters under mask (low) + s39_icy, // insert character + s39_idte, // invalidate dat table entry + s39_iedtr, // insert biased exponent (64 to long DFP) + s39_iextr, // insert biased exponent (64 to extended DFP) + s39_iihf, // insert immediate (high) + s39_iihh, // inset immediate (high high) + s39_iihl, // insert immediate (high low) + s39_iilf, // insert immediate (low) + s39_iilh, // insert immediate (low high) + s39_iill, // insert immediate (low low) + s39_ipk, // insert PSW key + s39_ipm, // insert program mask + s39_ipte, // invalidate page table entry + s39_irbm, // insert reference bits multiple + s39_iske, // insert storage key extended + s39_ivsk, // insert virtual storage key + s39_j, // unconditional jump + s39_jo, // conditional jump, jump on overflow / if ones + s39_jh, // conditional jump, jump on A high + s39_jnle, // conditional jump, jump on not low or equal + s39_jl, // conditional jump, jump on A low + s39_jnhe, // conditional jump, jump on not high or equal + s39_jlh, // conditional jump, jump on low or high + s39_jne, // conditional jump, jump on A not equal B + s39_je, // conditional jump, jump on A equal B + s39_jnlh, // conditional jump, jump on not low or high + s39_jhe, // conditional jump, jump on high or equal + s39_jnl, // conditional jump, jump on A not low + s39_jle, // conditional jump, jump on low or equal + s39_jnh, // conditional jump, jump on A not high + s39_jno, // conditional jump, jump on not overflow / if not ones + s39_jg, // unconditional jump long + s39_jgo, // conditional jump long, jump on overflow / if ones + s39_jgh, // conditional jump long, jump on A high + s39_jgnle, // conditional jump long, jump on not low or equal + s39_jgl, // conditional jump long, jump on A low + s39_jgnhe, // conditional jump long, jump on not high or equal + s39_jglh, // conditional jump long, jump on low or high + s39_jgne, // conditional jump long, jump on A not equal B + s39_jge, // conditional jump long, jump on A equal B + s39_jgnlh, // conditional jump long, jump on not low or high + s39_jghe, // conditional jump long, jump on high or equal + s39_jgnl, // conditional jump long, jump on A not low + s39_jgle, // conditional jump long, jump on low or equal + s39_jgnh, // conditional jump long, jump on A not high + s39_jgno, // conditional jump long, jump on not overflow / if not ones + s39_kdb, // compare and signal (long BFP) + s39_kdbr, // compare and signal (long BFP) + s39_kdsa, // + s39_kdtr, // compare and signal (long DFP) + s39_keb, // compare and signal (short BFP) + s39_kebr, // compare and signal (short BFP) + s39_kimd, // compute intermediate message digest + s39_klmd, // compute last message digest + s39_km, // cipher message + s39_kma, // cipher message with authentication + s39_kmac, // compute message with authentication + s39_kmc, // cipher message with chaining + s39_kmctr, // cipher message with counter + s39_kmf, // cipher message with cipher feedback + s39_kmo, // cipher message with output feedback + s39_kxbr, // compare and signal (extended BFP) + s39_kxtr, // compare and signal (extended DFP) + s39_l, // load (32) + s39_la, // load address + s39_laa, // load and add (32) + s39_laag, // load and add (64) + s39_laal, // load and add logical (32) + s39_laalg, // load and add logical (64) + s39_lae, // load address extended + s39_laey, // load address extended + s39_lam, // load access multiple + s39_lamy, // load access multiple + s39_lan, // load and and (32) + s39_lang, // load and and (64) + s39_lao, // load and or (32) + s39_laog, // load and or (64) + s39_larl, // load address relative long + s39_lasp, // load address space parameters + s39_lat, // load and trap (32L <- 32) + s39_lax, // load and exclusive or (32) + s39_laxg, // load and exclusive or (64) + s39_lay, // load address + s39_lb, // load byte (32 <- 8) + s39_lbh, // load byte high (32 <- 8) + s39_lbr, // load byte (32 <- 8) + s39_lcbb, // load count to block boundary + s39_lcctl, // load cpu-counter-set controls + s39_lcdbr, // load complement (long BFP) + s39_lcdfr, // load complement (long) + s39_lcdr, // load complement (long HFP) + s39_lcebr, // load complement (short BFP) + s39_lcer, // load complement (short HFP) + s39_lcgfr, // load complement (64 <- 32) + s39_lcgr, // load complement (64) + s39_lcr, // load complement (32) + s39_lctl, // load control (32) + s39_lctlg, // load control (64) + s39_lcxbr, // load complement (extended BFP) + s39_lcxr, // load complement (extended HFP) + s39_ld, // load (long) + s39_lde, // load lengthened (short to long HFP) + s39_ldeb, // load lengthened (short to long BFP) + s39_ldebr, // load lengthened (short to long BFP) + s39_lder, // load lengthened (short to long HFP) + s39_ldetr, // load lengthened (short to long DFP) + s39_ldgr, // load fpr from gr (64 to long) + s39_ldr, // load (long) + s39_ldrv, // + s39_ldxbr, // load rounded (extended to long BFP) + s39_ldxbra, // load rounded (extended to long BFP) + s39_ldxr, // load rounded (extended to long HFP) + s39_ldxtr, // load rounded (extended to long HFP) + s39_ldy, // load (long) + s39_le, // load (short) + s39_ledbr, // load rounded (long to short BFP) + s39_ledbra, // load rounded (long to short BFP) + s39_ledr, // load rounded (long to short HFP) + s39_ledtr, // load rounded (long to short DFP) + s39_ler, // load short + s39_lerv, // + s39_lexbr, // load rounded (extended to short BFP) + s39_lexbra, // load rounded (extended to short BFP) + s39_lexr, // load rounded (extended to short HFP) + s39_ley, // load (short) + s39_lfas, // load fpc and signal + s39_lfh, // load high (32) + s39_lfhat, // load high and trap (32h <- 32) + s39_lfpc, // load fpc + s39_lg, // load (64) + s39_lgat, // load and trap (64) + s39_lgb, // load byte (64 <- 8) + s39_lgbr, // load byte (64 <- 8) + s39_lgdr, // load gr from fpr (long to 64) + s39_lgf, // load (64 <- 32) + s39_lgfi, // load immediate (64 <- 32) + s39_lgfr, // load (64 <- 32) + s39_lgfrl, // load relative long (64 <- 32) + s39_lgg, // load guarded (64) + s39_lgh, // load halfword (64 <- 16) + s39_lghi, // load halfword immediate (64 <- 16) + s39_lghr, // load halfword (64 <- 16) + s39_lghrl, // load halfword relative long (64 <- 16) + s39_lgr, // load (64) + s39_lgrl, // load relative long (64) + s39_lgsc, // load guarded storage controls + s39_lh, // load halfword (32 <- 16) + s39_lhh, // load halfword high (32 <- 16) + s39_lhi, // load halfword immediate (32) <- 16 + s39_lhr, // load halfword (32 <- 16) + s39_lhrl, // load halfword relative long (32 <- 16) + s39_lhy, // load halfword (32 <- 16) + s39_llc, // load logical character (32 <- 8) + s39_llch, // load logical character high (32 <- 8) + s39_llcr, // load logical character (32 <- 8) + s39_llgc, // load logical character (64 <- 8) + s39_llgcr, // load logical character (64 <- 8) + s39_llgf, // load logical (64 <- 32) + s39_llgfat, // load logical and trap (64 <- 32) + s39_llgfr, // load logical (64 <- 32) + s39_llgfrl, // load logical relative long (64 <- 32) + s39_llgfsg, // load logical and shift guarded (64 <- 32) + s39_llgh, // load logical halfword (64 <- 16) + s39_llghr, // load logical halfword (64 <- 16) + s39_llghrl, // load logical halfword relative long (64 <- 16) + s39_llgt, // load logical thirty one bits (64 <- 31) + s39_llgtat, // load logical thirty one bits and trap (64 <- 31) + s39_llgtr, // load logical thirty one bits (64 <- 31) + s39_llh, // load logical halfword (32 <- 16) + s39_llhh, // load logical halfword high (32 <- 16) + s39_llhr, // load logical halfword (32 <- 16) + s39_llhrl, // load logical halfword relative long (32 <- 16) + s39_llihf, // load logical immediate (high) + s39_llihh, // load logical immediate (high high) + s39_llihl, // load logical immediate (high low) + s39_llilf, // load logical immediate (low) + s39_llilh, // load logical immediate (low high) + s39_llill, // load logical immediate (low low) + s39_llzrgf, // load logical and zero rightmost byte (64 <- 32) + s39_lm, // load multiple (32) + s39_lmd, // load multiple disjoint (64 <- 32& 32) + s39_lmg, // load multiple (64) + s39_lmh, // load multiple high (32) + s39_lmy, // load multiple high (32) + s39_lndbr, // load negative (long BFP) + s39_lndfr, // load negative (long) + s39_lndr, // load negative (long HFP) + s39_lnebr, // load negative (short BFP) + s39_lner, // load negative (short HFP) + s39_lngfr, // load negative (64 <- 32) + s39_lngr, // load negative (64) + s39_lnr, // load negative (32) + s39_lnxbr, // load negative (extended BFP) + s39_lnxr, // load negative (extended HFP) + s39_loc, // load on condition (32) + s39_loco, // load on condition (32), jump on overflow / if ones + s39_loch, // load on condition (32), jump on A high + s39_locnle, // load on condition (32), jump on not low or equal + s39_locl, // load on condition (32), jump on A low + s39_locnhe, // load on condition (32), jump on not high or equal + s39_loclh, // load on condition (32), jump on low or high + s39_locne, // load on condition (32), jump on A not equal B + s39_loce, // load on condition (32), jump on A equal B + s39_locnlh, // load on condition (32), jump on not low or high + s39_loche, // load on condition (32), jump on high or equal + s39_locnl, // load on condition (32), jump on A not low + s39_locle, // load on condition (32), jump on low or equal + s39_locnh, // load on condition (32), jump on A not high + s39_locno, // load on condition (32), jump on not overflow / if not ones + s39_locfh, // load high on condition (32) + s39_locfho, // load high on condition (32), jump on overflow / if ones + s39_locfhh, // load high on condition (32), jump on A high + s39_locfhnle, // load high on condition (32), jump on not low or equal + s39_locfhl, // load high on condition (32), jump on A low + s39_locfhnhe, // load high on condition (32), jump on not high or equal + s39_locfhlh, // load high on condition (32), jump on low or high + s39_locfhne, // load high on condition (32), jump on A not equal B + s39_locfhe, // load high on condition (32), jump on A equal B + s39_locfhnlh, // load high on condition (32), jump on not low or high + s39_locfhhe, // load high on condition (32), jump on high or equal + s39_locfhnl, // load high on condition (32), jump on A not low + s39_locfhle, // load high on condition (32), jump on low or equal + s39_locfhnh, // load high on condition (32), jump on A not high + s39_locfhno, // load high on condition (32), jump on not overflow / if not ones + s39_locfhr, // load high on condition (32) + s39_locfhro, // load high on condition (32), jump on overflow / if ones + s39_locfhrh, // load high on condition (32), jump on A high + s39_locfhrnle, // load high on condition (32), jump on not low or equal + s39_locfhrl, // load high on condition (32), jump on A low + s39_locfhrnhe, // load high on condition (32), jump on not high or equal + s39_locfhrlh, // load high on condition (32), jump on low or high + s39_locfhrne, // load high on condition (32), jump on A not equal B + s39_locfhre, // load high on condition (32), jump on A equal B + s39_locfhrnlh, // load high on condition (32), jump on not low or high + s39_locfhrhe, // load high on condition (32), jump on high or equal + s39_locfhrnl, // load high on condition (32), jump on A not low + s39_locfhrle, // load high on condition (32), jump on low or equal + s39_locfhrnh, // load high on condition (32), jump on A not high + s39_locfhrno, // load high on condition (32), jump on not overflow / if not ones + s39_locg, // load on condition (64) + s39_locgo, // load on condition (64), jump on overflow / if ones + s39_locgh, // load on condition (64), jump on A high + s39_locgnle, // load on condition (64), jump on not low or equal + s39_locgl, // load on condition (64), jump on A low + s39_locgnhe, // load on condition (64), jump on not high or equal + s39_locglh, // load on condition (64), jump on low or high + s39_locgne, // load on condition (64), jump on A not equal B + s39_locge, // load on condition (64), jump on A equal B + s39_locgnlh, // load on condition (64), jump on not low or high + s39_locghe, // load on condition (64), jump on high or equal + s39_locgnl, // load on condition (64), jump on A not low + s39_locgle, // load on condition (64), jump on low or equal + s39_locgnh, // load on condition (64), jump on A not high + s39_locgno, // load on condition (64), jump on not overflow / if not ones + s39_locghi, // load halfword immediate on condition (64 <- 16) + s39_locghio, // load halfword immediate on condition (64 <- 16), jump on overflow / if ones + s39_locghih, // load halfword immediate on condition (64 <- 16), jump on A high + s39_locghinle, // load halfword immediate on condition (64 <- 16), jump on not low or equal + s39_locghil, // load halfword immediate on condition (64 <- 16), jump on A low + s39_locghinhe, // load halfword immediate on condition (64 <- 16), jump on not high or equal + s39_locghilh, // load halfword immediate on condition (64 <- 16), jump on low or high + s39_locghine, // load halfword immediate on condition (64 <- 16), jump on A not equal B + s39_locghie, // load halfword immediate on condition (64 <- 16), jump on A equal B + s39_locghinlh, // load halfword immediate on condition (64 <- 16), jump on not low or high + s39_locghihe, // load halfword immediate on condition (64 <- 16), jump on high or equal + s39_locghinl, // load halfword immediate on condition (64 <- 16), jump on A not low + s39_locghile, // load halfword immediate on condition (64 <- 16), jump on low or equal + s39_locghinh, // load halfword immediate on condition (64 <- 16), jump on A not high + s39_locghino, // load halfword immediate on condition (64 <- 16), jump on not overflow / if not ones + s39_locgr, // load on condition (64) + s39_locgro, // load on condition (64), jump on overflow / if ones + s39_locgrh, // load on condition (64), jump on A high + s39_locgrnle, // load on condition (64), jump on not low or equal + s39_locgrl, // load on condition (64), jump on A low + s39_locgrnhe, // load on condition (64), jump on not high or equal + s39_locgrlh, // load on condition (64), jump on low or high + s39_locgrne, // load on condition (64), jump on A not equal B + s39_locgre, // load on condition (64), jump on A equal B + s39_locgrnlh, // load on condition (64), jump on not low or high + s39_locgrhe, // load on condition (64), jump on high or equal + s39_locgrnl, // load on condition (64), jump on A not low + s39_locgrle, // load on condition (64), jump on low or equal + s39_locgrnh, // load on condition (64), jump on A not high + s39_locgrno, // load on condition (64), jump on not overflow / if not ones + s39_lochhi, // load halfword high immediate on condition (32 <- 16) + s39_lochhio, // load halfword high immediate on condition (32 <- 16), jump on overflow / if ones + s39_lochhih, // load halfword high immediate on condition (32 <- 16), jump on A high + s39_lochhinle, // load halfword high immediate on condition (32 <- 16), jump on not low or equal + s39_lochhil, // load halfword high immediate on condition (32 <- 16), jump on A low + s39_lochhinhe, // load halfword high immediate on condition (32 <- 16), jump on not high or equal + s39_lochhilh, // load halfword high immediate on condition (32 <- 16), jump on low or high + s39_lochhine, // load halfword high immediate on condition (32 <- 16), jump on A not equal B + s39_lochhie, // load halfword high immediate on condition (32 <- 16), jump on A equal B + s39_lochhinlh, // load halfword high immediate on condition (32 <- 16), jump on not low or high + s39_lochhihe, // load halfword high immediate on condition (32 <- 16), jump on high or equal + s39_lochhinl, // load halfword high immediate on condition (32 <- 16), jump on A not low + s39_lochhile, // load halfword high immediate on condition (32 <- 16), jump on low or equal + s39_lochhinh, // load halfword high immediate on condition (32 <- 16), jump on A not high + s39_lochhino, // load halfword high immediate on condition (32 <- 16), jump on not overflow / if not ones + s39_lochi, // load halfword immediate on condition (32 <- 16) + s39_lochio, // load halfword immediate on condition (32 <- 16), jump on overflow / if ones + s39_lochih, // load halfword immediate on condition (32 <- 16), jump on A high + s39_lochinle, // load halfword immediate on condition (32 <- 16), jump on not low or equal + s39_lochil, // load halfword immediate on condition (32 <- 16), jump on A low + s39_lochinhe, // load halfword immediate on condition (32 <- 16), jump on not high or equal + s39_lochilh, // load halfword immediate on condition (32 <- 16), jump on low or high + s39_lochine, // load halfword immediate on condition (32 <- 16), jump on A not equal B + s39_lochie, // load halfword immediate on condition (32 <- 16), jump on A equal B + s39_lochinlh, // load halfword immediate on condition (32 <- 16), jump on not low or high + s39_lochihe, // load halfword immediate on condition (32 <- 16), jump on high or equal + s39_lochinl, // load halfword immediate on condition (32 <- 16), jump on A not low + s39_lochile, // load halfword immediate on condition (32 <- 16), jump on low or equal + s39_lochinh, // load halfword immediate on condition (32 <- 16), jump on A not high + s39_lochino, // load halfword immediate on condition (32 <- 16), jump on not overflow / if not ones + s39_locr, // load on condition (32) + s39_locro, // load on condition (32), jump on overflow / if ones + s39_locrh, // load on condition (32), jump on A high + s39_locrnle, // load on condition (32), jump on not low or equal + s39_locrl, // load on condition (32), jump on A low + s39_locrnhe, // load on condition (32), jump on not high or equal + s39_locrlh, // load on condition (32), jump on low or high + s39_locrne, // load on condition (32), jump on A not equal B + s39_locre, // load on condition (32), jump on A equal B + s39_locrnlh, // load on condition (32), jump on not low or high + s39_locrhe, // load on condition (32), jump on high or equal + s39_locrnl, // load on condition (32), jump on A not low + s39_locrle, // load on condition (32), jump on low or equal + s39_locrnh, // load on condition (32), jump on A not high + s39_locrno, // load on condition (32), jump on not overflow / if not ones + s39_lpctl, // load peripheral-counter-set controls + s39_lpd, // load pair disjoint (32) + s39_lpdbr, // load positive (long BFP) + s39_lpdfr, // load positive (long) + s39_lpdg, // load pair disjoint (64) + s39_lpdr, // load positive (long HFP) + s39_lpebr, // load positive (short BFP) + s39_lper, // load positive (short HFP) + s39_lpgfr, // load positive (64 <- 32) + s39_lpgr, // load positive (64) + s39_lpp, // load program parameter + s39_lpq, // load pair from quadword (64&64 <- 128) + s39_lpr, // load positive (32) + s39_lpsw, // load PSW + s39_lpswe, // load psw extended + s39_lptea, // load page-table-entry address + s39_lpxbr, // load positive (extended BFP) + s39_lpxr, // load positive (extended HFP) + s39_lr, // load (32) + s39_lra, // load real address (32) + s39_lrag, // load real address (64) + s39_lray, // load real address (32) + s39_lrdr, // load rounded (extended to long HFP) + s39_lrer, // load rounded (long to short HFP) + s39_lrl, // load relative long (32) + s39_lrv, // load reversed (32) + s39_lrvg, // load reversed (64) + s39_lrvgr, // load reversed (64) + s39_lrvh, // load reversed (16) + s39_lrvr, // load reversed (32) + s39_lsctl, // load sampling controls + s39_lt, // load and test (32) + s39_ltdbr, // load and test (long BFP) + s39_ltdr, // load and test (long HFP) + s39_ltdtr, // load and test (long DFP) + s39_ltebr, // load and test (short BFP) + s39_lter, // load and test (short HFP) + s39_ltg, // load and test (64) + s39_ltgf, // load and test (64 <- 32) + s39_ltgfr, // load and test (64 <- 32) + s39_ltgr, // load and test (64) + s39_ltr, // load and test (32) + s39_ltxbr, // load and test (extended BFP) + s39_ltxr, // load and test (extended HFP) + s39_ltxtr, // load and test (extended DFP) + s39_lura, // load using real address (32) + s39_lurag, // load using real address (64) + s39_lxd, // load lengthened (long to extended HFP) + s39_lxdb, // load lengthened (long to extended BFP) + s39_lxdbr, // load lengthened (long to extended BFP) + s39_lxdr, // load lengthened (long to extended HFP) + s39_lxdtr, // load lengthened (long to extended DFP) + s39_lxe, // load lengthened (short to extended HFP) + s39_lxeb, // load lengthened (short to extended BFP) + s39_lxebr, // load lengthened (short to extended BFP) + s39_lxer, // load lengthened (short to extended HFP) + s39_lxr, // load (extended) + s39_ly, // load (32) + s39_lzdr, // load zero (long) + s39_lzer, // load zero (short) + s39_lzrf, // load and zero rightmost byte (32) + s39_lzrg, // load and zero rightmost byte (64) + s39_lzxr, // load zero (extended) + s39_m, // multiply (64 <- 32) + s39_mad, // multiply and add (long HFP) + s39_madb, // multiply and add (long BFP) + s39_madbr, // multiply and add (long BFP) + s39_madr, // multiply and add (long HFP) + s39_mae, // multiply and add (short HFP) + s39_maeb, // multiply and add (short BFP) + s39_maebr, // multiply and add (short BFP) + s39_maer, // multiply and add (short HFP) + s39_may, // multiply & add unnormalized (long to extended HFP) + s39_mayh, // multiply & add unnormalized (long to extended high HFP) + s39_mayhr, // multiply & add unnormalized (long to extended high HFP) + s39_mayl, // multiply & add unnormalized (long to extended low HFP) + s39_maylr, // multiply & add unnormalized (long to extended low HFP) + s39_mayr, // multiply & add unnormalized (long to extended HFP) + s39_mc, // monitor call + s39_md, // multiply (long HFP) + s39_mdb, // multiply (long BFP) + s39_mdbr, // multiply (long BFP) + s39_mde, // multiply (short to long HFP) + s39_mdeb, // multiply (short to long BFP) + s39_mdebr, // multiply (short to long BFP) + s39_mder, // multiply (short to long HFP) + s39_mdr, // multiply (long HFP) + s39_mdtr, // multiply (long DFP) + s39_mdtra, // multiply (long DFP) + s39_me, // multiply (short to long HFP) + s39_mee, // multiply (short HFP) + s39_meeb, // multiply (short BFP) + s39_meebr, // multiply (short BFP) + s39_meer, // multiply (short HFP) + s39_mer, // multiply (short to long HFP) + s39_mfy, // multiply (64 <- 32) + s39_mg, // multiply (128 <- 64) + s39_mgh, // multiply halfword (64 <- 16) + s39_mghi, // multiply halfword immediate (64 <- 16) + s39_mgrk, // multiply (128 <- 64) + s39_mh, // multiply halfword (32 <- 16) + s39_mhi, // multiply halfword immediate (32 <- 16) + s39_mhy, // multiply halfword (32 <- 16) + s39_ml, // multiply logical (64 <- 32) + s39_mlg, // multiply logical (128 <- 64) + s39_mlgr, // multiply logical (128 <- 64) + s39_mlr, // multiply logical (64 <- 32) + s39_mp, // multiply decimal + s39_mr, // multiply (64 <- 32) + s39_ms, // multiply single (32) + s39_msc, // multiply single (32) + s39_msch, // modify subchannel + s39_msd, // multiply and subtract (long HFP) + s39_msdb, // multiply and subtract (long BFP) + s39_msdbr, // multiply and subtract (long BFP) + s39_msdr, // multiply and subtract (long HFP) + s39_mse, // multiply and subtract (short HFP) + s39_mseb, // multiply and subtract (short BFP) + s39_msebr, // multiply and subtract (short BFP) + s39_mser, // multiply and subtract (short HFP) + s39_msfi, // multiply single immediate (32) + s39_msg, // multiply single (64) + s39_msgc, // multiply single (64) + s39_msgf, // multiply single (64 <- 32) + s39_msgfi, // multiply single immediate (64 <- 32) + s39_msgfr, // multiply single (64 <- 32) + s39_msgr, // multiply single (64) + s39_msgrkc, // multiply single (64) + s39_msr, // multiply single (32) + s39_msrkc, // multiply single (32) + s39_msta, // modify stacked state + s39_msy, // multiply single (32) + s39_mvc, // move (character) + s39_mvcdk, // move with destination key + s39_mvcin, // move inverse + s39_mvck, // move with key + s39_mvcl, // move long + s39_mvcle, // move long extended + s39_mvclu, // move long unicode + s39_mvcos, // move with optional specifications + s39_mvcp, // move to primary + s39_mvcrl, // + s39_mvcs, // move to secondary + s39_mvcsk, // move with source key + s39_mvghi, // move (64 <- 16) + s39_mvhhi, // move (16 <- 16) + s39_mvhi, // move (32 <- 16) + s39_mvi, // move (immediate) + s39_mviy, // move (immediate) + s39_mvn, // move numerics + s39_mvo, // move with offset + s39_mvpg, // move page + s39_mvst, // move string + s39_mvz, // move zones + s39_mxbr, // multiply (extended BFP) + s39_mxd, // multiply (long to extended HFP) + s39_mxdb, // multiply (long to extended BFP) + s39_mxdbr, // multiply (long to extended BFP) + s39_mxdr, // multiply (long to extended HFP) + s39_mxr, // multiply (extended HFP) + s39_mxtr, // multiply (extended DFP) + s39_mxtra, // multiply (extended DFP) + s39_my, // multiply unnormalized (long to extended HFP) + s39_myh, // multiply unnormalized (long to extended high HFP) + s39_myhr, // multiply unnormalized (long to extended high HFP) + s39_myl, // multiply unnormalized (long to extended low HFP) + s39_mylr, // multiply unnormalized (long to extended low HFP) + s39_myr, // multiply unnormalized (long to extended HFP) + s39_n, // and (32) + s39_nc, // and (character) + s39_ncgrk, // + s39_ncrk, // + s39_ng, // and (64) + s39_ngr, // and (64) + s39_ngrk, // and (64) + s39_ni, // and (immediate) + s39_niai, // next instruction access intent + s39_nihf, // and immediate (high) + s39_nihh, // and immediate (high high) + s39_nihl, // and immediate (high low) + s39_nilf, // and immediate (low) + s39_nilh, // and immediate (low high) + s39_nill, // and immediate (low low) + s39_niy, // and (immediate) + s39_nngrk, // + s39_nnrk, // + s39_nogrk, // + s39_nop, // no operation + s39_nopr, // no operation + s39_nork, // + s39_nr, // and (32) + s39_nrk, // and (32) + s39_ntstg, // nontransactional store (64) + s39_nxgrk, // + s39_nxrk, // + s39_ny, // and (32) + s39_o, // or (32) + s39_oc, // or (character) + s39_ocgrk, // + s39_ocrk, // + s39_og, // or (64) + s39_ogr, // or (64) + s39_ogrk, // or (64) + s39_oi, // or (immediate) + s39_oihf, // or immediate (high) + s39_oihh, // or immediate (high high) + s39_oihl, // or immediate (high low) + s39_oilf, // or immediate (low) + s39_oilh, // or immediate (low high) + s39_oill, // or immediate (low low) + s39_oiy, // or (immediate) + s39_or, // or (32) + s39_ork, // or (32) + s39_oy, // or (32) + s39_pack, // pack + s39_palb, // purge ALB + s39_pc, // program call + s39_pcc, // perform cryptographic computation + s39_pckmo, // perform cryptographic key management operation + s39_pfd, // prefetch data + s39_pfdrl, // prefetch data relative long + s39_pfmf, // perform frame management function + s39_pfpo, // perform floating point operation + s39_pgin, // page in + s39_pgout, // page out + s39_pka, // pack ascii + s39_pku, // pack unicode + s39_plo, // perform locked operation + s39_popcnt, // population count + s39_ppa, // perform processor assist + s39_ppno, // perform pseudorandom number operation + s39_pr, // program return + s39_prno, // perform pseudorandom number operation + s39_pt, // program transfer + s39_ptf, // perform topology function + s39_ptff, // perform timing facility function + s39_pti, // program transfer with instance + s39_ptlb, // purge TLB + s39_qadtr, // quantize (long DFP) + s39_qaxtr, // quantize (extended DFP) + s39_qctri, // query counter information + s39_qsi, // query sampling information + s39_rchp, // reset channel path + s39_risbg, // rotate then insert selected bits (64) + s39_risbgn, // rotate then insert selected bits (64) + s39_risbhg, // rotate then insert selected bits high (64) + s39_risblg, // rotate then insert selected bits low (64) + s39_rll, // rotate left single logical (32) + s39_rllg, // rotate left single logical (64) + s39_rnsbg, // rotate then and selected bits (64) + s39_rosbg, // rotate then or selected bits (64) + s39_rp, // resume program + s39_rrbe, // reset reference bit extended + s39_rrbm, // reset reference bits multiple + s39_rrdtr, // reround (long DFP) + s39_rrxtr, // reround (extended DFP) + s39_rsch, // resume subchannel + s39_rxsbg, // rotate then exclusive or selected bits (64) + s39_s, // subtract (32) + s39_sac, // set address space control + s39_sacf, // set address space control fast + s39_sal, // set address limit + s39_sam24, // set addressing mode 24 + s39_sam31, // set addressing mode 31 + s39_sam64, // set addressing mode 64 + s39_sar, // set access + s39_scctr, // set cpu counter + s39_schm, // set channel monitor + s39_sck, // set clock + s39_sckc, // set clock comparator + s39_sckpf, // set clock programmable field + s39_sd, // subtract normalized (long HFP) + s39_sdb, // subtract (long BFP) + s39_sdbr, // subtract (long BFP) + s39_sdr, // subtract normalized (long HFP) + s39_sdtr, // subtract normalized (long DFP) + s39_sdtra, // subtract normalized (long DFP) + s39_se, // subtract normalized (short HFP) + s39_seb, // subtract (short BFP) + s39_sebr, // subtract (short BFP) + s39_selgr, // + s39_selgro, // , jump on overflow / if ones + s39_selgrh, // , jump on A high + s39_selgrnle, // , jump on not low or equal + s39_selgrl, // , jump on A low + s39_selgrnhe, // , jump on not high or equal + s39_selgrlh, // , jump on low or high + s39_selgrne, // , jump on A not equal B + s39_selgre, // , jump on A equal B + s39_selgrnlh, // , jump on not low or high + s39_selgrhe, // , jump on high or equal + s39_selgrnl, // , jump on A not low + s39_selgrle, // , jump on low or equal + s39_selgrnh, // , jump on A not high + s39_selgrno, // , jump on not overflow / if not ones + s39_selhhhr, // + s39_selhhhro, // , jump on overflow / if ones + s39_selhhhrh, // , jump on A high + s39_selhhhrnle, // , jump on not low or equal + s39_selhhhrl, // , jump on A low + s39_selhhhrnhe, // , jump on not high or equal + s39_selhhhrlh, // , jump on low or high + s39_selhhhrne, // , jump on A not equal B + s39_selhhhre, // , jump on A equal B + s39_selhhhrnlh, // , jump on not low or high + s39_selhhhrhe, // , jump on high or equal + s39_selhhhrnl, // , jump on A not low + s39_selhhhrle, // , jump on low or equal + s39_selhhhrnh, // , jump on A not high + s39_selhhhrno, // , jump on not overflow / if not ones + s39_selr, // + s39_selro, // , jump on overflow / if ones + s39_selrh, // , jump on A high + s39_selrnle, // , jump on not low or equal + s39_selrl, // , jump on A low + s39_selrnhe, // , jump on not high or equal + s39_selrlh, // , jump on low or high + s39_selrne, // , jump on A not equal B + s39_selre, // , jump on A equal B + s39_selrnlh, // , jump on not low or high + s39_selrhe, // , jump on high or equal + s39_selrnl, // , jump on A not low + s39_selrle, // , jump on low or equal + s39_selrnh, // , jump on A not high + s39_selrno, // , jump on not overflow / if not ones + s39_ser, // subtract normalized (short HFP) + s39_sfasr, // set fpc and signal + s39_sfpc, // set fpc + s39_sg, // subtract (64) + s39_sgf, // subtract (64 <- 32) + s39_sgfr, // subtract (64 <- 32) + s39_sgh, // sintract halfword (64 <- 16) + s39_sgr, // subtract (64) + s39_sgrk, // subtract (64) + s39_sh, // subtract halfword (32 <- 16) + s39_shhhr, // subtract high (32) + s39_shhlr, // subtract high (32) + s39_shy, // subtract halfword (32 <- 16) + s39_sie, // start interpretive execution + s39_siga, // signal adapter + s39_sigp, // signal processor + s39_sl, // subtract logical (32) + s39_sla, // shift left single (32) + s39_slag, // shift left single (64) + s39_slak, // shift left single (32) + s39_slb, // subtract logical with borrow (32) + s39_slbg, // subtract logical with borrow (64) + s39_slbgr, // subtract logical with borrow (64) + s39_slbr, // subtract logical with borrow (32) + s39_slda, // shift left double (64) + s39_sldl, // shift left double logical (64) + s39_sldt, // shift significand left (long DFP) + s39_slfi, // subtract logical immediate (32) + s39_slg, // subtract logical (64) + s39_slgf, // subtract logical (64 <- 32) + s39_slgfi, // subtract logical (64 <- 32) + s39_slgfr, // subtract logical (64 <- 32) + s39_slgr, // subtract logical (64) + s39_slgrk, // subtract logical (64) + s39_slhhhr, // subtract logical high (32) + s39_slhhlr, // subtract logical high (32) + s39_sll, // shift left single logical (32) + s39_sllg, // shift left single logical (64) + s39_sllk, // shift left single logical (32) + s39_slr, // subtract logical (32) + s39_slrk, // subtract logical (32) + s39_slxt, // shift significand left (extended DFP) + s39_sly, // subtract logical (32) + s39_sortl, // + s39_sp, // subtract decimal + s39_spctr, // set peripheral counter + s39_spka, // set PSW key from address + s39_spm, // set program mask + s39_spt, // set CPU timer + s39_spx, // set prefix + s39_sqd, // square root (long HFP) + s39_sqdb, // square root (long BFP) + s39_sqdbr, // square root (long BFP) + s39_sqdr, // square root (long HFP) + s39_sqe, // square root (short HFP) + s39_sqeb, // square root (short BFP) + s39_sqebr, // square root (short BFP) + s39_sqer, // square root (short HFP) + s39_sqxbr, // square root (extended BFP) + s39_sqxr, // square root (extended HFP) + s39_sr, // subtract (32) + s39_sra, // shift right single (32) + s39_srag, // shift right single (64) + s39_srak, // shift right single (32) + s39_srda, // shift right double (64) + s39_srdl, // shift right double logical (64) + s39_srdt, // shift significand right (long DFP) + s39_srk, // subtract (32) + s39_srl, // shift right single logical (32) + s39_srlg, // shift right single logical (64) + s39_srlk, // shift right single logical (64) + s39_srnm, // set bfp rounding mode (2bit) + s39_srnmb, // set bfp rounding mode (3bit) + s39_srnmt, // set dfp rounding mode + s39_srp, // shift and round decimal + s39_srst, // search string + s39_srstu, // search string unicode + s39_srxt, // shift significand right (extended DFP) + s39_ssair, // set secondary ASN with instance + s39_ssar, // set secondary ASN + s39_ssch, // start subchannel + s39_sske, // set storage key extended + s39_ssm, // set system mask + s39_st, // store (32) + s39_stam, // store access multiple + s39_stamy, // store access multiple + s39_stap, // store CPU address + s39_stc, // store character + s39_stch, // store character high (8) + s39_stck, // store clock + s39_stckc, // store clock comparator + s39_stcke, // store clock extended + s39_stckf, // store clock fast + s39_stcm, // store characters under mask (low) + s39_stcmh, // store characters under mask (high) + s39_stcmy, // store characters under mask (low) + s39_stcps, // store channel path status + s39_stcrw, // store channel report word + s39_stctg, // store control (64) + s39_stctl, // store control (64) + s39_stcy, // store character + s39_std, // store (long) + s39_stdrv, // + s39_stdy, // store (long) + s39_ste, // store (short) + s39_sterv, // + s39_stey, // store (short) + s39_stfh, // store high (32) + s39_stfl, // store facility list + s39_stfle, // store facility list extended + s39_stfpc, // store fpc + s39_stg, // store (64) + s39_stgrl, // store relative long (64) + s39_stgsc, // store guarded storage controls + s39_sth, // store halfword (16) + s39_sthh, // store halfword high (16) + s39_sthrl, // store halfword relative long (16) + s39_sthy, // store halfword (16) + s39_stidp, // store CPU id + s39_stm, // store multiple (32) + s39_stmg, // store multiple (64) + s39_stmh, // store multiple high (32) + s39_stmy, // store multiple (32) + s39_stnsm, // store then and system mask + s39_stoc, // store on condition (32) + s39_stoco, // store on condition (32), jump on overflow / if ones + s39_stoch, // store on condition (32), jump on A high + s39_stocnle, // store on condition (32), jump on not low or equal + s39_stocl, // store on condition (32), jump on A low + s39_stocnhe, // store on condition (32), jump on not high or equal + s39_stoclh, // store on condition (32), jump on low or high + s39_stocne, // store on condition (32), jump on A not equal B + s39_stoce, // store on condition (32), jump on A equal B + s39_stocnlh, // store on condition (32), jump on not low or high + s39_stoche, // store on condition (32), jump on high or equal + s39_stocnl, // store on condition (32), jump on A not low + s39_stocle, // store on condition (32), jump on low or equal + s39_stocnh, // store on condition (32), jump on A not high + s39_stocno, // store on condition (32), jump on not overflow / if not ones + s39_stocfh, // store high on condition + s39_stocfho, // store high on condition, jump on overflow / if ones + s39_stocfhh, // store high on condition, jump on A high + s39_stocfhnle, // store high on condition, jump on not low or equal + s39_stocfhl, // store high on condition, jump on A low + s39_stocfhnhe, // store high on condition, jump on not high or equal + s39_stocfhlh, // store high on condition, jump on low or high + s39_stocfhne, // store high on condition, jump on A not equal B + s39_stocfhe, // store high on condition, jump on A equal B + s39_stocfhnlh, // store high on condition, jump on not low or high + s39_stocfhhe, // store high on condition, jump on high or equal + s39_stocfhnl, // store high on condition, jump on A not low + s39_stocfhle, // store high on condition, jump on low or equal + s39_stocfhnh, // store high on condition, jump on A not high + s39_stocfhno, // store high on condition, jump on not overflow / if not ones + s39_stocg, // store on condition (64) + s39_stocgo, // store on condition (64), jump on overflow / if ones + s39_stocgh, // store on condition (64), jump on A high + s39_stocgnle, // store on condition (64), jump on not low or equal + s39_stocgl, // store on condition (64), jump on A low + s39_stocgnhe, // store on condition (64), jump on not high or equal + s39_stocglh, // store on condition (64), jump on low or high + s39_stocgne, // store on condition (64), jump on A not equal B + s39_stocge, // store on condition (64), jump on A equal B + s39_stocgnlh, // store on condition (64), jump on not low or high + s39_stocghe, // store on condition (64), jump on high or equal + s39_stocgnl, // store on condition (64), jump on A not low + s39_stocgle, // store on condition (64), jump on low or equal + s39_stocgnh, // store on condition (64), jump on A not high + s39_stocgno, // store on condition (64), jump on not overflow / if not ones + s39_stosm, // store then or system mask + s39_stpq, // store pair to quadword + s39_stpt, // store CPU timer + s39_stpx, // store prefix + s39_strag, // store real address + s39_strl, // store relative long (32) + s39_strv, // store reversed (32) + s39_strvg, // store reversed (64) + s39_strvh, // store reversed (16) + s39_stsch, // store subchannel + s39_stsi, // store system information + s39_stura, // store using real address (32) + s39_sturg, // store using real address (64) + s39_sty, // store (32) + s39_su, // subtract unnormalized (short HFP) + s39_sur, // subtract unnormalized (short HFP) + s39_svc, // supervisor call + s39_sw, // subtract unnormalized (long HFP) + s39_swr, // subtract unnormalized (long HFP) + s39_sxbr, // subtract (extended BFP) + s39_sxr, // subtract normalized (extended HFP) + s39_sxtr, // subtract (extended DFP) + s39_sxtra, // subtract (extended DFP) + s39_sy, // subtract (32) + s39_tabort, // transaction abort + s39_tam, // test addressing mode + s39_tar, // test access + s39_tb, // test block + s39_tbdr, // convert HFP to BFP (long) + s39_tbedr, // convert HFP to BFP (long to short) + s39_tbegin, // transaction begin (nonconstrained) + s39_tbeginc, // transaction begin (constrained) + s39_tcdb, // test data class (long BFP) + s39_tceb, // test data class (short BFP) + s39_tcxb, // test data class (extended BFP) + s39_tdcdt, // test data class (long DFP) + s39_tdcet, // test data class (short DFP) + s39_tdcxt, // test data class (extended DFP) + s39_tdgdt, // test data group (long DFP) + s39_tdget, // test data group (short DFP) + s39_tdgxt, // test data group (extended DFP) + s39_tend, // transaction end + s39_thder, // convert BFP to HFP (short to long) + s39_thdr, // convert BFP to HFP (long) + s39_tm, // test under mask + s39_tmhh, // test under mask (high high) + s39_tmhl, // test under mask (high low) + s39_tmlh, // test under mask (low high) + s39_tmll, // test under mask (low low) + s39_tmy, // test under mask + s39_tp, // test decimal + s39_tpei, // test pending external interruption + s39_tpi, // test pending interruption + s39_tprot, // test protection + s39_tr, // translate + s39_trace, // trace (32) + s39_tracg, // trace (64) + s39_trap2, // trap + s39_trap4, // trap + s39_tre, // translate extended + s39_troo, // translate one to one + s39_trot, // translate one to two + s39_trt, // translate and test + s39_trte, // translate and test extended + s39_trto, // translate two to one + s39_trtr, // translate and test reverse + s39_trtre, // translate and test reverse extended + s39_trtt, // translate two to two + s39_ts, // test and set + s39_tsch, // test subchannel + s39_unpk, // unpack + s39_unpka, // unpack ascii + s39_unpku, // unpack unicode + s39_upt, // update tree + s39_va, // vector add + s39_vab, // vector add byte + s39_vac, // vector add with carry + s39_vacc, // vector add compute carry + s39_vaccb, // vector add compute carry byte + s39_vaccc, // vector add with carry compute carry + s39_vacccq, // vector add with carry compute carry quadword + s39_vaccf, // vector add compute carry word + s39_vaccg, // vector add compute carry doubleword + s39_vacch, // vector add compute carry halfword + s39_vaccq, // vector add compute carry quadword + s39_vacq, // vector add with carry quadword + s39_vaf, // vector add word + s39_vag, // vector add double word + s39_vah, // vector add halfword + s39_vap, // vector add decimal + s39_vaq, // vector add quad word + s39_vavg, // vector average + s39_vavgb, // vector average byte + s39_vavgf, // vector average word + s39_vavgg, // vector average double word + s39_vavgh, // vector average half word + s39_vavgl, // vector average logical + s39_vavglb, // vector average logical byte + s39_vavglf, // vector average logical word + s39_vavglg, // vector average logical double word + s39_vavglh, // vector average logical half word + s39_vbperm, // vector bit permute + s39_vcdg, // vector fp convert from fixed 64 bit + s39_vcdgb, // vector fp convert from fixed 64 bit + s39_vcdlg, // vector fp convert from logical 64 bit + s39_vcdlgb, // vector fp convert from logical 64 bit + s39_vcefb, // + s39_vcelfb, // + s39_vceq, // vector compare equal + s39_vceqb, // vector compare equal byte + s39_vceqbs, // vector compare equal byte + s39_vceqf, // vector compare equal word + s39_vceqfs, // vector compare equal word + s39_vceqg, // vector compare equal double word + s39_vceqgs, // vector compare equal double word + s39_vceqh, // vector compare equal half word + s39_vceqhs, // vector compare equal half word + s39_vcfeb, // + s39_vcfpl, // + s39_vcfps, // + s39_vcgd, // vector fp convert to fixed 64 bit + s39_vcgdb, // vector fp convert to fixed 64 bit + s39_vch, // vector compare high + s39_vchb, // vector compare high byte + s39_vchbs, // vector compare high byte + s39_vchf, // vector compare high word + s39_vchfs, // vector compare high word + s39_vchg, // vector compare high double word + s39_vchgs, // vector compare high double word + s39_vchh, // vector compare high half word + s39_vchhs, // vector compare high half word + s39_vchl, // vector compare high logical + s39_vchlb, // vector compare high logical byte + s39_vchlbs, // vector compare high logical byte + s39_vchlf, // vector compare high logical word + s39_vchlfs, // vector compare high logical word + s39_vchlg, // vector compare high logical double word + s39_vchlgs, // vector compare high logical double word + s39_vchlh, // vector compare high logical half word + s39_vchlhs, // vector compare high logical half word + s39_vcksm, // vector checksum + s39_vclfeb, // + s39_vclfp, // + s39_vclgd, // vector fp convert to logical 64 bit + s39_vclgdb, // vector fp convert to logical 64 bit + s39_vclz, // vector count leading zeros + s39_vclzb, // vector count leading zeros byte + s39_vclzf, // vector count leading zeros word + s39_vclzg, // vector count leading zeros doubleword + s39_vclzh, // vector count leading zeros halfword + s39_vcp, // vector compare decimal + s39_vcsfp, // + s39_vctz, // vector count trailing zeros + s39_vctzb, // vector count trailing zeros byte + s39_vctzf, // vector count trailing zeros word + s39_vctzg, // vector count trailing zeros doubleword + s39_vctzh, // vector count trailing zeros halfword + s39_vcvb, // vector convert to binary + s39_vcvbg, // vector convert to binary + s39_vcvd, // vector convert to decimal + s39_vcvdg, // vector convert to decimal + s39_vdp, // vector divide decimal + s39_vec, // vector element compare + s39_vecb, // vector element compare byte + s39_vecf, // vector element compare word + s39_vecg, // vector element compare double word + s39_vech, // vector element compare half word + s39_vecl, // vector element compare logical + s39_veclb, // vector element compare logical byte + s39_veclf, // vector element compare logical word + s39_veclg, // vector element compare logical double word + s39_veclh, // vector element compare logical half word + s39_verim, // vector element rotate and insert under mask + s39_verimb, // vector element rotate and insert under mask byte + s39_verimf, // vector element rotate and insert under mask word + s39_verimg, // vector element rotate and insert under mask doubleword + s39_verimh, // vector element rotate and insert under mask halfword + s39_verll, // vector element rotate left logical + s39_verllb, // vector element rotate left logical byte + s39_verllf, // vector element rotate left logical word + s39_verllg, // vector element rotate left logical doubleword + s39_verllh, // vector element rotate left logical halfword + s39_verllv, // vector element rotate left logical + s39_verllvb, // vector element rotate left logical byte + s39_verllvf, // vector element rotate left logical word + s39_verllvg, // vector element rotate left logical doubleword + s39_verllvh, // vector element rotate left logical halfword + s39_vesl, // vector element shift left + s39_veslb, // vector element shift left byte + s39_veslf, // vector element shift left word + s39_veslg, // vector element shift left doubleword + s39_veslh, // vector element shift left halfword + s39_veslv, // vector element shift left + s39_veslvb, // vector element shift left byte + s39_veslvf, // vector element shift left word + s39_veslvg, // vector element shift left doubleword + s39_veslvh, // vector element shift left halfword + s39_vesra, // vector element shift right arithmetic + s39_vesrab, // vector element shift right arithmetic byte + s39_vesraf, // vector element shift right arithmetic word + s39_vesrag, // vector element shift right arithmetic doubleword + s39_vesrah, // vector element shift right arithmetic halfword + s39_vesrav, // vector element shift right arithmetic + s39_vesravb, // vector element shift right arithmetic byte + s39_vesravf, // vector element shift right arithmetic word + s39_vesravg, // vector element shift right arithmetic doubleword + s39_vesravh, // vector element shift right arithmetic halfword + s39_vesrl, // vector element shift right logical + s39_vesrlb, // vector element shift right logical byte + s39_vesrlf, // vector element shift right logical word + s39_vesrlg, // vector element shift right logical doubleword + s39_vesrlh, // vector element shift right logical halfword + s39_vesrlv, // vector element shift right logical + s39_vesrlvb, // vector element shift right logical byte + s39_vesrlvf, // vector element shift right logical word + s39_vesrlvg, // vector element shift right logical doubleword + s39_vesrlvh, // vector element shift right logical halfword + s39_vfa, // vector fp add + s39_vfadb, // vector fp add long + s39_vfae, // vector find any element equal + s39_vfaeb, // vector find any element equal byte + s39_vfaebs, // vector find any element equal + s39_vfaef, // vector find any element equal word + s39_vfaefs, // vector find any element equal + s39_vfaeh, // vector find any element equal halfword + s39_vfaehs, // vector find any element equal + s39_vfaezb, // vector find any element equal + s39_vfaezbs, // vector find any element equal + s39_vfaezf, // vector find any element equal + s39_vfaezfs, // vector find any element equal + s39_vfaezh, // vector find any element equal + s39_vfaezhs, // vector find any element equal + s39_vfasb, // vector fp add short + s39_vfce, // vector fp compare equal + s39_vfcedb, // vector fp compare equal long + s39_vfcedbs, // vector fp compare equal long + s39_vfcesb, // vector fp compare equal short + s39_vfcesbs, // vector fp compare equal short + s39_vfch, // vector fp compare high + s39_vfchdb, // vector fp compare high long + s39_vfchdbs, // vector fp compare high long + s39_vfche, // vector fp compare high or equal + s39_vfchedb, // vector fp compare high or equal long + s39_vfchedbs, // vector fp compare high or equal long + s39_vfchesb, // vector fp compare high or equal short + s39_vfchesbs, // vector fp compare high or equal short + s39_vfchsb, // vector fp compare high short + s39_vfchsbs, // vector fp compare high short + s39_vfd, // vector fp divide + s39_vfddb, // vector fp divide long + s39_vfdsb, // vector fp divide short + s39_vfee, // vector find element equal + s39_vfeeb, // vector find element equal byte + s39_vfeebs, // vector find element equal byte + s39_vfeef, // vector find element equal word + s39_vfeefs, // vector find element equal word + s39_vfeeh, // vector find element equal halfword + s39_vfeehs, // vector find element equal halfword + s39_vfeezb, // vector find element equal byte + s39_vfeezbs, // vector find element equal byte + s39_vfeezf, // vector find element equal word + s39_vfeezfs, // vector find element equal word + s39_vfeezh, // vector find element equal halfword + s39_vfeezhs, // vector find element equal halfword + s39_vfene, // vector find element not equal + s39_vfeneb, // vector find element not equal byte + s39_vfenebs, // vector find element not equal byte + s39_vfenef, // vector find element not equal word + s39_vfenefs, // vector find element not equal word + s39_vfeneh, // vector find element not equal halfword + s39_vfenehs, // vector find element not equal halfword + s39_vfenezb, // vector find element not equal byte + s39_vfenezbs, // vector find element not equal byte + s39_vfenezf, // vector find element not equal word + s39_vfenezfs, // vector find element not equal word + s39_vfenezh, // vector find element not equal halfword + s39_vfenezhs, // vector find element not equal halfword + s39_vfi, // vector load fp integer + s39_vfidb, // vector load fp integer long + s39_vfisb, // vector load fp integer short + s39_vfkedb, // vector fp compare and signal equal long + s39_vfkedbs, // vector fp compare and signal equal long + s39_vfkesb, // vector fp compare and signal equal short + s39_vfkesbs, // vector fp compare and signal equal short + s39_vfkhdb, // vector fp compare and signal high long + s39_vfkhdbs, // vector fp compare and signal high long + s39_vfkhedb, // vector fp compare and signal high or equal long + s39_vfkhedbs, // vector fp compare and signal high or equal long + s39_vfkhesb, // vector fp compare and signal high or equal short + s39_vfkhesbs, // vector fp compare and signal high or equal short + s39_vfkhsb, // vector fp compare and signal high short + s39_vfkhsbs, // vector fp compare and signal high short + s39_vflcdb, // vector fp perform sign operation long + s39_vflcsb, // vector fp perform sign operation short + s39_vfll, // vector fp load lengthened + s39_vflls, // vector fp load lengthened + s39_vflndb, // vector fp perform sign operation long + s39_vflnsb, // vector fp perform sign operation short + s39_vflpdb, // vector fp perform sign operation long + s39_vflpsb, // vector fp perform sign operation short + s39_vflr, // vector fp load rounded + s39_vflrd, // vector fp load rounded long + s39_vfm, // vector fp multiply + s39_vfma, // vector fp multiply and add + s39_vfmadb, // vector fp multiply and add long + s39_vfmasb, // vector fp multiply and add short + s39_vfmax, // vector fp maximum + s39_vfmaxdb, // vector fp maximum long + s39_vfmaxsb, // vector fp maximum short + s39_vfmdb, // vector fp multiply long + s39_vfmin, // vector fp minimum + s39_vfmindb, // vector fp minimum long + s39_vfminsb, // vector fp minimum short + s39_vfms, // vector fp multiply and subtract + s39_vfmsb, // vector fp multiply short + s39_vfmsdb, // vector fp multiply and subtract long + s39_vfmssb, // vector fp multiply and subtract short + s39_vfnma, // vector fp negative multiply and add + s39_vfnmadb, // vector fp negative multiply and add long + s39_vfnmasb, // vector fp negative multiply and add short + s39_vfnms, // vector fp negative multiply and subtract + s39_vfnmsdb, // vector fp negative multiply and subtract long + s39_vfnmssb, // vector fp negative multiply and subtract short + s39_vfpso, // vector fp perform sign operation + s39_vfpsodb, // vector fp perform sign operation long + s39_vfpsosb, // vector fp perform sign operation short + s39_vfs, // vector fp subtract + s39_vfsdb, // vector fp subtract long + s39_vfsq, // vector fp square root + s39_vfsqdb, // vector fp square root long + s39_vfsqsb, // vector fp square root short + s39_vfssb, // vector fp subtract short + s39_vftci, // vector fp test data class immediate + s39_vftcidb, // vector fp test data class immediate + s39_vftcisb, // vector fp test data class immediate short + s39_vgbm, // vector generate byte mask + s39_vgef, // vector gather element (32) + s39_vgeg, // vector gather element (64) + s39_vgfm, // vector galois field multiply sum + s39_vgfma, // vector galois field multiply sum and accumulate + s39_vgfmab, // vector galois field multiply sum and accumulate byte + s39_vgfmaf, // vector galois field multiply sum and accumulate word + s39_vgfmag, // vector galois field multiply sum and accumulate doubleword + s39_vgfmah, // vector galois field multiply sum and accumulate halfword + s39_vgfmb, // vector galois field multiply sum byte + s39_vgfmf, // vector galois field multiply sum word + s39_vgfmg, // vector galois field multiply sum doubleword + s39_vgfmh, // vector galois field multiply sum halfword + s39_vgm, // vector generate mask + s39_vgmb, // vector generate mask byte + s39_vgmf, // vector generate mask word + s39_vgmg, // vector generate mask double word + s39_vgmh, // vector generate mask halfword + s39_vistr, // vector isolate string + s39_vistrb, // vector isolate string byte + s39_vistrbs, // vector isolate string byte + s39_vistrf, // vector isolate string word + s39_vistrfs, // vector isolate string word + s39_vistrh, // vector isolate string halfword + s39_vistrhs, // vector isolate string halfword + s39_vl, // vector load + s39_vlbb, // vector load to block boundary + s39_vlbr, // + s39_vlbrf, // + s39_vlbrg, // + s39_vlbrh, // + s39_vlbrq, // + s39_vlbrrep, // + s39_vlbrrepf, // + s39_vlbrrepg, // + s39_vlbrreph, // + s39_vlc, // vector load complement + s39_vlcb, // vector load complement byte + s39_vlcf, // vector load complement word + s39_vlcg, // vector load complement doubleword + s39_vlch, // vector load complement halfword + s39_vlde, // vector fp load lengthened + s39_vldeb, // vector fp load lengthened short to long + s39_vleb, // vector load byte element + s39_vlebrf, // + s39_vlebrg, // + s39_vlebrh, // + s39_vled, // vector fp load rounded + s39_vledb, // vector fp load rounded long to short + s39_vlef, // vector load element (32) + s39_vleg, // vector load element (64) + s39_vleh, // vector load element (16) + s39_vleib, // vector load element immediate (8) + s39_vleif, // vector load element immediate (32) + s39_vleig, // vector load element immediate (64) + s39_vleih, // vector load element immediate (16) + s39_vler, // + s39_vlerf, // + s39_vlerg, // + s39_vlerh, // + s39_vlgv, // vector load gr from vr element + s39_vlgvb, // vector load gr from vr byte element + s39_vlgvf, // vector load gr from vr word element + s39_vlgvg, // vector load gr from vr double word element + s39_vlgvh, // vector load gr from vr halfword element + s39_vlip, // vector load immediate decimal + s39_vll, // vector load with length + s39_vllebrz, // + s39_vllebrze, // + s39_vllebrzf, // + s39_vllebrzg, // + s39_vllebrzh, // + s39_vllez, // vector load logical element and zero + s39_vllezb, // vector load logical byte element and zero + s39_vllezf, // vector load logical word element and zero + s39_vllezg, // vector load logical double word element and zero + s39_vllezh, // vector load logical halfword element and zero + s39_vllezlf, // vector load logical word element and zero - left aligned + s39_vlm, // vector load multiple + s39_vlp, // vector load positive + s39_vlpb, // vector load positive byte + s39_vlpf, // vector load positive word + s39_vlpg, // vector load positive doubleword + s39_vlph, // vector load positive halfword + s39_vlr, // vector load + s39_vlrep, // vector load and replicate + s39_vlrepb, // vector load and replicate byte elements + s39_vlrepf, // vector load and replicate word elements + s39_vlrepg, // vector load and replicate double word elements + s39_vlreph, // vector load and replicate halfword elements + s39_vlrl, // vector load rightmost with immediate length + s39_vlrlr, // vector load rightmost with immediate length + s39_vlvg, // vector load VR element from GR + s39_vlvgb, // vector load VR byte element from GR + s39_vlvgf, // vector load VR word element from GR + s39_vlvgg, // vector load VR double word element from GR + s39_vlvgh, // vector load VR halfword element from GR + s39_vlvgp, // vector load VR from GRs disjoint + s39_vmae, // vector multiply and add even + s39_vmaeb, // vector multiply and add even byte + s39_vmaef, // vector multiply and add even word + s39_vmaeh, // vector multiply and add even halfword + s39_vmah, // vector multiply and add high + s39_vmahb, // vector multiply and add high byte + s39_vmahf, // vector multiply and add high word + s39_vmahh, // vector multiply and add high halfword + s39_vmal, // vector multiply and add low + s39_vmalb, // vector multiply and add low byte + s39_vmale, // vector multiply and add logical even + s39_vmaleb, // vector multiply and add logical even byte + s39_vmalef, // vector multiply and add logical even word + s39_vmaleh, // vector multiply and add logical even halfword + s39_vmalf, // vector multiply and add low word + s39_vmalh, // vector multiply and add logical high + s39_vmalhb, // vector multiply and add logical high byte + s39_vmalhf, // vector multiply and add logical high word + s39_vmalhh, // vector multiply and add logical high halfword + s39_vmalhw, // vector multiply and add low halfword + s39_vmalo, // vector multiply and add logical odd + s39_vmalob, // vector multiply and add logical odd byte + s39_vmalof, // vector multiply and add logical odd word + s39_vmaloh, // vector multiply and add logical odd halfword + s39_vmao, // vector multiply and add odd + s39_vmaob, // vector multiply and add odd byte + s39_vmaof, // vector multiply and add odd word + s39_vmaoh, // vector multiply and add odd halfword + s39_vme, // vector multiply even + s39_vmeb, // vector multiply even byte + s39_vmef, // vector multiply even word + s39_vmeh, // vector multiply even halfword + s39_vmh, // vector multiply high + s39_vmhb, // vector multiply high byte + s39_vmhf, // vector multiply high word + s39_vmhh, // vector multiply high halfword + s39_vml, // vector multiply low + s39_vmlb, // vector multiply low byte + s39_vmle, // vector multiply logical even + s39_vmleb, // vector multiply logical even byte + s39_vmlef, // vector multiply logical even word + s39_vmleh, // vector multiply logical even halfword + s39_vmlf, // vector multiply low word + s39_vmlh, // vector multiply logical high + s39_vmlhb, // vector multiply logical high byte + s39_vmlhf, // vector multiply logical high word + s39_vmlhh, // vector multiply logical high halfword + s39_vmlhw, // vector multiply low halfword + s39_vmlo, // vector multiply logical odd + s39_vmlob, // vector multiply logical odd byte + s39_vmlof, // vector multiply logical odd word + s39_vmloh, // vector multiply logical odd halfword + s39_vmn, // vector minimum + s39_vmnb, // vector minimum byte + s39_vmnf, // vector minimum word + s39_vmng, // vector minimum doubleword + s39_vmnh, // vector minimum halfword + s39_vmnl, // vector minimum logical + s39_vmnlb, // vector minimum logical byte + s39_vmnlf, // vector minimum logical word + s39_vmnlg, // vector minimum logical doubleword + s39_vmnlh, // vector minimum logical halfword + s39_vmo, // vector multiply odd + s39_vmob, // vector multiply odd byte + s39_vmof, // vector multiply odd word + s39_vmoh, // vector multiply odd halfword + s39_vmp, // vector multiply decimal + s39_vmrh, // vector merge high + s39_vmrhb, // vector merge high byte + s39_vmrhf, // vector merge high word + s39_vmrhg, // vector merge high double word + s39_vmrhh, // vector merge high halfword + s39_vmrl, // vector merge low + s39_vmrlb, // vector merge low byte + s39_vmrlf, // vector merge low word + s39_vmrlg, // vector merge low double word + s39_vmrlh, // vector merge low halfword + s39_vmsl, // vector multiply sum logical + s39_vmslg, // vector multiply sum logical double word + s39_vmsp, // vector multiply and shift decimal + s39_vmx, // vector maximum + s39_vmxb, // vector maximum byte + s39_vmxf, // vector maximum word + s39_vmxg, // vector maximum doubleword + s39_vmxh, // vector maximum halfword + s39_vmxl, // vector maximum logical + s39_vmxlb, // vector maximum logical byte + s39_vmxlf, // vector maximum logical word + s39_vmxlg, // vector maximum logical doubleword + s39_vmxlh, // vector maximum logical halfword + s39_vn, // vector and + s39_vnc, // vector and with complement + s39_vnn, // vector nand + s39_vno, // vector nor + s39_vnot, // vector not + s39_vnx, // vector not exclusive or + s39_vo, // vector or + s39_voc, // vector or with complement + s39_vone, // vector set to ones + s39_vpdi, // vector permute double word immediate + s39_vperm, // vector permute + s39_vpk, // vector pack + s39_vpkf, // vector pack word + s39_vpkg, // vector pack double word + s39_vpkh, // vector pack halfword + s39_vpkls, // vector pack logical saturate + s39_vpklsf, // vector pack logical saturate word + s39_vpklsfs, // vector pack logical saturate word + s39_vpklsg, // vector pack logical saturate double word + s39_vpklsgs, // vector pack logical saturate double word + s39_vpklsh, // vector pack logical saturate halfword + s39_vpklshs, // vector pack logical saturate halfword + s39_vpks, // vector pack saturate + s39_vpksf, // vector pack saturate word + s39_vpksfs, // vector pack saturate word + s39_vpksg, // vector pack saturate double word + s39_vpksgs, // vector pack saturate double word + s39_vpksh, // vector pack saturate halfword + s39_vpkshs, // vector pack saturate halfword + s39_vpkz, // vector pack zoned + s39_vpopct, // vector population count + s39_vpopctb, // vector population count byte + s39_vpopctf, // vector population count word + s39_vpopctg, // vector population count double word + s39_vpopcth, // vector population count halfword + s39_vpsop, // vector perform sign operation decimal + s39_vrep, // vector replicate + s39_vrepb, // vector replicate byte + s39_vrepf, // vector replicate word + s39_vrepg, // vector replicate double word + s39_vreph, // vector replicate halfword + s39_vrepi, // vector replicate immediate + s39_vrepib, // vector replicate immediate byte + s39_vrepif, // vector replicate immediate word + s39_vrepig, // vector replicate immediate double word + s39_vrepih, // vector replicate immediate halfword + s39_vrp, // vector remainder decimal + s39_vs, // vector subtract + s39_vsb, // vector subtract byte + s39_vsbcbi, // vector subtract with borrow compute borrow indication + s39_vsbcbiq, // vector subtract with borrow compute borrow indication quadword + s39_vsbi, // vector subtract with borrow indication + s39_vsbiq, // vector subtract with borrow indication quadword + s39_vscbi, // vector subtract compute borrow indication + s39_vscbib, // vector subtract compute borrow indication byte + s39_vscbif, // vector subtract compute borrow indication word + s39_vscbig, // vector subtract compute borrow indication doubleword + s39_vscbih, // vector subtract compute borrow indication halfword + s39_vscbiq, // vector subtract compute borrow indication quadword + s39_vscef, // vector scatter element (32) + s39_vsceg, // vector scatter element (64) + s39_vsdp, // vector shift and divide decimal + s39_vseg, // vector sign extend to double word + s39_vsegb, // vector sign extend byte to double word + s39_vsegf, // vector sign extend word to double word + s39_vsegh, // vector sign extend halfword to double word + s39_vsel, // vector select + s39_vsf, // vector subtract word + s39_vsg, // vector subtract doubleword + s39_vsh, // vector subtract halfword + s39_vsl, // vector shift left + s39_vslb, // vector shift left by byte + s39_vsld, // + s39_vsldb, // vector shift left double by byte + s39_vsp, // vector subtract decimal + s39_vsq, // vector subtract quadword + s39_vsra, // vector shift right arithmetic + s39_vsrab, // vector shift right arithmetic by byte + s39_vsrd, // + s39_vsrl, // vector shift right logical + s39_vsrlb, // vector shift right logical by byte + s39_vsrp, // vector shift and round decimal + s39_vst, // vector store + s39_vstbr, // + s39_vstbrf, // + s39_vstbrg, // + s39_vstbrh, // + s39_vstbrq, // + s39_vsteb, // vector store element (8) + s39_vstebrf, // + s39_vstebrg, // + s39_vstebrh, // + s39_vstef, // vector store element (32) + s39_vsteg, // vector store element (64) + s39_vsteh, // vector store element (16) + s39_vster, // + s39_vsterf, // + s39_vsterg, // + s39_vsterh, // + s39_vstl, // vector store with length + s39_vstm, // vector store multiple + s39_vstrc, // vector string range compare + s39_vstrcb, // vector string range compare byte + s39_vstrcbs, // vector string range compare byte + s39_vstrcf, // vector string range compare word + s39_vstrcfs, // vector string range compare word + s39_vstrch, // vector string range compare halfword + s39_vstrchs, // vector string range compare halfword + s39_vstrczb, // vector string range compare byte + s39_vstrczbs, // vector string range compare byte + s39_vstrczf, // vector string range compare word + s39_vstrczfs, // vector string range compare word + s39_vstrczh, // vector string range compare halfword + s39_vstrczhs, // vector string range compare halfword + s39_vstrl, // vector store rightmost with length + s39_vstrlr, // vector store rightmost with length + s39_vstrs, // + s39_vstrsb, // + s39_vstrsf, // + s39_vstrsh, // + s39_vstrszb, // + s39_vstrszf, // + s39_vstrszh, // + s39_vsum, // vector sum across word + s39_vsumb, // vector sum across word - byte elements + s39_vsumg, // vector sum across doubleword + s39_vsumgf, // vector sum across doubleword - word + s39_vsumgh, // vector sum across doubleword - halfword + s39_vsumh, // vector sum across word - halfword elements + s39_vsumq, // vector sum across quadword + s39_vsumqf, // vector sum across quadword - word elements + s39_vsumqg, // vector sum across quadword - doubleword elements + s39_vtm, // vector test under mask + s39_vtp, // vector test decimal + s39_vuph, // vector unpack high + s39_vuphb, // vector unpack high byte + s39_vuphf, // vector unpack high word + s39_vuphh, // vector unpack high halfword + s39_vupkz, // vector unpack zoned + s39_vupl, // vector unpack low + s39_vuplb, // vector unpack low byte + s39_vuplf, // vector unpack low word + s39_vuplh, // vector unpack logical high + s39_vuplhb, // vector unpack logical high byte + s39_vuplhf, // vector unpack logical high word + s39_vuplhh, // vector unpack logical high halfword + s39_vuplhw, // vector unpack low halfword + s39_vupll, // vector unpack logical low + s39_vupllb, // vector unpack logical low byte + s39_vupllf, // vector unpack logical low word + s39_vupllh, // vector unpack logical low halfword + s39_vzero, // vector set to zero + s39_wcdgb, // vector fp convert from fixed 64 bit + s39_wcdlgb, // vector fp convert from logical 64 bit + s39_wcefb, // + s39_wcelfb, // + s39_wcfeb, // + s39_wcgdb, // vector fp convert to fixed 64 bit + s39_wclfeb, // + s39_wclgdb, // vector fp convert to logical 64 bit + s39_wfadb, // vector fp add long + s39_wfasb, // scalar vector fp add scalar short + s39_wfaxb, // scalar vector fp add scalar extended + s39_wfc, // vector fp compare scalar + s39_wfcdb, // vector fp compare scalar long + s39_wfcedb, // vector fp compare equal long + s39_wfcedbs, // vector fp compare equal long + s39_wfcesb, // scalar vector fp compare equal scalar short + s39_wfcesbs, // scalar fp compare equal scalar short + s39_wfcexb, // scalar vector fp compare equal scalar extended + s39_wfcexbs, // scalar vector fp compare equal scalar extended + s39_wfchdb, // vector fp compare high long + s39_wfchdbs, // vector fp compare high long + s39_wfchedb, // vector fp compare high or equal long + s39_wfchedbs, // vector fp compare high or equal long + s39_wfchesb, // scalar vector fp compare high or equal scalar short + s39_wfchesbs, // scalar vector fp compare high or equal scalar short + s39_wfchexb, // scalar vector fp compare high or equal scalar extended + s39_wfchexbs, // scalar vector fp compare high or equal scalar extended + s39_wfchsb, // scalar vector fp compare high scalar short + s39_wfchsbs, // scalar vector fp compare high scalar short + s39_wfchxb, // scalar vector fp compare high scalar extended + s39_wfchxbs, // scalar vector fp compare high scalar extended + s39_wfcsb, // scalar vector fp compare scalar short + s39_wfcxb, // scalar vector fp compare scalar extended + s39_wfddb, // vector fp divide long + s39_wfdsb, // scalar vector fp divide scalar short + s39_wfdxb, // scalar vector fp divide scalar extended + s39_wfidb, // vector load fp integer long + s39_wfisb, // scalar vector load fp integer scalar short + s39_wfixb, // scalar vector load fp integer scalar extended + s39_wfk, // vector fp compare and signal scalar + s39_wfkdb, // vector fp compare and signal scalar long + s39_wfkedb, // vector fp compare and signal equal long + s39_wfkedbs, // vector fp compare and signal equal long + s39_wfkesb, // scalar vector fp compare and signal equal scalar short + s39_wfkesbs, // scalar fp compare and signal equal scalar short + s39_wfkexb, // scalar vector fp compare and signal equal scalar extended + s39_wfkexbs, // scalar vector fp compare and signal equal scalar extended + s39_wfkhdb, // vector fp compare and signal high long + s39_wfkhdbs, // vector fp compare and signal high long + s39_wfkhedb, // vector fp compare and signal high or equal long + s39_wfkhedbs, // vector fp compare and signal high or equal long + s39_wfkhesb, // scalar vector fp compare and signal high or equal scalar short + s39_wfkhesbs, // scalar vector fp compare and signal high or equal scalar short + s39_wfkhexb, // scalar vector fp compare and signal high or equal scalar extended + s39_wfkhexbs, // scalar vector fp compare and signal high or equal scalar extended + s39_wfkhsb, // scalar vector fp compare and signal high scalar short + s39_wfkhsbs, // scalar vector fp compare and signal high scalar short + s39_wfkhxb, // scalar vector fp compare and signal high scalar extended + s39_wfkhxbs, // scalar vector fp compare and signal high scalar extended + s39_wfksb, // scalar vector fp compare and signal scalar short + s39_wfkxb, // scalar vector fp compare and signal scalar extended + s39_wflcdb, // vector fp perform sign operation long + s39_wflcsb, // scalar vector fp perform sign operation scalar short + s39_wflcxb, // scalar vector fp perform sign operation scalar extended + s39_wflld, // scalar vector fp load lengthened long + s39_wflls, // scalar vector fp load lengthened short + s39_wflndb, // vector fp perform sign operation long + s39_wflnsb, // scalar vector fp perform sign operation scalar short + s39_wflnxb, // scalar vector fp perform sign operation scalar extended + s39_wflpdb, // vector fp perform sign operation long + s39_wflpsb, // scalar vector fp perform sign operation scalar short + s39_wflpxb, // scalar vector fp perform sign operation scalar extended + s39_wflrd, // scalar vector fp load rounded long + s39_wflrx, // scalar vector fp load rounded extended + s39_wfmadb, // vector fp multiply and add long + s39_wfmasb, // scalar vector fp multiply and add scalar short + s39_wfmaxb, // scalar vector fp multiply and add scalar extended + s39_wfmaxdb, // scalar fp maximum scalar long + s39_wfmaxsb, // scalar fp maximum scalar short + s39_wfmaxxb, // scalar fp maximum scalar extended + s39_wfmdb, // vector fp multiply long + s39_wfmindb, // scalar fp minimum scalar long + s39_wfminsb, // scalar fp minimum scalar short + s39_wfminxb, // scalar fp minimum scalar extended + s39_wfmsb, // scalar vector fp multiply scalar short + s39_wfmsdb, // vector fp multiply and subtract long + s39_wfmssb, // scalar vector fp multiply and subtract scalar short + s39_wfmsxb, // scalar vector fp multiply and subtract scalar extended + s39_wfmxb, // scalar vector fp multiply scalar extended + s39_wfnmadb, // scalar vector fp negative multiply and add scalar long + s39_wfnmasb, // scalar vector fp negative multiply and add scalar short + s39_wfnmaxb, // scalar vector fp negative multiply and add scalar extended + s39_wfnmsdb, // scalar vector fp negative multiply and subtract scalar long + s39_wfnmssb, // scalar vector fp negative multiply and subtract scalar short + s39_wfnmsxb, // scalar vector fp negative multiply and subtract scalar extended + s39_wfpsodb, // vector fp perform sign operation long + s39_wfpsosb, // scalar vector fp perform sign operation scalar short + s39_wfpsoxb, // scalar vector fp perform sign operation scalar extended + s39_wfsdb, // vector fp subtract long + s39_wfsqdb, // vector fp square root long + s39_wfsqsb, // scalar vector fp square root scalar short + s39_wfsqxb, // scalar vector fp square root scalar extended + s39_wfssb, // scalar vector fp subtract scalar short + s39_wfsxb, // scalar vector fp subtract scalar extended + s39_wftcidb, // vector fp test data class immediate + s39_wftcisb, // scalar vector fp test data class immediate scalar short + s39_wftcixb, // scalar vector fp test data class immediate scalar extended + s39_wldeb, // vector fp load lengthened short to long + s39_wledb, // vector fp load rounded long to short + s39_x, // exclusive or (32) + s39_xc, // exclusive or (character) + s39_xg, // exclusive or (64) + s39_xgr, // exclusive or (64) + s39_xgrk, // exclusive or (64) + s39_xi, // exclusive or (immediate) + s39_xihf, // exclusive or immediate (high) + s39_xilf, // exclusive or immediate (low) + s39_xiy, // exclusive or immediate (immediate) + s39_xr, // exclusive or (32) + s39_xrk, // exclusive or (32) + s39_xsch, // cancel subchannel + s39_xy, // exclusive or with long offset + s39_zap, // zero and add + s39_vx, // vector exclusive or + s39_last, +}; + +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + * Risc-V module + * + */ + + + +enum +{ + RISCV_null = 0, // Unknown Operation + + // Set 32 bits + // I(nteger) extension + RISCV_lui, // Load Upper Immediate + RISCV_auipc, // Add Upper Immediate to pc + RISCV_jal, // Jump and link + RISCV_jalr, // Jump register rs1 & write pc +2 in rs1 + RISCV_beq, // Branch on equal + RISCV_bne, // Branch not equal + RISCV_blt, // Branch less than + RISCV_bge, // Branch greater or equal + RISCV_bltu, // Branch less than unsigned + RISCV_bgeu, // Branch greater or equal unsigned + RISCV_lb, // Load byte + RISCV_lh, // Load halfword + RISCV_lw, // Load word + RISCV_lbu, // Load byte unsigned + RISCV_lhu, // Load halfword unsigned + RISCV_sb, // Store byte + RISCV_sh, // Store halfword + RISCV_sw, // Store word + RISCV_addi, // Add immediate + RISCV_slti, // Set less than immediate + RISCV_sltiu, // Set less than immediate unsigned + RISCV_xori, // Xor immediate + RISCV_ori, // Or immediate + RISCV_andi, // And Immediate + RISCV_slli, // Shift left logical immediate + RISCV_srli, // Shift right logical immediate + RISCV_srai, // Shift right arithmetical immediate + RISCV_add, // Add integer + RISCV_sub, // Substract integer + RISCV_sll, // Shift left logical + RISCV_slt, // Set Less Than + RISCV_sltu, // Set Less Than unsigned + RISCV_xor, // eXclusive Or + RISCV_srl, // Shift right logical + RISCV_sra, // Shift right arithmetical + RISCV_or, // Or + RISCV_and, // And + RISCV_fence, // Fence + RISCV_ecall, // Ecall + RISCV_ebreak, // Ebreak + RISCV_uret, // User lvl return + RISCV_sret, // Supervisor lvl return + RISCV_mret, // Machine lvl return + RISCV_wfi, // Wait for interrupt + RISCV_sfence, // Supervisor fence + RISCV_hfenceb, // Hypervisor fence b + RISCV_hfenceg, // Hypervisor fence g + // Set 64 bits + // Integer extension + RISCV_lwu, // Load word unsigned + RISCV_ld, // Load double + RISCV_sd, // Store double + RISCV_addiw, // Add immediate word + RISCV_slliw, // Shift left logical immediate word + RISCV_srliw, // Shift right logical immediate word + RISCV_sraiw, // Shift right arithmetical immediate word + RISCV_addw, // Add integer word + RISCV_subw, // Substract integer word + RISCV_sllw, // Shift left logical word + RISCV_sltw, // Set Less Than word + RISCV_srlw, // Shift right logical word + RISCV_sraw, // Shift right arithmetical word + // Zifencei Standard extension + RISCV_fencei, // Fence immediate + // Zicsr Standard extension + RISCV_csrrw, // Atomic Read Write CSR + RISCV_csrrs, // Atomic Read and Set bit CSR + RISCV_csrrc, // Atomic Read and clear bit CSR + RISCV_csrrwi, // Atomic Read Write CSR immediate + RISCV_csrrsi, // Atomic Read and Set bit CSR immediate + RISCV_csrrci, // Atomic Read and clear bit CSR immediate + // M(ultiplication and divison for integer) extension + RISCV_mul, // Multiplication + RISCV_mulh, // Multiplication half (signed signed) + RISCV_mulhsu, // Multiplication half (signed unsigned) + RISCV_mulhu, // Multiplication half (unsigned unsigned) + RISCV_div, // Division + RISCV_divu, // Division unsigned + RISCV_rem, // Remainder of div + RISCV_remu, // Remainder of div unsigned + // 64b M(ultiplication and divison for integer) extension + RISCV_mulw, // Multiplication word + RISCV_divw, // Division word + RISCV_divuw, // Division unsigned word + RISCV_remw, // Remainder of div word + RISCV_remuw, // Remainder of div unsigned word + // A(tomic) operation standtard extension + RISCV_lr, // Load reserved + RISCV_sc, // Store conditional + RISCV_amoswap, // Atomic memory operation swap + RISCV_amoadd, // Atomic memory operation add + RISCV_amoxor, // Atomic memory operation xop + RISCV_amoand, // Atomic memory operation and + RISCV_amoor, // Atomic memory operation or + RISCV_amomin, // Atomic memory operation min + RISCV_amomax, // Atomic memory operation max + RISCV_amominu, // Atomic memory operation min unsigned + RISCV_amomaxu, // Atomic memory operation max unsigned + // Zacas extension + RISCV_amocas, // Atomic compare and swap + // simple precision F(loat) instruction extension + RISCV_flw, // Load word (32b) + RISCV_fsw, // Store word (32b) + RISCV_fmadd, // Multiply then add + RISCV_fmsub, // Multiply then substract + RISCV_fnmsub, // Multiply then negate then substract + RISCV_fnmadd, // Multiply then negate then add + RISCV_fadd, // Float addition + RISCV_fsub, // Float substraction + RISCV_fmul, // Float multiplication + RISCV_fdiv, // Float division + RISCV_fsqrt, // Float square root + RISCV_fsgnj, // Float sign injection + RISCV_fsgnjn, // Float sign injection negate + RISCV_fsgnjx, // Float sign injection xor between operands + RISCV_fmin, // Float minimum + RISCV_fmax, // Float maximum + RISCV_fcvtf2f, // F2F conve + RISCV_fcvtf2i, // F2I conve + RISCV_fcvti2f, // I2F conve + RISCV_fmv, // Mv + RISCV_feq, // Float equal + RISCV_flt, // Float less than + RISCV_fle, // Float less or equal than + RISCV_fclass, // Classify the float + // 64b simple precision F(loat) instruction extension + // D(ouble) precision float instruction extension + RISCV_fld, // Load double (64b) + RISCV_fsd, // Store double (64b) + // 64b D(ouble) precision float instruction + // Q(uadruple) precision float instruction extension + RISCV_flq, // Load quadruple (128b) + RISCV_fsq, // Store quadruple (128b) + // 64b Q(uadruple) precision float extension + // pseudo insn + RISCV_nop, // No operation + RISCV_li, // Load Immediate + RISCV_mv, // Copy register + RISCV_not, // One's complement + RISCV_neg, // Two's complement + RISCV_negw, // Two's complement word + RISCV_sext, // Sign extend word + RISCV_seqz, // Set if = zero + RISCV_snez, // Set if != zero + RISCV_sltz, // Set if < zero + RISCV_sgtz, // Set if > zero + RISCV_fabs, // Float absolute value + RISCV_fneg, // Float negate + RISCV_beqz, // Branch == 0 + RISCV_bnez, // Branch != 0 + RISCV_blez, // Branch <= 0 + RISCV_bgez, // Branch >= 0 + RISCV_bltz, // Branch < 0 + RISCV_bgtz, // Branch > 0 + RISCV_j, // Jump + RISCV_jr, // Jump register + RISCV_ret, // Return + RISCV_rdinstret, // Read instructions-retired counter + RISCV_rdcycle, // Read cycle counter + RISCV_rdtime, // Read real-time clock + RISCV_rdinstreth, // Read instructions-retired counter Upper 32 bits + RISCV_rdcycleh, // Read cycle counter Upper 32 bits + RISCV_rdtimeh, // Read real-time clock Upper 32 bits + RISCV_csrr, // Read CSR + RISCV_csrw, // Write CSR + RISCV_csrs, // Set bit CSR + RISCV_csrc, // Clear bit + RISCV_csrwi, // Write CSR immediate + RISCV_csrsi, // Set bit CSR immediate + RISCV_csrci, // Clear bit CSR immediate + RISCV_frcsr, // Read FP control/status register + RISCV_fscsr, // Write/Swap FP control/status register + RISCV_frrm, // Read FP rounding mode + RISCV_fsrm, // Write/Swap FP rounding mode + RISCV_frflags, // Read FP exception flags + RISCV_fsflags, // Write/Swap FP exception flags + // Macro + RISCV_la, // Load Address macro + RISCV_call, // Call macro + RISCV_tail, // Tail macro + RISCV_jump, // Jump macro + // Vector extension + RISCV_vsetvl, // Set vector length rd = new vl, rs1 = AVL, rs2 = new vtype value + RISCV_vsetvli, // Set vector length vtype immediate + RISCV_vsetivli, // Set vector length avl immediate and vtype immediate + RISCV_vl, // Vector load + RISCV_vs, // Vector store + // Vector extention op I(nteger) + RISCV_vadd, // Vector add + RISCV_vsub, // Vector sub + RISCV_vrsub, // Vector reverse substract + RISCV_vminu, // Vector unsigned minimum + RISCV_vmin, // Vector signed minimum + RISCV_vmaxu, // Vector unsigned maximum + RISCV_vmax, // Vector signed maximum + RISCV_vand, // Vector bitwise logical and + RISCV_vor, // Vector bitwise logical or + RISCV_vxor, // Vector bitwise logical xor + RISCV_vrgather, // Vector register gather + RISCV_vslideup, // Vector slide up + RISCV_vrgatherei16, // Vector register gather + RISCV_vslidedown, // Vector slide down + RISCV_vadc, // Vector produce sum with carry + RISCV_vmadc, // Vector produce carry out in mask register format + RISCV_vsbc, // Vector produce difference with borrow + RISCV_vmsbc, // Vector produce borrow out in mask register format + RISCV_vmerge, // Vector integer merge + RISCV_vmv, // Vector integer move + RISCV_vmseq, // Vector set if equal + RISCV_vmsne, // Vector set if not equal + RISCV_vmsltu, // Vector set if less than, unsigned + RISCV_vmslt, // Vector set if less than, signed + RISCV_vmsleu, // Vector set if less than or equal, unsigned + RISCV_vmsle, // Vector set if less than or equal, signed + RISCV_vmsgtu, // Vector set if greater than, unsigned + RISCV_vmsgt, // Vector set if greater than, signed + RISCV_vsaddu, // Vector saturating adds of unsigned integers + RISCV_vsadd, // Vector saturating adds of signed integers + RISCV_vssubu, // Vector saturating subtract of unsigned integers + RISCV_vssub, // Vector saturating subtract of signed integers + RISCV_vsll, // Vector bit shift left logical + RISCV_vsmul, // Vector signed saturating and rounding fractional multiply + RISCV_vmvr, // Vector whole register move reg + RISCV_vsrl, // Vector bit shift right logical + RISCV_vsra, // Vector bit shift right arithmetic + RISCV_vssrl, // Vector scaling shift right logical + RISCV_vssra, // Vector scaling shift right arithmetic + RISCV_vnsrl, // Vector narrowing shift right logical, SEW = (2*SEW) >> SEW + RISCV_vnsra, // Vector narrowing shift right arithmetic, SEW = (2*SEW) >> SEW + RISCV_vnclipu, // Vector narrowing unsigned clip + RISCV_vnclip, // Vector narrowing signed clip + RISCV_vwredsumu, // Vector unsigned sum reduction into double-width accumulator + RISCV_vwredsum, // Vector signed sum reduction into double-width accumulator + // Vector extention op M(ultiplication and divison for integer) + RISCV_vredsum, // Vector single-width reduction sum + RISCV_vredand, // Vector single-width reduction and + RISCV_vredor, // Vector single-width reduction or + RISCV_vredxor, // Vector single-width reduction xor + RISCV_vredminu, // Vector single-width reduction min unsigned + RISCV_vredmin, // Vector single-width reduction min + RISCV_vredmaxu, // Vector single-width reduction max unsigned + RISCV_vredmax, // Vector single-width reduction max + RISCV_vaaddu, // Vector averaging adds of unsigned integers + RISCV_vaadd, // Vector averaging adds of signed integers + RISCV_vasubu, // Vector averaging subtract of unsigned integers + RISCV_vasub, // Vector averaging subtract of signed integers + RISCV_vslide1up, // Vector integer slide one element up + RISCV_vslide1down, // Vector integer slide one element down + RISCV_vcpop, // Vector count population in mask + RISCV_vfirst, // Vector find-first-set mask bit + RISCV_vmvi, // Vector integer scalar move + RISCV_vzext, // Vector zero extend + RISCV_vsext, // Vector sign extend + RISCV_vmsbf, // Vector Vector set-before-first mask bit + RISCV_vmsof, // Vector Vector set-only-first mask bit + RISCV_vmsif, // Vector Vector set-including-first mask bit + RISCV_viota, // Vector Vector iota + RISCV_vid, // Vector Vector element index + RISCV_vcompress, // Vector compress + RISCV_vmandn, // Vector mask-register not(vs2.mask[i] and vs1.mask[i]) + RISCV_vmand, // Vector mask-register (vs2.mask[i] and vs1.mask[i]) + RISCV_vmor, // Vector mask-register (vs2.mask[i] or vs1.mask[i]) + RISCV_vmxor, // Vector mask-register (vs2.mask[i] xor vs1.mask[i]) + RISCV_vmorn, // Vector mask-register not(vs2.mask[i] or vs1.mask[i]) + RISCV_vmnand, // Vector mask-register not(vs2.mask[i] and vs1.mask[i]) + RISCV_vmnor, // Vector mask-register not(vs2.mask[i] or vs1.mask[i]) + RISCV_vmxnor, // Vector mask-register not(vs2.mask[i] xor vs1.mask[i]) + RISCV_vdivu, // Vector unsigned divide + RISCV_vdiv, // Vector signed divide + RISCV_vremu, // Vector unsigned remainder + RISCV_vrem, // Vector signed remainder + RISCV_vmulhu, // Vector unsigned multiply, returning high bits of product + RISCV_vmul, // Vector signed multiply, returning low bits of product + RISCV_vmulhsu, // Vector signed-unsigned multiply, returning high bits of product + RISCV_vmulh, // Vector signed multiply, returning high bits of product + RISCV_vmadd, // Vector integer multiply-add, overwrite multiplicand + RISCV_vnmsub, // Vector integer multiply-sub, overwrite multiplicand + RISCV_vmacc, // Vector integer multiply-add, overwrite addend + RISCV_vnmsac, // Vector integer multiply-sub, overwrite minuend + RISCV_vwaddu, // Vector widening unsigned integer add , 2*SEW = SEW +/- SEW + RISCV_vwadd, // Vector widening signed integer add , 2*SEW = SEW +/- SEW + RISCV_vwsubu, // Vector widening unsigned integer substract , 2*SEW = SEW +/- SEW + RISCV_vwsub, // Vector widening signed integer substract , 2*SEW = SEW +/- SEW + RISCV_vwadduw, // Vector widening unsigned integer add , 2*SEW = 2*SEW +/- SEW + RISCV_vwaddw, // Vector widening signed integer add , 2*SEW = 2*SEW +/- SEW + RISCV_vwsubuw, // Vector widening unsigned integer substract , 2*SEW = 2*SEW +/- SEW + RISCV_vwsubw, // Vector widening signed integer substract , 2*SEW = 2*SEW +/- SEW + RISCV_vwmulu, // Vector widening unsigned-integer multiply + RISCV_vwmulsu, // Vector widening signed(vs2)-unsigned integer multiply + RISCV_vwmul, // Vector widening signed-integer multiply + RISCV_vwmaccu, // Vector widening unsigned-integer multiply-add, overwrite addend + RISCV_vwmacc, // Vector widening signed-integer multiply-add, overwrite addend + RISCV_vwmaccus, // Vector widening unsigned-signed-integer multiply-add, overwrite addend + RISCV_vwmaccsu, // Vector widening signed-unsigned-integer multiply-add, overwrite addend + // Vector extention op F(loat) + RISCV_vfadd, // Vector floating-point add + RISCV_vfredusum, // Vector fp simple-width reductions unordered sum + RISCV_vfsub, // Vector floating-point subtract + RISCV_vfredosum, // Vector fp simple-width reductions ordered sum + RISCV_vfmin, // Vector floating-point minimum + RISCV_vfredmin, // Vector fp simple-width reductions minimum value + RISCV_vfmax, // Vector floating-point maximum + RISCV_vfredmax, // Vector fp simple-width reductions maximum value + RISCV_vfsgnj, // Vector sign injection + RISCV_vfsgnjn, // Vector sign injection negated + RISCV_vfsgnjx, // Vector sign injection xor + RISCV_vfslide1up, // Vector fp slide one element up + RISCV_vfslide1down, // Vector fp slide one element down + RISCV_vfmov, // Vector floating-point scalar move + RISCV_vfcvt, // Vector convert float to integer / integer to float + RISCV_vfwcvt, // Vector convert single width float/integer to double-width integer/float + RISCV_vfncvt, // Vector convert double-width float/integer to single width float/integer + RISCV_vfsqrt, // Vector floating-point square root + RISCV_vfrsqrt7, // Vector floating-point reciprocal square-root estimate to 7 bits + RISCV_vfrec7, // Vector floating-point reciprocal estimate to 7 bits + RISCV_vfclass, // Vector classification of float + RISCV_vfmerge, // Vector floating-point merge + RISCV_vfmv, // Vector floating-point move + RISCV_vmfeq, // Vector fp compare equal + RISCV_vmfle, // Vector fp compare less than or equal + RISCV_vmflt, // Vector fp compare less than + RISCV_vmfne, // Vector fp compare not equal + RISCV_vmfgt, // Vector fp compare greater than + RISCV_vmfge, // Vector fp compare greater than or equal + RISCV_vfdiv, // Vector floating-point divide + RISCV_vfrdiv, // Vector floating-point reverse divide + RISCV_vfmul, // Vector floating-point multiply + RISCV_vfrsub, // Vector floating-point reverse subtract + RISCV_vfmadd, // Vector fp multiply-add, overwrites multiplicand + RISCV_vfnmadd, // Vector fp negate-(multiply-add), overwrites multiplicand + RISCV_vfmsub, // Vector fp multiply-sub, overwrites multiplicand + RISCV_vfnmsub, // Vector fp negate-(multiply-sub), overwrites multiplicand + RISCV_vfmacc, // Vector fp multiply-accumulate, overwrites addend + RISCV_vfnmacc, // Vector fp negate-(multiply-accumulate), overwrites subtrahend + RISCV_vfmsac, // Vector fp multiply-subtract-accumulator, overwrites subtrahend + RISCV_vfnmsac, // Vector fp negate-(multiply-subtract-accumulator), overwrites minuend + RISCV_vfwadd, // Vector widening fp add 2*SEW = SEW +/- SEW + RISCV_vfwredusum, // Vector fp widened reduction unordered sum + RISCV_vfwsub, // Vector widening fp substract 2*SEW = SEW +/- SEW + RISCV_vfwredosum, // Vector fp widened reduction ordered sum + RISCV_vfwaddw, // Vector widening fp add 2*SEW = 2*SEW +/- SEW + RISCV_vfwsubw, // Vector widening fp substract 2*SEW = 2*SEW +/- SEW + RISCV_vfwmul, // Vector widening floating-point multiply + RISCV_vfwmacc, // Vector fp widening multiply-accumulate, overwrites addend + RISCV_vfwnmacc, // Vector fp widening negate-(multiply-accumulate), overwrites addend + RISCV_vfwmsac, // Vector fp widening multiply-subtract-accumulator, overwrites addend + RISCV_vfwnmsac, // Vector fp widening negate-(multiply-subtract-accumulator), overwrites addend + // Pseudo vector instruction + RISCV_vneg, // Vector negate + RISCV_vwcvt, // Vector double width signed + RISCV_vwcvtu, // Vector double width unsigned + RISCV_vnot, // Vector not + RISCV_vncvt, // Vector half width + RISCV_vfneg, // Vector floating point negate + RISCV_vfabs, // Vector floating point absolute + RISCV_vmmv, // Vector copy mask register + RISCV_vmclr, // Vector clear mask register + RISCV_vmset, // Vector set mask register + RISCV_vmnot, // Vector invert bits + // Bits manipulations instructions + RISCV_andn, // And with inverted operand rs2 + RISCV_bclr, // Single-bit clear (register) + RISCV_bclri, // Single-bit clear (immediate) + RISCV_bext, // Single-bit extract (register) + RISCV_bexti, // Single-bit extract (immediate) + RISCV_binv, // Single-bit invert (register) + RISCV_binvi, // Single-bit invert (immediate) + RISCV_bset, // Single-bit set (register) + RISCV_bseti, // Single-bit set (immediate) + RISCV_clmul, // Carry-less multiply (low-part) + RISCV_clmulh, // Carry-less multiply (high-part) + RISCV_clmulr, // Carry-less multiply (reversed) + RISCV_clz, // Count leading zero bits + RISCV_clzw, // Count leading zero bits in word + RISCV_cpop, // Count set bits + RISCV_cpopw, // Count set bits in word + RISCV_ctz, // Count trailing zeros + RISCV_ctzw, // Count trainling zeros ni word + RISCV_max, // Maximum signed + RISCV_maxu, // Maximum unsigned + RISCV_min, // Minimum signed + RISCV_minu, // Minimum unsigned + RISCV_orc, // Bitwise OR-Combine + RISCV_orn, // Or with inverted operand rs2 + RISCV_rev8, // Byte-reverse register + RISCV_rol, // Rotate left (Register) + RISCV_rolw, // Rotate Left Word (Register) + RISCV_ror, // Rotate right + RISCV_rori, // Rotate right immediate + RISCV_roriw, // Rotate Right Word by Immediate + RISCV_rorw, // Rotate Right Word (Register) + RISCV_sh1add, // Shift left by 1 and add + RISCV_sh2add, // Shift left by 2 and add + RISCV_sh3add, // Shift left by 3 and add + RISCV_xnor, // Exclusive nor + RISCV_zext, // Zero-extend halfword + // H(alf) precision float instruction extension + RISCV_flh, // Load floating point half-precision (16 bits) + RISCV_fsh, // Store floating point half-precision (16 bits) + // Hypervisor virtual-machine instructions + RISCV_hl, // Load hypervisor mode + RISCV_hs, // Store hypervisor mode + // Svinval Fine-Grained Address-Translation Cache Invalidation + RISCV_sinval, // invalidates address-translation cache + RISCV_sfencefg, // fine grained supervisor fence + RISCV_hinval, // invalidates address-translation cache in hypervisor mode + // Pause hint + RISCV_pause, // Pause + // Cache management operation + RISCV_cbo, // cache base operation + RISCV_prefetch, // prefetch address hint + // Legacy instructions + RISCV_sfencevm, // Supervisor memory-management fence (spec 1.9.1, 2016) + // Cryptography extension + RISCV_aes, // AES encryption decryption + RISCV_aes64im, // inverse MixColumns step of the AES Block Cipher + RISCV_aes64ks1i, // KeySchedule operation for the AES Block cipher involving the SBox operation + RISCV_aes64ks2, // KeySchedule operation for the AES Block cipher + RISCV_brev8, // Reverse the bits in each byte of a source register + RISCV_pack, // Pack the low halves of rs1 and rs2 into rd + RISCV_packh, // Pack the low bytes of rs1 and rs2 into rd + RISCV_packw, // Pack the low bytes of rs1 and rs2 into rd + RISCV_sha256, // transformation for SHA2-256 hash function + RISCV_sha512, // transformation for SHA2-512 hash function + RISCV_sm3, // transformation for SM3 hash function + RISCV_sm4, // Acceleration operations for the SM4 block cipher + RISCV_unzip, // Place odd and even bits of the source word into upper/lower halves of the destination + RISCV_xperm4, // Nibble-wise lookup of indicies into a vector + RISCV_xperm8, // Byte-wise lookup of indicies into a vector in registers + RISCV_zip, // Place upper/lower halves of the source register into odd/even bits of the destination respectivley + // Zwars extension + RISCV_wrsnto, // Wait on reservation with no timeout + RISCV_wrssto, // Wait on reservation with short timeout + // T-head extension + RISCV_thdcachecall, // Clean all D-cache + RISCV_thdcacheciall, // Clean & invalidate all D-cache + RISCV_thdcacheiall, // Invalidate all D-cache + RISCV_thdcachecpa, // Clean D-cache at PA + RISCV_thdcachecipa, // Clean and invalidate D-cache at PA + RISCV_thdcacheipa, // Invalidate D-cache at PA + RISCV_thdcachecva, // Clean D-cache at VA + RISCV_thdcacheciva, // Clean and invalidate D-cache at VA + RISCV_thdcacheiva, // Invalidate D-cache at VA + RISCV_thdcachecsw, // Clean D-cache by set/way + RISCV_thdcachecisw, // Clean & invalidate D-cache by set/way + RISCV_thdcacheisw, // Invalidate D-cache by set/way + RISCV_thdcachecpal1, // Clean L1 D-cache at PA + RISCV_thdcachecval1, // Clean L1 D-cache at VA + RISCV_thicacheiall, // Invalidate all I-cache + RISCV_thicacheialls, // Invalidate all I-cache on all harts + RISCV_thicacheipa, // Invalidate I-cache at PA + RISCV_thicacheiva, // Invalidate I-cache at VA + RISCV_thl2cachecall, // Clean all L2 cache + RISCV_thl2cacheciall, // Clean & invalidate all L2 cache + RISCV_thl2cacheiall, // Invalidate all L2 cache + RISCV_thsfencevmas, // Invalidate TLB (page table cache) on all harts via broadcasting + RISCV_thsync, // Ensures that preceding instructions retire earlier and subsequent instructions retire later + RISCV_thsyncs, // Ensures that preceding instructions retire earlier and subsequent instructions retire later + RISCV_thsynci, // Ensures that preceding instructions retire earlier and subsequent instructions retire later + RISCV_thsyncis, // Ensures that preceding instructions retire earlier and subsequent instructions retire later + RISCV_thaddsl, // Add a shifted operand to a second operand + RISCV_thsrri, // Rotate Right (by Immediate) + RISCV_thsrriw, // Rotate Right Word (by Immediate) + RISCV_thext, // Extract and sign-extend bits + RISCV_thextu, // Extract and zero-extend bits + RISCV_thff0, // Find first '0'-bit + RISCV_thff1, // Find first '1'-bit + RISCV_threv, // Reverse the byte order + RISCV_threvw, // Reverse the byte order of a word operand + RISCV_thtstnbz, // Test for NUL bytes + RISCV_thtst, // Tests if a single bit is set + RISCV_thmveqz, // Move if equal zero + RISCV_thmvnez, // Move if not equal zero + RISCV_thlbia, // Load indexed byte, increment address after loading + RISCV_thlbib, // Load indexed byte, increment address before loading + RISCV_thlbuia, // Load indexed unsigned byte, increment address after loading + RISCV_thlbuib, // Load indexed unsigned byte, increment address before loading + RISCV_thlhia, // Load indexed half-word, increment address after loading + RISCV_thlhib, // Load indexed half-word, increment address before loading + RISCV_thlhuia, // Load indexed unsigned half-word, increment address after loading + RISCV_thlhuib, // Load indexed unsigned half-word, increment address before loading + RISCV_thlwia, // Load indexed word, increment address after loading + RISCV_thlwib, // Load indexed word, increment address before loading + RISCV_thlwuia, // Load indexed unsigned word, increment address after loading + RISCV_thlwuib, // Load indexed unsigned word, increment address before loading + RISCV_thldia, // Load indexed double-word, increment address after loading + RISCV_thldib, // Load indexed double-word, increment address before loading + RISCV_thsbia, // Store indexed byte, increment address after loading + RISCV_thsbib, // Store indexed byte, increment address before loading + RISCV_thshia, // Store indexed half-word, increment address after loading + RISCV_thshib, // Store indexed half-word, increment address before loading + RISCV_thswia, // Store indexed word, increment address after loading + RISCV_thswib, // Store indexed word, increment address before loading + RISCV_thsdia, // Store indexed double-word, increment address after loading + RISCV_thsdib, // Store indexed double-word, increment address before loading + RISCV_thlrb, // Load indexed byte + RISCV_thlrbu, // Load indexed unsigned byte + RISCV_thlrh, // Load indexed half-word + RISCV_thlrhu, // Load indexed unsigned half-word + RISCV_thlrw, // Load indexed word + RISCV_thlrwu, // Load indexed unsigned word + RISCV_thlrd, // Load indexed word + RISCV_thsrb, // Store indexed byte + RISCV_thsrh, // Store indexed half-word + RISCV_thsrw, // Store indexed word + RISCV_thsrd, // Store indexed double-word + RISCV_thlurb, // Load unsigned indexed byte + RISCV_thlurbu, // Load unsigned indexed unsigned byte + RISCV_thlurh, // Load unsigned indexed half-word + RISCV_thlurhu, // Load unsigned indexed unsigned half-word + RISCV_thlurw, // Load unsigned indexed word + RISCV_thlurwu, // Load unsigned indexed unsigned word + RISCV_thlurd, // Load unsigned indexed double-word + RISCV_thsurb, // Store unsigned indexed byte + RISCV_thsurh, // Store unsigned indexed half-word + RISCV_thsurw, // Store unsigned indexed word + RISCV_thsurd, // Store unsigned indexed double-word + RISCV_thldd, // Load two 64-bit values from memory into two GPRs + RISCV_thlwd, // Load two signed 32-bit values from memory into two GPRs + RISCV_thlwud, // Load two unsigned 32-bit values from memory into two GPRs + RISCV_thsdd, // Store two 64-bit values to memory from two GPRs + RISCV_thswd, // Store two 32-bit values to memory from two GPRs + RISCV_thflrd, // Load indexed double-precision floating point value + RISCV_thflrw, // Load indexed single-precision floating point value + RISCV_thflurd, // Load unsigned indexed double-precision floating point value + RISCV_thflurw, // Load unsigned indexed single-precision floating point value + RISCV_thfsrd, // Store indexed double-precision floating point value + RISCV_thfsrw, // Store indexed single-precision floating point value + RISCV_thfsurd, // Store unsigned indexed double-precision floating point value + RISCV_thfsurw, // Store unsigned indexed single-precision floating point value + RISCV_thmula, // Compute multiply-add result of double-word operands + RISCV_thmulah, // Compute multiply-add result of half-word operands + RISCV_thmulaw, // Compute multiply-add result of word operands + RISCV_thmuls, // Compute multiply-subtract result of double-word operands + RISCV_thmulsh, // Compute multiply-subtract result of half-word operands + RISCV_thmulsw, // Compute multiply-subtract result of word operands + RISCV_thfmv, // R/W double-precision floating-point high-bit data + RISCV_thipush, // Pushes register context on the interrupt stack + RISCV_thipop, // Pop register context from the interrupt stack + RISCV_thandn, // And with inverted operand rs2 (undocumented) + RISCV_thorn, // Or with inverted operand rs2 (undocumented) + RISCV_thxorn, // Exclusive nor (undocumented) + RISCV_thpackl, // Pack some bytes of rs1 and rs2 into rd (undocumented) + RISCV_thpackh, // Pack the low bytes of rs1 and rs2 into rd (undocumented) + RISCV_thpackhl, // Pack some bytes of rs1 and rs2 into rd (undocumented) + RISCV_thvmaqa, // Four signed 8-bit multiply with 32-bit add + RISCV_thvmaqau, // Four unsigned 8-bit multiply with 32-bit add + RISCV_thvmaqasu, // Four signed-unsigned 8-bit multiply with 32-bit add + RISCV_thvmaqaus, // Four unsigned-signed 8-bit multiply with 32-bit add + RISCV_thvpmaqa, // Similar to vmaqa, presumably involving packed values (undocumented) + RISCV_thvpmaqau, // Similar to vmaqau, presumably involving packed values (undocumented) + RISCV_thvpmaqaus, // Similar to vmaqaus, presumably involving packed values (undocumented) + RISCV_thvpmaqasu, // Similar to vmaqasu, presumably involving packed values (undocumented) + RISCV_thvpnclip, // Similar to vnclip, presumably involving packed values (undocumented) + RISCV_thvpnclipu, // Similar to vnclipu, presumably involving packed values (undocumented) + RISCV_thvpwadd, // Similar to vadd, presumably involving packed values (undocumented) + RISCV_thvpwaddu, // Similar to vaddu, presumably involving packed values (undocumented) + + // Pseudo-instruction containing info about unimplemented instructions, such as length + RISCV_unk, // Unsupported instruction + + RISCV_last +}; + +/* + * Interactive disassembler (IDA). + * Renesas Electronics RL78 module + * + */ + + + +enum +{ + RL78_null = 0, // Unknown Operation + RL78_subw, // Subtract Word - Word Data Subtraction + RL78_movw, // Move Word - Word Data Transfer + RL78_call, // Call - Subroutine Call + RL78_clrw, // Clear Word - Word Data Clear + RL78_addw, // Add Word - Word Data Addition + RL78_ret, // Return - Return from Subroutine + RL78_sel, // Select Register Bank + RL78_push, // Push + RL78_onew, // One Word - Word Data 0001 Set + RL78_incw, // Increment Word - Word Data Increment + RL78_cmpw, // Compare Word - Word Data Comparison + RL78_bnz, // Branch if Not Zero + RL78_clrb, // Clear byte - Byte Data Clear + RL78_and, // And - Logical Product of Byte Data + RL78_or, // Or - Logical Sum of Byte Data + RL78_br, // Branch - Unconditional Branch + RL78_pop, // Pop + RL78_cmp, // Compare - Byte Data Comparison + RL78_decw, // Decrement Word - Word Data Decrement + RL78_inc, // Increment - Byte Data Increment + RL78_bz, // Branch if Zero + RL78_sknz, // Skip if not Z - Skip with Zero Flag (Z = 0) + RL78_sknh, // Skip if not Higher than - Skip with numeric value comparison ((Z \/ CY) = 1) + RL78_skh, // Skip if Higher than - Skip with numeric value comparison ((Z \/ CY) = 0) + RL78_xor1, // Exclusive Or Single Bit - 1 Bit Data Exclusive Logical Sum + RL78_or1, // Or Single Bit - 1 Bit Data Logical Sum + RL78_and1, // And Single Bit - 1 Bit Data Logical Product + RL78_mov1, // Move Single Bit - 1 Bit Data Transfer + RL78_clr1, // Clear Single Bit (Carry Flag) - 1 Bit Data Clear + RL78_set1, // Set Single Bit (Carry Flag) - 1 Bit Data Set + RL78_not1, // Not Single Bit (Carry Flag) - 1 Bit Data Logical Negation + RL78_skc, // Skip if CY - Skip with Carry Flag (CY = 1) + RL78_sknc, // Skip if not CY - Skip with Carry Flag (CY = 0) + RL78_skz, // Skip if Z - Skip with Zero Flag (Z = 1) + RL78_mov, // Move - Byte Data Transfer + RL78_btclr, // Branch if True and Clear - Conditional Branch and Clear by Bit Test (Byte Data Bit =1) + RL78_bt, // Branch if True - Conditional Branch by Bit Test (Byte Data Bit = 1) + RL78_bf, // Branch if False - Conditional Branch by Bit Test (Byte Data Bit = 0) + RL78_shl, // Shift Left - Logical Shift to the Left + RL78_shr, // Shift Right - Logical Shift to the Right + RL78_sar, // Shift Arithmetic Right - Arithmetic Shift to the Right + RL78_shlw, // Shift Left Word - Logical Shift to the Left + RL78_shrw, // Shift Right Word - Logical Shift to the Right + RL78_sarw, // Shift Arithmetic Right Word - Arithmetic Shift to the Right + RL78_bc, // Branch if Carry - Conditional Branch with Carry Flag (CY = 1) + RL78_bnc, // Branch if Not Carry - Conditional Branch with Carry Flag (CY = 0) + RL78_bh, // Branch if Higher than - Conditional branch by numeric value comparison ((Z \/ CY) = 0) + RL78_bnh, // Branch if Not Higher than - Conditional branch by numeric value comparison ((Z \/ CY) = 1) + RL78_add, // Add - Byte Data Addition + RL78_addc, // Add with Carry - Addition of Byte Data with Carry + RL78_sub, // Subtract - Byte Data Subtraction + RL78_subc, // Subtract with Carry -Subtraction of Byte Data with Carry + RL78_xor, // Exclusive Or - Exclusive Logical Sum of Byte Data + RL78_xch, // Exchange - Byte Data Transfer + RL78_dec, // Decrement - Byte Data Decrement + RL78_rolwc, // Rotate Left word with Carry - Word Data Rotation to the Left with Carry + RL78_xchw, // Exchange Word - Word Data Transfer + RL78_cmps, // Compare - Byte Data Comparison + RL78_movs, // Move and change PSW - Byte Data Transfer and PSW Change + RL78_halt, // Halt - HALT Mode Set + RL78_cmp0, // Compare 00H - Byte Data Zero Comparison + RL78_mulu, // Multiply Unsigned - Unsigned Multiplication of Data + RL78_oneb, // One byte - Byte Data 01H Set + RL78_ror, // Rotate Right - Byte Data Rotation to the Right + RL78_rol, // Rotate Left - Byte Data Rotation to the Left + RL78_rorc, // Rotate Right with Carry - Byte Data Rotation to the Right with Carry + RL78_rolc, // Rotate Left with Carry - Byte Data Rotation to the Left with Carry + RL78_brk, // Break - Software Vectored Interrupt + RL78_retb, // Return from Break - Return from Software Vectored Interrupt + RL78_reti, // Return from Interrupt - Return from Interrupt Vectored Interrupt + RL78_stop, // Stop - Stop Mode Set + RL78_nop, // No Operation + RL78_callt, // Call Table - Subroutine Call (Refer to the Call Table) + // S3-core + RL78_mulhu, // Multiply Unsigned - Unsigned Multiplication of Data + RL78_mulh, // Multiply Signed - Signed Multiplication of Data + RL78_divhu, // 16-bit Divide Unsigned - Unsigned Division of Data + RL78_divwu, // 32-bit Divide Unsigned - Unsigned Division of Data + RL78_machu, // Multiply and Accumulate Unsigned - Unsigned Multiplication and Accumulation of Data + RL78_mach, // Multiply and Accumulate Signed - Signed Multiplication and Accumulation of Data + + RL78_last +}; + +/* + * Interactive disassembler (IDA). + * RX module + * + */ + + + +enum +{ + RX_null = 0, // Unknown Operation + + RX_abs, // Absolute value + RX_adc, // Addition with carry + RX_add, // Addition without carry + RX_and, // Logical AND + RX_bra, // Unconditional relative branch + RX_brk, // Unconditional trap + RX_bclr, // Clearing a bit + RX_bfmov, // Transferring bit-fields + RX_bfmovz, // Transferring a bit-field and setting the other bits at the destination to zero + RX_beq, // Relative conditional branch, Equal to + RX_bne, // Relative conditional branch, Not equal to + RX_bgeu, // Relative conditional branch, Equal to or greater than + RX_bltu, // Relative conditional branch, Less than + RX_bgtu, // Relative conditional branch, Greater than + RX_bleu, // Relative conditional branch, Equal to or less than + RX_bpz, // Relative conditional branch, Positive or zero + RX_bn, // Relative conditional branch, Negative + RX_bge, // Relative conditional branch, Equal to or greater than as signed integer + RX_blt, // Relative conditional branch, Less than as signed integer + RX_bgt, // Relative conditional branch, Greater than as signed integer + RX_ble, // Relative conditional branch, Equal to or less than as signed integer + RX_bo, // Relative conditional branch, O flag is 1 + RX_bno, // Relative conditional branch, O flag is 0 + + RX_bmeq, // Conditional bit transfer, Equal to + RX_bmne, // Conditional bit transfer, Not equal to + RX_bmgeu, // Conditional bit transfer, Equal to or greater than + RX_bmltu, // Conditional bit transfer, Less than + RX_bmgtu, // Conditional bit transfer, Greater than + RX_bmleu, // Conditional bit transfer, Equal to or less than + RX_bmpz, // Conditional bit transfer, Positive or zero + RX_bmn, // Conditional bit transfer, Negative + RX_bmge, // Conditional bit transfer, Equal to or greater than as signed integer + RX_bmlt, // Conditional bit transfer, Less than as signed integer + RX_bmgt, // Conditional bit transfer, Greater than as signed integer + RX_bmle, // Conditional bit transfer, Equal to or less than as signed integer + RX_bmo, // Conditional bit transfer, O flag is 1 + RX_bmno, // Conditional bit transfer, O flag is 0 + + RX_bnot, // Inverting a bit + RX_bset, // Setting a bit + RX_bsr, // Relative subroutine branch + RX_btst, // Testing a bit + RX_clrpsw, // Clear a flag or bit in the PSW + RX_cmp, // Comparison + RX_div, // Signed division + RX_divu, // Unsigned division + RX_emaca, // Extend multiply-accumulate to the accumulator + RX_emsba, // Extended multiply-subtract to the accumulator + RX_emula, // Extended multiply to the accumulator + RX_emul, // Signed multiplication + RX_emulu, // Unsigned multiplication + RX_fadd, // Single-precision floating-point addition + RX_fcmp, // Single-precision floating-point comparison + RX_fdiv, // Single-precision floating-point division + RX_fmul, // Single-precision floating-point multiplication + RX_fsqrt, // Single-precision floating-point square root + RX_fsub, // Single-precision floating-point subtraction + RX_ftoi, // Single-precision floating-point number to signed integer conversion + RX_ftou, // Single-precision floating-point number to unsigned integer conversion + RX_int, // Software interrupt + RX_itof, // Signed integer to single-precision floating-point number conversion + RX_jmp, // Unconditional jump + RX_jsr, // Jump to a subroutine + RX_machi, // Multiply-Accumulate the upper word + RX_maclo, // Multiply-Accumulate the lower word + RX_maclh, // Multiply-Accumulate the lower word and upper word + RX_max, // Selecting the highest value + RX_min, // Selecting the lowest value + RX_mov, // Transferring data + RX_movco, // Storing with LI flag clear + RX_movli, // Loading with LI flag set + RX_movu, // Transfer unsigned data + RX_msbhi, // Multiply-Subtract the upper word + RX_msblo, // Multiply-Subtract the lower word + RX_msblh, // Multiply-Subtract the lower word and upper word + RX_mul, // Multiplication + RX_mulhi, // Multiply the upper word + RX_mullo, // Multiply the lower word + RX_mullh, // Multiply the lower word and upper word + RX_mvfachi, // Move the upper longword from accumulator + RX_mvfaclo, // Move the lower longword from the accumulator + RX_mvfacmi, // Move the middle-order longword from the accumulator + RX_mvfacgu, // Move the guard longword from the accumulator + RX_mvfc, // Transfer from a control register + RX_mvtacgu, // Move the guard longword to the accumulator + RX_mvtachi, // Move the upper longword to the accumulator + RX_mvtaclo, // Move the lower longword to the accumulator + RX_mvtc, // Transfer to a control register + RX_mvtipl, // Interrupt priority level setting + RX_neg, // Negation + RX_nop, // No operation + RX_not, // Bitwise negation + RX_or, // Logical OR + RX_pop, // Restoring data from stack to register + RX_popc, // Restoring a control register + RX_popm, // Restoring multiple registers from the stack + RX_push, // Saving data on the stack + RX_pushc, // Saving a control register + RX_pushm, // Saving multiple registers + RX_racl, // Round the accumulator longword + RX_rdacl, // Round the accumulator longword + RX_racw, // Round the accumulator word + RX_rdacw, // Round the accumulator word + RX_revl, // Endian conversion + RX_revw, // Endian conversion + RX_rmpa, // Multiply-and-accumulate operation + RX_rolc, // Rotation with carry to left + RX_rorc, // Rotation with carry to right + RX_rotl, // Rotation to left + RX_rotr, // Rotation to right + RX_round, // Conversion from single-precision floating-point number to signed integer + RX_rstr, // Collective restoration of register values + RX_rte, // Return from the exception + RX_rtfi, // Return from the fast interrupt + RX_rts, // Returning from a subroutine + RX_rtsd, // Releasing stack frame and returning from subroutine + RX_sat, // Saturation of signed 32-bit data + RX_satr, // Saturation of signed 64-bit data for RMPA + RX_save, // Collective saving of register values + RX_sbb, // Subtraction with borrow + + RX_sceq, // Condition setting, Equal to + RX_scne, // Condition setting, Not equal to + RX_scgeu, // Condition setting, Equal to or greater than + RX_scltu, // Condition setting, Less than + RX_scgtu, // Condition setting, Greater than + RX_scleu, // Condition setting, Equal to or less than + RX_scpz, // Condition setting, Positive or zero + RX_scn, // Condition setting, Negative + RX_scge, // Condition setting, Equal to or greater than as signed integer + RX_sclt, // Condition setting, Less than as signed integer + RX_scgt, // Condition setting, Greater than as signed integer + RX_scle, // Condition setting, Equal to or less than as signed integer + RX_sco, // Condition setting, O flag is 1 + RX_scno, // Condition setting, O flag is 0 + + RX_scmpu, // String comparison + RX_setpsw, // Setting a flag or bit in the PSW + RX_shar, // Arithmetic shift to the right + RX_shll, // Logical and arithmetic shift to the left + RX_shlr, // Logical shift to the right + RX_smovb, // Transferring a string backward + RX_smovf, // Transferring a string forward + RX_smovu, // Transferring a string + RX_sstr, // Storing a string + RX_stnz, // Transfer with condition (Z == 0) + RX_stz, // Transfer with condition (Z == 1) + RX_sub, // Subtraction without borrow + RX_suntil, // Searching for a string + RX_swhile, // Searching for a string + RX_tst, // Logical test + RX_utof, // Unsigned integer to single-precision floating-point number conversion + RX_wait, // Waiting + RX_xchg, // Exchanging values + RX_xor, // Logical Exclusive OR + // double instructions + RX_dabs, // Double-precision floating-point absolute value + RX_dadd, // Double-precision floating-point addition without carry + RX_dcmpun, // Double-precision floating-point comparison, This condition is for detecting cases where classification of order based on the comparison is impossible. + RX_dcmpeq, // Double-precision floating-point comparison, This condition is for detecting that src2 is equal to src. + RX_dcmplt, // Double-precision floating-point comparison, This condition is for detecting that src2 is less than src. + RX_dcmple, // Double-precision floating-point comparison, This condition is for detecting that src2 is less than or equal to src. + RX_ddiv, // Double-precision floating-point division + RX_dmov, // Double-precision floating-point transferring data + RX_dmul, // Double-precision floating-point multiplication + RX_dneg, // Double-precision floating-point negate + RX_dpopm, // Restoring multiple double-precision floating-point registers + RX_dpushm, // Saving multiple double-precision floating-point registers + RX_dround, // Conversion from double-precision floating-point number to signed integer + RX_dsqrt, // Double-precision floating-point square root + RX_dsub, // Double-precision floating-point subtraction + RX_dtof, // Double-precision floating-point number to single-precision floating-point number conversion + RX_dtoi, // Double-precision floating-point number to signed integer conversion + RX_dtou, // Double-precision floating-point number to unsigned integer conversion + RX_ftod, // Single-precision floating-point number to double-precision floating-point number conversion + RX_itod, // Signed integer to double-precision floating-point number conversion + RX_mvfdc, // Transfer from double-precision floating-point control register + RX_mvfdr, // Transfer from double-precision floating-point comparison result register + RX_mvtdc, // Transfer to double-precision floating-point control register + RX_utod, // Unsigned integer to double-precision floating-point number conversion + RX_last +}; + +/* + * Interactive disassembler (IDA). + * Tensilica Xtensa module + * + */ + + +//------------------------------------------------------------------------ + + +enum +{ + XTENSA_null = 0, // Unknown Operation + + XTENSA_abs, // Absolute value + XTENSA_add, // Add two registers + XTENSA_addi, // Add signed constant to register + XTENSA_addmi, // Add a register and a shifted 8-bit immediate + XTENSA_addx2, // Add register to register shifted by 1 + XTENSA_addx4, // Add register to register shifted by 2 + XTENSA_addx8, // Add register to register shifted by 3 + XTENSA_and, // Bitwise logical AND + XTENSA_ball, // Branch if all of masked bits set + XTENSA_bany, // Branch if any of masked bits set + XTENSA_bbc, // Branch if bit clear + XTENSA_bbs, // Branch if bit set + XTENSA_bbci, // Branch if bit clear immediate + XTENSA_bbsi, // Branch if bit set immediate + XTENSA_beq, // Branch if equal + XTENSA_beqi, // Branch if equal immediate + XTENSA_beqz, // Branch if equal to zero + XTENSA_bge, // Branch if greater than or equal + XTENSA_bgei, // Branch if one register is greater than or equal to an encoded constant + XTENSA_bgeu, // Branch if greater than or equal unsigned + XTENSA_bgeui, // Branch if greater than or equal unsigned immediate + XTENSA_bgez, // Branch if greater than or equal to zero + XTENSA_blt, // Branch if less than + XTENSA_blti, // Branch if less than immediate + XTENSA_bltu, // Branch if less than Unsigned + XTENSA_bltui, // Branch if less than unsigned immediate + XTENSA_bltz, // Branch if less than zero + XTENSA_bnall, // Branch if not all of masked bits set + XTENSA_bnone, // Branch if none of masked bits set (All Clear) + XTENSA_bne, // Branch if not equal + XTENSA_bnei, // Branch if not equal immediate + XTENSA_bnez, // Branch if not equal to zero + XTENSA_break, // Breakpoint + XTENSA_call0, // Call subroutine, PC-relative + XTENSA_call4, // Call subroutine, PC-relative, rotate window by 4 + XTENSA_call8, // Call subroutine, PC-relative, rotate window by 8 + XTENSA_call12, // Call subroutine, PC-relative, rotate window by 12 + XTENSA_callx0, // Call subroutine register specified location + XTENSA_callx4, // Call Register, Rotate Window by 4 + XTENSA_callx8, // Call Register, Rotate Window by 8 + XTENSA_callx12, // Call Register, Rotate Window by 12 + XTENSA_dsync, // Load/store synchronize + XTENSA_entry, // Subroutine entry + XTENSA_esync, // Register value synchronize + XTENSA_excw, // Exception Wait + XTENSA_extui, // Extract field specified by immediates from a register + XTENSA_extw, // Wait for any possible external ordering requirement + XTENSA_isync, // Instruction fetch synchronize + // XTENSA_ill, // Illegal instruction executed + XTENSA_j, // Jump to PC plus offset + XTENSA_jx, // Jump to register specified location + XTENSA_loop, // Loop + XTENSA_loopgtz, // Loop if Greater Than Zero + XTENSA_loopnez, // Loop if Not-Equal Zero + XTENSA_lsi, // Load single-precision immediate + XTENSA_lsx, // Load single-precision indexed + XTENSA_l8ui, // Load zero extended byte + XTENSA_l16si, // 16-bit signed load (8-bit shifted offset) + XTENSA_l16ui, // 16-bit unsigned load (8-bit shifted offset) + XTENSA_l32i, // 32-bit load (8-bit shifted offset) + XTENSA_l32r, // 32-bit load PC-relative (16-bit negative word offset) + XTENSA_max, // Maximum Value Signed + XTENSA_maxu, // Maximum Value Unsigned + XTENSA_memw, // Wait for any possible memory ordering requirement + XTENSA_min, // Minimum Value Signed + XTENSA_minu, // Minimum Value Unsigned + XTENSA_mov, // Move a 32-bit register to a register + XTENSA_moveqz, // Conditional move if zero + XTENSA_movgez, // Conditional move if greater than or equal to zero + XTENSA_movi, // Move a 12-bit immediate to a register + XTENSA_movltz, // Conditional move if less than zero + XTENSA_movnez, // Conditional move if non-zero + XTENSA_mul16s, // Signed 16-bit multiplication + XTENSA_mul16u, // Unsigned 16-bit multiplication + XTENSA_mull, // Multiply low + XTENSA_neg, // Negate + XTENSA_nsa, // Normalization shift amount signed + XTENSA_nsau, // Normalization shift amount unsigned + XTENSA_nop, // No operation + XTENSA_or, // Bitwise logical OR + XTENSA_ret, // Return + XTENSA_retw, // Windowed Return + XTENSA_rfe, // Returns from the KernelExceptionVector exception + XTENSA_rfi, // Return from high-priority interrupt + XTENSA_rsil, // Read and set interrupt level + XTENSA_rsr, // Read Special Register + XTENSA_rsync, // Wait for dispatch related changes to resolve + XTENSA_s8i, // 8-bit store (8-bit offset) + XTENSA_s16i, // 16-bit store (8-bit shifted offset) + XTENSA_s32i, // Store 32-bit quantity + XTENSA_s32ri, // Store 32-bit release + XTENSA_sext, // Sign Extend + XTENSA_sll, // Shift left logical + XTENSA_slli, // Shift left logical by SAR/immediate + XTENSA_sra, // Shift right arithmetic + XTENSA_srai, // Shift right arithmetic immediate by 0..31 bit positions + XTENSA_src, // Shift right combined + XTENSA_srl, // Shift right logical + XTENSA_srli, // Shift right logical immediate by 0..15 bit positions + XTENSA_ssa8b, // Set shift amount register (SAR) for big-endian byte align + XTENSA_ssa8l, // Set shift amount register (SAR) for little-endian byte align + XTENSA_ssai, // Set shift amount register (SAR) immediate + XTENSA_ssl, // Set shift amount register (SAR) for shift left logica + XTENSA_ssr, // Set shift amount register (SAR) for shift right logical + XTENSA_sub, // Subtract two registers + XTENSA_subx2, // Subtract register from register shifted by 1 + XTENSA_subx4, // Subtract register from register shifted by 2 + XTENSA_subx8, // Subtract register from register shifted by 3 + XTENSA_waiti, // Wait for interrupt + XTENSA_wdtlb, // Write data TLB + XTENSA_witlb, // Write instruction TLB + XTENSA_wsr, // Write Special Register + XTENSA_xor, // Bitwise logical exclusive OR + XTENSA_xsr, // Exchange Special Register + + // narrow + XTENSA_ill, // Illegal instruction executed + + // macro + XTENSA_andi, // Bitwise logical AND with immediate + XTENSA_ori, // Bitwise logical OR with immediate +// XTENSA_bbci_l, // Branch if Bit Clear Immediate LE (no need to implement - module will always show mbbci anyway) +// XTENSA_bbsi_l, // Branch if Bit Set Immediate LE (no need to implement - module will always show mbbsi anyway) +// XTENSA_j_l, // Unconditional Jump Long (no need to implement - module will always show j or jx anyway) +// XTENSA_mov, // Move - Macro for "or" +// XTENSA_moveqz_d, // Move Double if Equal to Zero - Macro for "moveqz.s" +// XTENSA_movf_d, // Move Double if False - Macro for "movf.s" +// XTENSA_movgez_d, // Move Double if Greater Than or Equal to Zero - Macro for "movgez.s" +// XTENSA_movltz_d, // Move Double if Less Than Zero - Macro for "movltz.s" +// XTENSA_movnez_d, // Move Double if Not Equal to Zero - Macro for "movnez.s" +// XTENSA_movt_d, // Move Double if True - Macro for "movt.s" + + // other +// XTENSA_const16, // CONST16-Shift In 16-bit Constant - unspecified/custom + XTENSA_rur, // Read User Register + XTENSA_wur, // Write User Register + + // core + XTENSA_fsync, // Fetch Synchronize + XTENSA_rer, // Read External Register + XTENSA_s32nb, // Store 32-bit Non-Buffered + XTENSA_salt, // Set AR if Less Than + XTENSA_saltu, // Set AR if Less Than Unsigned + XTENSA_wer, // Write External Register + + // block prefetch + XTENSA_dhi_b, // Block Data Cache Hit Invalidate + XTENSA_dhwb_b, // Block Data Cache Hit Writeback + XTENSA_dhwbi_b, // Block Data Cache Hit Writeback Inv + XTENSA_dpfm_b, // Block Data Cache Prefetch and Modify + XTENSA_dpfm_bf, // Block Data Cache Prefetch/Modify First + XTENSA_dpfr_b, // Block Data Cache Prefetch for Read + XTENSA_dpfr_bf, // Block Data Cache Prefetch for Read First + XTENSA_dpfw_b, // Block Data Cache Prefetch for Write + XTENSA_dpfw_bf, // Block Data Cache Prefetch for Write First + + // boolean + XTENSA_all4, // All 4 Booleans True + XTENSA_all8, // All 8 Booleans True + XTENSA_andb, // Boolean And + XTENSA_andbc, // Boolean And with Complement + XTENSA_any4, // Any 4 Booleans True + XTENSA_any8, // Any 8 Booleans True + XTENSA_bf, // Branch if False + XTENSA_bt, // Branch if True + XTENSA_movf, // Move if False + XTENSA_movt, // Move if True + XTENSA_orb, // Boolean Or + XTENSA_orbc, // Boolean Or with Complement + XTENSA_xorb, // Boolean Exclusive Or + + // conditional + XTENSA_s32c1i, // Store 32-bit Compare Conditional + + // data cache + XTENSA_dci, // Data Cache Coherent Hit Invalidate + XTENSA_dcwb, // Data Cache Coherent Hit Writeback + XTENSA_dcwbi, // Data Cache Coherent Hit WB Invalidate + XTENSA_dhi, // Data Cache Hit Invalidate + XTENSA_dhu, // Data Cache Hit Unlock + XTENSA_dhwb, // Data Cache Hit Writeback + XTENSA_dhwbi, // Data Cache Hit Writeback Invalidate + XTENSA_dii, // Data Cache Index Invalidate + XTENSA_diu, // Data Cache Index Unlock + XTENSA_diwb, // Data Cache Index Write Back + XTENSA_diwbi, // Data Cache Index Write Back Invalidate + XTENSA_diwbui_p, // Data Cache Empty + XTENSA_dpfl, // Data Cache Prefetch and Lock + XTENSA_dpfr, // Data Cache Prefetch for Read + XTENSA_dpfro, // Data Cache Prefetch for Read Once + XTENSA_dpfw, // Data Cache Prefetch for Write + XTENSA_dpfwo, // Data Cache Prefetch for Write Once + XTENSA_ldct, // Load Data Cache Tag + XTENSA_ldcw, // Load Data Cache Word + XTENSA_sdct, // Store Data Cache Tag + XTENSA_sdcw, // Store Data Cache Word + + // debug + XTENSA_lddr32_p, // Load to DDR Register + XTENSA_rfdd, // Return from Debug and Dispatch + XTENSA_rfdo, // Return from Debug Operation + XTENSA_sddr32_p, // Store from DDR Register + + // deposit bits option + XTENSA_depbits, // Deposit Bits - same encoding as "add.s" + + // exception + XTENSA_rfde, // Return from Double Exception + XTENSA_rfue, // Return from User-Mode Exception + XTENSA_syscall, // System Call + + // exclusive + XTENSA_clrex, // Clear Exclusive + XTENSA_getex, // Get Exclusive Result + XTENSA_l32ex, // Load 32-bit Exclusive + XTENSA_s32ex, // Store 32-bit Exclusive + + // floating point + XTENSA_abs_d, // Absolute Value Double + XTENSA_abs_s, // Absolute Value Single + XTENSA_add_d, // Add Double + XTENSA_add_s, // Add Single - same encoding as "depbits" + XTENSA_addexp_d, // Add Exponent Double + XTENSA_addexp_s, // Add Exponent Single + XTENSA_addexpm_d, // Add Exponent from Mantissa Double + XTENSA_addexpm_s, // Add Exponent from Mantissa Single + XTENSA_ceil_d, // Ceiling Double to Fixed + XTENSA_ceil_s, // Ceiling Single to Fixed + XTENSA_const_d, // Constant Double + XTENSA_const_s, // Constant Single + XTENSA_cvtd_s, // Convert Single to Double + XTENSA_cvts_d, // Convert Double to Single + XTENSA_div0_d, // Divide Begin Double + XTENSA_div0_s, // Divide Begin Single + XTENSA_divn_d, // Divide Final Double + XTENSA_divn_s, // Divide Final Single + XTENSA_float_d, // Convert Fixed to Double + XTENSA_float_s, // Convert Fixed to Single + XTENSA_floor_d, // Floor Double to Fixed + XTENSA_floor_s, // Floor Single to Fixed + XTENSA_ldi, // Load Double Immediate + XTENSA_ldip, // Load Double Immediate Post-Increment + XTENSA_ldx, // Load Double Indexed + XTENSA_ldxp, // Load Double Indexed Post-Increment + XTENSA_lsip, // Load Single Immediate Post-Increment - newer version of lsiu + XTENSA_lsiu, // Load Single Immediate with Update - fp2000 version of lsip + XTENSA_lsxp, // Load Single Indexed Post-Increment - newer version of lsxu + XTENSA_lsxu, // Load Single Indexed with Update - fp2000 version of lsxp + XTENSA_madd_d, // Multiply and Add Double + XTENSA_madd_s, // Multiply and Add Single + XTENSA_maddn_d, // Multiply and Add Double Round Nearest + XTENSA_maddn_s, // Multiply and Add Single Round Nearest + XTENSA_mkdadj_d, // Make Divide Adjust Double + XTENSA_mkdadj_s, // Make Divide Adjust Single + XTENSA_mksadj_d, // Make Square Root Adjust Double + XTENSA_mksadj_s, // Make Square Root Adjust Single + XTENSA_mov_d, // Move Double + XTENSA_mov_s, // Move Single + XTENSA_moveqz_s, // Move Single if Equal to Zero + XTENSA_movf_s, // Move Single if False + XTENSA_movgez_s, // Move Single if Greater Than or Equal to Zero + XTENSA_movltz_s, // Move Single if Less Than Zero + XTENSA_movnez_s, // Move Single if Not Equal to Zero + XTENSA_movt_s, // Move Single if True + XTENSA_msub_d, // Multiply and Subtract Double + XTENSA_msub_s, // Multiply and Subtract Single + XTENSA_mul_d, // Multiply Double + XTENSA_mul_s, // Multiply Single + XTENSA_neg_d, // Negate Double + XTENSA_neg_s, // Negate Single + XTENSA_nexp01_d, // Narrow Exponent Range Double + XTENSA_nexp01_s, // Narrow Exponent Range Single + XTENSA_oeq_d, // Compare Double Equal + XTENSA_oeq_s, // Compare Single Equal + XTENSA_ole_d, // Compare Double Ordered and Less Than or Equal + XTENSA_ole_s, // Compare Single Ordered and Less Than or Equal + XTENSA_olt_d, // Compare Double Ordered and Less Than + XTENSA_olt_s, // Compare Single Ordered and Less Than + XTENSA_recip0_d, // Reciprocal Begin Double + XTENSA_recip0_s, // Reciprocal Begin Single + XTENSA_rfr, // Move FR to AR + XTENSA_rfrd, // Move FR to AR Upper + XTENSA_round_d, // Round Double to Fixed + XTENSA_round_s, // Round Single to Fixed + XTENSA_rsqrt0_d, // Reciprocal Sqrt Begin Double + XTENSA_rsqrt0_s, // Reciprocal Sqrt Begin Single + XTENSA_sdi, // Store Double Immediate + XTENSA_sdip, // Store Double Immediate Post-Increment + XTENSA_sdx, // Store Double Indexed + XTENSA_sdxp, // Store Double Indexed Post-Increment + XTENSA_sqrt0_d, // Sqrt Begin Double + XTENSA_sqrt0_s, // Sqrt Begin Single + XTENSA_ssi, // Store Single Immediate + XTENSA_ssip, // Store Single Immediate Post-Increment - newer version of ssiu + XTENSA_ssiu, // Store Single Immediate with Update - fp2000 version of ssip + XTENSA_ssx, // Store Single Indexed + XTENSA_ssxp, // Store Single Indexed Post-Increment - newer version of ssxu + XTENSA_ssxu, // Store Single Indexed with Update - fp2000 version of ssxp + XTENSA_sub_d, // Subtract Double + XTENSA_sub_s, // Subtract Single + XTENSA_trunc_d, // Truncate Double to Fixed + XTENSA_trunc_s, // Truncate Single to Fixed + XTENSA_ueq_d, // Compare Double Unordered or Equal + XTENSA_ueq_s, // Compare Single Unordered or Equal + XTENSA_ufloat_d, // Convert Unsigned Fixed to Double + XTENSA_ufloat_s, // Convert Unsigned Fixed to Single + XTENSA_ule_d, // Compare Double Unord or Less Than or Equal + XTENSA_ule_s, // Compare Single Unord or Less Than or Equal + XTENSA_ult_d, // Compare Double Unordered or Less Than + XTENSA_ult_s, // Compare Single Unordered or Less Than + XTENSA_un_d, // Compare Double Unordered + XTENSA_un_s, // Compare Single Unordered + XTENSA_utrunc_d, // Truncate Double to Fixed Unsigned + XTENSA_utrunc_s, // Truncate Single to Fixed Unsigned + XTENSA_wfr, // Move AR to FR + XTENSA_wfrd, // Move AR to FR Double + + // instruction cache + XTENSA_ihi, // Instruction Cache Hit Invalidate + XTENSA_ihu, // Instruction Cache Hit Unlock + XTENSA_iii, // Instruction Cache Index Invalidate + XTENSA_iiu, // Instruction Cache Index Unlock + XTENSA_ipf, // Instruction Cache Prefetch + XTENSA_ipfl, // Instruction Cache Prefetch and Lock + XTENSA_lict, // Load Instruction Cache Tag + XTENSA_licw, // Load Instruction Cache Word + XTENSA_sict, // Store Instruction Cache Tag + XTENSA_sicw, // Store Instruction Cache Word + + // int32 + XTENSA_mulsh, // Multiply Signed High + XTENSA_muluh, // Multiply Unsigned High + XTENSA_quos, // Quotient Signed + XTENSA_quou, // Quotient Unsigned + XTENSA_rems, // Remainder Signed + XTENSA_remu, // Remainder Unsigned + + // mac16 + XTENSA_lddec, // Load with Autodecrement + XTENSA_ldinc, // Load with Autoincrement + XTENSA_mul_aa, // Signed Multiply + XTENSA_mul_ad, // Signed Multiply + XTENSA_mul_da, // Signed Multiply + XTENSA_mul_dd, // Signed Multiply + XTENSA_mula_aa, // Signed Multiply/Accumulate + XTENSA_mula_ad, // Signed Multiply/Accumulate + XTENSA_mula_da, // Signed Multiply/Accumulate + XTENSA_mula_da_lddec,// Signed Multiply/Accumulate, Ld/Autodec + XTENSA_mula_da_ldinc,// Signed Multiply/Accumulate, Ld/Autoinc + XTENSA_mula_dd, // Signed Multiply/Accumulate + XTENSA_mula_dd_lddec,// Signed Multiply/Accumulate, Ld/Autodec + XTENSA_mula_dd_ldinc,// Signed Multiply/Accumulate, Ld/Autoinc + XTENSA_muls_aa, // Signed Multiply/Subtract + XTENSA_muls_ad, // Signed Multiply/Subtract + XTENSA_muls_da, // Signed Multiply/Subtract + XTENSA_muls_dd, // Signed Multiply/Subtract + XTENSA_umul_aa, // Unsigned Multiply + + // memory ecc + XTENSA_rfme, // Return from Memory Error + + // misc + XTENSA_clamps, // Signed Clamp + + // multiprocessor + XTENSA_l32ai, // Load 32-bit Aquire + + // region + XTENSA_idtlb, // Invalidate Data TLB Entry + XTENSA_iitlb, // Invalidate Instruction TLB Entry + XTENSA_pdtlb, // Probe Data TLB + XTENSA_pitlb, // Probe Intruction TLB + XTENSA_pptlb, // Probe Protection TLB - same encoding as "pdtlb" + XTENSA_rdtlb0, // Read Data TLB Entry Virtual + XTENSA_rdtlb1, // Read Data TLB Entry Translation + XTENSA_ritlb0, // Read Instruction TLB Entry Virtual + XTENSA_ritlb1, // Read Instruction TLB Entry Translation + XTENSA_rptlb0, // Read Protection TLB Entry Address - same encoding as "rdtlb0" + XTENSA_rptlb1, // Read Protection TLB Entry Info - same encoding as "rdtlb1" + XTENSA_wptlb, // Write Protection TLB Entry - same encoding as "wdtlb" + + // sim + XTENSA_simcall, // Simulator Call + + // windowed + XTENSA_l32e, // Load 32-bit for Window Exceptions + XTENSA_movsp, // Move to Stack Pointer + XTENSA_rfwo, // Return from Window Overflow + XTENSA_rfwu, // Return from Window Underflow + XTENSA_rotw, // Rotate Window + XTENSA_s32e, // Store 32-bit for Window Exceptions + + // reserved - these are undocumented in the main docs + // but exist in some GNU binutils implementations. + // they might be custom to some specific implementation of xtensa + // but as long as they dont conflict with anything else we can include support for them + XTENSA_clrb_expstate, // ? + XTENSA_hwwdtlba, // ? + XTENSA_hwwitlba, // ? + XTENSA_ldpte, // ? + XTENSA_read_impwire, // ? + XTENSA_setb_expstate, // ? + XTENSA_wrmsk_expstate,// ? + + // These seem to be part of the AE (Audio Extension?) instruction set. + // Looking at output of "AE" objdump bruteforce there are about 120 AE instructions. + // I didn't find public docs on the AE instructions so won't try to guess them all. + XTENSA_ae2_rur, // Read User Register (AE2 version) + XTENSA_ae2_wur, // Write User Register (AE2 version) + XTENSA_ae_rur, // Read User Register (AE version) + XTENSA_ae_wur, // Write User Register (AE version) + + // These seem to be part of the DSP instruction set. + // Looking at output of DSP objdump bruteforce there are about 180 DSP instructions. + // I didn't find public docs on the DSP instructions so won't try to guess them all. +// XTENSA_lac_il, // ? +// XTENSA_sac2x32, // ? +// XTENSA_sac32_r, // ? + + XTENSA_last +}; + + +``` + +`IdaSDK/allow_deprecated.hpp`: + +```hpp +// this file should be included before calling deprecated functions +// it should be included at the point where the definitions of deprecated +// functions begin in the source file. this way a deprecated function may call +// another deprecated function without raising a warning. + +// deprecated functions may call each other +#ifdef _MSC_VER +#pragma warning(disable:4996) +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + +``` + +`IdaSDK/auto.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _AUTO_HPP +#define _AUTO_HPP +#include + +/*! \file auto.hpp + + \brief Functions that work with the autoanalyzer queue. + + The autoanalyzer works when IDA is not busy processing + the user keystrokes. It has several queues, each queue having + its own priority. The analyzer stops when all queues are empty. + + A queue contains addresses or address ranges. + The addresses are kept sorted by their values. + The analyzer will process all addresses from the first queue, then + switch to the second queue and so on. + There are no limitations on the size of the queues. + + This file also contains functions that deal with the IDA status + indicator and the autoanalysis indicator. + You may use these functions to change the indicator value. +*/ + +typedef int atype_t; ///< identifies an autoanalysis queue - see \ref AU_ + +/// \defgroup AU_ Autoanalysis queues +/// Names and priorities of the analyzer queues +///@{ +const atype_t + AU_NONE = 00, ///< placeholder, not used + AU_UNK = 10, ///< 0: convert to unexplored + AU_CODE = 20, ///< 1: convert to instruction + AU_WEAK = 25, ///< 2: convert to instruction (ida decision) + AU_PROC = 30, ///< 3: convert to procedure start + AU_TAIL = 35, ///< 4: add a procedure tail + AU_FCHUNK=38, ///< 5: find func chunks + AU_USED = 40, ///< 6: reanalyze + AU_USD2 = 45, ///< 7: reanalyze, second pass + AU_TYPE = 50, ///< 8: apply type information + AU_LIBF = 60, ///< 9: apply signature to address + AU_LBF2 = 70, ///< 10: the same, second pass + AU_LBF3 = 80, ///< 11: the same, third pass + AU_CHLB = 90, ///< 12: load signature file (file name is kept separately) + AU_FINAL=200; ///< 13: final pass +///@} + + +typedef int idastate_t; ///< IDA status indicator - see \ref st_ + +/// \defgroup st_ Status indicator states +///@{ +const idastate_t + // meaning + st_Ready = 0, ///< READY: IDA is doing nothing + st_Think = 1, ///< THINKING: Autoanalysis on, the user may press keys + st_Waiting = 2, ///< WAITING: Waiting for the user input + st_Work = 3; ///< BUSY: IDA is busy +///@} + + +/// Get current state of autoanalyzer. +/// If auto_state == ::AU_NONE, IDA is currently not running the analysis +/// (it could be temporarily interrupted to perform the user's requests, for example). + +idaman atype_t ida_export get_auto_state(void); + + +/// Set current state of autoanalyzer. +/// \param new_state new state of autoanalyzer +/// \return previous state + +idaman atype_t ida_export set_auto_state(atype_t new_state); + + +/// See ::get_auto_display +struct auto_display_t +{ + atype_t type = AU_NONE; + ea_t ea = BADADDR; + idastate_t state = st_Ready; +}; + +/// Get structure which holds the autoanalysis indicator contents + +idaman bool ida_export get_auto_display(auto_display_t *auto_display); + + +/// Change autoanalysis indicator value. +/// \param ea linear address being analyzed +/// \param type autoanalysis type (see \ref AU_) + +idaman void ida_export show_auto(ea_t ea, atype_t type=AU_NONE); + + +/// Show an address on the autoanalysis indicator. +/// The address is displayed in the form " @:12345678". +/// \param ea - linear address to display + +inline void show_addr(ea_t ea) { show_auto(ea); } + + +/// Change IDA status indicator value +/// \param st - new indicator status +/// \return old indicator status + +idaman idastate_t ida_export set_ida_state(idastate_t st); + + +/// Is it allowed to create stack variables automatically?. +/// This function should be used by IDP modules before creating stack vars. + +inline bool may_create_stkvars(void) +{ + return inf_should_create_stkvars() && get_auto_state() == AU_USED; +} + + +/// Is it allowed to trace stack pointer automatically?. +/// This function should be used by IDP modules before tracing sp. + +inline bool may_trace_sp(void) +{ + if ( inf_should_trace_sp() ) + { + atype_t auto_state = get_auto_state(); + return auto_state == AU_USED; + } + return false; +} + + +/// Put range of addresses into a queue. +/// 'start' may be higher than 'end', the kernel will swap them in this case. +/// 'end' doesn't belong to the range. + +idaman void ida_export auto_mark_range(ea_t start, ea_t end, atype_t type); + + +/// Put single address into a queue. Queues keep addresses sorted. + +inline void auto_mark(ea_t ea, atype_t type) +{ + auto_mark_range(ea, ea+1, type); +} + + +/// Remove range of addresses from a queue. +/// 'start' may be higher than 'end', the kernel will swap them in this case. +/// 'end' doesn't belong to the range. + +idaman void ida_export auto_unmark(ea_t start, ea_t end, atype_t type); + +// Convenience functions + +/// Plan to perform reanalysis +inline void plan_ea(ea_t ea) +{ + auto_mark(ea, AU_USED); +} +/// Plan to perform reanalysis +inline void plan_range(ea_t sEA, ea_t eEA) +{ + auto_mark_range(sEA, eEA, AU_USED); +} +/// Plan to make code +inline void auto_make_code(ea_t ea) +{ + auto_mark(ea, AU_CODE); +} +/// Plan to make code&function +inline void auto_make_proc(ea_t ea) +{ + auto_make_code(ea); + auto_mark(ea, AU_PROC); +} +/// Plan to reanalyze on the second pass +/// The typical usage of this function in emu.cpp is: +/// if ( !auto_postpone_analysis(ea) ) +/// op_offset(ea, 0, ...); +/// (we make an offset only on the second pass) +inline bool auto_postpone_analysis(ea_t ea) +{ + atype_t state = get_auto_state(); + if ( state == AU_USD2 ) + return false; + if ( state == AU_USED ) + auto_mark(ea, AU_USD2); + return true; +} + +/// Plan to reanalyze callers of the specified address. +/// This function will add to ::AU_USED queue all instructions that +/// call (not jump to) the specified address. +/// \param ea linear address of callee +/// \param noret !=0: the callee doesn't return, mark to undefine subsequent +/// instructions in the caller. 0: do nothing. + +idaman void ida_export reanalyze_callers(ea_t ea, bool noret); + + +/// Delete all analysis info that IDA generated for for the given range + +idaman void ida_export revert_ida_decisions(ea_t ea1, ea_t ea2); + + +/// Plan to apply the callee's type to the calling point + +idaman void ida_export auto_apply_type(ea_t caller, ea_t callee); + +/// Plan to apply the tail_ea chunk to the parent +/// \param tail_ea linear address of start of tail +/// \param parent_ea linear address within parent. If BADADDR, automatically +/// try to find parent via xrefs. + +idaman void ida_export auto_apply_tail(ea_t tail_ea, ea_t parent_ea); + +/// Analyze the specified range. +/// Try to create instructions where possible. +/// Make the final pass over the specified range if specified. +/// This function doesn't return until the range is analyzed. +/// \retval 1 ok +/// \retval 0 Ctrl-Break was pressed + +idaman int ida_export plan_and_wait(ea_t ea1, ea_t ea2, bool final_pass=true); + + +/// Process everything in the queues and return true. +/// \return false if the user clicked cancel. +/// (the wait box must be displayed by the caller if desired) + +idaman bool ida_export auto_wait(void); + + +/// Process everything in the specified range and return true. +/// \return number of autoanalysis steps made. -1 if the user clicked cancel. +/// (the wait box must be displayed by the caller if desired) + +idaman ssize_t ida_export auto_wait_range(ea_t ea1, ea_t ea2); + + +/// Analyze one address in the specified range and return true. +/// \return if processed anything. false means that there is nothing to +/// process in the specified range. + +idaman bool ida_export auto_make_step(ea_t ea1, ea_t ea2); + + +/// Remove an address range (ea1..ea2) from queues ::AU_CODE, ::AU_PROC, ::AU_USED. +/// To remove an address range from other queues use auto_unmark() function. +/// 'ea1' may be higher than 'ea2', the kernel will swap them in this case. +/// 'ea2' doesn't belong to the range. + +idaman void ida_export auto_cancel(ea_t ea1, ea_t ea2); + + +/// Are all queues empty? +/// (i.e. has autoanalysis finished?). + +idaman bool ida_export auto_is_ok(void); + + +/// Peek into a queue 'type' for an address not lower than 'low_ea'. +/// Do not remove address from the queue. +/// \return the address or #BADADDR + +idaman ea_t ida_export peek_auto_queue(ea_t low_ea, atype_t type); + + +/// Retrieve an address from queues regarding their priority. +/// Returns #BADADDR if no addresses not lower than 'lowEA' and less than +/// 'highEA' are found in the queues. +/// Otherwise *type will have queue type. + +idaman ea_t ida_export auto_get(atype_t *type, ea_t lowEA, ea_t highEA); + + +/// Try to create instruction +/// \param ea linear address of callee +/// \return the length of the instruction or 0 + +idaman int ida_export auto_recreate_insn(ea_t ea); + + +/// Get autoanalyzer state + +idaman bool ida_export is_auto_enabled(void); + + +/// Temporarily enable/disable autoanalyzer. Not user-facing, but rather because +/// IDA sometimes need to turn AA on/off regardless of inf.s_genflags:INFFL_AUTO +/// \return old state + +idaman bool ida_export enable_auto(bool enable); + + + +#endif // _AUTO_HPP + +``` + +`IdaSDK/bitrange.hpp`: + +```hpp +#ifndef _BITMASK_HPP +#define _BITMASK_HPP + +/*! \file bitrange.hpp + + \brief Definition of the ::bitrange_t class + +*/ + +//--------------------------------------------------------------------------- +/// This class manages the offset and size of a value that occupies +/// a number of contiguous bits within some container - generally a byte array. +/// A special state - empty range (both offset and size are zeroes) - determines +/// the value as all bits of the container. +class bitrange_t +{ +public: + /// Constructor + explicit bitrange_t(uint16 bit_ofs = 0, uint16 size_in_bits = 0); + /// Initialize offset and size to given values + inline void init(uint16 bit_ofs, uint16 size_in_bits); + + /// Make the bitrange empty + inline void reset(); + /// Is the bitrange empty? + inline bool empty() const; + + /// Get offset of 1st bit + inline uint bitoff() const; + /// Get size of the value in bits + inline uint bitsize() const; + /// Size of the value in bytes + inline uint bytesize() const; + /// Convert to mask of 64 bits + inline uint64 mask64() const; + + /// Does have common bits with another bitrange? + inline bool has_common(const bitrange_t &r) const; + + /// Apply mask to a bitrange + /// \param subrange range *inside* the main bitrange to keep + /// After this operation the main bitrange will be truncated + /// to have only the bits that are specified by subrange. + /// Example: [off=8,nbits=4], subrange[off=1,nbits=2] => [off=9,nbits=2] + /// \return success + inline bool apply_mask(const bitrange_t &subrange); + + /// Intersect two ranges + inline void intersect(const bitrange_t &r); + + /// Create union of 2 ranges including the hole between them + inline void create_union(const bitrange_t &r); + + /// Subtract a bitrange + inline bool sub(const bitrange_t &r); + + /// Shift range down (left) + inline void shift_down(uint cnt); + /// Shift range up (right) + inline void shift_up(uint cnt); + + /// Initialize bitrange_t with offset/size defined by given mask + template inline void assign_max_nonzero(T mask); + + /// \name Extract + /// Extract a value from 'src' according to the bitrange + /// \param dst vector the extracted value will be stored to + /// \param src source buffer + /// \param src_size size of source buffer + /// \param is_mf is Msb First? (TRUE-big endian, FALSE-little endian) + ///@{ + inline bool extract( + bytevec_t *dst, + const void *src, + size_t src_size, + bool is_mf) const; + inline bool extract( + void *dst, + size_t dst_size, + const void *src, + size_t src_size, + bool is_mf) const; + ///@} + + /// \name Inject + /// Inject a value into 'dst' according to the bitrange + /// \param dst a buffer the value will be injected into + /// \param dst_size size of buffer + /// \param src source value + /// \param is_mf is Msb First? (TRUE-big endian, FALSE-little endian) + ///@{ + inline bool inject( + void *dst, + size_t dst_size, + const void *src, + size_t src_size, + bool is_mf) const; + inline bool inject( + void *dst, + size_t dst_size, + const bytevec_t &src, + bool is_mf) const; + ///@} + + DECLARE_COMPARISONS(bitrange_t); + +private: + uint16 offset; ///< offset of 1st bit starting with the lowest bit + uint16 nbits; ///< size of the value in bits +}; + +//--------------------------------------------------------------------------- +inline bitrange_t::bitrange_t(uint16 bit_ofs, uint16 size_in_bits) + : offset(bit_ofs), nbits(size_in_bits) +{ +} + +//--------------------------------------------------------------------------- +inline void bitrange_t::init(uint16 bit_ofs, uint16 size_in_bits) +{ + offset = bit_ofs; + nbits = size_in_bits; +} + +//--------------------------------------------------------------------------- +inline void bitrange_t::reset() +{ + init(0, 0); +} + +//--------------------------------------------------------------------------- +inline bool bitrange_t::empty() const +{ + return nbits == 0; +} + +//--------------------------------------------------------------------------- +inline uint bitrange_t::bitoff() const +{ + return offset; +} + +//--------------------------------------------------------------------------- +inline uint bitrange_t::bitsize() const +{ + return nbits; +} + +//--------------------------------------------------------------------------- +inline uint bitrange_t::bytesize() const +{ + return (nbits + 8-1) / 8; +} + +//-------------------------------------------------------------------------- +inline uint64 bitrange_t::mask64() const +{ + return empty() ? 0 : (left_shift(uint64(1), nbits)-1) << offset; +} + +//-------------------------------------------------------------------------- +inline bool bitrange_t::apply_mask(const bitrange_t &submask) +{ + if ( submask.bitoff() + submask.bitsize() > bitsize() ) + return false; + init(bitoff() + submask.bitoff(), submask.bitsize()); + return true; +} + +//-------------------------------------------------------------------------- +inline void bitrange_t::intersect(const bitrange_t &r) +{ + uint16 e1 = offset + nbits; + uint16 e2 = r.offset + r.nbits; + uint16 e = qmin(e1, e2); + uint16 s = qmax(offset, r.offset); + if ( s > e ) + { + s = 0; + e = 0; + } + init(s, e-s); +} + +//-------------------------------------------------------------------------- +inline bool bitrange_t::has_common(const bitrange_t &r) const +{ + return interval::overlap(offset, nbits, r.offset, r.nbits); +} + +//-------------------------------------------------------------------------- +inline void bitrange_t::create_union(const bitrange_t &r) +{ + uint16 e1 = offset + nbits; + uint16 e2 = r.offset + r.nbits; + uint16 e = qmax(e1, e2); + uint16 s = qmin(offset, r.offset); + init(s, e-s); +} + +//-------------------------------------------------------------------------- +inline bool bitrange_t::sub(const bitrange_t &r) +{ + // r is in the middle of our bitrange, cannot handle this case + // because it would require 2 bitranges :/ + uint16 end = offset + nbits; + uint16 rend = r.offset + r.nbits; + if ( r.offset > offset && rend < end ) + return false; + if ( r.offset <= offset ) + { + if ( rend > end ) + { + reset(); + } + else if ( rend > offset ) + { + offset = rend; + nbits = end - offset; + } + } + else if ( r.offset < end ) + { + nbits = r.offset - offset; + } + return true; +} + +//-------------------------------------------------------------------------- +inline int bitrange_t::compare(const bitrange_t &r) const +{ + if ( offset != r.offset ) + return offset < r.offset ? -1 : 1; + if ( nbits != r.nbits ) + return nbits < r.nbits ? -1 : 1; + return 0; +} + +//-------------------------------------------------------------------------- +inline void bitrange_t::shift_down(uint cnt) +{ + if ( cnt > offset ) + { + cnt -= offset; + offset = 0; + if ( cnt > nbits ) + nbits = 0; + else + nbits -= cnt; + } + else + { + offset -= cnt; + } +} + +//-------------------------------------------------------------------------- +inline void bitrange_t::shift_up(uint cnt) +{ + offset += cnt; +} + +//-------------------------------------------------------------------------- +template inline void bitrange_t::assign_max_nonzero(T mask) +{ + if ( mask == T(0) ) + { + reset(); + return; + } + int i = 0; + T bit = T(1); + for ( i=0; i < sizeof(T)*8; ++i, bit <<= 1 ) + if ( (mask & bit) != 0 ) + break; + offset = i; + i = sizeof(T)*8 - 1; + bit = left_shift(T(1), i); + while ( i >= offset ) + { + if ( (mask & bit) != 0 ) + break; + --i; + bit >>= 1; + } + nbits = i - offset + 1; +} + +//-------------------------------------------------------------------------- +#ifndef SWIG +/// \name Helper functions +/// Should not be called directly! +///@{ +idaman bool ida_export bitrange_t_extract_using_bitrange(const bitrange_t *bm, void *dst, size_t dst_size, const void *src, size_t src_size, bool is_mf); +idaman bool ida_export bitrange_t_inject_using_bitrange(const bitrange_t *bm, void *dst, size_t dst_size, const void *src, size_t src_size, bool is_mf); +///@} +#else +#endif // SWIG + +//-------------------------------------------------------------------------- +inline bool bitrange_t::extract( + void *dst, + size_t dst_size, + const void *src, + size_t src_size, + bool is_mf) const +{ + return bitrange_t_extract_using_bitrange(this, dst, dst_size, src, src_size, is_mf); +} + +//-------------------------------------------------------------------------- +inline bool bitrange_t::extract( + bytevec_t *dst, + const void *src, + size_t src_size, + bool is_mf) const +{ + size_t dst_size = empty() ? src_size : bytesize(); + dst->resize(dst_size); + return bitrange_t_extract_using_bitrange(this, + dst->begin(), dst_size, + src, src_size, + is_mf); +} + +//-------------------------------------------------------------------------- +inline bool bitrange_t::inject( + void *dst, + size_t dst_size, + const void *src, + size_t src_size, + bool is_mf) const +{ + return bitrange_t_inject_using_bitrange(this, dst, dst_size, src, src_size, is_mf); +} + +//-------------------------------------------------------------------------- +inline bool bitrange_t::inject( + void *dst, + size_t dst_size, + const bytevec_t &src, + bool is_mf) const +{ + return bitrange_t_inject_using_bitrange(this, + dst, dst_size, + src.begin(), src.size(), + is_mf); +} + +#endif // define _BITMASK_HPP + +``` + +`IdaSDK/bytes.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef BYTES_HPP +#define BYTES_HPP + +#include +#include +#include + +class insn_t; + +/*! \file bytes.hpp + + \brief Contains functions that deal with individual byte characteristics. + + Each byte of the disassembled program is represented by a 32-bit + value. We will call this value 'flags'. The structure of the flags is + here. + + You are not allowed to inspect individual bits of flags and modify them directly. + Use special functions to inspect and/or modify flags. + + Flags are kept in a virtual array file (*.id1). + Addresses (ea) are all 32-bit (or 64-bit) quantities. +*/ + +//-------------------------------------------------------------------------- +/// Allocate flags for address range. +/// This function does not change the storage type of existing ranges. +/// Exit with an error message if not enough disk space. +/// \param start_ea should be lower than end_ea. +/// \param end_ea does not belong to the range. +/// \param stt ::storage_type_t +/// \return 0 if ok, otherwise an error code + +idaman error_t ida_export enable_flags(ea_t start_ea, ea_t end_ea, storage_type_t stt); + + +/// Deallocate flags for address range. +/// Exit with an error message if not enough disk space (this may occur too). +/// \param start_ea should be lower than end_ea. +/// \param end_ea does not belong to the range. +/// \return 0 if ok, otherwise return error code + +idaman error_t ida_export disable_flags(ea_t start_ea, ea_t end_ea); + + +/// Change flag storage type for address range. +/// \param start_ea should be lower than end_ea. +/// \param end_ea does not belong to the range. +/// \param stt ::storage_type_t +/// \return error code + +idaman error_t ida_export change_storage_type(ea_t start_ea, ea_t end_ea, storage_type_t stt); + + +/// Get next address in the program (i.e. next address which has flags). +/// \return #BADADDR if no such address exist. + +idaman ea_t ida_export next_addr(ea_t ea); + + +/// Get previous address in the program. +/// \return #BADADDR if no such address exist. + +idaman ea_t ida_export prev_addr(ea_t ea); + + +/// Get the first address of next contiguous chunk in the program. +/// \return #BADADDR if next chunk doesn't exist. + +idaman ea_t ida_export next_chunk(ea_t ea); + + +/// Get the last address of previous contiguous chunk in the program. +/// \return #BADADDR if previous chunk doesn't exist. + +idaman ea_t ida_export prev_chunk(ea_t ea); + + +/// Get start of the contiguous address block containing 'ea'. +/// \return #BADADDR if 'ea' doesn't belong to the program. + +idaman ea_t ida_export chunk_start(ea_t ea); + + +/// Get size of the contiguous address block containing 'ea'. +/// \return 0 if 'ea' doesn't belong to the program. + +idaman asize_t ida_export chunk_size(ea_t ea); + + +/// Search for a hole in the addressing space of the program. +/// \param start Address to start searching from +/// \param size Size of the desired empty range +/// \param alignment Alignment bitmask, must be a pow2-1. +/// (for example, 0xF would align the returned range to 16 bytes). +/// \return Start of the found empty range or #BADADDR + +idaman ea_t ida_export find_free_chunk(ea_t start, asize_t size, asize_t alignment); + + +/// Flag tester - see next_that(), prev_that() +typedef bool idaapi testf_t(flags64_t flags, void *ud); + + +/// Find next address with a flag satisfying the function 'testf'. +/// \note do not pass is_unknown() to this function to find unexplored bytes. +/// It will fail under the debugger. To find unexplored bytes, use next_unknown(). +/// \param ea start searching at this address + 1 +/// \param maxea not included in the search range. +/// \param testf test function to find next address +/// \param ud user data - may point to anything. it will be passed to testf. +/// \return the found address or #BADADDR. + +idaman ea_t ida_export next_that( + ea_t ea, + ea_t maxea, + testf_t *testf, + void *ud=nullptr); + + +/// Similar to next_that(), but will find the next address that is unexplored + +inline ea_t idaapi next_unknown(ea_t ea, ea_t maxea) +{ + return next_that(ea, maxea, nullptr); +} + + +/// Find previous address with a flag satisfying the function 'testf'. +/// \note do not pass is_unknown() to this function to find unexplored bytes +/// It will fail under the debugger. To find unexplored bytes, use prev_unknown(). +/// \param ea start searching from this address - 1. +/// \param minea included in the search range. +/// \param testf test function to find previous address +/// \param ud user data - may point to anything. it will be passed to testf. +/// \return the found address or #BADADDR. + +idaman ea_t ida_export prev_that( + ea_t ea, + ea_t minea, + testf_t *testf, + void *ud=nullptr); + + +/// Similar to prev_that(), but will find the previous address that is unexplored + +inline ea_t idaapi prev_unknown(ea_t ea, ea_t minea) +{ + return prev_that(ea, minea, nullptr); +} + + +/// Get start of previous defined item. +/// \param ea begin search at this address +/// \param minea included in the search range +/// \return #BADADDR if none exists. + +idaman ea_t ida_export prev_head(ea_t ea, ea_t minea); + + +/// Get start of next defined item. +/// \param ea begin search at this address +/// \param maxea not included in the search range +/// \return #BADADDR if none exists. + +idaman ea_t ida_export next_head(ea_t ea, ea_t maxea); + + +/// Get address of previous non-tail byte. +/// \return #BADADDR if none exists. + +idaman ea_t ida_export prev_not_tail(ea_t ea); + + +/// Get address of next non-tail byte. +/// \return #BADADDR if none exists. + +idaman ea_t ida_export next_not_tail(ea_t ea); + + +/// Adjust the address and get the nearest visible address. +/// (i.e. an address which will appear in the disassembly) +/// \return #BADADDR only if no addresses are valid + +ea_t adjust_visea(ea_t ea); + + +/// Get previous visible address. +/// \return #BADADDR if none exists. + +idaman ea_t ida_export prev_visea(ea_t ea); + + +/// Get next visible address. +/// \return #BADADDR if none exists. + +idaman ea_t ida_export next_visea(ea_t ea); + + +/// Is an address the first visible address? + +bool is_first_visea(ea_t ea); + + +/// Is an address the last visible address? + +bool is_last_visea(ea_t ea); + + +/// Is the address visible on the screen (not hidden)? + +bool is_visible_finally(ea_t ea); // do we need to show anything + // at this address? + + + +/// Get the start address of the item at 'ea'. +/// If there is no current item, then 'ea' will be returned +/// (see definition at the end of bytes.hpp source) + +inline ea_t idaapi get_item_head(ea_t ea); + + +/// Get the end address of the item at 'ea'. The returned address +/// doesn't belong to the current item. Unexplored bytes are counted as +/// 1 byte entities. + +idaman ea_t ida_export get_item_end(ea_t ea); + + +/// Calculate maximal reasonable end address of a new item. +/// This function will limit the item with the current segment bounds. +/// \param ea linear address +/// \param how when to stop the search. A combination of \ref ITEM_END_ +/// \return end of new item. If it is not possible to create an item, +/// it will return 'ea'. If operation was cancelled by user, +/// it will return 'ea' + +idaman ea_t ida_export calc_max_item_end(ea_t ea, int how=15); +/// \defgroup ITEM_END_ Item end search flags +/// passed as 'how' parameter to calc_max_item_end() +///@{ +#define ITEM_END_FIXUP 0x0001 ///< stop at the first fixup +#define ITEM_END_INITED 0x0002 ///< stop when initialization changes + ///< i.e. + ///< - if is_loaded(ea): stop if uninitialized byte is encountered + ///< - if !is_loaded(ea): stop if initialized byte is encountered +#define ITEM_END_NAME 0x0004 ///< stop at the first named location +#define ITEM_END_XREF 0x0008 ///< stop at the first referenced location +#define ITEM_END_CANCEL 0x0010 ///< stop when operation cancelled, + ///< it is the responsibility of the caller to show the wait dialog +///@} + + +/// Get size of item (instruction/data) in bytes. +/// Unexplored bytes have length of 1 byte. This function returns 0 only for +/// BADADDR. + +inline asize_t get_item_size(ea_t ea) { return get_item_end(ea) - ea; } + + + + +/// Is the specified address 'ea' present in the program? + +idaman bool ida_export is_mapped(ea_t ea); + + +/// Get flags for the specified address, extended form + +idaman flags64_t ida_export get_flags_ex(ea_t ea, int how); + +#define GFE_VALUE 0x0001 ///< get flags with #FF_IVL & #MS_VAL. + ///< It is much slower under remote debugging + ///< because the kernel needs to read + ///< the process memory. +#define GFE_IDB_VALUE 0x0002 ///< get flags with #FF_IVL & #MS_VAL. + ///< but never use the debugger memory. +#define GFE_32BIT 0x0004 ///< get only low 32 bits of flags + +/// Get only 32 low bits of flags. +/// This function returns the most commonly used bits of the flags. +/// However, it does not return the operand info for the operands beyond the +/// first two operands (0,1). If you need to deal with the operands (2..n), +/// then use get_flags(). +/// It is customary to assign the return value to the variable named "F32", +/// to distinguish is from 64-bit flags. +/// \return 0 if address is not present in the program +inline flags64_t idaapi get_flags32(ea_t ea) { return get_flags_ex(ea, GFE_32BIT); } + +/// Get flags value for address 'ea'. +/// The byte value is not included in the flags. +/// This function should be used if the operand types of any operand beyond +/// the first two operands is required. +/// This function is more expensive to use than get_flags32() +/// \return 0 if address is not present in the program +inline flags64_t idaapi get_flags(ea_t ea) { return get_flags_ex(ea, 0); } + +/// Get full flags value for address 'ea'. +/// This function returns the byte value in the flags as well. +/// See FF_IVL and MS_VAL. +/// This function is more expensive to use than get_flags() +/// \return 0 if address is not present in the program +inline flags64_t idaapi get_full_flags(ea_t ea) { return get_flags_ex(ea, GFE_VALUE); } + + +/// Get flag of the item at 'ea' even if it is a tail byte of some +/// array or structure. This function is used to get flags of structure members +/// or array elements. +/// \param from linear address of the instruction which refers to 'ea' +/// \param n operand number which refers to 'ea' +/// or OPND_ALL for one of the operands +/// \param ea the referenced address +/// \param appzero append a struct field name if the field offset is zero? +/// meaningful only if the name refers to a structure. +/// \return flags or 0 (if failed) + +idaman flags64_t ida_export get_item_flag(ea_t from, int n, ea_t ea, bool appzero); + + +//-------------------------------------------------------------------------- +/// Get refinfo of the item at 'ea'. +/// This function works for a regular offset operand as well as for a tail byte +/// of a structure variable (in this case refinfo to corresponding structure +/// member will be returned) +/// \param[out] ri refinfo holder +/// \param ea the item address +/// \param n operand number which refers to 'ea' +/// or OPND_ALL for one of the operands +/// \return success +idaman bool ida_export get_item_refinfo(refinfo_t *ri, ea_t ea, int n); + +//-------------------------------------------------------------------------- +/// \defgroup FF_ Flags structure +/// Here we define the organization of ::flags64_t values. +/// Low 8 bits contain value of corresponding byte of the program. +/// The next bit is set if the byte is initialized. +///@{ +#define MS_VAL 0x000000FF ///< Mask for byte value +#define FF_IVL 0x00000100 ///< Byte has value ? +///@} + +/// Do flags contain byte value? + +inline THREAD_SAFE constexpr bool idaapi has_value(flags64_t F) { return (F & FF_IVL) != 0; } + + +/// Delete byte value from flags. The corresponding byte becomes +/// uninitialized. + +idaman void ida_export del_value(ea_t ea); + + +/// Does the specified address have a byte value (is initialized?) + +idaman bool ida_export is_loaded(ea_t ea); + + +/// Get number of bits in a byte at the given address. +/// \return \ph{dnbits()} if the address doesn't +/// belong to a segment, otherwise the result depends on the +/// segment type + +idaman int ida_export nbits(ea_t ea); + + +/// Get number of bytes required to store a byte at the given address + +inline int bytesize(ea_t ea) + { return (nbits(ea)+7)/8; } + + +/// Get one byte (8-bit) of the program at 'ea'. +/// This function works only for 8bit byte processors. + +idaman uchar ida_export get_byte(ea_t ea); + + +/// Get one byte (8-bit) of the program at 'ea' from the database. +/// Works even if the debugger is active. +/// See also get_dbg_byte() to read the process memory directly. +/// This function works only for 8bit byte processors. + +idaman uchar ida_export get_db_byte(ea_t ea); + + +/// Get one word (16-bit) of the program at 'ea'. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// This function works only for 8bit byte processors. + +idaman ushort ida_export get_word(ea_t ea); + + +/// Get one dword (32-bit) of the program at 'ea'. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// This function works only for 8bit byte processors. + +idaman uint32 ida_export get_dword(ea_t ea); + + +/// Get one qword (64-bit) of the program at 'ea'. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// This function works only for 8bit byte processors. + +idaman uint64 ida_export get_qword(ea_t ea); + + +/// Get one wide byte of the program at 'ea'. +/// Some processors may access more than 8bit quantity at an address. +/// These processors have 32-bit byte organization from the IDA's point of view. + +idaman uint64 ida_export get_wide_byte(ea_t ea); + + +/// Get one wide word (2 'byte') of the program at 'ea'. +/// Some processors may access more than 8bit quantity at an address. +/// These processors have 32-bit byte organization from the IDA's point of view. +/// This function takes into account order of bytes specified in \inf{is_be()} + +idaman uint64 ida_export get_wide_word(ea_t ea); + + +/// Get two wide words (4 'bytes') of the program at 'ea'. +/// Some processors may access more than 8bit quantity at an address. +/// These processors have 32-bit byte organization from the IDA's point of view. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// \note this function works incorrectly if \ph{nbits} > 16 + +idaman uint64 ida_export get_wide_dword(ea_t ea); + + +/// Get 8 bits of the program at 'ea'. +/// The main usage of this function is to iterate range of bytes. +/// Here is an example: +/// \code +/// octet_generator_t ogen(ea); +/// for ( ... ) +/// { +/// if ( !get_octet(&byte, &ogen) ) +/// failed to read an octet +/// else +/// success +/// } +/// \endcode + +struct octet_generator_t +{ + uint64 value = 0; + ea_t ea; + int avail_bits = 0; + bool high_byte_first = inf_is_wide_high_byte_first(); + octet_generator_t(ea_t _ea) : ea(_ea) {} + void invert_byte_order() { high_byte_first = !high_byte_first; } +#ifndef SWIG + DECLARE_COMPARISONS(octet_generator_t) + { + COMPARE_FIELDS(ea); + if ( avail_bits < r.avail_bits ) + return 1; // less bits available means that we have advanced more + if ( avail_bits > r.avail_bits ) + return -1; // more bits available means that we have advanced less + return 0; + } +#endif +}; + +idaman bool ida_export get_octet(uchar *out, octet_generator_t *ogen); + + + +/// Get 16bits of the program at 'ea'. +/// \return 1 byte (getFullByte()) if the current processor has 16-bit byte, +/// otherwise return get_word() + +idaman uint32 ida_export get_16bit(ea_t ea); + + +/// Get not more than 32bits of the program at 'ea'. +/// \return 32 bit value, depending on \ph{nbits}: +/// - if ( nbits <= 8 ) return get_dword(ea); +/// - if ( nbits <= 16) return get_wide_word(ea); +/// - return get_wide_byte(ea); + +idaman uint32 ida_export get_32bit(ea_t ea); + + +/// Get not more than 64bits of the program at 'ea'. +/// \return 64 bit value, depending on \ph{nbits}: +/// - if ( nbits <= 8 ) return get_qword(ea); +/// - if ( nbits <= 16) return get_wide_dword(ea); +/// - return get_wide_byte(ea); + +idaman uint64 ida_export get_64bit(ea_t ea); + + +/// Get the value at of the item at 'ea'. +/// This function works with entities up to sizeof(ea_t) +/// (bytes, word, etc) +/// \param v pointer to the result. may be nullptr +/// \param ea linear address +/// \param size size of data to read. If 0, then the item +/// type at 'ea' will be used +/// \return success + +idaman bool ida_export get_data_value(uval_t *v, ea_t ea, asize_t size); + + +/// Visit all the patched bytes one byte at a time. +/// \param ea1 start linear address +/// \param ea2 end linear address +/// \param cb callback called for each found byte. +/// if the callback returns non-zero then that value will be +/// returned to the caller and the enumeration will be interrupted. +/// \param ud user data passed to the callback +/// \return the return value returned by the callback (if any) or zero +/// if the enumeration was completed. + +idaman int ida_export visit_patched_bytes( + ea_t ea1, + ea_t ea2, + int (idaapi *cb)(ea_t ea, qoff64_t fpos, uint64 o, uint64 v, void *ud), + void *ud = nullptr); + + +/// Get original byte value (that was before patching). +/// This function works for wide byte processors too. + +idaman uint64 ida_export get_original_byte(ea_t ea); + + +/// Get original word value (that was before patching). +/// This function works for wide byte processors too. +/// This function takes into account order of bytes specified in \inf{is_be()} + +idaman uint64 ida_export get_original_word(ea_t ea); + + +/// Get original dword (that was before patching) +/// This function works for wide byte processors too. +/// This function takes into account order of bytes specified in \inf{is_be()} + +idaman uint64 ida_export get_original_dword(ea_t ea); + + +/// Get original qword value (that was before patching) +/// This function DOESN'T work for wide byte processors too. +/// This function takes into account order of bytes specified in \inf{is_be()} + +idaman uint64 ida_export get_original_qword(ea_t ea); + + +/// Set value of one byte of the program. +/// This function modifies the database. If the debugger is active +/// then the debugged process memory is patched too. +/// \note The original value of the byte is completely lost and can't +/// be recovered by the get_original_byte() function. +/// See also put_dbg_byte() to write to the process memory directly when +/// the debugger is active. +/// This function can handle wide byte processors. +/// \param ea linear address +/// \param x byte value +/// \return true if the database has been modified + +idaman bool ida_export put_byte(ea_t ea, uint64 x); + + +/// Set value of one word of the program. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// This function works for wide byte processors too. +/// \note The original value of the word is completely lost and can't +/// be recovered by the get_original_word() function. +/// ea - linear address +/// x - word value + +idaman void ida_export put_word(ea_t ea, uint64 x); + + +/// Set value of one dword of the program. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// This function works for wide byte processors too. +/// \param ea linear address +/// \param x dword value +/// \note the original value of the dword is completely lost and can't +/// be recovered by the get_original_dword() function. + +idaman void ida_export put_dword(ea_t ea, uint64 x); + + +/// Set value of one qword (8 bytes) of the program. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// This function DOESN'T works for wide byte processors. +/// \param ea linear address +/// \param x qword value + +idaman void ida_export put_qword(ea_t ea, uint64 x); + + +/// Patch a byte of the program. The original value of the byte is saved +/// and can be obtained by get_original_byte(). +/// This function works for wide byte processors too. +/// \retval true the database has been modified, +/// \retval false the debugger is running and the process' memory +/// has value 'x' at address 'ea', or +/// the debugger is not running, and the IDB +/// has value 'x' at address 'ea already. + +idaman bool ida_export patch_byte(ea_t ea, uint64 x); + + +/// Patch a word of the program. The original value of the word is saved +/// and can be obtained by get_original_word(). +/// This function works for wide byte processors too. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// \retval true the database has been modified, +/// \retval false the debugger is running and the process' memory +/// has value 'x' at address 'ea', or +/// the debugger is not running, and the IDB +/// has value 'x' at address 'ea already. + +idaman bool ida_export patch_word(ea_t ea, uint64 x); + + +/// Patch a dword of the program. The original value of the dword is saved +/// and can be obtained by get_original_dword(). +/// This function DOESN'T work for wide byte processors. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// \retval true the database has been modified, +/// \retval false the debugger is running and the process' memory +/// has value 'x' at address 'ea', or +/// the debugger is not running, and the IDB +/// has value 'x' at address 'ea already. + +idaman bool ida_export patch_dword(ea_t ea, uint64 x); + + +/// Patch a qword of the program. The original value of the qword is saved +/// and can be obtained by get_original_qword(). +/// This function DOESN'T work for wide byte processors. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// \retval true the database has been modified, +/// \retval false the debugger is running and the process' memory +/// has value 'x' at address 'ea', or +/// the debugger is not running, and the IDB +/// has value 'x' at address 'ea already. + +idaman bool ida_export patch_qword(ea_t ea, uint64 x); + + +/// Revert patched byte +/// \retval true byte was patched before and reverted now + +idaman bool ida_export revert_byte(ea_t ea); + + +/// Add a value to one byte of the program. +/// This function works for wide byte processors too. +/// \param ea linear address +/// \param value byte value + +idaman void ida_export add_byte(ea_t ea, uint32 value); + + +/// Add a value to one word of the program. +/// This function works for wide byte processors too. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// \param ea linear address +/// \param value byte value + +idaman void ida_export add_word(ea_t ea, uint64 value); + + +/// Add a value to one dword of the program. +/// This function works for wide byte processors too. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// \note this function works incorrectly if \ph{nbits} > 16 +/// \param ea linear address +/// \param value byte value + +idaman void ida_export add_dword(ea_t ea, uint64 value); + + +/// Add a value to one qword of the program. +/// This function does not work for wide byte processors. +/// This function takes into account order of bytes specified in \inf{is_be()} +/// \param ea linear address +/// \param value byte value + +idaman void ida_export add_qword(ea_t ea, uint64 value); + + +/// Return set of ranges with zero initialized bytes. +/// The returned set includes only big zero initialized ranges (at least >1KB). +/// Some zero initialized byte ranges may be not included. +/// Only zero bytes that use the sparse storage method (STT_MM) are reported. +/// \param zranges pointer to the return value. cannot be nullptr +/// \param range the range of addresses to verify. can be nullptr - means all ranges +/// \return true if the result is a non-empty set + +idaman bool ida_export get_zero_ranges(rangeset_t *zranges, const range_t *range); + + +/// Get the specified number of bytes of the program into the buffer. +/// If mask was specified it will contain a bitmap of initialized / uninitialized +/// database bytes. +/// \param ea linear address +/// \param buf buffer to hold bytes +/// \param size size of buffer in normal 8-bit bytes (sizeof(buf)) +/// \param gmb_flags combination of \ref GMB_ bits +/// \param mask bitmap of initialize/uninitialized bytes +/// (may be nullptr; must be at least (size+7)/8) +/// \return if the user cancelled, return -1; otherwise number of read bytes. + +idaman ssize_t ida_export get_bytes( + void *buf, + ssize_t size, + ea_t ea, + int gmb_flags=0, + void *mask=nullptr); + +/// \defgroup GMB_ flags for get_bytes() +///@{ +#define GMB_READALL 0x01 ///< try to read all bytes; + ///< if this bit is not set, fail at first uninited byte +#define GMB_WAITBOX 0x02 ///< show wait box (may return -1 in this case) +///@} + + +/// Modify the specified number of bytes of the program. +/// This function does not save the original values of bytes. +/// See also patch_bytes(). +/// \param ea linear address +/// \param buf buffer with new values of bytes +/// \param size size of buffer in normal 8-bit bytes (sizeof(buf)) + +idaman void ida_export put_bytes(ea_t ea, const void *buf, size_t size); + + +/// Patch the specified number of bytes of the program. +/// Original values of bytes are saved and are available with get_original...() +/// functions. +/// See also put_bytes(). +/// \param ea linear address +/// \param buf buffer with new values of bytes +/// \param size size of buffer in normal 8-bit bytes (sizeof(buf)) + +idaman void ida_export patch_bytes(ea_t ea, const void *buf, size_t size); + +//------------------------------------------------------------------------- +/// \defgroup FF_states States +/// \ingroup FF_ +/// Represent general characteristics of a byte in the program. +/// +/// Each byte of the program may be in one of four states. +/// - unexplored +/// - start of instruction +/// - start of data +/// - second, third (tail) byte of instruction or data. +/// +/// Initially, all bytes of the program are unexplored. +/// IDA modifies flags and doing so converts bytes to instructions +/// and data. +///@{ + +/// \defgroup FF_statebits Bits: byte states +///@{ +#define MS_CLS 0x00000600 ///< Mask for typing +#define FF_CODE 0x00000600 ///< Code ? +#define FF_DATA 0x00000400 ///< Data ? +#define FF_TAIL 0x00000200 ///< Tail ? +#define FF_UNK 0x00000000 ///< Unknown ? +///@} + +/// \defgroup FF_statefuncs Functions: examine byte states +///@{ + +/// Does flag denote start of an instruction? + +inline THREAD_SAFE constexpr bool idaapi is_code(flags64_t F) { return (F & MS_CLS) == FF_CODE; } +inline THREAD_SAFE constexpr bool idaapi f_is_code(flags64_t F, void *) { return is_code(F); } ///< \copydoc is_code() + + +/// Does flag denote start of data? + +inline THREAD_SAFE constexpr bool idaapi is_data(flags64_t F) { return (F & MS_CLS) == FF_DATA; } +inline THREAD_SAFE constexpr bool idaapi f_is_data(flags64_t F, void *) { return is_data(F); } ///< \copydoc is_data() + + +/// Does flag denote tail byte? + +inline THREAD_SAFE constexpr bool idaapi is_tail(flags64_t F) { return (F & MS_CLS) == FF_TAIL; } +inline THREAD_SAFE constexpr bool idaapi f_is_tail(flags64_t F, void *) { return is_tail(F); } ///< \copydoc is_tail() +inline THREAD_SAFE constexpr bool idaapi is_not_tail(flags64_t F) { return !is_tail(F); } ///< \copydoc is_tail() +inline THREAD_SAFE constexpr bool idaapi f_is_not_tail(flags64_t F, void *) { return is_not_tail(F); } ///< \copydoc is_tail() + + +/// Does flag denote unexplored byte? + +inline THREAD_SAFE constexpr bool idaapi is_unknown(flags64_t F) { return (F & MS_CLS) == FF_UNK; } + + +/// Does flag denote start of instruction OR data? + +inline THREAD_SAFE constexpr bool idaapi is_head(flags64_t F) { return (F & FF_DATA) != 0; } +inline THREAD_SAFE constexpr bool idaapi f_is_head(flags64_t F, void *) { return is_head(F); } ///< \copydoc is_head() + +///@} FF_statefuncs +///@} FF_states + +/// del_items' callback function +typedef bool idaapi may_destroy_cb_t(ea_t); + +/// Convert item (instruction/data) to unexplored bytes. +/// The whole item (including the head and tail bytes) will be destroyed. +/// It is allowed to pass any address in the item to this function +/// \param ea any address within the first item to delete +/// \param flags combination of \ref DELIT_ +/// \param nbytes number of bytes in the range to be undefined +/// \param may_destroy optional routine invoked before deleting a head +/// item. If callback returns false then item is not to +/// be deleted and operation fails +/// \return true on sucessful operation, otherwise false + +idaman bool ida_export del_items( + ea_t ea, + int flags=0, + asize_t nbytes=1, + may_destroy_cb_t *may_destroy=nullptr); + +/// \defgroup DELIT_ Unexplored byte conversion flags +/// passed as 'flags' parameter to del_items() +///@{ +#define DELIT_SIMPLE 0x0000 ///< simply undefine the specified item(s) +#define DELIT_EXPAND 0x0001 ///< propagate undefined items; for example + ///< if removing an instruction removes all + ///< references to the next instruction, then + ///< plan to convert to unexplored the next + ///< instruction too. +#define DELIT_DELNAMES 0x0002 ///< delete any names at the specified + ///< address range (except for the starting + ///< address). this bit is valid if nbytes > 1 +#define DELIT_NOTRUNC 0x0004 ///< don't truncate the current function + ///< even if #AF_TRFUNC is set +#define DELIT_NOUNAME 0x0008 ///< reject to delete if a user name is + ///< in address range (except for the starting + ///< address). this bit is valid if nbytes > 1 +#define DELIT_NOCMT 0x0010 ///< reject to delete if a comment is + ///< in address range (except for the starting + ///< address). this bit is valid if nbytes > 1 +#define DELIT_KEEPFUNC 0x0020 ///< do not undefine the function start. + ///< Just delete xrefs, ops e.t.c. +///@} + + +//------------------------------------------------------------------------- +// Manual instructions (they are used to completely override an automatically +// generated instruction by a user specified string). + +/// Is the instruction overridden? +/// \param ea linear address of the instruction or data item + +idaman bool ida_export is_manual_insn(ea_t ea); // Is the instruction overridden? + + +/// Retrieve the user-specified string for the manual instruction. +/// \param buf output buffer +/// \param ea linear address of the instruction or data item +/// \return size of manual instruction or -1 + +idaman ssize_t ida_export get_manual_insn(qstring *buf, ea_t ea); + + +/// Set manual instruction string. +/// \param ea linear address of the instruction or data item +/// \param manual_insn "" - delete manual string. +/// nullptr - do nothing + +idaman void ida_export set_manual_insn(ea_t ea, const char *manual_insn); // Set user-specified string + + +//------------------------------------------------------------------------- +/*! \defgroup FF_statespecb Bits: specific state information + \ingroup FF_states + Flags keep information common to all four states of bytes. + This information will not be automatically discarded during + transitions between different states. +*/ +///@{ +#define MS_COMM 0x000FF800 ///< Mask of common bits +#define FF_COMM 0x00000800 ///< Has comment? +#define FF_REF 0x00001000 ///< has references +#define FF_LINE 0x00002000 ///< Has next or prev lines? +#define FF_NAME 0x00004000 ///< Has name? +#define FF_LABL 0x00008000 ///< Has dummy name? +#define FF_FLOW 0x00010000 ///< Exec flow from prev instruction +#define FF_SIGN 0x00020000 ///< Inverted sign of operands +#define FF_BNOT 0x00040000 ///< Bitwise negation of operands +#define FF_UNUSED 0x00080000 ///< unused bit (was used for variable bytes) +///@} + +/// \defgroup FF_statespecf Functions: examine specific state information +/// \ingroup FF_states +///@{ + +/// Does the previous instruction exist and pass execution flow to the current byte? + +inline THREAD_SAFE constexpr bool idaapi is_flow(flags64_t F) { return (F & FF_FLOW) != 0; } + + +/// Does the current byte have additional anterior or posterior lines? + +inline THREAD_SAFE constexpr bool idaapi has_extra_cmts(flags64_t F) { return (F & FF_LINE) != 0; } +inline THREAD_SAFE constexpr bool idaapi f_has_extra_cmts(flags64_t f, void *) { return has_extra_cmts(f); } + +/// Does the current byte have an indented comment? + +inline THREAD_SAFE constexpr bool idaapi has_cmt(flags64_t F) { return (F & FF_COMM) != 0; } +inline THREAD_SAFE constexpr bool idaapi f_has_cmt(flags64_t f, void *) { return has_cmt(f); } + +/// Does the current byte have cross-references to it? + +inline THREAD_SAFE constexpr bool idaapi has_xref(flags64_t F) { return (F & FF_REF) != 0; } +inline THREAD_SAFE constexpr bool idaapi f_has_xref(flags64_t f, void *) { return has_xref(f); } ///< \copydoc has_xref() + + +/// Does the current byte have non-trivial (non-dummy) name? + +inline THREAD_SAFE constexpr bool idaapi has_name(flags64_t F) { return (F & FF_NAME) != 0; } +inline THREAD_SAFE constexpr bool idaapi f_has_name(flags64_t f, void *) { return has_name(f); } ///< \copydoc has_name() + + + +#define FF_ANYNAME (FF_LABL|FF_NAME) ///< Has name or dummy name? + +/// Does the current byte have dummy (auto-generated, with special prefix) name? + +inline THREAD_SAFE constexpr bool idaapi has_dummy_name(flags64_t F) { return (F & FF_ANYNAME) == FF_LABL; } +inline THREAD_SAFE constexpr bool idaapi f_has_dummy_name(flags64_t f, void *) { return has_dummy_name(f); } ///< \copydoc has_dummy_name() + + +/// Does the current byte have auto-generated (no special prefix) name? + +inline THREAD_SAFE constexpr bool idaapi has_auto_name(flags64_t F) { return (F & FF_ANYNAME) == FF_ANYNAME; } + + +/// Does the current byte have any name? + +inline THREAD_SAFE constexpr bool idaapi has_any_name(flags64_t F) { return (F & FF_ANYNAME) != 0; } + + +/// Does the current byte have user-specified name? + +inline THREAD_SAFE constexpr bool idaapi has_user_name(flags64_t F) { return (F & FF_ANYNAME) == FF_NAME; } +inline THREAD_SAFE constexpr bool idaapi f_has_user_name(flags64_t F, void *) { return has_user_name(F); } ///< \copydoc has_user_name() + +// signness deals with the form of operands of the current instruction/data. +// inverted sign means the following: +// if the bit is clear |then when the bit is set +// and the output is |the output should be: +// ------------ |---------- +// unsigned |signed +// signed |unsigned +// + +/// Should sign of n-th operand inverted during output?. +/// allowed values of n: 0-first operand, 1-other operands + +idaman bool ida_export is_invsign(ea_t ea, flags64_t F, int n); + + +/// Toggle sign of n-th operand. +/// allowed values of n: 0-first operand, 1-other operands + +idaman bool ida_export toggle_sign(ea_t ea, int n); + + +/// Should we negate the operand?. +/// \ash{a_bnot} should be defined in the idp module in order to work +/// with this function + +idaman bool ida_export is_bnot(ea_t ea, flags64_t F, int n); +idaman bool ida_export toggle_bnot(ea_t ea, int n); ///< Toggle binary negation of operand. also see is_bnot() + + +/// Display leading zeroes? +/// Display leading zeroes in operands. +/// The global switch for the leading zeroes is in \inf{s_genflags} +/// Note: the leading zeroes doesn't work if for the target assembler +/// octal numbers start with 0. +/// \param ea the item (insn/data) address +/// \param n the operand number (0-first operand, 1-other operands) +/// \return success + +idaman bool ida_export is_lzero(ea_t ea, int n); + +/// Set toggle lzero bit. +/// This function changes the display of leading zeroes for the specified operand. +/// If the default is not to display leading zeroes, this function will display +/// them and vice versa. +/// \param ea the item (insn/data) address +/// \param n the operand number (0-first operand, 1-other operands) +/// \return success + +idaman bool ida_export set_lzero(ea_t ea, int n); + +/// Clear toggle lzero bit. +/// This function reset the display of leading zeroes for the specified operand +/// to the default. If the default is not to display leading zeroes, leading +/// zeroes will not be displayed, as vice versa. +/// \param ea the item (insn/data) address +/// \param n the operand number (0-first operand, 1-other operands) +/// \return success + +idaman bool ida_export clr_lzero(ea_t ea, int n); + +/// Toggle lzero bit. +/// \param ea the item (insn/data) address +/// \param n the operand number (0-first operand, 1-other operands) +/// \return success + +inline bool idaapi toggle_lzero(ea_t ea, int n) +{ + return (is_lzero(ea, n) ? clr_lzero : set_lzero)(ea, n); +} + + +///@} FF_statespecf + + +/// Check if leading zeroes are important + +idaman bool ida_export leading_zero_important(ea_t ea, int n); + +//------------------------------------------------------------------------- +/// \defgroup FF_op Instruction/Data operands +/// \ingroup FF_ +/// Represent instruction/data operands. +/// +/// IDA keeps bitmask representations for a maximum of 8 operands: +/// +/// For data bytes, only the first bitmask is used (i.e. all elements of +/// an array have the same type). +///@{ + +#define MS_N_TYPE 0xFLLU ///< Mask for nth arg (a 64-bit constant) +#define FF_N_VOID 0x0 ///< Void (unknown)? +#define FF_N_NUMH 0x1 ///< Hexadecimal number? +#define FF_N_NUMD 0x2 ///< Decimal number? +#define FF_N_CHAR 0x3 ///< Char ('x')? +#define FF_N_SEG 0x4 ///< Segment? +#define FF_N_OFF 0x5 ///< Offset? +#define FF_N_NUMB 0x6 ///< Binary number? +#define FF_N_NUMO 0x7 ///< Octal number? +#define FF_N_ENUM 0x8 ///< Enumeration? +#define FF_N_FOP 0x9 ///< Forced operand? +#define FF_N_STRO 0xA ///< Struct offset? +#define FF_N_STK 0xB ///< Stack variable? +#define FF_N_FLT 0xC ///< Floating point number? +#define FF_N_CUST 0xD ///< Custom representation? + +/// Get the shift in `flags64_t` for the nibble representing +/// operand `n`'s type +/// +/// Note: n must be < UA_MAXOP, and is not checked +/// +/// \param n the operand number +/// \return the shift to the nibble + +inline constexpr int get_operand_type_shift(uint32 n) +{ + return 20 + (4 * (n + (n > 1))); +} + +/// Place operand `n`'s type flag in the right nibble of a +/// 64-bit flags set. +/// +/// \param typebits the type bits (one of `FF_N_`) +/// \param n the operand number +/// \return the shift to the nibble + +inline constexpr flags64_t get_operand_flag(uint8 typebits, int n) +{ + return n >= 0 && n < UA_MAXOP ? flags64_t(typebits) << get_operand_type_shift(n) : 0; +} + +/// Check that the 64-bit flags set has the expected type +/// for operand `n`. +/// +/// \param F the flags +/// \param typebits the type bits (one of `FF_N_`) +/// \param n the operand number +/// \return success + +inline constexpr bool is_flag_for_operand(flags64_t F, uint8 typebits, int n) +{ + return n < UA_MAXOP && (F & get_operand_flag(MS_N_TYPE, n)) == get_operand_flag(typebits, n); +} + +/// \defgroup FF_opfuncs1 Functions: examine operand flags (specific operands) +///@{ + +/// Is the first operand defined? Initially operand has no defined representation + +inline THREAD_SAFE constexpr bool idaapi is_defarg0(flags64_t F) { return !is_flag_for_operand(F, FF_N_VOID, 0); } + + +/// Is the second operand defined? Initially operand has no defined representation + +inline THREAD_SAFE constexpr bool idaapi is_defarg1(flags64_t F) { return !is_flag_for_operand(F, FF_N_VOID, 1); } + + +/// Is the first operand offset? (example: push offset xxx) + +inline THREAD_SAFE constexpr bool idaapi is_off0(flags64_t F) { return is_flag_for_operand(F, FF_N_OFF, 0); } + + +/// Is the second operand offset? (example: mov ax, offset xxx) + +inline THREAD_SAFE constexpr bool idaapi is_off1(flags64_t F) { return is_flag_for_operand(F, FF_N_OFF, 1); } + + +/// Is the first operand character constant? (example: push 'a') + +inline THREAD_SAFE constexpr bool idaapi is_char0(flags64_t F) { return is_flag_for_operand(F, FF_N_CHAR, 0); } + + +/// Is the second operand character constant? (example: mov al, 'a') + +inline THREAD_SAFE constexpr bool idaapi is_char1(flags64_t F) { return is_flag_for_operand(F, FF_N_CHAR, 1); } + + +/// Is the first operand segment selector? (example: push seg seg001) + +inline THREAD_SAFE constexpr bool idaapi is_seg0(flags64_t F) { return is_flag_for_operand(F, FF_N_SEG, 0); } + + +/// Is the second operand segment selector? (example: mov dx, seg dseg) + +inline THREAD_SAFE constexpr bool idaapi is_seg1(flags64_t F) { return is_flag_for_operand(F, FF_N_SEG, 1); } + + +/// Is the first operand a symbolic constant (enum member)? + +inline THREAD_SAFE constexpr bool idaapi is_enum0(flags64_t F) { return is_flag_for_operand(F, FF_N_ENUM, 0); } + + +/// Is the second operand a symbolic constant (enum member)? + +inline THREAD_SAFE constexpr bool idaapi is_enum1(flags64_t F) { return is_flag_for_operand(F, FF_N_ENUM, 1); } + + +/// Is the first operand an offset within a struct? + +inline THREAD_SAFE constexpr bool idaapi is_stroff0(flags64_t F) { return is_flag_for_operand(F, FF_N_STRO, 0); } + + +/// Is the second operand an offset within a struct? + +inline THREAD_SAFE constexpr bool idaapi is_stroff1(flags64_t F) { return is_flag_for_operand(F, FF_N_STRO, 1); } + + +/// Is the first operand a stack variable? + +inline THREAD_SAFE constexpr bool idaapi is_stkvar0(flags64_t F) { return is_flag_for_operand(F, FF_N_STK, 0); } + + +/// Is the second operand a stack variable? + +inline THREAD_SAFE constexpr bool idaapi is_stkvar1(flags64_t F) { return is_flag_for_operand(F, FF_N_STK, 1); } + + +/// Is the first operand a floating point number? + +inline THREAD_SAFE constexpr bool idaapi is_float0(flags64_t F) { return is_flag_for_operand(F, FF_N_FLT, 0); } + + +/// Is the second operand a floating point number? + +inline THREAD_SAFE constexpr bool idaapi is_float1(flags64_t F) { return is_flag_for_operand(F, FF_N_FLT, 1); } + + +/// Does the first operand use a custom data representation? + +inline THREAD_SAFE constexpr bool idaapi is_custfmt0(flags64_t F) { return is_flag_for_operand(F, FF_N_CUST, 0); } + + +/// Does the second operand use a custom data representation? + +inline THREAD_SAFE constexpr bool idaapi is_custfmt1(flags64_t F) { return is_flag_for_operand(F, FF_N_CUST, 1); } + + +/// Is the first operand a number (i.e. binary, octal, decimal or hex?) + +idaman bool ida_export is_numop0(flags64_t F); + + +/// Is the second operand a number (i.e. binary, octal, decimal or hex?) + +idaman bool ida_export is_numop1(flags64_t F); + + +/// Get flags for first operand + +inline THREAD_SAFE constexpr flags64_t get_optype_flags0(flags64_t F) { return F & (MS_N_TYPE << get_operand_type_shift(0)); } + + +/// Get flags for second operand + +inline THREAD_SAFE constexpr flags64_t get_optype_flags1(flags64_t F) { return F & (MS_N_TYPE << get_operand_type_shift(1)); } + +///@} FF_opfuncs1 + +//------------------------------------------------------------------------- +// +// The following 2 masks are used with operand numbers +// +#define OPND_OUTER 0x80 ///< outer offset base (combined with operand number). + ///< used only in set, get, del_offset() functions +#define OPND_MASK 0x0F ///< mask for operand number +#define OPND_ALL OPND_MASK ///< all operands + +/*! \defgroup FF_opfuncs2 Functions: examine operand flags (arbitrary operand) + For the following functions, 'n' may be: + - zero based operand number if in 0..UA_MAXOP-1 + - #OPND_ALL : all operands - function returns 1 if one of the 8 operands + satisfies the condition +*/ +///@{ +idaman bool ida_export is_defarg(flags64_t F, int n); ///< is defined? +idaman bool ida_export is_off(flags64_t F, int n); ///< is offset? +idaman bool ida_export is_char(flags64_t F, int n); ///< is character constant? +idaman bool ida_export is_seg(flags64_t F, int n); ///< is segment? +idaman bool ida_export is_enum(flags64_t F, int n); ///< is enum? +idaman bool ida_export is_manual(flags64_t F, int n); ///< is forced operand? (use is_forced_operand()) +idaman bool ida_export is_stroff(flags64_t F, int n); ///< is struct offset? +idaman bool ida_export is_stkvar(flags64_t F, int n); ///< is stack variable? +idaman bool ida_export is_fltnum(flags64_t F, int n); ///< is floating point number? +idaman bool ida_export is_custfmt(flags64_t F, int n); ///< is custom data format? +idaman bool ida_export is_numop(flags64_t F, int n); ///< is number (bin, oct, dec, hex)? +idaman bool ida_export is_suspop(ea_t ea, flags64_t F, int n); ///< is suspicious operand? +///@} + + +/// Should processor module create xrefs from the operand?. +/// Currently 'offset', 'structure offset', 'stack' and 'enum' operands create xrefs + +idaman bool ida_export op_adds_xrefs(flags64_t F, int n); + + +/// (internal function) change representation of operand(s). +/// \param ea linear address +/// \param type new flag value (should be obtained from char_flag(), num_flag() and +/// similar functions) +/// \param n 0..#UA_MAXOP-1 operand number, OPND_ALL all operands +/// \retval 1 ok +/// \retval 0 failed (applied to a tail byte) + +idaman bool ida_export set_op_type(ea_t ea, flags64_t type, int n); + + +/// Set operand representation to be 'segment'. +/// If applied to unexplored bytes, converts them to 16/32bit word data +/// \param ea linear address +/// \param n 0..#UA_MAXOP-1 operand number, OPND_ALL all operands +/// \return success + +idaman bool ida_export op_seg(ea_t ea, int n); + + +/// Set operand representation to be enum type +/// If applied to unexplored bytes, converts them to 16/32bit word data +/// \param ea linear address +/// \param n 0..#UA_MAXOP-1 operand number, OPND_ALL all operands +/// \param id id of enum +/// \param serial the serial number of the constant in the enumeration, +/// usually 0. the serial numbers are used if the enumeration +/// contains several constants with the same value +/// \return success + +idaman bool ida_export op_enum(ea_t ea, int n, tid_t id, uchar serial=0); + + +/// Get enum id of 'enum' operand. +/// \param ea linear address +/// \param n 0..#UA_MAXOP-1 operand number, OPND_ALL one of the operands +/// \param serial pointer to variable to hold the serial number of the +/// constant in the enumeration +/// \return id of enum or #BADNODE + +idaman tid_t ida_export get_enum_id(uchar *serial, ea_t ea, int n); + + +/// Set operand representation to be 'struct offset'. +/// \param insn the instruction +/// \param n 0..#UA_MAXOP-1 operand number, OPND_ALL all operands +/// \param path structure path (strpath). see nalt.hpp for more info. +/// \param path_len length of the structure path +/// \param delta struct offset delta. usually 0. denotes the difference +/// between the structure base and the pointer into the structure. +/// \return success + +idaman bool ida_export op_stroff( + const insn_t &insn, + int n, + const tid_t *path, + int path_len, + adiff_t delta); + + +/// Set operand representation to be 'struct offset' +/// if the operand likely points to a structure member. +/// For example, let's there is a structure at 1000 +/// 1000 stru_1000 Elf32_Sym <...> +/// the operand #8 will be represented as '#Elf32_Sym.st_size' after the +/// call of 'op_based_stroff(..., 8, 0x1000)' +/// By the way, after the call of 'op_plain_offset(..., 0x1000)' it will be +/// represented as '#(stru_1000.st_size - 0x1000)' +/// \param insn the instruction +/// \param n 0..#UA_MAXOP-1 operand number, OPND_ALL all operands +/// \param opval operand value (usually op_t::value or op_t::addr) +/// \param base base reference +/// \return success + +idaman bool ida_export op_based_stroff( + const insn_t &insn, + int n, + adiff_t opval, + ea_t base); + + +/// Get struct path of operand. +/// \param path buffer for structure path (strpath). see nalt.hpp for more info. +/// \param delta struct offset delta +/// \param ea linear address +/// \param n 0..#UA_MAXOP-1 operand number, OPND_ALL one of the operands +/// \return length of strpath + +idaman int ida_export get_stroff_path(tid_t *path, adiff_t *delta, ea_t ea, int n); + +/// Set operand representation to be 'stack variable'. +/// Should be applied to an instruction within a function. +/// Should be applied after creating a stack var using +/// insn_t::create_stkvar(). +/// \param ea linear address +/// \param n 0..#UA_MAXOP-1 operand number, OPND_ALL all operands +/// \return success + +idaman bool ida_export op_stkvar(ea_t ea, int n); + + +/// Set forced operand. +/// \param ea linear address +/// \param n 0..#UA_MAXOP-1 operand number +/// \param op text of operand +/// - nullptr: do nothing (return 0) +/// - "" : delete forced operand +/// \return success + +idaman bool ida_export set_forced_operand(ea_t ea, int n, const char *op); + + +/// Get forced operand. +/// \param buf output buffer, may be nullptr +/// \param ea linear address +/// \param n 0..#UA_MAXOP-1 operand number +/// \return size of forced operand or -1 + +idaman ssize_t ida_export get_forced_operand(qstring *buf, ea_t ea, int n); + + +/// Is operand manually defined?. +/// \param ea linear address +/// \param n 0..#UA_MAXOP-1 operand number + +idaman bool ida_export is_forced_operand(ea_t ea, int n); + + +//------------------------------------------------------------------------- +inline constexpr flags64_t idaapi combine_flags(flags64_t F) +{ + return (F << get_operand_type_shift(0)) + | (F << get_operand_type_shift(1)) + | (F << get_operand_type_shift(2)) + | (F << get_operand_type_shift(3)) + | (F << get_operand_type_shift(4)) + | (F << get_operand_type_shift(5)) + | (F << get_operand_type_shift(6)) + | (F << get_operand_type_shift(7)); +} + +//------------------------------------------------------------------------- +/*! \defgroup FF_opfuncs3 Functions: get type information bits for flags + Values of these functions are used as input to set_op_type() function +*/ +///@{ +inline constexpr flags64_t idaapi char_flag(void) { return combine_flags(FF_N_CHAR); } ///< see \ref FF_opbits +inline constexpr flags64_t idaapi off_flag(void) { return combine_flags(FF_N_OFF); } ///< see \ref FF_opbits +inline constexpr flags64_t idaapi enum_flag(void) { return combine_flags(FF_N_ENUM); } ///< see \ref FF_opbits +inline constexpr flags64_t idaapi stroff_flag(void) { return combine_flags(FF_N_STRO); } ///< see \ref FF_opbits +inline constexpr flags64_t idaapi stkvar_flag(void) { return combine_flags(FF_N_STK); } ///< see \ref FF_opbits +inline constexpr flags64_t idaapi flt_flag(void) { return combine_flags(FF_N_FLT); } ///< see \ref FF_opbits +inline constexpr flags64_t idaapi custfmt_flag(void) { return combine_flags(FF_N_CUST); } ///< see \ref FF_opbits +inline constexpr flags64_t idaapi seg_flag(void) { return combine_flags(FF_N_SEG); } ///< see \ref FF_opbits + +idaman flags64_t ida_export num_flag(void); ///< Get number of default base (bin, oct, dec, hex) +/// Get number flag of the base, regardless of current processor - better to use num_flag() +inline constexpr flags64_t idaapi hex_flag(void) { return combine_flags(FF_N_NUMH); } +inline constexpr flags64_t idaapi dec_flag(void) { return combine_flags(FF_N_NUMD); } ///< \copydoc hex_flag() +inline constexpr flags64_t idaapi oct_flag(void) { return combine_flags(FF_N_NUMO); } ///< \copydoc hex_flag() +inline constexpr flags64_t idaapi bin_flag(void) { return combine_flags(FF_N_NUMB); } ///< \copydoc hex_flag() +///@} + +/*! \defgroup FF_opfuncs4 Functions: set operand representation + The following functions set operand representation. + If they are applied to unexplored bytes, they convert them. + - no segment : fail + - 16bit segment : to 16bit word data + - 32bit segment : to dword + \param ea linear address + \param n 0..#UA_MAXOP-1 operand number, OPND_ALL all operands + \return success +*/ +///@{ +inline bool idaapi op_chr(ea_t ea, int n) { return set_op_type(ea, char_flag(), n); } ///< set op type to char_flag() +inline bool idaapi op_num(ea_t ea, int n) { return set_op_type(ea, num_flag(), n); } ///< set op type to num_flag() +inline bool idaapi op_hex(ea_t ea, int n) { return set_op_type(ea, hex_flag(), n); } ///< set op type to hex_flag() +inline bool idaapi op_dec(ea_t ea, int n) { return set_op_type(ea, dec_flag(), n); } ///< set op type to dec_flag() +inline bool idaapi op_oct(ea_t ea, int n) { return set_op_type(ea, oct_flag(), n); } ///< set op type to oct_flag() +inline bool idaapi op_bin(ea_t ea, int n) { return set_op_type(ea, bin_flag(), n); } ///< set op type to bin_flag() +inline bool idaapi op_flt(ea_t ea, int n) { return set_op_type(ea, flt_flag(), n); } ///< set op type to flt_flag() +///@} + +/// Set custom data format for operand (fid-custom data format id) + +idaman bool ida_export op_custfmt(ea_t ea, int n, int fid); + + +/// Remove operand representation information. +/// (set operand representation to be 'undefined') +/// \param ea linear address +/// \param n 0..#UA_MAXOP-1 operand number, OPND_ALL all operands +/// \return success + +idaman bool ida_export clr_op_type(ea_t ea, int n); + + +/// Get default base of number for the current processor. +/// \return 2, 8, 10, 16 + +idaman int ida_export get_default_radix(void); + + +/// Get radix of the operand, in: flags. +/// If the operand is not a number, returns get_default_radix() +/// \param F flags +/// \param n number of operand (0, 1, -1) +/// \return 2, 8, 10, 16 + +idaman int ida_export get_radix(flags64_t F, int n); + + +//------------------------------------------------------------------------- +/// \defgroup FF_databits Bits: data bytes +///@{ +#define DT_TYPE 0xF0000000 ///< Mask for DATA typing + +#define FF_BYTE 0x00000000 ///< byte +#define FF_WORD 0x10000000 ///< word +#define FF_DWORD 0x20000000 ///< double word +#define FF_QWORD 0x30000000 ///< quadro word +#define FF_TBYTE 0x40000000 ///< tbyte +#define FF_STRLIT 0x50000000 ///< string literal +#define FF_STRUCT 0x60000000 ///< struct variable +#define FF_OWORD 0x70000000 ///< octaword/xmm word (16 bytes/128 bits) +#define FF_FLOAT 0x80000000 ///< float +#define FF_DOUBLE 0x90000000 ///< double +#define FF_PACKREAL 0xA0000000 ///< packed decimal real +#define FF_ALIGN 0xB0000000 ///< alignment directive +// 0xC0000000 ///< reserved +#define FF_CUSTOM 0xD0000000 ///< custom data type +#define FF_YWORD 0xE0000000 ///< ymm word (32 bytes/256 bits) +#define FF_ZWORD 0xF0000000 ///< zmm word (64 bytes/512 bits) +///@} + +/// \defgroup FF_datafuncs1 Functions: examine data bits +///@{ +inline constexpr flags64_t idaapi code_flag(void) { return FF_CODE; } ///< #FF_CODE +inline constexpr flags64_t idaapi byte_flag(void) { return FF_DATA|FF_BYTE; } ///< Get a flags64_t representing a byte +inline constexpr flags64_t idaapi word_flag(void) { return FF_DATA|FF_WORD; } ///< Get a flags64_t representing a word +inline constexpr flags64_t idaapi dword_flag(void) { return FF_DATA|FF_DWORD; } ///< Get a flags64_t representing a double word +inline constexpr flags64_t idaapi qword_flag(void) { return FF_DATA|FF_QWORD; } ///< Get a flags64_t representing a quad word +inline constexpr flags64_t idaapi oword_flag(void) { return FF_DATA|FF_OWORD; } ///< Get a flags64_t representing a octaword +inline constexpr flags64_t idaapi yword_flag(void) { return FF_DATA|FF_YWORD; } ///< Get a flags64_t representing a ymm word +inline constexpr flags64_t idaapi zword_flag(void) { return FF_DATA|FF_ZWORD; } ///< Get a flags64_t representing a zmm word +inline constexpr flags64_t idaapi tbyte_flag(void) { return FF_DATA|FF_TBYTE; } ///< Get a flags64_t representing a tbyte +inline constexpr flags64_t idaapi strlit_flag(void) { return FF_DATA|FF_STRLIT; } ///< Get a flags64_t representing a string literal +inline constexpr flags64_t idaapi stru_flag(void) { return FF_DATA|FF_STRUCT; } ///< Get a flags64_t representing a struct +inline constexpr flags64_t idaapi cust_flag(void) { return FF_DATA|FF_CUSTOM; } ///< Get a flags64_t representing custom type data +inline constexpr flags64_t idaapi align_flag(void) { return FF_DATA|FF_ALIGN; } ///< Get a flags64_t representing an alignment directive +inline constexpr flags64_t idaapi float_flag(void) { return FF_DATA|FF_FLOAT; } ///< Get a flags64_t representing a float +inline constexpr flags64_t idaapi double_flag(void) { return FF_DATA|FF_DOUBLE; } ///< Get a flags64_t representing a double +inline constexpr flags64_t idaapi packreal_flag(void) { return FF_DATA|FF_PACKREAL; } ///< Get a flags64_t representing a packed decimal real + +inline THREAD_SAFE constexpr bool idaapi is_byte(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_BYTE; } ///< #FF_BYTE +inline THREAD_SAFE constexpr bool idaapi is_word(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_WORD; } ///< #FF_WORD +inline THREAD_SAFE constexpr bool idaapi is_dword(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_DWORD; } ///< #FF_DWORD +inline THREAD_SAFE constexpr bool idaapi is_qword(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_QWORD; } ///< #FF_QWORD +inline THREAD_SAFE constexpr bool idaapi is_oword(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_OWORD; } ///< #FF_OWORD +inline THREAD_SAFE constexpr bool idaapi is_yword(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_YWORD; } ///< #FF_YWORD +inline THREAD_SAFE constexpr bool idaapi is_zword(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_ZWORD; } ///< #FF_ZWORD +inline THREAD_SAFE constexpr bool idaapi is_tbyte(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_TBYTE; } ///< #FF_TBYTE +inline THREAD_SAFE constexpr bool idaapi is_float(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_FLOAT; } ///< #FF_FLOAT +inline THREAD_SAFE constexpr bool idaapi is_double(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_DOUBLE; } ///< #FF_DOUBLE +inline THREAD_SAFE constexpr bool idaapi is_pack_real(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_PACKREAL; } ///< #FF_PACKREAL +inline THREAD_SAFE constexpr bool idaapi is_strlit(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_STRLIT; } ///< #FF_STRLIT +inline THREAD_SAFE constexpr bool idaapi is_struct(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_STRUCT; } ///< #FF_STRUCT +inline THREAD_SAFE constexpr bool idaapi is_align(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_ALIGN; } ///< #FF_ALIGN +inline THREAD_SAFE constexpr bool idaapi is_custom(flags64_t F) { return is_data(F) && (F & DT_TYPE) == FF_CUSTOM; } ///< #FF_CUSTOM + +inline THREAD_SAFE constexpr bool idaapi f_is_byte(flags64_t F, void *) { return is_byte(F); } ///< See is_byte() +inline THREAD_SAFE constexpr bool idaapi f_is_word(flags64_t F, void *) { return is_word(F); } ///< See is_word() +inline THREAD_SAFE constexpr bool idaapi f_is_dword(flags64_t F, void *) { return is_dword(F); } ///< See is_dword() +inline THREAD_SAFE constexpr bool idaapi f_is_qword(flags64_t F, void *) { return is_qword(F); } ///< See is_qword() +inline THREAD_SAFE constexpr bool idaapi f_is_oword(flags64_t F, void *) { return is_oword(F); } ///< See is_oword() +inline THREAD_SAFE constexpr bool idaapi f_is_yword(flags64_t F, void *) { return is_yword(F); } ///< See is_yword() +inline THREAD_SAFE constexpr bool idaapi f_is_tbyte(flags64_t F, void *) { return is_tbyte(F); } ///< See is_tbyte() +inline THREAD_SAFE constexpr bool idaapi f_is_float(flags64_t F, void *) { return is_float(F); } ///< See is_float() +inline THREAD_SAFE constexpr bool idaapi f_is_double(flags64_t F, void *) { return is_double(F); } ///< See is_double() +inline THREAD_SAFE constexpr bool idaapi f_is_pack_real(flags64_t F, void *) { return is_pack_real(F); } ///< See is_pack_real() +inline THREAD_SAFE constexpr bool idaapi f_is_strlit(flags64_t F, void *) { return is_strlit(F); } ///< See is_strlit() +inline THREAD_SAFE constexpr bool idaapi f_is_struct(flags64_t F, void *) { return is_struct(F); } ///< See is_struct() +inline THREAD_SAFE constexpr bool idaapi f_is_align(flags64_t F, void *) { return is_align(F); } ///< See is_align() +inline THREAD_SAFE constexpr bool idaapi f_is_custom(flags64_t F, void *) { return is_custom(F); } ///< See is_custom() + + +/// Do the given flags specify the same data type? + +inline THREAD_SAFE constexpr bool idaapi is_same_data_type(flags64_t F1, flags64_t F2) { return ((F1 ^ F2) & DT_TYPE) == 0; } + + +/// Get flags from size (in bytes). +/// Supported sizes: 1, 2, 4, 8, 16, 32. +/// For other sizes returns 0 + +idaman flags64_t ida_export get_flags_by_size(size_t size); +///@} FF_datafuncs1 + + +/// \defgroup FF_datafuncs2 Functions: manipulate data bits +/// \param ea linear address +/// \param length size of array in bytes. should be divisible by the size of +/// one item of the specified type. +/// \return success +///@{ + +/// Convert to data (byte, word, dword, etc). +/// This function may be used to create arrays. +/// \param ea linear address +/// \param dataflag type of data. Value of function byte_flag(), word_flag(), etc. +/// \param size size of array in bytes. should be divisible by the size of +/// one item of the specified type. for variable sized items +/// it can be specified as 0, and the kernel will try to calculate the size. +/// \param tid type id. If the specified type is a structure, +/// then tid is structure id. Otherwise should be #BADNODE. +/// \return success + +idaman bool ida_export create_data( + ea_t ea, + flags64_t dataflag, + asize_t size, + tid_t tid); + + +inline THREAD_SAFE flags64_t idaapi calc_dflags(flags64_t f, bool force) { return f | (force ? FF_COMM : 0); } +/// Convert to byte +inline bool idaapi create_byte(ea_t ea, asize_t length, bool force=false) +{ + return create_data(ea, calc_dflags(FF_BYTE, force), length, BADNODE); +} +/// Convert to word +inline bool idaapi create_word(ea_t ea, asize_t length, bool force=false) +{ + return create_data(ea, calc_dflags(FF_WORD, force), length, BADNODE); +} +/// Convert to dword +inline bool idaapi create_dword(ea_t ea, asize_t length, bool force=false) +{ + return create_data(ea, calc_dflags(FF_DWORD, force), length, BADNODE); +} +/// Convert to quadword +inline bool idaapi create_qword(ea_t ea, asize_t length, bool force=false) +{ + return create_data(ea, calc_dflags(FF_QWORD, force), length, BADNODE); +} +/// Convert to octaword/xmm word +inline bool idaapi create_oword(ea_t ea, asize_t length, bool force=false) +{ + return create_data(ea, calc_dflags(FF_OWORD, force), length, BADNODE); +} +/// Convert to ymm word +inline bool idaapi create_yword(ea_t ea, asize_t length, bool force=false) +{ + return create_data(ea, calc_dflags(FF_YWORD, force), length, BADNODE); +} +/// Convert to zmm word +inline bool idaapi create_zword(ea_t ea, asize_t length, bool force=false) +{ + return create_data(ea, calc_dflags(FF_ZWORD, force), length, BADNODE); +} +/// Convert to tbyte +inline bool idaapi create_tbyte(ea_t ea, asize_t length, bool force=false) +{ + return create_data(ea, calc_dflags(FF_TBYTE, force), length, BADNODE); +} +/// Convert to float +inline bool idaapi create_float(ea_t ea, asize_t length, bool force=false) +{ + return create_data(ea, calc_dflags(FF_FLOAT, force), length, BADNODE); +} +/// Convert to double +inline bool idaapi create_double(ea_t ea, asize_t length, bool force=false) +{ + return create_data(ea, calc_dflags(FF_DOUBLE, force), length, BADNODE); +} +/// Convert to packed decimal real +inline bool idaapi create_packed_real(ea_t ea, asize_t length, bool force=false) +{ + return create_data(ea, calc_dflags(FF_PACKREAL, force), length, BADNODE); +} +/// Convert to struct +inline bool idaapi create_struct(ea_t ea, asize_t length, tid_t tid, bool force=false) +{ + return create_data(ea, calc_dflags(FF_STRUCT, force), length, tid); +} +/// Convert to custom data type +inline bool idaapi create_custdata(ea_t ea, asize_t length, int dtid, int fid, bool force=false) +{ + return create_data(ea, calc_dflags(FF_CUSTOM, force), length, dtid|(fid<<16)); +} + + +/// Create an alignment item. +/// \param ea linear address +/// \param length size of the item in bytes. 0 means to infer from ALIGNMENT +/// \param alignment alignment exponent. Example: 3 means align to 8 bytes. +/// 0 means to infer from LENGTH +/// It is forbidden to specify both LENGTH and ALIGNMENT as 0. +/// \return success + +idaman bool ida_export create_align(ea_t ea, asize_t length, int alignment); + +/// Calculate the minimal possible alignment exponent. +/// \param length size of the item in bytes. +/// \return a value in the 1..32 range + +idaman int ida_export calc_min_align(asize_t length); + + +/// Calculate the maximal possible alignment exponent. +/// \param endea end address of the alignment item. +/// \return a value in the 0..32 range + +idaman int ida_export calc_max_align(ea_t endea); + +/// Calculate the default alignment exponent. +/// \param ea linear address +/// \param mina minimal possible alignment exponent. +/// \param maxa minimal possible alignment exponent. + +idaman int ida_export calc_def_align(ea_t ea, int mina, int maxa); + + +/// Convert to 16-bit quantity (take the byte size into account) + +idaman bool ida_export create_16bit_data(ea_t ea, asize_t length); + + +/// Convert to 32-bit quantity (take the byte size into account) + +idaman bool ida_export create_32bit_data(ea_t ea, asize_t length); + + +///@} FF_datafuncs2 + +///@} FF_op + +/// \defgroup ALOPT_ string literal length options +/// passed as 'options' parameter to get_max_strlit_length() +///@{ +#define ALOPT_IGNHEADS 0x01 ///< don't stop if another data item is encountered. + ///< only the byte values will be used to determine + ///< the string length. + ///< if not set, a defined data item or instruction + ///< will truncate the string +#define ALOPT_IGNPRINT 0x02 ///< if set, don't stop at non-printable codepoints, + ///< but only at the terminating character (or not + ///< unicode-mapped character (e.g., 0x8f in CP1252)) +#define ALOPT_IGNCLT 0x04 ///< if set, don't stop at codepoints that are not + ///< part of the current 'culture'; accept all + ///< those that are graphical (this is typically + ///< used used by user-initiated actions creating + ///< string literals.) +#define ALOPT_MAX4K 0x08 ///< if string length is more than 4K, return the + ///< accumulated length +#define ALOPT_ONLYTERM 0x10 ///< only the termination characters can be at the + ///< string end. Without this option illegal + ///< characters also terminate the string. +#define ALOPT_APPEND 0x20 ///< if an existing strlit is encountered, then + ///< append it to the string. + +///@} + +/// Determine maximum length of string literal. +/// +/// If the string literal has a length prefix (e.g., STRTYPE_LEN2 has +/// a two-byte length prefix), the length of that prefix (i.e., 2) +/// will be part of the returned value. +/// +/// \param ea starting address +/// \param strtype string type. one of \ref STRTYPE_ +/// \param options combination of \ref ALOPT_ +/// \return length of the string in octets (octet==8bit) + +idaman size_t ida_export get_max_strlit_length( + ea_t ea, + int32 strtype, + int options = 0); + +/// \defgroup STRCONV_ string conversion flags +/// passed as 'flags' parameter to get_strlit_contents() +///@{ +#define STRCONV_ESCAPE 0x00000001 ///< convert non-printable characters to C escapes (\n, \xNN, \uNNNN) +#define STRCONV_REPLCHAR 0x00000002 ///< convert non-printable characters to the Unicode replacement character (U+FFFD) +#define STRCONV_INCLLEN 0x00000004 ///< for Pascal-style strings, include the prefixing length byte(s) as C-escaped sequence +///@} + +/// Get contents of string literal, as UTF-8-encoded codepoints. +/// This function returns the displayed part of the string +/// It works even if the string has not been created in the database yet. +/// +/// If 'len' is size_t(-1), it will be computed like so: +/// - if a string literal is present at 'ea', get_item_size() * bytesize(ea) will be used +/// - otherwise, get_max_strlit_length(..., ALOPT_IGNHEADS) will be used +/// +/// About 'maxcps': this specifies a limit to the number of codepoints, +/// not bytes in the UTF-8 output buffer. So for example although U+4e12 +/// will use 3 bytes in the output buffer, it still counts as only 1 +/// character -- unless STRCONV_ESCAPE is used. +/// If 'STRCONV_ESCAPE' is used, U+4e12 will be converted to the string +/// "\u4E12", and will use 6 bytes in the output buffer and also count +/// as 6 codepoints. +/// +/// If 'STRCONV_REPLCHAR', any undecodable byte will re represented +/// as U+FFFD, occupy 3 bytes in the output buffer, and count for 1 codepoint. +/// +/// \param[out] utf8 output buffer +/// \param[in] ea linear address of the string +/// \param[in] len length of the string, in octets (octet=8bit) +/// \param[in] type type of the string. one of \ref STRTYPE_ +/// \param[in, out] maxcps maximum length of codepoints, after possible +/// escaping, in output buffer (not counting terminating zero) +/// on exit, will be set to 0 if string got truncated +/// can be nullptr if not needed +/// \param[in] flags combination of \ref STRCONV_ +/// \return length of generated text (in bytes) or -1 + +idaman ssize_t ida_export get_strlit_contents( + qstring *utf8, + ea_t ea, + size_t len, + int32 type, + size_t *maxcps = nullptr, + int flags = 0); + + +/// Convert to string literal and give a meaningful name. +/// 'start' may be higher than 'end', the kernel will swap them in this case +/// \param start starting address +/// \param len length of the string in bytes. +/// if 0, then get_max_strlit_length() will be used +/// to determine the length +/// \param strtype string type. one of \ref STRTYPE_ +/// \return success + +idaman bool ida_export create_strlit(ea_t start, size_t len, int32 strtype); + + + +//------------------------------------------------------------------------- +/// \defgroup PSTF_ flags for use with get_strlit_type_info +///@{ +#define PSTF_TNORM 0x00 ///< use normal name +#define PSTF_TBRIEF 0x01 ///< use brief name (e.g., in the 'Strings' window) +#define PSTF_TINLIN 0x02 ///< use 'inline' name (e.g., in the structures comments) +#define PSTF_TMASK 0x03 ///< type mask +#define PSTF_HOTKEY 0x04 ///< have hotkey markers part of the name +#define PSTF_ENC 0x08 ///< if encoding is specified, append it +#define PSTF_ONLY_ENC 0x0B ///< generate only the encoding name +#define PSTF_ATTRIB 0x10 ///< generate for type attribute usage +///@} + + +/// Get string type information: the string type name (possibly +/// decorated with hotkey markers), and the tooltip. +/// +/// \param out the output buffer +/// \param strtype the string type +/// \param out_tooltip an optional output buffer for the tooltip +/// \param flags or'ed PSTF_* constants +/// \return length of generated text + +idaman bool ida_export print_strlit_type( + qstring *out, + int32 strtype, + qstring *out_tooltip = nullptr, + int flags = 0); + + +/// Get additional information about an operand representation. +/// \param buf buffer to receive the result. may not be nullptr +/// \param ea linear address of item +/// \param n number of operand, 0 or 1 +/// \param flags flags of the item +/// \return nullptr if no additional representation information + +idaman opinfo_t *ida_export get_opinfo( + opinfo_t *buf, + ea_t ea, + int n, + flags64_t flags); + + +/// Set additional information about an operand representation. +/// This function is a low level one. Only the kernel should use it. +/// \param ea linear address of the item +/// \param n number of operand, 0 or 1 (see the note below) +/// \param flag flags of the item +/// \param ti additional representation information +/// \param suppress_events do not generate changing_op_type and op_type_changed events +/// \return success +/// \note for custom formats (if is_custfmt(flag, n) is true) or for offsets +/// (if is_off(flag, n) is true) N can be in range 0..UA_MAXOP-1 or equal to OPND_ALL. +/// In the case of OPND_ALL the additional information about all operands will be +/// set. + +idaman bool ida_export set_opinfo( + ea_t ea, + int n, + flags64_t flag, + const opinfo_t *ti, + bool suppress_events=false); + + +/// Get size of data type specified in flags 'F'. +/// \param ea linear address of the item +/// \param F flags +/// \param ti additional information about the data type. For example, +/// if the current item is a structure instance, +/// then ti->tid is structure id. Otherwise is ignored (may be nullptr). +/// If specified as nullptr, will be automatically retrieved from the database +/// \return +/// - byte : 1 +/// - word : 2 +/// - etc... +/// +/// If flags doesn't specify a data, then return 1 + +idaman asize_t ida_export get_data_elsize(ea_t ea, flags64_t F, const opinfo_t *ti=nullptr); + + +/// Get full size of data type specified in flags 'F'. +/// takes into account processors with wide bytes +/// e.g. returns 2 for a byte element with 16-bit bytes +inline asize_t get_full_data_elsize(ea_t ea, flags64_t F, const opinfo_t *ti=nullptr) +{ + asize_t nbytes = get_data_elsize(ea, F, ti); + return nbytes * bytesize(ea); +} + + +/// Is the item at 'ea' variable size?. +/// \param ea linear address of the item +/// \param F flags +/// \param ti additional information about the data type. For example, +/// if the current item is a structure instance, +/// then ti->tid is structure id. Otherwise is ignored (may be nullptr). +/// If specified as nullptr, will be automatically retrieved from the database +/// \param itemsize if not nullptr and the item is varsize, itemsize +/// will contain the calculated item size (for struct types, the minimal size is returned) +/// \retval 1 varsize item +/// \retval 0 fixed item +/// \retval -1 error (bad data definition) + +idaman int ida_export is_varsize_item( + ea_t ea, + flags64_t F, + const opinfo_t *ti=nullptr, + asize_t *itemsize=nullptr); + + +/// Return the possible size of the item at EA of type TIF +/// if TIF is the variable structure. +/// \param ea the linear address of the item +/// \param tif the item type +/// \return the possible size +/// \retval asize_t(-1) TIF is not a variable structure + +idaman asize_t ida_export get_possible_item_varsize( + ea_t ea, + const tinfo_t &tif); + + +/// Can define item (instruction/data) of the specified 'length', starting at 'ea'? +/// \note if there is an item starting at 'ea', this function ignores it +/// \note this function converts to unexplored all encountered data items +/// with fixup information. Should be fixed in the future. +/// \param ea start of the range for the new item +/// \param length length of the new item in bytes +/// \param flags if not 0, then the kernel will ignore the data types +/// specified by the flags and destroy them. For example: +///
+///                  1000 dw 5
+///                  1002 db 5 ; undef
+///                  1003 db 5 ; undef
+///                  1004 dw 5
+///                  1006 dd 5
+///                  
+/// can_define_item(1000, 6, 0) - false because of dw at 1004 \n +/// can_define_item(1000, 6, word_flag()) - true, word at 1004 is destroyed +/// \return 1-yes, 0-no +/// +/// This function may return 0 if: +/// - a new item would cross segment boundaries +/// - a new item would overlap with existing items (except items specified by 'flags') + +idaman bool ida_export can_define_item(ea_t ea, asize_t length, flags64_t flags); + +/// \defgroup FF_CODE Code bytes +/// \ingroup FF_ +/// Represent characteristics of instructions +///@{ + +//------------------------------------------------------------------------- +/// \defgroup FF_codebits Bits: code bytes +///@{ +#define MS_CODE 0xF0000000 ///< Mask for code bits +#define FF_FUNC 0x10000000 ///< function start? +// 0x20000000 // not used +#define FF_IMMD 0x40000000 ///< Has Immediate value ? +#define FF_JUMP 0x80000000 ///< Has jump table or switch_info? +///@} + +/// \defgroup FF_codefuncs Functions: work with code bits +///@{ + +/// Has immediate value? + +inline THREAD_SAFE bool idaapi has_immd(flags64_t F) { return is_code(F) && (F & FF_IMMD) != 0; } + + +/// Is function start? + +inline THREAD_SAFE bool idaapi is_func(flags64_t F) { return is_code(F) && (F & FF_FUNC) != 0; } + + +/// Set 'has immediate operand' flag. +/// Returns true if the #FF_IMMD bit was not set and now is set + +idaman bool ida_export set_immd(ea_t ea); + + +///@} FF_codefuncs +///@} FF_CODE + +//----------------------------------------------------------------------- +// Custom data type and format definitions +//----------------------------------------------------------------------- + +/// Information about a data type +struct data_type_t +{ + int cbsize; ///< size of this structure + void *ud; ///< user-defined data to be passed to callbacks + int props; ///< properties +#define DTP_NODUP 0x0001 ///< do not use dup construct + const char *name; ///< name of the data type. must be unique + const char *menu_name; ///< Visible data type name to use in menus + ///< if nullptr, no menu item will be created + const char *hotkey; ///< Hotkey for the corresponding menu item + ///< if nullptr, no hotkey will be associated with the menu item + const char *asm_keyword; ///< keyword to use for this type in the assembly + ///< if nullptr, the data type cannot be used in the listing + ///< it can still be used in cpuregs window + asize_t value_size; ///< size of the value in bytes + + /// Should this type be shown in UI menus + /// \return success + bool is_present_in_menus() const { return menu_name != nullptr && asm_keyword != nullptr; } + + /// May create data? nullptr means always may + /// \param ud user-defined data + /// \param ea address of the future item + /// \param nbytes size of the future item + bool (idaapi *may_create_at)( + void *ud, + ea_t ea, + size_t nbytes); + + /// This function is used to determine size of the (possible) item at 'ea'. + /// This callback is required only for varsize datatypes. + /// \param ud user-defined data + /// \param ea address of the item + /// \param maxsize maximal size of the item + /// \return 0 if no such item can be created/displayed + asize_t (idaapi *calc_item_size)( + void *ud, + ea_t ea, + asize_t maxsize); + +#ifndef SWIG + DECLARE_COMPARISONS(data_type_t); +#endif +}; + +/// Information about a data format +struct data_format_t +{ + int32 cbsize; ///< size of this structure + void *ud; ///< user-defined data to be passed to callbacks + int props; ///< properties (currently 0) + const char *name; ///< Format name, must be unique + const char *menu_name; ///< Visible format name to use in menus + ///< if nullptr, no menu item will be created + const char *hotkey; ///< Hotkey for the corresponding menu item + ///< if nullptr, no hotkey will be associated with the menu item + asize_t value_size; ///< size of the value in bytes + ///< 0 means any size is ok + ///< data formats that are registered for standard types (dtid 0) + ///< may be called with any value_size (instruction operands only) + int32 text_width; ///< Usual width of the text representation + ///< This value is used to calculate the width + ///< of the control to display values of this type + + /// Should this format be shown in UI menus + /// \return success + bool is_present_in_menus() const { return menu_name != nullptr; } + + /// Convert to colored string. + /// \param ud user-defined data + /// \param out output buffer. may be nullptr + /// \param value value to print. may not be nullptr + /// \param size size of value in 8-bit bytes + /// \param current_ea current address (BADADDR if unknown) + /// \param operand_num current operand number + /// \param dtid custom data type id (0-standard built-in data type) + /// \return success + bool (idaapi *print)( + void *ud, + qstring *out, + const void *value, + asize_t size, + ea_t current_ea, + int operand_num, + int dtid); + + /// Convert from uncolored string. + /// \param ud user-defined data + /// \param value output buffer. may be nullptr + /// \param input input string. may not be nullptr + /// \param current_ea current address (BADADDR if unknown) + /// \param operand_num current operand number (-1 if unknown) + /// \param errstr buffer for error message + /// \return success + bool (idaapi *scan)( + void *ud, + bytevec_t *value, + const char *input, + ea_t current_ea, + int operand_num, + qstring *errstr); + + /// Analyze custom data format occurrence + /// This callback can be used to create xrefs from the current item. + /// This callback may be missing. + /// \param ud user-defined data + /// \param current_ea current address (BADADDR if unknown) + /// \param operand_num current operand number + void (idaapi *analyze)( + void *ud, + ea_t current_ea, + int operand_num); + +#ifndef SWIG + DECLARE_COMPARISONS(data_format_t); +#endif +}; + + +/// Register a new data type. +/// \param dtinfo description of the new data type +/// \return > 0 : id of the new custom data type, +/// < 0 : error when the custom data type with the same name has +/// already been registered +/// \note dtid 0 is reserved for built-in data types. + +idaman int ida_export register_custom_data_type(const data_type_t *dtinfo); + + +/// Unregister a data type. +/// When the idb is closed, all custom data types are automatically +/// unregistered, but since it happens too late (plugin modules could +/// already be unloaded) one has to unregister custom data types explicitly. +/// The ids of unregistered custom data types remain allocated and when the +/// same name is reused to register a custom data type, it will get assigned +/// the same id. +/// \param dtid data type to unregister +/// \retval true ok +/// \retval false no such dtid + +idaman bool ida_export unregister_custom_data_type(int dtid); + + +/// Register a new data format. +/// \param dtform description of the new data format +/// \return > 0 : id of the new custom data format, +/// < 0 : error when the custom data format with the same name has +/// already been registered to the data type +/// \note dfid 0 is unused. + +idaman int ida_export register_custom_data_format(const data_format_t *dtform); + + +/// Unregister a data format. +/// \sa unregister_custom_data_type() +/// \param dfid data format to unregister +/// \retval true ok +/// \retval false no such dfid + +idaman bool ida_export unregister_custom_data_format(int dfid); + + +/// Get definition of a registered custom data type. +/// \param dtid data type id +/// \return data type definition or nullptr + +idaman const data_type_t *ida_export get_custom_data_type(int dtid); + + +/// Get definition of a registered custom data format. +/// \param dfid data format id +/// \return data format definition or nullptr + +idaman const data_format_t *ida_export get_custom_data_format(int dfid); + + +/// Attach the data format to the data type. +/// \param dtid data type id that can use the data format. +/// 0 means all standard data types. Such data formats can be +/// applied to any data item or instruction operands. For +/// instruction operands, the data_format_t::value_size check +/// is not performed by the kernel. +/// \param dfid data format id +/// \retval true ok +/// \retval false no such `dtid`, or no such `dfid', or the data format has +/// already been attached to the data type + +idaman bool ida_export attach_custom_data_format(int dtid, int dfid); + + +/// Detach the data format from the data type. +/// Unregistering a custom data type detaches all attached data formats, +/// no need to detach them explicitly. You still need unregister them. +/// Unregistering a custom data format detaches it from all attached data +/// types. +/// \param dtid data type id to detach data format from +/// \param dfid data format id to detach +/// \retval true ok +/// \retval false no such `dtid`, or no such `dfid', or the data format was +/// not attached to the data type + +idaman bool ida_export detach_custom_data_format(int dtid, int dfid); + + +/// Is the custom data format attached to the custom data type? +/// \param dtid data type id +/// \param dfid data format id +/// \return true or false + +idaman bool ida_export is_attached_custom_data_format(int dtid, int dfid); + +/// Get list of registered custom data type ids. +/// \param out buffer for the output. may be nullptr +/// \param min_size minimum value size +/// \param max_size maximum value size +/// \return number of custom data types with the specified size limits + +idaman int ida_export get_custom_data_types( + intvec_t *out, + asize_t min_size=0, + asize_t max_size=BADADDR); + + +/// Get list of attached custom data formats for the specified data type. +/// \param out buffer for the output. may be nullptr +/// \param dtid data type id +/// \return number of returned custom data formats. if error, returns -1 + +idaman int ida_export get_custom_data_formats(intvec_t *out, int dtid); + + +/// Get id of a custom data type. +/// \param name name of the custom data type +/// \return id or -1 + +idaman int ida_export find_custom_data_type(const char *name); + + +/// Get id of a custom data format. +/// \param name name of the custom data format +/// \return id or -1 + +idaman int ida_export find_custom_data_format(const char *name); + + +//-------------------------------------------------------------------------- +// I N D E N T E D C O M M E N T S +//-------------------------------------------------------------------------- + +/// Set an indented comment. +/// \param ea linear address +/// \param comm comment string +/// - nullptr: do nothing (return 0) +/// - "" : delete comment +/// \param rptble is repeatable? +/// \return success + +idaman bool ida_export set_cmt(ea_t ea, const char *comm, bool rptble); + + +/// Get an indented comment. +/// \param buf output buffer, may be nullptr +/// \param ea linear address. may point to tail byte, the function +/// will find start of the item +/// \param rptble get repeatable comment? +/// \return size of comment or -1 + +idaman ssize_t ida_export get_cmt(qstring *buf, ea_t ea, bool rptble); + + +/// Append to an indented comment. +/// Creates a new comment if none exists. +/// Appends a newline character and the specified string otherwise. +/// \param ea linear address +/// \param str comment string to append +/// \param rptble append to repeatable comment? +/// \return success + +idaman bool ida_export append_cmt(ea_t ea, const char *str, bool rptble); + + +//-------------------------------------------------------------------- +// P R E D E F I N E D C O M M E N T S +//-------------------------------------------------------------------- + +/// Get predefined comment. +/// \param buf buffer for the comment +/// \param ins current instruction information +/// \return size of comment or -1 + +idaman ssize_t ida_export get_predef_insn_cmt( + qstring *buf, + const insn_t &ins); + + +//-------------------------------------------------------------------------- +// S E A R C H F U N C T I O N S +//-------------------------------------------------------------------------- +/// Find forward a byte with the specified value (only 8-bit value from the database). +/// example: ea=4 size=3 will inspect addresses 4, 5, and 6 +/// \param sEA linear address +/// \param size number of bytes to inspect +/// \param value value to find +/// \param bin_search_flags combination of \ref BIN_SEARCH_ +/// \return address of byte or #BADADDR + +idaman ea_t ida_export find_byte(ea_t sEA, asize_t size, uchar value, int bin_search_flags); + + +/// Find reverse a byte with the specified value (only 8-bit value from the database). +/// example: ea=4 size=3 will inspect addresses 6, 5, and 4 +/// \param sEA the lower address of the search range +/// \param size number of bytes to inspect +/// \param value value to find +/// \param bin_search_flags combination of \ref BIN_SEARCH_ +/// \return address of byte or #BADADDR + +idaman ea_t ida_export find_byter(ea_t sEA, asize_t size, uchar value, int bin_search_flags); + + +//------------------------------------------------------------------------- +struct compiled_binpat_t // compiled binary pattern compiled_binpat_t +{ + bytevec_t bytes; + bytevec_t mask; + rangevec_t strlits; // range of string literals, in _bytes_ ranges (not CPs) + int encidx; + + compiled_binpat_t() : encidx(-1) {} + bool all_bytes_defined() const { return mask.empty(); } + void qclear() { bytes.qclear(); mask.qclear(); strlits.qclear(); encidx = -1; } + + bool operator==(const compiled_binpat_t &r) const + { + return bytes == r.bytes + && mask == r.mask + && strlits == r.strlits + && encidx == r.encidx; + } + bool operator!=(const compiled_binpat_t &r) const { return !(*this == r); } +}; +DECLARE_TYPE_AS_MOVABLE(compiled_binpat_t); +typedef qvector compiled_binpat_vec_t; + +#define PBSENC_DEF1BPU 0 ///< Use the default 1 byte-per-unit IDB encoding +#define PBSENC_ALL -1 ///< Use all IDB encodings + +/// Convert user-specified binary string to internal representation. +/// The 'in' parameter contains space-separated tokens: +/// \code +/// - numbers (numeric base is determined by 'radix') +/// - if value of number fits a byte, it is considered as a byte +/// - if value of number fits a word, it is considered as 2 bytes +/// - if value of number fits a dword,it is considered as 4 bytes +/// - "..." string constants +/// - 'x' single-character constants +/// - ? variable bytes +/// \endcode +/// +/// Note that string constants are surrounded with double quotes. +/// +/// Here are a few examples (assuming base 16): +/// \code +/// CD 21 - bytes 0xCD, 0x21 +/// 21CD - bytes 0xCD, 0x21 (little endian ) or 0x21, 0xCD (big-endian) +/// "Hello", 0 - the null terminated string "Hello" +/// L"Hello" - 'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0 +/// B8 ? ? ? ? 90 - byte 0xB8, 4 bytes with any value, byte 0x90 +/// \endcode +/// \param [out] out a vector of compiled binary patterns, for use with bin_search() +/// \param ea linear address to convert for (the conversion depends on the +/// address, because the number of bits in a byte depend on the +/// segment type) +/// \param in input text string +/// \param radix numeric base of numbers (8,10,16) +/// \param strlits_encoding the target encoding into which the string +/// literals present in 'in', should be encoded. +/// Can be any from [1, get_encoding_qty()), or +/// the special values PBSENC_* +/// \param errbuf error buffer (can be nullptr) +/// \return false either in case of parsing error, or if at least one +/// requested target encoding couldn't encode the string +/// literals present in "in". +// true otherwise +idaman bool ida_export parse_binpat_str( + compiled_binpat_vec_t *out, + ea_t ea, + const char *in, + int radix, + int strlits_encoding=PBSENC_DEF1BPU, + qstring *errbuf=nullptr); + + +/// Search for a pattern in the program. +/// \param start_ea linear address, start of range to search +/// \param end_ea linear address, end of range to search (exclusive) +/// \param data the prepared data to search for (see parse_binpat_str()) +/// \param flags combination of \ref BIN_SEARCH_ +/// \param out_matched_idx index in data where the pattern was found +/// \return #BADADDR (if pressed Ctrl-Break or not found) or string address. + +idaman ea_t ida_export bin_search( + ea_t start_ea, + ea_t end_ea, + const compiled_binpat_vec_t &data, + int flags, + size_t *out_matched_idx=nullptr); + +inline ea_t bin_search( + ea_t start_ea, + ea_t end_ea, + const uchar *image, + const uchar *mask, + size_t len, + int flags) +{ + compiled_binpat_vec_t bbv; + compiled_binpat_t &bv = bbv.push_back(); + bv.bytes.append(image, len); + if ( mask != nullptr ) + bv.mask.append(mask, len); + return bin_search(start_ea, end_ea, bbv, flags); +} + +/// +/// \defgroup BIN_SEARCH_ Search flags +/// passed as 'flags' parameter to bin_search() +///@{ +#define BIN_SEARCH_CASE 0x01 ///< case sensitive +#define BIN_SEARCH_NOCASE 0x00 ///< case insensitive +#define BIN_SEARCH_NOBREAK 0x02 ///< don't check for Ctrl-Break +#define BIN_SEARCH_INITED 0x04 ///< find_byte, find_byter: any initilized value +#define BIN_SEARCH_NOSHOW 0x08 ///< don't show search progress or update screen +#define BIN_SEARCH_FORWARD 0x00 ///< search forward for bytes +#define BIN_SEARCH_BACKWARD 0x10 ///< search backward for bytes +#define BIN_SEARCH_BITMASK 0x20 ///< searching using strict bit mask +///@} + + +/// Find the next initialized address + +inline ea_t idaapi next_inited(ea_t ea, ea_t maxea) +{ + if ( ea >= maxea ) + return BADADDR; + ++ea; + return find_byte(ea, maxea-ea, 0, BIN_SEARCH_INITED); +} + +/// Find the previous initialized address + +inline ea_t idaapi prev_inited(ea_t ea, ea_t minea) +{ + if ( ea <= minea ) + return BADADDR; + --ea; + return find_byter(minea, ea-minea, 0, BIN_SEARCH_INITED); +} + +/// Compare 'len' bytes of the program starting from 'ea' with 'image'. +/// \param ea linear address +/// \param image bytes to compare with +/// \param mask array of mask bytes, it's length is 'len'. +/// if the flag BIN_SEARCH_BITMASK is passsed, 'bitwise AND' is used +/// to compare. +/// if not; 1 means to perform the comparison of the corresponding byte. +/// 0 means not to perform. +/// if mask == nullptr, then all bytes of 'image' will be compared. +/// if mask == #SKIP_FF_MASK then 0xFF bytes will be skipped +/// \param len length of block to compare in bytes. +/// \param bin_search_flags combination of \ref BIN_SEARCH_ +/// \retval 1 equal +/// \retval 0 not equal + +idaman bool ida_export equal_bytes( + ea_t ea, + const uchar *image, + const uchar *mask, + size_t len, + int bin_search_flags); + +/// Used by equal_bytes() to skip 0xFF when searching the program +#define SKIP_FF_MASK ((const uchar *)0xFF) + +// Compare two bytes. This helper function is used for binary search. +inline bool bytes_match_for_bin_search( + uchar c1, + uchar c2, + const uchar *mask, + int i, + int bin_search_flags) +{ + if ( (bin_search_flags & BIN_SEARCH_CASE) == 0 ) + { + c1 = qtoupper(c1); + c2 = qtoupper(c2); + } + if ( mask != nullptr ) + { + if ( (bin_search_flags & BIN_SEARCH_BITMASK) != 0 ) + return ((c1 ^ c2) & mask[i]) == 0; // are bytes equal with mask applied? + if ( mask == SKIP_FF_MASK ) + { + if ( c2 == 0xFF ) + return true; + } + else if ( mask[i] == 0 ) + { + return true; + } + } + return c1 == c2; +} + +idaman ea_t ida_export find_binary( + ea_t startea, + ea_t endea, + const char *ubinstr, + int radix, + int sflag, + int strlits_encoding=0); + +//------------------------------------------------------------------------ +// H I D D E N A R E A S +//------------------------------------------------------------------------ + +/// Hidden ranges - address ranges which can be replaced by their descriptions. +/// There is also a possibility to hide individual items completely (nalt.hpp, hide_item) +/// \note After modifying any of this struct's fields please call update_hidden_range() + +struct hidden_range_t : public range_t +{ + char *description; ///< description to display if the range is collapsed + char *header; ///< header lines to display if the range is expanded + char *footer; ///< footer lines to display if the range is expanded + bool visible; ///< the range state + bgcolor_t color; ///< range color +}; + +/// Update hidden range information in the database. +/// You cannot use this function to change the range boundaries +/// \param ha range to update +/// \return success + +idaman bool ida_export update_hidden_range(const hidden_range_t *ha); + + +/// Mark a range of addresses as hidden. +/// The range will be created in the invisible state with the default color +/// \param ea1 linear address of start of the address range +/// \param ea2 linear address of end of the address range +/// \param description, header, footer range parameters +/// \param color the range color +/// \return success + +idaman bool ida_export add_hidden_range( + ea_t ea1, + ea_t ea2, + const char *description, + const char *header, + const char *footer, + bgcolor_t color=DEFCOLOR); + + +/// Get pointer to hidden range structure, in: linear address. +/// \param ea any address in the hidden range + +idaman hidden_range_t *ida_export get_hidden_range(ea_t ea); + + +/// Get pointer to hidden range structure, in: number of hidden range. +/// \param n number of hidden range, is in range 0..get_hidden_range_qty()-1 + +idaman hidden_range_t *ida_export getn_hidden_range(int n); + + +/// Get number of hidden ranges + +idaman int ida_export get_hidden_range_qty(void); + + +/// Get number of a hidden range. +/// \param ea any address in the hidden range +/// \return number of hidden range (0..get_hidden_range_qty()-1) + +idaman int ida_export get_hidden_range_num(ea_t ea); + + +/// Get pointer to previous hidden range. +/// \param ea any address in the program +/// \return ptr to hidden range or nullptr if previous hidden range doesn't exist + +idaman hidden_range_t *ida_export get_prev_hidden_range(ea_t ea); + + +/// Get pointer to next hidden range. +/// \param ea any address in the program +/// \return ptr to hidden range or nullptr if next hidden range doesn't exist + +idaman hidden_range_t *ida_export get_next_hidden_range(ea_t ea); + + +/// Get pointer to the first hidden range. +/// \return ptr to hidden range or nullptr + +idaman hidden_range_t *ida_export get_first_hidden_range(void); + + +/// Get pointer to the last hidden range. +/// \return ptr to hidden range or nullptr + +idaman hidden_range_t *ida_export get_last_hidden_range(void); + + +/// Delete hidden range. +/// \param ea any address in the hidden range +/// \return success + +idaman bool ida_export del_hidden_range(ea_t ea); + + +//-------------------------------------------------------------------------- +#define GET_ITEM_HEAD_BODY \ + if ( is_tail(get_flags32(ea)) ) \ + ea = prev_not_tail(ea); \ + return ea; +inline ea_t idaapi get_item_head(ea_t ea) { GET_ITEM_HEAD_BODY } + +//------------------------------------------------------------------------ +// M E M O R Y M A P P I N G +//------------------------------------------------------------------------ + +/// IDA supports memory mapping. References to the addresses from +/// the mapped range use data and meta-data from the mapping range. +/// \note You should set flag PR2_MAPPING in ph.flag2 to use memory mapping + + +/// Add memory mapping range. +/// \param from start of the mapped range (nonexistent address) +/// \param to start of the mapping range (existent address) +/// \param size size of the range +/// \return success + +idaman bool ida_export add_mapping(ea_t from, ea_t to, asize_t size); + + +/// Delete memory mapping range. +/// \param ea any address in the mapped range + +idaman void ida_export del_mapping(ea_t ea); + + +/// Translate address according to current mappings. +/// \param ea address to translate +/// \return translated address + +idaman ea_t ida_export use_mapping(ea_t ea); + +/// Get number of mappings. + +idaman size_t ida_export get_mappings_qty(void); + +/// Get memory mapping range by its number. +/// \param from start of the mapped range +/// \param to start of the mapping range +/// \param size size of the range +/// \param n number of mapping range (0..get_mappings_qty()-1) +/// \return false if the specified range doesn't exist, +/// otherwise returns `from`, `to`, `size` +idaman bool ida_export get_mapping( + ea_t *from, + ea_t *to, + asize_t *size, + size_t n); + + +#ifndef BYTES_SOURCE // undefined bit masks so no one can use them directly +#undef MS_VAL +#undef FF_IVL +#undef MS_CLS +#undef FF_CODE +#undef FF_DATA +#undef FF_TAIL +#undef FF_UNK +#undef MS_COMM +#undef FF_COMM +#undef FF_REF +#undef FF_LINE +#undef FF_NAME +#undef FF_LABL +#undef FF_ANYNAME +#undef FF_FLOW +#undef FF_SIGN +#undef FF_BNOT +#undef DT_TYPE +#undef FF_BYTE +#undef FF_WORD +#undef FF_DWORD +#undef FF_QWORD +#undef FF_OWORD +#undef FF_YWORD +#undef FF_ZWORD +#undef FF_FLOAT +#undef FF_DOUBLE +#undef FF_TBYTE +#undef FF_PACKREAL +#undef FF_STRLIT +#undef FF_STRUCT +#undef FF_ALIGN +#undef FF_CUSTOM +#undef MS_CODE +#undef FF_FUNC +#undef FF_IMMD +//#undef FF_JUMP +#undef MS_TAIL +#undef TL_TSFT +#undef TL_TOFF +#undef MAX_TOFF +#endif // BYTES_SOURCE + +// byte array to hex string +inline THREAD_SAFE ssize_t get_hex_string(char *buf, size_t bufsize, const uchar *bytes, size_t len) +{ + const char *const start = buf; + const char *const end = buf + bufsize; + for ( size_t i = 0; i < len; i++ ) + buf += ::qsnprintf(buf, end - buf, "%02X", *bytes++); + return buf - start; +} + + +#endif // BYTES_HPP + +``` + +`IdaSDK/compress.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef COMPRESS_HPP +#define COMPRESS_HPP + +#include + +/*! \file compress.hpp + + \brief Data compression functions + +*/ + +/// Compress data. +/// This function depends on the value of legacy_idb, so it is not completely +/// thread safe. However, legacy_idb does not change its value. +/// \return \ref PKZ_ + +idaman THREAD_SAFE int ida_export zip_deflate( + void *ud, + ssize_t (idaapi *file_reader)(void *ud, void *buf, size_t size), + ssize_t (idaapi *file_writer)(void *ud, const void *buf, size_t size)); + + +/// Uncompress data. +/// This function depends on the value of legacy_idb, so it is not completely +/// thread safe. However, legacy_idb does not change its value. +/// \return \ref PKZ_ + +idaman THREAD_SAFE int ida_export zip_inflate( + void *ud, + ssize_t (idaapi *file_reader)(void *ud, void *buf, size_t size), + ssize_t (idaapi *file_writer)(void *ud, const void *buf, size_t size)); + + +/// Process zip file and enumerate all files stored in it +/// \param zipfile name of zip file +/// \param callback callback for each file. params: +/// - ud: user data +/// - offset: offset in the zip file +/// - method: compression method (\ref compression_methods) +/// - csize: compressed size +/// - ucsize: uncompressed size +/// - attributes: file attributes +/// \param ud user data +/// \return \ref PKZ_ + +idaman THREAD_SAFE int ida_export process_zipfile( + const char *zipfile, + int (idaapi *callback)( + void *ud, + qoff64_t offset, + int method, + uint64 csize, + uint64 ucsize, + uint32 attributes, + const char *filename), + void *ud = nullptr); + + +/// Process zip file and enumerate all files stored in it +/// \param li input file +/// \param callback callback for each file. params: +/// - ud: user data +/// - offset: offset in the zip file +/// - method: compression method (\ref compression_methods) +/// - csize: compressed size +/// - ucsize: uncompressed size +/// - attributes: file attributes +/// \param ud user data +/// \return \ref PKZ_ + +idaman THREAD_SAFE int ida_export process_zip_linput( + linput_t *li, + int (idaapi *callback)( + void *ud, + qoff64_t offset, + int method, + uint64 csize, + uint64 ucsize, + uint32 attributes, + const char *filename), + void *ud = nullptr); + + +/// Search for specified entry in zip file, and calls the +/// callback with it, if found. +/// \param zipfile name of zip file +/// \param entry entry in zip file. E.g., "path/to/entry.dat" +/// \param callback callback for each file. params: +/// - ud: user data +/// - offset: offset in the zip file +/// - method: compression method (\ref compression_methods) +/// - csize: compressed size +/// - ucsize: uncompressed size +/// - attributes: file attributes +/// \param ud user data +/// \param case_sensitive should the search be case sensitive? +/// \return \ref PKZ_ + +idaman THREAD_SAFE int ida_export process_zipfile_entry( + const char *zipfile, + const char *entry, + int (idaapi *callback)( + void *ud, + qoff64_t offset, + int method, + uint64 csize, + uint64 ucsize, + uint32 attributes, + const char *filename), + void *ud = nullptr, + bool case_sensitive = true); + + +/// \defgroup PKZ_ Compression error codes +/// Returned by functions in compress.hpp +///@{ +#define PKZ_OK 0 +#define PKZ_ERRNO 1 +#define PKZ_STREAM_ERROR 2 +#define PKZ_DATA_ERROR 3 +#define PKZ_MEM_ERROR 4 +#define PKZ_BUF_ERROR 5 +#define PKZ_VERSION_ERROR 6 +#define PKZ_RERR 777 // read error +#define PKZ_WERR 778 // write error +///@} + +/// \defgroup compression_methods Compression methods +/// passed as 'method' parameter to callback functions in compress.hpp +///@{ +#define STORED 0 +#define SHRUNK 1 +#define REDUCED1 2 +#define REDUCED2 3 +#define REDUCED3 4 +#define REDUCED4 5 +#define IMPLODED 6 +#define TOKENIZED 7 +#define DEFLATED 8 +#define NUM_METHODS 9 /* index of last method + 1 */ +///@} + +extern bool legacy_idb; ///< for old idb files + +/// Create a linput to read a compressed input stream +/// \param in linput with compressed data, seeked to the stream beginning +/// \param insize size of compressed data. -1 - unknown +/// \param loc what to do upon closing the resulting linput +/// \return linput that can be used to read uncompressed data. +/// nullptr if any error (no more linput descriptors). + +idaman THREAD_SAFE linput_t *ida_export create_zip_linput( + linput_t *in, + ssize_t insize=-1, + linput_close_code_t loc=LOC_CLOSE); + + +#endif + +``` + +`IdaSDK/config.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _CONFIG_HPP +#define _CONFIG_HPP + +//----------------------------------------------------------------------- +/// \defgroup IDPOPT_T Option value types +/// Passed as 'value_type' parameter to ::set_options_t callbacks +///@{ +#define IDPOPT_STR 1 ///< string constant (char *) +#define IDPOPT_NUM 2 ///< number (uval_t *) +#define IDPOPT_BIT 3 ///< bit, yes/no (int *) +#define IDPOPT_I64 5 ///< 64bit number (int64 *) +#define IDPOPT_CST 6 ///< lexer (lexer_t*) + ///< Custom type, starting with a '{' + ///< Values of this type should be handled by + ///< ::set_options_t callbacks. E.g.,: + ///< \code + ///< ERROR_STRINGS = + ///< { + ///< {0, "Unknown error"}, + ///< {1, "Missing filename"}, + ///< {5, "Out-of-memory"} + ///< } + ///< \endcode + ///< For values of this type, the data that will + ///< be passed as the callback's 'value' parameter + ///< is the lexer instance that is being used + ///< to parse the configuration file. + ///< You can use \ref parse_json() (see parsejson.hpp) + ///< to parse JSON-format data + ///< NB: the '{' is already consumed by the parser, + ///< so you need to push it again if it's a part of the JSON object +#define IDPOPT_JVL 7 ///< json value (jvalue_t *) +///@} + +/// \defgroup IDPOPT_RET Option result codes +/// Predefined return values for ::set_options_t callbacks +///@{ +#define IDPOPT_OK nullptr ///< ok +#define IDPOPT_BADKEY ((char*)1) ///< illegal keyword +#define IDPOPT_BADTYPE ((char*)2) ///< illegal type of value +#define IDPOPT_BADVALUE ((char*)3) ///< illegal value (bad range, for example) +///@} + + +/// Callback - called when a config directive is processed in IDA. +/// Also see read_config_file() and processor_t::set_idp_options +/// \param keyword keyword encountered in IDA.CFG/user config file. +/// if nullptr, then an interactive dialog form should be displayed +/// \param value_type type of value of the keyword - one of \ref IDPOPT_T +/// \param value pointer to value +/// \param idb_loaded true if the ev_oldfile/ev_newfile events have been generated? +/// \return one of \ref IDPOPT_RET, otherwise a pointer to an error message + +typedef const char *(idaapi set_options_t)( + const char *keyword, + int value_type, + const void *value, + bool idb_loaded); + +/// \defgroup IDAOPT_PRIO Option priority +/// Specifies the priority of a configuration option. Since options may +/// be specified in different way, and applied in various orders, we need +/// option priorities. +/// Normally the default priority option does not overwrite the existing value +/// whereas the high priority one does. +/// High priority options may be stored in the database to be available +/// in the next session. +///@{ +#define IDPOPT_PRI_DEFAULT 1 ///< default priority - taken from config file +#define IDPOPT_PRI_HIGH 2 ///< high priority - received from UI or a script function +///@} + + +//------------------------------------------------------------------------- +/// Parse the value type for the value token 'value'. +/// This is mostly used for converting from values that a cfgopt_handler_t +/// receives, into data that callbacks +/// - processor_t::set_idp_options +/// - debugger_t::set_dbg_options +/// expect. +/// +/// Plugins that wish to use options shouldn't rely on this, +/// and use the cfgopt_t utility instead. +/// +/// \param out parsed data +/// \param lx the lexer in use +/// \param value the value token +/// \return true if guessing didn't lead to an error, false otherwise. +/// note that even if 'true' is returned, it doesn't mean the +/// type could be guessed: merely that no syntax error occurred. +class lexer_t; +struct token_t; +class idc_value_t; +idaman bool ida_export parse_config_value( + idc_value_t *out, + lexer_t *lx, + const token_t &value); + +//------------------------------------------------------------------------- +typedef const char *(idaapi cfgopt_handler_t)( + lexer_t *lx, + const token_t &keyword, + const token_t &value); + +//------------------------------------------------------------------------- +typedef const char *(idaapi cfgopt_handler2_t)( + lexer_t *lx, + const token_t &keyword, + const token_t &value, + int64 param1, + int64 param2); + +//------------------------------------------------------------------------- +typedef const char *(idaapi cfgopt_handler3_t)( + lexer_t *lx, + const token_t &keyword, + const token_t &value, + int64 param1, + int64 param2, + void *obj); + +//----------------------------------------------------------------------- +/// used by cfgopt_t. You shouldn't have to deal with those directly. +#define IDPOPT_NUM_INT (0) +#define IDPOPT_NUM_CHAR (1 << 24) +#define IDPOPT_NUM_SHORT (2 << 24) +#define IDPOPT_NUM_RANGE (1 << 26) +#define IDPOPT_NUM_UNS (1 << 27) + +#define IDPOPT_BIT_UINT 0 +#define IDPOPT_BIT_UCHAR (1 << 24) +#define IDPOPT_BIT_USHORT (2 << 24) +#define IDPOPT_BIT_BOOL (3 << 24) + +#define IDPOPT_STR_QSTRING (1 << 24) +#define IDPOPT_STR_LONG (1 << 25) + +#define IDPOPT_I64_RANGE (1 << 24) +#define IDPOPT_I64_UNS (1 << 25) + +#define IDPOPT_CST_PARAMS (1 << 24) + +#define IDPOPT_MBROFF (1 << 18) + +//------------------------------------------------------------------------- +struct cfgopt_t; +idaman const char *ida_export cfgopt_t__apply( + const cfgopt_t *_this, + int vtype, + const void *vdata); +idaman const char *ida_export cfgopt_t__apply2( + const cfgopt_t *_this, + int vtype, + const void *vdata, + void *obj); +idaman const char *ida_export cfgopt_t__apply3( + const cfgopt_t *_this, + lexer_t *lx, + int vtype, + const void *vdata, + void *obj); + +struct jvalue_t; +//------------------------------------------------------------------------- +// cfgopt_t objects are suitable for being statically initialized, and +// passed to 'read_config_file'. +// +// E.g., +// --- +// static const cfgopt_t g_opts[] = +// { +// cfgopt_t("AUTO_UNDEFINE", &auto_undefine, -1, 1), +// cfgopt_t("NOVICE", &novice, true), +// cfgopt_t("EDITOR", editor_buf, sizeof(editor_buf)), +// cfgopt_t("SCREEN_PALETTE", set_screen_palette), // specific handler for SCREEN_PALETTE +// }; +// +// ... +// +// read_config_file("myfile", g_opts, qnumber(g_opts), other_handler) +// --- +// +// NOTES: +// * so-called 'long' strings (the default) can span on multiple lines, +// and are terminated by a ';' +struct cfgopt_t +{ + const char *name; + union + { + void *ptr; + size_t mbroff; // offset of a structure member + cfgopt_handler_t *hnd; // to avoid reinterpret_cast and gcc's error: + cfgopt_handler2_t *hnd2; // "a reinterpret_cast is not a constant expression" + cfgopt_handler3_t *hnd3; // + }; + int flags; + struct num_range_t + { + constexpr num_range_t(int64 _min, int64 _max) : minval(_min), maxval(_max) {} + int64 minval; + int64 maxval; + }; + struct params_t + { + constexpr params_t(int64 _p1, int64 _p2) : p1(_p1), p2(_p2) {} + int64 p1; + int64 p2; + }; + union + { + size_t buf_size; + num_range_t num_range; + uint32 bit_flags; + params_t params; + void *mbroff_obj; + }; + + // IDPOPT_STR + constexpr cfgopt_t(const char *_n, char *_p, size_t _sz, bool _long = true) + : name(_n), ptr(_p), flags(IDPOPT_STR | (_long ? IDPOPT_STR_LONG : 0)), buf_size(_sz) + {} + constexpr cfgopt_t(const char *_n, qstring *_p, bool _long = true) + : name(_n), ptr(_p), flags(IDPOPT_STR | IDPOPT_STR_QSTRING | (_long ? IDPOPT_STR_LONG : 0)), buf_size(0) + {} + + // IDPOPT_NUM + constexpr cfgopt_t(const char *_n, int *_p) + : name(_n), ptr(_p), flags(IDPOPT_NUM), buf_size(0) {} + constexpr cfgopt_t(const char *_n, uint *_p) + : name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS), buf_size(0) {} + constexpr cfgopt_t(const char *_n, char *_p) + : name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_CHAR), buf_size(0) {} + constexpr cfgopt_t(const char *_n, uchar *_p) + : name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_CHAR), buf_size(0) {} + constexpr cfgopt_t(const char *_n, short *_p) + : name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_SHORT), buf_size(0) {} + constexpr cfgopt_t(const char *_n, ushort *_p) + : name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_SHORT), buf_size(0) {} + // IDPOPT_NUM + ranges + constexpr cfgopt_t(const char *_n, int *_p, int _min, int _max) + : name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_RANGE), num_range(_min, _max) {} + constexpr cfgopt_t(const char *_n, uint *_p, uint _min, uint _max) + : name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_RANGE), num_range(_min, _max) {} + constexpr cfgopt_t(const char *_n, char *_p, char _min, char _max) + : name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_CHAR | IDPOPT_NUM_RANGE), num_range(_min, _max) {} + constexpr cfgopt_t(const char *_n, uchar *_p, uchar _min, uchar _max) + : name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_CHAR | IDPOPT_NUM_RANGE), num_range(_min, _max) {} + constexpr cfgopt_t(const char *_n, short *_p, short _min, short _max) + : name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_RANGE | IDPOPT_NUM_SHORT), num_range(_min, _max) {} + constexpr cfgopt_t(const char *_n, ushort *_p, ushort _min, ushort _max) + : name(_n), ptr(_p), flags(IDPOPT_NUM | IDPOPT_NUM_UNS | IDPOPT_NUM_RANGE | IDPOPT_NUM_SHORT), num_range(_min, _max) {} + + // IDPOPT_BIT + constexpr cfgopt_t(const char *_n, bool *_p, bool _flags) : name(_n), ptr(_p), flags(IDPOPT_BIT | IDPOPT_BIT_BOOL), bit_flags(_flags) {} + constexpr cfgopt_t(const char *_n, uchar *_p, uchar _flags) : name(_n), ptr(_p), flags(IDPOPT_BIT | IDPOPT_BIT_UCHAR), bit_flags(_flags) {} + constexpr cfgopt_t(const char *_n, ushort *_p, ushort _flags) : name(_n), ptr(_p), flags(IDPOPT_BIT | IDPOPT_BIT_USHORT), bit_flags(_flags) {} + constexpr cfgopt_t(const char *_n, uint32 *_p, uint32 _flags) : name(_n), ptr(_p), flags(IDPOPT_BIT), bit_flags(_flags) {} + + // IDPOPT_I64 + constexpr cfgopt_t(const char *_n, int64 *_p) : name(_n), ptr(_p), flags(IDPOPT_I64), buf_size(0) {} + constexpr cfgopt_t(const char *_n, uint64 *_p) : name(_n), ptr(_p), flags(IDPOPT_I64 | IDPOPT_NUM_UNS), buf_size(0) {} + // IDPOPT_I64 + ranges + constexpr cfgopt_t(const char *_n, int64 *_p, int64 _min, int64 _max) + : name(_n), ptr(_p), flags(IDPOPT_I64 | IDPOPT_I64_RANGE), num_range(_min, _max) {} + constexpr cfgopt_t(const char *_n, uint64 *_p, uint64 _min, uint64 _max) + : name(_n), ptr(_p), flags(IDPOPT_I64 | IDPOPT_I64_UNS | IDPOPT_I64_RANGE), num_range(int64(_min), int64(_max)) {} + + // IDPOPT_CST + constexpr cfgopt_t(const char *_n, cfgopt_handler_t *_p) + : name(_n), hnd(_p), flags(IDPOPT_CST), buf_size(0) {} + // IDPOPT_CST + params + constexpr cfgopt_t(const char *_n, cfgopt_handler2_t *_p, int64 _p1=0, int64 _p2=0) + : name(_n), hnd2(_p), flags(IDPOPT_CST | IDPOPT_CST_PARAMS), params(_p1, _p2) {} + + // IDPOPT_JVL + constexpr cfgopt_t(const char *_n, jvalue_t *_p) + : name(_n), ptr(_p), flags(IDPOPT_JVL), buf_size(0) {} + + // configuration option based on the offset of a structure member + + // IDPOPT_STR + template + constexpr cfgopt_t(const char *_n, qstring T:: *, size_t _mbroff, bool _long = true) + : name(_n), + mbroff(_mbroff), + flags(IDPOPT_MBROFF | IDPOPT_STR | IDPOPT_STR_QSTRING | (_long ? IDPOPT_STR_LONG : 0)), + buf_size(0) + {} +#define CFGOPT_QS(nm, cfgt, cfgm, _long) \ + cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm), _long) + +#define CFGOPT_INNER_QS(nm, cfgt, cfgm, mt, mf, _long) \ + cfgopt_t(nm, &mt::mf, qoffsetof(cfgt, cfgm) + qoffsetof(mt, mf), _long) + + // IDPOPT_STR + template + constexpr cfgopt_t(const char *_n, char * T:: *, size_t _mbroff, bool _long = true) + : name(_n), + mbroff(_mbroff), + flags(IDPOPT_MBROFF | IDPOPT_STR | (_long ? IDPOPT_STR_LONG : 0)), + buf_size(0) + {} + + + // IDPOPT_NUM +#define CTR_CFGOPT(ctrtype, ctrflags) \ + template \ + constexpr cfgopt_t(const char *_n, ctrtype T:: *, size_t _mbroff) \ + : name(_n), \ + mbroff(_mbroff), \ + flags(IDPOPT_MBROFF|IDPOPT_NUM|ctrflags), \ + buf_size(0) \ + {} + CTR_CFGOPT(int, 0) + CTR_CFGOPT(uint, IDPOPT_NUM_UNS) + CTR_CFGOPT(char, IDPOPT_NUM_CHAR) + CTR_CFGOPT(uchar, IDPOPT_NUM_UNS|IDPOPT_NUM_CHAR) + CTR_CFGOPT(short, IDPOPT_NUM_SHORT) + CTR_CFGOPT(ushort, IDPOPT_NUM_SHORT|IDPOPT_NUM_UNS) +#undef CTR_CFGOPT + +#define CFGOPT_N(nm, cfgt, cfgm) \ + cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm)) + +#define CFGOPT_INNER_N(nm, cfgt, cfgm, mt, mf) \ + cfgopt_t(nm, &mt::mf, qoffsetof(cfgt, cfgm) + qoffsetof(mt, mf)) + + + // IDPOPT_NUM + ranges +#define CTR_CFGOPT(ctrtype, ctrflags) \ + template \ + constexpr cfgopt_t(const char *_n, ctrtype T:: *, size_t _mbroff, int64 _min, int64 _max) \ + : name(_n), \ + mbroff(_mbroff), \ + flags(IDPOPT_MBROFF|IDPOPT_NUM|IDPOPT_NUM_RANGE|ctrflags), \ + num_range(_min, _max) \ + {} + CTR_CFGOPT(int, 0) + CTR_CFGOPT(uint, IDPOPT_NUM_UNS) + CTR_CFGOPT(char, IDPOPT_NUM_CHAR) + CTR_CFGOPT(uchar, IDPOPT_NUM_UNS|IDPOPT_NUM_CHAR) + CTR_CFGOPT(short, IDPOPT_NUM_SHORT) + CTR_CFGOPT(ushort, IDPOPT_NUM_SHORT|IDPOPT_NUM_UNS) +#undef CTR_CFGOPT + +#define CFGOPT_R(nm, cfgt, cfgm, min, max) \ + cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm), min, max) + +#define CFGOPT_INNER_R(nm, cfgt, cfgm, mt, mf, min, max) \ + cfgopt_t(nm, &mt::mf, qoffsetof(cfgt, cfgm) + qoffsetof(mt, mf), min, max) + + + // IDPOPT_BIT +#define CTR_CFGOPT(ctrtype, ctrflags) \ + template \ + constexpr cfgopt_t(const char *_n, ctrtype T:: *, size_t _mbroff, ctrtype _flags) \ + : name(_n), \ + mbroff(_mbroff), \ + flags(IDPOPT_MBROFF|IDPOPT_BIT|ctrflags), \ + bit_flags(_flags) \ + {} + CTR_CFGOPT(bool, IDPOPT_BIT_BOOL); + CTR_CFGOPT(uchar, IDPOPT_BIT_UCHAR); + CTR_CFGOPT(ushort, IDPOPT_BIT_USHORT); + CTR_CFGOPT(uint32, 0); +#undef CTR_CFGOPT +#define CFGOPT_B(nm, cfgt, cfgm, _flags) \ + cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm), _flags) + +#define CFGOPT_INNER_B(nm, cfgt, cfgm, mt, mf, _flags) \ + cfgopt_t(nm, &mt::mf, qoffsetof(cfgt, cfgm) + qoffsetof(mt, mf), _flags) + + + // IDPOPT_I64 + template + constexpr cfgopt_t(const char *_n, int64 T:: *, size_t _mbroff) + : name(_n), + mbroff(_mbroff), + flags(IDPOPT_MBROFF|IDPOPT_I64), + buf_size(0) + {} + template + constexpr cfgopt_t(const char *_n, uint64 T:: *, size_t _mbroff) + : name(_n), + mbroff(_mbroff), + flags(IDPOPT_MBROFF|IDPOPT_I64|IDPOPT_NUM_UNS), + buf_size(0) + {} + + // IDPOPT_JVL + template + constexpr cfgopt_t(const char *_n, jvalue_t T:: *, size_t _mbroff) + : name(_n), + mbroff(_mbroff), + flags(IDPOPT_MBROFF | IDPOPT_JVL), + buf_size(0) + {} + +#define CFGOPT_J(nm, cfgt, cfgm) \ + cfgopt_t(nm, &cfgt::cfgm, qoffsetof(cfgt, cfgm)) + + // IDPOPT_I64 + ranges + template + constexpr cfgopt_t(const char *_n, int64 T:: *, size_t _mbroff, int64 _min, int64 _max) + : name(_n), + mbroff(_mbroff), + flags(IDPOPT_MBROFF|IDPOPT_I64|IDPOPT_I64_RANGE), + num_range(_min, _max) + {} + template + constexpr cfgopt_t(const char *_n, uint64 T:: *, size_t _mbroff, uint64 _min, uint64 _max) + : name(_n), + mbroff(_mbroff), + flags(IDPOPT_MBROFF|IDPOPT_I64|IDPOPT_I64_UNS|IDPOPT_I64_RANGE), + num_range(int64(_min), int64(_max)) + {} + + // IDPOPT_CST + params + constexpr cfgopt_t(const char *_n, cfgopt_handler3_t *_p, int64 _p1=0, int64 _p2=0) + : name(_n), hnd3(_p), flags(IDPOPT_MBROFF|IDPOPT_CST), params(_p1, _p2) {} + + int type() const { return flags & 0xf; } + int qualifier() const { return flags & 0xf000000; } + bool is_mbroff() const { return (flags & IDPOPT_MBROFF) != 0; } + bool get_number( + int64 *out, + lexer_t *lx, + const token_t &_t, + int range_bit, + int usign_bit) const; + const char *apply(int vtype, const void *vdata, void *obj=nullptr) const + { + return cfgopt_t__apply3(this, nullptr, vtype, vdata, obj); + } + const char *apply(lexer_t *lx, int vtype, const void *vdata, void *obj=nullptr) const + { + return cfgopt_t__apply3(this, lx, vtype, vdata, obj); + } +}; + +enum cfg_input_kind_t +{ + cik_string = 0, + cik_filename, + cik_path, +}; + +/// Parse the input, and apply options. +/// +/// \param input input file name, or string +/// \param input_kind is input a string, filename or file path +/// \param opts options destcriptions +/// \param nopts the number of entries present in the 'opts' array +/// \param defhdlr a handler to be called, if a directive couldn't be found in 'opts' +/// \param defines a list of preprocessor identifiers to define (so it is +/// possible to use #ifdef checks in the file.) +/// NB: the actual identifier defined by the parser will be +/// surrounded with double underscores (e.g., passing 'FOO' +/// will result in '__FOO__' being defined) +/// Additionally, the parser will also define a similar macro +/// with the current processor name (e.g., __ARM__) +/// \param ndefines the number of defines in the list +/// \param obj see cfgopt_t constructor based on the offset of a structure member +/// \return true if parsing finished without errors, false if there was a +/// syntax error, callback returned an error, or no file was found +/// at all. + +idaman bool ida_export read_config( + const char *input, + cfg_input_kind_t input_kind, + const cfgopt_t opts[], + size_t nopts, + cfgopt_handler_t *defhdlr, + const char *const *defines = nullptr, + size_t ndefines = 0); + +idaman bool ida_export read_config2( + const char *input, + cfg_input_kind_t input_kind, + const cfgopt_t opts[], + size_t nopts, + cfgopt_handler_t *defhdlr, + const char *const *defines = nullptr, + size_t ndefines = 0, + void *obj = nullptr); + +inline bool read_config_file2( + const char *filename, + const cfgopt_t opts[], + size_t nopts, + cfgopt_handler_t *defhdlr, + const char *const *defines = nullptr, + size_t ndefines = 0, + void *obj = nullptr) +{ + return read_config2(filename, cik_filename, opts, nopts, defhdlr, defines, ndefines, obj); +} + +/// Search for all IDA system files with the given name. +/// This function will search, in that order, for the following files: +/// -# %IDADIR%/cfg/ +/// -# for each directory 'ONEDIR' in %IDAUSR%: %ONEDIR%/cfg/ +/// +/// For each directive in each of those files, the same processing as +/// that of read_config will be performed. + +inline bool read_config_file( + const char *filename, + const cfgopt_t opts[], + size_t nopts, + cfgopt_handler_t *defhdlr, + const char *const *defines = nullptr, + size_t ndefines = 0) +{ + return read_config(filename, cik_filename, opts, nopts, defhdlr, defines, ndefines); +} + + +/// For each directive in 'string', the same processing as that of +/// read_config will be performed. +inline bool read_config_string( + const char *string, + const cfgopt_t opts[], + size_t nopts, + cfgopt_handler_t *defhdlr, + const char *const *defines = nullptr, + size_t ndefines = 0) +{ + return read_config(string, cik_string, opts, nopts, defhdlr, defines, ndefines); +} + + +typedef void idaapi config_changed_cb_t(const cfgopt_t &opt, int vtype, const void *vdata); + +/// Register array of config options. +/// This function can be used by a plugin to register the config options. +/// After registering an option, it becomes usable by the +/// process_config_directive() function. +/// \param opts array of config options +/// \param nopts number of options to install. 0 means uninstall +/// \param cb callback that will be invoked upon changing a config option +/// \param obj see cfgopt_t constructor based on the offset of a structure member +/// \return success + +idaman bool ida_export register_cfgopts( + const cfgopt_t opts[], + size_t nopts, + config_changed_cb_t cb=nullptr, + void *obj=nullptr); + +/// Get json value from ida.cfg +/// \param out returned json value +/// \param key configuration key +/// \return success + +idaman bool ida_export get_config_value(jvalue_t *out, const char *key); + +//------------------------------------------------------------------------- +// A set of (static const) config options +struct cfgopt_set_t +{ + const cfgopt_t *opts = nullptr; + size_t nopts = 0; + config_changed_cb_t *cb = nullptr; + void *obj = nullptr; +}; +DECLARE_TYPE_AS_MOVABLE(cfgopt_set_t); +struct cfgopt_set_vec_t : public qvector {}; + + +#endif // _CONFIG_HPP + +``` + +`IdaSDK/cvt64.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 2005-2025 Hex-Rays SA + * ALL RIGHTS RESERVED. + * + * Convert 'idb' file to 'i64' file. + * + * Conversion is performed by IDA64 in the special conversion mode. + * To convert data we need to read it in the 32-bit format from the idb file, + * convert it to the 64-bit format and write it to the i64 file. + * + * This file contains the conversion helpers functions for plugins. + * + * Also see the following events: + * * processor_t::ev_cvt64_supval + * * processor_t::ev_cvt64_hashval + * + * For more information check the cvt64.md file. + */ + +#pragma once + +//------------------------------------------------------------------------- +// Conversion of 32-bit databases to 64-bit is performed by ida64, +// only in IDA Pro and IDA Teams +#if !defined(IDAFREE) && !defined(IDAHOME) +# if defined(__EA64__) +# define CVT64 +# else +# define UPG32 // enable the logic to upgrade 32-bit databases +# endif +#endif + +//------------------------------------------------------------------------ +// This class keeps 32-bit addresses regardless of IDA bitness +struct range32_t +{ + ea32_t start_ea = 0; + ea32_t end_ea = 0; + + range32_t() {} + range32_t(ea_t ea1, ea_t ea2) : start_ea(ea1), end_ea(ea2) {} +}; + +//------------------------------------------------------------------------ +/// Unpack an address. +/// This helper deserializes an address from its serialialed form. +/// If IDA is running in the conversion mode, it reads a 32-bit address, otherwise +/// the number of bits in the address depends on the bitness of IDA (IDA32 assumes +/// 32-bit addresses, and IDA64 assumes 64-bit addresses). +/// 32-bit BADADDR is converted into 64-bit BADADDR in the conversion mode. +THREAD_SAFE inline ea_t mmdsr_unpack_ea(memory_deserializer_t &mmdsr, ea_t base = 0) +{ +#ifdef CVT64 + if ( is_cvt64() ) + { + ea_t ea = ea32_t(mmdsr.unpack_dd()+base); + return ea == BADADDR32 ? BADADDR : ea; + } +#endif + return mmdsr.unpack_ea()+base; +} + +//------------------------------------------------------------------------ +/// Unpack an address relative to the specified base address. +THREAD_SAFE inline ea_t mmdsr_unpack_ea_neg(memory_deserializer_t &mmdsr, ea_t base) +{ +#ifdef CVT64 + if ( is_cvt64() ) + { + ea_t ea = ea32_t(base - mmdsr.unpack_dd()); + return ea == BADADDR32 ? BADADDR : ea; + } +#endif + return base - mmdsr.unpack_ea(); +} + +//------------------------------------------------------------------------ +/// Unpack a netnode index +THREAD_SAFE inline ea_t mmdsr_unpack_node2ea(memory_deserializer_t &mmdsr) +{ + nodeidx_t ndx = mmdsr_unpack_ea(mmdsr); + return node2ea(ndx); +} + +//------------------------------------------------------------------------ +/// Unpack as a signed value +THREAD_SAFE inline sval_t mmdsr_unpack_sval(memory_deserializer_t &mmdsr, sval_t base=0) +{ +#ifdef CVT64 + if ( is_cvt64() ) + return (int32)(mmdsr.unpack_dd() + base); +#endif + return mmdsr.unpack_ea() + base; +} + +//------------------------------------------------------------------------ +/// Unpack vector of addresses. +THREAD_SAFE inline void mmdsr_unpack_eavec( + eavec_t *vec, + memory_deserializer_t &mmdsr, + ea_t ea) +{ +#ifdef CVT64 + if ( is_cvt64() ) + { + ea_t old = ea; + int n = mmdsr.unpack_dw(); + vec->resize_noinit(n); + for ( int i=0; i < n; ++i ) + { + old = mmdsr_unpack_ea(mmdsr, old); + vec->at(i) = old; + } + return; + } +#endif + return mmdsr.unpack_eavec(vec, ea); +} + +//------------------------------------------------------------------------ +/// Read a stored (without packing) address. +THREAD_SAFE inline ea_t mmdsr_read_ea(memory_deserializer_t &mmdsr) +{ +#ifdef CVT64 + if ( is_cvt64() ) + { + ea32_t ea32 = BADADDR32; + mmdsr.read(&ea32, sizeof(ea32)); + return ea32; + } +#endif + ea_t ea = BADADDR; + mmdsr.read(&ea, sizeof(ea)); + return ea; +} + +//------------------------------------------------------------------------ +/// Convert a blob whose content does not depend on the IDA bitness. +/// This function is very simple: it reads from a netnode and writes back to the +/// same netnode. While this action looks superfluous, we need it because the +/// netnode level functions read from the input 32-bit btree and write the +/// output 64-bit btree. In other words, this function copies blob of information +/// from one btree to another. +inline int cvt64_blob(netnode node, nodeidx_t start, uchar tag) +{ + bytevec_t buf; + if ( node.getblob(&buf, start, tag) > 0 ) + { + node.setblob(buf.begin(), buf.size(), start, tag); + return 1; + } + return 0; +} + +#ifdef CVT64 +//------------------------------------------------------------------------ +/// Descriptor of information stored in a netnode. +/// Each NETNODE+TAG require a separate descriptor. +/// One descriptor can be used for a single index or for all indexes. +/// This description is used by cvt64_node_supval_for_event() to convert +/// netnode data. +struct cvt64_node_tag_t +{ + /// netnode id + nodeidx_t node; + + /// tag, can be supplemented with NETMAP_... and additional CVT64_ZERO_IDX flags + /// use the following flags to specify: + /// NETMAP_VAL - value type is nodeidx32_t that will be converted into nodeidx64_t + /// example: any altval() + /// NETMAP_VAL_NDX - value is a netnode index or address + /// valid with NETMAP_VAL, + /// BADADDR32 will be converted to BADADDR + /// NETMAP_V8 - uchar value + /// If none of the above flags are set, then the value will be copied without any conversion. + /// For example, if value was written to database using the following call: + /// helper.easet(ea, dxref, DXREF_TAG) + /// then use the following value for TAG: + /// NETMAP_VAL|NETMAP_VAL_NDX|DXREF_TAG +#define CVT64_ZERO_IDX 0x00800000 ///< EXACT_ALT specifies the single value at index 0 + int tag; + + /// The index described by the descriptor. + /// For all indexes of the given tag, set EXACT_ALT to 0. + /// For a single index, set EXACT_ALT to the index. If the index to convert + /// is zero, also set CVT64_ZERO_IDX. + /// Note: + /// Place descriptors with EXACT_ALT above all other descriptors in the table. + nodeidx_t exact_alt; +}; + +// Descriptors for popular database values +#define CVT64_NODE_DEVICE { helper, stag, nodeidx_t(-1) } +#define CVT64_NODE_IDP_FLAGS { helper, atag|NETMAP_VAL, nodeidx_t(-1) } + +/// Helper for the processor_t::ev_cvt64_supval event. +/// This function converts the information stored in netnodes. It can +/// handle standard types of information like altvals, addreses, supvals. +/// If information is stored as a blob with complex structure, then it must +/// be handled manually: read, convert, and write back to the same netnode. +/// \param va - arguments for processor_t::ev_cvt64_supval +/// \param node_info - descriptors +/// \param node_info_qty - size of the descriptors array +/// \return result of processor_t::ev_cvt64_supval event processing + +idaman int ida_export cvt64_node_supval_for_event( + va_list va, + const cvt64_node_tag_t *node_info, + size_t node_info_qty); + +#endif // CVT64 + +``` + +`IdaSDK/dbg.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef DBG_HPP +#define DBG_HPP + +#include +#include +#include // for callui() and ui_notification_t + +/*! \file dbg.hpp + + \brief Contains functions to control the debugging of a process. + + See \ref dbg_funcs for a complete explanation of these functions. + + These functions are inlined for the kernel. + They are not inlined for the user-interfaces. +*/ + +//-------------------------------------------------------------------- +// D E B U G G E R I N F O R M A T I O N +//-------------------------------------------------------------------- + +/// This structure contains information about the current debugger. +/// (nullptr if no debugger was loaded) - see idd.hpp for details about this structure. +/// +/// All functions defined in this structure should only be called by the kernel !!! +idaman debugger_t ida_export_data *dbg; + +//-------------------------------------------------------------------- +// D E B U G G E R C A L L B A C K S +//-------------------------------------------------------------------- +/// Debugger notification codes. +/// +/// A plugin can receive notifications of all major events in the +/// debugger, by calling the hook_event_listener() function +/// with ::HT_DBG as ::hook_type_t (see loader.hpp for details about +/// installing and removing such callbacks). +/// +/// IDA generates two major different types of debugger notifications: +/// +/// - debugger event notification: +/// this notification monitors usual events occurring during the +/// execution of a process. +/// These event notifications are always generated for any process. +/// Some of these event notifications are interpreted by IDA +/// (high-level events), while others are directly generated by the +/// debugger module (low-level events). +/// Low-level events always return a ::debug_event_t structure as an argument. +/// +/// - debugger asynchronous function result notification: +/// such a notification occurs only when a debugger properly terminated +/// the execution of an asynchronous function (see \ref dbg_funcs) +/// +/// How to control the process execution (after the execution of all notification +/// handlers) from the notification handler: +/// +/// - to force the process to STOP: +/// call suspend_process(). +/// In this case, the current debugger command will be aborted and no new +/// request will be started. +/// +/// - to force the process to CONTINUE: +/// call continue_process(). +/// In this case, no new request will be started. +/// +/// - to start new debugger command(s): +/// call as many request_COMMAND() as needed, then call run_requests(). +/// In this case, the current debugger command (if any) will be aborted. +/// (see \ref dbg_funcs in this file for more details about requests) +/// +/// - else, the process execution will depend on the current debugger options or +/// object settings. Some examples: +/// - a new loaded library will stop the process depending on the associated debugger option. +/// - a breakpoint will stop the process depending on its properties. +/// +/// A plugin must not call asynchronous debugger functions from the notification handler! +/// Use the REQUEST QUEUE mechanism instead (request_...()). +/// +/// If the plugin wants to access the process memory from a notification point, +/// it should call invalidate_dbgmem_config() and/or invalidate_dbgmem_contents() +/// functions. The invalidate_dbgmem_config() is really slow, so do not call it +/// unless the process memory config have changed after the last time the process +/// was suspended. The invalidate_dbgmem_contents() is fast and flushes the +/// memory cache in the ida kernel. Without it, functions like get_byte() would +/// return stale values! +enum dbg_notification_t +{ + dbg_null = 0, + + // debugger low-level event notifications (see IDD.HPP for details). + + dbg_process_start, ///< \param event (const ::debug_event_t *) + ///< \note This event notification is also an asynchronous + ///< function result notification for start_process() ! + + dbg_process_exit, ///< \param event (const ::debug_event_t *) + ///< \note This event notification is also an asynchronous + ///< function result notification for start_process() ! + + dbg_process_attach, ///< \param event (const ::debug_event_t *) + ///< \note This event notification is also an asynchronous + ///< function result notification for start_process() ! + + dbg_process_detach, ///< \param event (const ::debug_event_t *) + ///< \note This event notification is also an asynchronous + ///< function result notification for start_process() ! + + dbg_thread_start, ///< \param event (const ::debug_event_t *) + + dbg_thread_exit, ///< \param event (const ::debug_event_t *) + + dbg_library_load, ///< \param event (const ::debug_event_t *) + + dbg_library_unload, ///< \param event (const ::debug_event_t *) + + dbg_information, ///< \param event (const ::debug_event_t *) + + dbg_exception, ///< \param event (const ::debug_event_t *) + ///< \param[out] warn (int *) filled with: + ///< - -1: display an exception warning dialog + ///< if the process is suspended. + ///< - 0: never display an exception warning dialog. + ///< - 1: always display an exception warning dialog. + + // debugger high-level event notifications + + dbg_suspend_process, ///< The process is now suspended. + ///< \param event (const ::debug_event_t *) + ///< \note This event notification is also an asynchronous + ///< function result notification for suspend_process() ! + + dbg_bpt, ///< A user defined breakpoint was reached. + ///< \param tid (::thid_t) + ///< \param bptea (::ea_t) + ///< \param[out] warn (int *) filled with: + ///< - -1: display an exception warning dialog + ///< if the process is suspended. + ///< - 0: never display an exception warning dialog. + ///< - 1: always display an exception warning dialog. + + dbg_trace, ///< A step occurred (one instruction was executed). This event + ///< notification is only generated if step tracing is enabled. + ///< \param tid (::thid_t) thread ID + ///< \param ip (::ea_t) current instruction pointer. + ///< usually points after the executed instruction + ///< \retval 1 do not log this trace event + ///< \retval 0 log it + + dbg_request_error, ///< An error occurred during the processing of a request. + ///< \param failed_command (::ui_notification_t) + ///< \param failed_dbg_notification (::dbg_notification_t) + + // debugger asynchronous function result notifications + // Please note some low-level event notifications also act as asynchronous + // function result notifications. + + dbg_step_into, ///< \param event (const ::debug_event_t *) + + dbg_step_over, ///< \param event (const ::debug_event_t *) + + dbg_run_to, ///< \param event (const ::debug_event_t *) + + dbg_step_until_ret, ///< \param event (const ::debug_event_t *) + + dbg_bpt_changed, ///< Breakpoint has been changed. + ///< \param bptev_code (int) \ref BPTEV_ + ///< \param bpt (::bpt_t *) + + dbg_started_loading_bpts, ///< Started loading breakpoint info from idb + dbg_finished_loading_bpts, ///< Finished loading breakpoint info from idb + + dbg_last, ///< The last debugger notification code +}; + +/// \defgroup BPTEV_ Breakpoint modification events +/// Passed as 'bptev_code' parameter to ::dbg_bpt_changed callback +///@{ +#define BPTEV_ADDED 0 ///< Breakpoint has been added +#define BPTEV_REMOVED 1 ///< Breakpoint has been removed +#define BPTEV_CHANGED 2 ///< Breakpoint has been modified +///@} + +#ifndef __UI__ + +//-------------------------------------------------------------------- +// D E B U G G E R F U N C T I O N S +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs Debugger functions +/// +/// Control the debugging of a process. +/// +/// Debugger functions complete either SYNCHRONOUSLY or ASYNCHRONOUSLY: +/// +/// - SYNCHRONOUS FUNCTIONS execute the entire action before the function returns. +/// +/// - ASYNCHRONOUS FUNCTIONS return before the action has executed in its +/// entirety. They simply start the action, but the result of the action will +/// only be available later. For example, run_to() can execute a lot of +/// instructions before terminating. +/// Such functions provide a notification code to indicate the end of their +/// execution (see the 'Notification' keyword in the function documentation). +/// Install a callback using hook_event_listener() to be notified +/// when the action is terminated. +/// +/// DEBUGGER COMMANDS are functions who influence the execution of the debugged +/// process. They are available in 2 forms: +/// +/// - COMMAND(): (e.g. suspend_process()) +/// In this mode, the command will be directly executed. However, it is forbidden +/// to use asynchronous commands in this mode from a debugger notification handler +/// (see ::dbg_notification_t). +/// +/// - request_COMMAND(): (e.g. request_suspend_process()) +/// In this mode, a REQUEST to run the command will be memorized at the end of +/// the REQUEST QUEUE (see below). This is mandatory to use this mode for asynchronous +/// commands from a debugger notification handler (see ::dbg_notification_t). +/// +/// +/// The REQUEST QUEUE contains a list of planned debugger commands. +/// These commands will be started only in the following cases: +/// +/// - the previous command terminated, and no call to suspend_process() +/// or continue_process() occurred in the asynchronous function result +/// notification handler (if any). +/// +/// - run_requests() was called. +/// Please note that when called from a debugger notification handler the +/// queued requests will only be started after the execution of all +/// notification handlers. +/// +/// A request which fails to start (by returning 0) will generate a +/// ::dbg_request_error notification. +///@{ + +/// Execute requests until all requests are processed or an asynchronous +/// function is called. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \return false if not all requests could be processed +/// (indicates an asynchronous function was started) +/// \note If called from a notification handler, the execution of requests will +/// be postponed to the end of the execution of all notification handlers. + +inline bool idaapi run_requests(void) { return callui(ui_dbg_run_requests).cnd; } + + +/// Get the current running request. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \return ui_null if no running request + +inline ui_notification_t idaapi get_running_request(void) { return (ui_notification_t)callui(ui_dbg_get_running_request).i; } + + +/// Is a request currently running? + +inline bool is_request_running(void) { return get_running_request() != ui_null; } + + +/// Get the notification associated (if any) with the current running request. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \return dbg_null if no running request + +inline dbg_notification_t idaapi get_running_notification(void) { return (dbg_notification_t)callui(ui_dbg_get_running_notification).i; } + + +/// Clear the queue of waiting requests. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \note If a request is currently running, this one isn't stopped. + +inline void idaapi clear_requests_queue(void) { callui(ui_dbg_clear_requests_queue); } +///@} dbg_funcs + +//-------------------------------------------------------------------- +// P R O C E S S C O M M A N D S +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_cmds Process commands +/// \ingroup dbg_funcs +/// +/// Use these functions to manipulate the debugged process. +///@{ + +/// Return the state of the currently debugged process. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \return one of \ref DSTATE_ + +inline int idaapi get_process_state(void) { return callui(ui_dbg_get_process_state).i; } + +#endif // __UI__ + +/// \defgroup DSTATE_ Debugged process states +/// See get_process_state(), set_process_state(), invalidate_dbg_state() +///@{ +#define DSTATE_SUSP -1 ///< process is suspended and will not continue +#define DSTATE_NOTASK 0 ///< no process is currently debugged +#define DSTATE_RUN 1 ///< process is running +///@} +inline bool idaapi is_valid_dstate(int state) +{ + return state == DSTATE_SUSP || state == DSTATE_NOTASK || state == DSTATE_RUN; +} + +/// \defgroup DBGINV_ Debugged process invalidation options +/// See set_process_state() and invalidate_dbg_state() +///@{ +#define DBGINV_MEMORY 0x0001 ///< invalidate cached memory contents +#define DBGINV_MEMCFG 0x0002 ///< invalidate cached process segmentation +#define DBGINV_REGS 0x0004 ///< invalidate cached register values +#define DBGINV_ALL 0x7FFF ///< invalidate everything +#define DBGINV_REDRAW 0x8000 ///< refresh the screen +#define DBGINV_NONE 0 ///< invalidate nothing +///@} + +#ifndef __UI__ + +/// Set new state for the debugged process. +/// Notifies the IDA kernel about the change of the debugged process state. +/// For example, a debugger module could call this function when it knows +/// that the process is suspended for a short period of time. +/// Some IDA API calls can be made only when the process is suspended. +/// The process state is usually restored before returning control to the caller. +/// You must know that it is ok to change the process state, doing it at arbitrary +/// moments may crash the application or IDA. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param newstate new process state (one of \ref DSTATE_) +/// if #DSTATE_NOTASK is passed then the state is not changed +/// \param p_thid ptr to new thread id. may be nullptr or pointer to #NO_THREAD. +/// the pointed variable will contain the old thread id upon return +/// \param dbginv \ref DBGINV_ +/// \return old debugger state (one of \ref DSTATE_) + +inline int idaapi set_process_state(int newstate, thid_t *p_thid, int dbginv) { return callui(ui_dbg_set_process_state, newstate, p_thid, dbginv).i; } + + +/// Invalidate cached debugger information. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param dbginv \ref DBGINV_ +/// \return current debugger state (one of \ref DSTATE_) + +inline int idaapi invalidate_dbg_state(int dbginv) +{ + return set_process_state(DSTATE_NOTASK, nullptr, dbginv); +} + + +/// Start a process in the debugger. +/// \sq{Type, Asynchronous function - available as Request, +/// Notification, ::dbg_process_start} +/// \note You can also use the run_to() function to easily start the execution +/// of a process until a given address is reached. +/// \note For all parameters, a nullptr value indicates the debugger will take +/// the value from the defined Process Options. +/// \param path path to the executable to start +/// \param args arguments to pass to process +/// \param sdir starting directory for the process +/// \retval -1 impossible to create the process +/// \retval 0 the starting of the process was cancelled by the user +/// \retval 1 the process was properly started + +inline int idaapi start_process( + const char *path = nullptr, + const char *args = nullptr, + const char *sdir = nullptr) +{ + return callui(ui_dbg_start_process, path, args, sdir).i; +} + + +/// Post a start_process() request + +inline int idaapi request_start_process( + const char *path = nullptr, + const char *args = nullptr, + const char *sdir = nullptr) +{ + return callui(ui_dbg_request_start_process, path, args, sdir).i; +} + + +/// Suspend the process in the debugger. +/// \sq{ +/// Type, +/// - Synchronous function (if in a notification handler) +/// - Asynchronous function (everywhere else) +/// - available as Request, +/// Notification, +/// - none (if in a notification handler) +/// - ::dbg_suspend_process (everywhere else) +/// } +/// +/// \note The suspend_process() function can be called from a notification +/// handler to force the stopping of the process. +/// In this case, no notification will be generated. +/// When you suspend a process, the running command is always aborted. + +inline bool idaapi suspend_process(void) { return callui(ui_dbg_suspend_process).cnd; } + +/// Post a suspend_process() request + +inline bool idaapi request_suspend_process(void) { return callui(ui_dbg_request_suspend_process).cnd; } + + +/// Continue the execution of the process in the debugger. +/// \sq{Type, Synchronous function - available as Request, +/// Notification, none (synchronous function)} +/// \note The continue_process() function can be called from a notification +/// handler to force the continuation of the process. In this case +/// the request queue will not be examined, IDA will simply resume +/// execution. Usually it makes sense to call request_continue_process() +/// followed by run_requests(), so that IDA will first start a queued +/// request (if any) and then resume the application. + +inline bool idaapi continue_process(void) { return callui(ui_dbg_continue_process).cnd; } + +/// Post a continue_process() request. +/// \note This requires an explicit call to run_requests() + +inline bool idaapi request_continue_process(void) { return callui(ui_dbg_request_continue_process).cnd; } + +/// Continue the execution of the process in the debugger backwards. +/// Can only be used with debuggers that support time-travel debugging. +/// \sq{Type, Synchronous function - available as Request, +/// Notification, none (synchronous function)} +/// \note The continue_backwards() function can be called from a notification +/// handler to force the continuation of the process. In this case +/// the request queue will not be examined, IDA will simply resume +/// execution. Usually it makes sense to call request_continue_process() +/// followed by run_requests(), so that IDA will first start a queued +/// request (if any) and then resume the application. + +inline bool idaapi continue_backwards(void) { return callui(ui_dbg_continue_backwards).cnd; } + +/// Post a continue_backwards() request. +/// \note This requires an explicit call to run_requests() + +inline bool idaapi request_continue_backwards(void) { return callui(ui_dbg_request_continue_backwards).cnd; } + +/// Terminate the debugging of the current process. +/// \sq{Type, Asynchronous function - available as Request, +/// Notification, ::dbg_process_exit} + +inline bool idaapi exit_process(void) { return callui(ui_dbg_exit_process).cnd; } + +/// Post an exit_process() request. + +inline bool idaapi request_exit_process(void) { return callui(ui_dbg_request_exit_process).cnd; } + + +/// Take a snapshot of running processes and return their description. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param[out] proclist array with information about each running process +/// \return number of processes or -1 on error + +inline ssize_t idaapi get_processes(procinfo_vec_t *proclist) { return callui(ui_dbg_get_processes, proclist).ssize; } + + +/// Attach the debugger to a running process. +/// \sq{Type, Asynchronous function - available as Request, +/// Notification, ::dbg_process_attach} +/// \note This function shouldn't be called as a request if #NO_PROCESS is used. +/// \param pid PID of the process to attach to. If #NO_PROCESS, a dialog box +/// will interactively ask the user for the process to attach to. +/// \param event_id event to trigger upon attaching +/// \retval -4 debugger was not inited +/// \retval -3 the attaching is not supported +/// \retval -2 impossible to find a compatible process +/// \retval -1 impossible to attach to the given process (process died, privilege +/// needed, not supported by the debugger plugin, ...) +/// \retval 0 the user cancelled the attaching to the process +/// \retval 1 the debugger properly attached to the process + +inline int idaapi attach_process(pid_t pid=NO_PROCESS, int event_id=-1) { return callui(ui_dbg_attach_process, pid, event_id).i; } + +/// Post an attach_process() request + +inline int idaapi request_attach_process(pid_t pid, int event_id) { return callui(ui_dbg_request_attach_process, pid, event_id).i; } + + +/// Detach the debugger from the debugged process. +/// \sq{Type, Asynchronous function - available as Request, +/// Notification, ::dbg_process_detach} + +inline bool idaapi detach_process(void) { return callui(ui_dbg_detach_process).cnd; } + +/// Post a detach_process() request + +inline bool idaapi request_detach_process(void) { return callui(ui_dbg_request_detach_process).cnd; } + + +/// Is the debugger busy?. +/// Some debuggers do not accept any commands while the debugged application +/// is running. For such a debugger, it is unsafe to do anything with the +/// database (even simple queries like get_byte may lead to undesired consequences). +/// Returns: true if the debugged application is running under such a debugger + +inline bool idaapi is_debugger_busy(void) { return callui(ui_dbg_is_busy).cnd; } + +///@} dbg_funcs_cmds + + +//-------------------------------------------------------------------- +// T H R E A D S +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_threads Threads +/// \ingroup dbg_funcs +/// +/// Inspect/Manipulate threads of debugged process. +///@{ + +/// Get number of threads. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline int idaapi get_thread_qty(void) { return callui(ui_dbg_get_thread_qty).i; } + + +/// Get the ID of a thread. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param n number of thread, is in range 0..get_thread_qty()-1 +/// \return #NO_THREAD if the thread doesn't exist. + +inline thid_t idaapi getn_thread(int n) { return (thid_t)callui(ui_dbg_getn_thread, n).i; } + + +/// Get current thread ID. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline thid_t idaapi get_current_thread(void) { return callui(ui_dbg_get_current_thread).i; } + + +/// Get the NAME of a thread +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param n number of thread, is in range 0..get_thread_qty()-1 +/// or -1 for the current thread +/// \return thread name or nullptr if the thread doesn't exist. + +inline const char *idaapi getn_thread_name(int n) { return callui(ui_dbg_getn_thread_name, n).cptr; } + + +/// Select the given thread as the current debugged thread. +/// All thread related execution functions will work on this thread. +/// The process must be suspended to select a new thread. +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +/// \param tid ID of the thread to select +/// \return false if the thread doesn't exist. + +inline bool idaapi select_thread(thid_t tid) { return callui(ui_dbg_select_thread, tid).cnd; } + +/// Post a select_thread() request + +inline bool idaapi request_select_thread(thid_t tid) { return callui(ui_dbg_request_select_thread, tid).cnd; } + + +/// Suspend thread. +/// Suspending a thread may deadlock the whole application if the suspended +/// was owning some synchronization objects. +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +/// \param tid thread id +/// \retval -1 network error +/// \retval 0 failed +/// \retval 1 ok + +inline int idaapi suspend_thread(thid_t tid) { return callui(ui_dbg_suspend_thread, tid).i; } + +/// Post a suspend_thread() request + +inline int idaapi request_suspend_thread(thid_t tid) { return callui(ui_dbg_request_suspend_thread, tid).i; } + + +/// Resume thread. +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +/// \param tid thread id +/// \retval -1 network error +/// \retval 0 failed +/// \retval 1 ok + +inline int idaapi resume_thread(thid_t tid) { return callui(ui_dbg_resume_thread, tid).i; } + +/// Post a resume_thread() request + +inline int idaapi request_resume_thread(thid_t tid) { return callui(ui_dbg_request_resume_thread, tid).i; } + +///@} dbg_funcs_threads + + +//-------------------------------------------------------------------- +// M O D U L E S +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_modules Modules +/// \ingroup dbg_funcs +/// +/// Functions to enumerate modules loaded into the process. +/// +/// \param modinfo structure to receive the answer +/// \return false if there are no (more) modules +/// +/// Typical loop to enumerate modules would look like: +/// \code +/// modinfo_t minfo; +/// for ( bool ok=get_first_module(&minfo); ok; ok=get_next_module(&minfo) ) +/// ... +/// \endcode +///@{ +inline bool idaapi get_first_module(modinfo_t *modinfo) ///< See \ref dbg_funcs_modules +{ return callui(ui_dbg_get_first_module, modinfo).cnd; } + +inline bool idaapi get_next_module(modinfo_t *modinfo) ///< See \ref dbg_funcs_modules +{ return callui(ui_dbg_get_next_module, modinfo).cnd; } + +///@} + + +//-------------------------------------------------------------------- +// E X E C U T I O N F L O W C O N T R O L C O M M A N D S +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_flow Execution flow control +/// \ingroup dbg_funcs +/// +/// Use these functions to run instructions in the debugged process. +///@{ + +/// Execute one instruction in the current thread. +/// Other threads are kept suspended. +/// \sq{Type, Asynchronous function - available as Request, +/// Notification, ::dbg_step_into} + +inline bool idaapi step_into(void) { return callui(ui_dbg_step_into).cnd; } + +/// Post a step_into() request + +inline bool idaapi request_step_into(void) { return callui(ui_dbg_request_step_into).cnd; } + + +/// Execute one instruction in the current thread, +/// but without entering into functions. +/// Others threads keep suspended. +/// \sq{Type, Asynchronous function - available as Request, +/// Notification, ::dbg_step_over} + +inline bool idaapi step_over(void) { return callui(ui_dbg_step_over).cnd; } + +/// Post a step_over() request + +inline bool idaapi request_step_over(void) { return callui(ui_dbg_request_step_over).cnd; } + + +/// Execute one instruction backwards in the current thread. +/// Other threads are kept suspended. +/// \sq{Type, Asynchronous function - available as Request, +/// Notification, ::dbg_step_into} + +inline bool idaapi step_into_backwards(void) { return callui(ui_dbg_step_into_backwards).cnd; } + +/// Post a step_into_backwards() request + +inline bool idaapi request_step_into_backwards(void) { return callui(ui_dbg_request_step_into_backwards).cnd; } + + +/// Execute one instruction backwards in the current thread, +/// but without entering into functions. +/// Other threads are kept suspended. +/// \sq{Type, Asynchronous function - available as Request, +/// Notification, ::dbg_step_over} + +inline bool idaapi step_over_backwards(void) { return callui(ui_dbg_step_over_backwards).cnd; } + +/// Post a step_over_backwards() request + +inline bool idaapi request_step_over_backwards(void) { return callui(ui_dbg_request_step_over_backwards).cnd; } + + +/// Execute the process until the given address is reached. +/// If no process is active, a new process is started. +/// Technically, the debugger sets up a temporary breakpoint at +/// the given address, and continues (or starts) the execution of +/// the whole process. +/// So, all threads continue their execution! +/// \sq{Type, Asynchronous function - available as Request, +/// Notification, ::dbg_run_to} +/// \param ea target address +/// \param pid not used yet. please do not specify this parameter. +/// \param tid not used yet. please do not specify this parameter. + +inline bool idaapi run_to(ea_t ea, pid_t pid = NO_PROCESS, thid_t tid = NO_THREAD) { return callui(ui_dbg_run_to, ea, pid, tid).cnd; } + +/// Post a run_to() request + +inline bool idaapi request_run_to(ea_t ea, pid_t pid = NO_PROCESS, thid_t tid = NO_THREAD) { return callui(ui_dbg_request_run_to, ea, pid, tid).cnd; } + + +/// Execute the process backwards until the given address is reached. +/// Technically, the debugger sets up a temporary breakpoint at +/// the given address, and continues (or starts) the execution of +/// the whole process. +/// \sq{Type, Asynchronous function - available as Request, +/// Notification, ::dbg_run_to} +/// \param ea target address +/// \param pid not used yet. please do not specify this parameter. +/// \param tid not used yet. please do not specify this parameter. + +inline bool idaapi run_to_backwards(ea_t ea, pid_t pid = NO_PROCESS, thid_t tid = NO_THREAD) { return callui(ui_dbg_run_to_backwards, ea, pid, tid).cnd; } + +/// Post a run_to_backwards() request + +inline bool idaapi request_run_to_backwards(ea_t ea, pid_t pid = NO_PROCESS, thid_t tid = NO_THREAD) { return callui(ui_dbg_request_run_to_backwards, ea, pid, tid).cnd; } + + +/// Execute instructions in the current thread until +/// a function return instruction is executed (aka "step out"). +/// Other threads are kept suspended. +/// \sq{Type, Asynchronous function - available as Request, +/// Notification, ::dbg_step_until_ret} + +inline bool idaapi step_until_ret(void) { return callui(ui_dbg_step_until_ret).cnd; } + +/// Post a step_until_ret() request + +inline bool idaapi request_step_until_ret(void) { return callui(ui_dbg_request_step_until_ret).cnd; } + + +/// How to resume the application. +/// Set resume mode but do not resume process. + +inline bool idaapi set_resume_mode(thid_t tid, resume_mode_t mode) { return callui(ui_dbg_set_resume_mode, tid, mode).cnd; } + +/// Post a set_resume_mode() request + +inline bool idaapi request_set_resume_mode(thid_t tid, resume_mode_t mode) { return callui(ui_dbg_request_set_resume_mode, tid, mode).cnd; } + +///@} dbg_funcs_flow + + +//-------------------------------------------------------------------- +// R E G I S T E R S +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_regs Registers +/// \ingroup dbg_funcs +/// +/// Inspect/Manipulate registers for debugged process. +/// The debugger structure defines a set of hardware registers in \dbg{registers} +/// IDA also recognizes register names for each defined bit in bit registers. +/// You can use all these names to set or get a register value. +/// +/// For example, with the x86 Userland Win32 debugger you can use +/// register names like: +/// - "EAX", ... "EBP", "ESP", "EFL": for classical integer registers +/// - "CS", "DS", ... : for segment registers +/// - "ST0", "ST1", ... : for FPU registers +/// - "CF", "PF", "AF", "ZF", ... : for special bit values +///@{ + +/// Get register information +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline bool idaapi get_dbg_reg_info(const char *regname, register_info_t *ri) { return callui(ui_dbg_get_reg_info, regname, ri).cnd; } + +/// Read a register value from the current thread. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline bool idaapi get_reg_val(const char *regname, regval_t *regval) { return callui(ui_dbg_get_reg_val, regname, regval).cnd; } + +/// Get register value as an unsigned 64-bit int + +inline bool idaapi get_reg_val(const char *regname, uint64 *ival) { return callui(ui_dbg_get_reg_val_i, regname, ival).cnd; } + + +/// Get value of the SP register for the current thread. +/// Requires a suspended debugger. +inline bool idaapi get_sp_val(ea_t *out) { return callui(ui_dbg_get_sp_val, out).cnd; } + +/// Get value of the IP (program counter) register for the current thread. +/// Requires a suspended debugger. +inline bool idaapi get_ip_val(ea_t *out) { return callui(ui_dbg_get_ip_val, out).cnd; } + +/// Write a register value to the current thread. +/// \sq{Type, Synchronous function - available as Request, +/// Notification, none (synchronous function)} + +inline bool idaapi set_reg_val(const char *regname, const regval_t *regval) { return callui(ui_dbg_set_reg_val, regname, regval).cnd; } + +/// Write a register value to the current thread + +inline bool idaapi set_reg_val(const char *regname, uint64 ival) { return callui(ui_dbg_set_reg_val_i, regname, ival).cnd; } + +/// Post a set_reg_val() request + +inline bool idaapi request_set_reg_val(const char *regname, const regval_t *regval) { return callui(ui_dbg_request_set_reg_val, regname, regval).cnd; } + + +/// Does a register contain an integer value? +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline bool idaapi is_reg_integer(const char *regname) { return callui(ui_dbg_get_reg_value_type, regname).i-2 == RVT_INT; } + + +/// Does a register contain a floating point value? +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline bool idaapi is_reg_float(const char *regname) { return callui(ui_dbg_get_reg_value_type, regname).i-2 == RVT_FLOAT; } + + +/// Does a register contain a value of a custom data type? +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline bool idaapi is_reg_custom(const char *regname) { return callui(ui_dbg_get_reg_value_type, regname).i >= 2; } + +///@} dbg_funcs_regs + +#endif // __UI__ + +//-------------------------------------------------------------------- +// B R E A K P O I N T S +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_bpts Breakpoints +/// \ingroup dbg_funcs +/// +/// Work with debugger breakpoints. +///@{ + +int idaapi set_bptloc_string(const char *s); +const char *idaapi get_bptloc_string(int i); + +struct bpt_t; +struct bpt_location_t; +struct movbpt_info_t; +typedef qvector movbpt_infos_t; +typedef qvector bpt_constptr_vec_t; +typedef qvector bptptr_vec_t; +typedef qvector bpt_vec_t; ///< vector of breakpoints + +enum movbpt_code_t +{ + MOVBPT_OK, ///< moved ok + MOVBPT_NOT_FOUND, ///< source bpt not found + MOVBPT_DEST_BUSY, ///< destination location is busy (we already have such a bpt) + MOVBPT_BAD_TYPE, ///< BPLT_ABS is not supported +}; +typedef qvector movbpt_codes_t; + +struct bptaddrs_t : public eavec_t +{ + bpt_t *bpt; // bpt described by this structure. + // we need it to handle internal srcbpts +}; + +typedef qvector bpteas_t; + + + +/// Breakpoint location types +enum bpt_loctype_t +{ + BPLT_ABS, ///< absolute address: ea + BPLT_REL, ///< relative address: module_path, offset + BPLT_SYM, ///< symbolic: symbol_name, offset + BPLT_SRC, ///< source level: filename, lineno +}; + +/// Describes a breakpoint location +struct bpt_location_t +{ +// private: + ea_t info; + int index; + bpt_loctype_t loctype; +// public: + bpt_loctype_t type(void) const { return loctype; } ///< Get bpt type + bool is_empty_path(void) const { return index == 0; } ///< No path/filename specified? (::BPLT_REL, ::BPLT_SRC) + const char *path(void) const { return get_bptloc_string(index); } ///< Get path/filename (::BPLT_REL, ::BPLT_SRC) + const char *symbol(void) const { return get_bptloc_string(index); } ///< Get symbol name (::BPLT_SYM) + int lineno(void) const { return int(info); } ///< Get line number (::BPLT_SRC) + uval_t offset(void) const { return (uval_t)info; } ///< Get offset (::BPLT_REL, ::BPLT_SYM) + ea_t ea(void) const { return info; } ///< Get address (::BPLT_ABS) + + bpt_location_t(void) : info(BADADDR), index(0), loctype(BPLT_ABS) {} ///< Constructor (default type is ::BPLT_ABS) + + /// Specify an absolute address location + void set_abs_bpt(ea_t a) + { + info = a; + loctype = BPLT_ABS; + } + + /// Specify a source level location + void set_src_bpt(const char *fn, int _lineno) + { + index = set_bptloc_string(fn); + info = _lineno; + loctype = BPLT_SRC; + } + + /// Specify a symbolic location + void set_sym_bpt(const char *_symbol, uval_t _offset=0) + { + index = set_bptloc_string(_symbol); + info = _offset; + loctype = BPLT_SYM; + } + + /// Specify a relative address location + void set_rel_bpt(const char *mod, uval_t _offset) + { + index = set_bptloc_string(mod); + info = _offset; + loctype = BPLT_REL; + } + + /// Lexically compare two breakpoint locations. + /// Bpt locations are first compared based on type (i.e. ::BPLT_ABS < ::BPLT_REL). + /// ::BPLT_ABS locations are compared based on their ea values. + /// For all other location types, locations are first compared based on their + /// string (path/filename/symbol), then their offset/lineno. + int compare(const bpt_location_t &r) const { return callui(ui_dbg_compare_bpt_locs, this, &r).i; } + bool operator==(const bpt_location_t &r) const { return compare(r) == 0; } + bool operator!=(const bpt_location_t &r) const { return compare(r) != 0; } + bool operator< (const bpt_location_t &r) const { return compare(r) < 0; } + bool operator> (const bpt_location_t &r) const { return compare(r) > 0; } + bool operator<=(const bpt_location_t &r) const { return compare(r) <= 0; } + bool operator>=(const bpt_location_t &r) const { return compare(r) >= 0; } + + /// Internal function + size_t print(qstring *buf) const; +}; +DECLARE_TYPE_AS_MOVABLE(bpt_location_t); + +/// Characteristics of a breakpoint +struct bpt_t +{ + size_t cb; ///< size of this structure + qstring cndbody; ///< Condition as entered by the user + bpt_location_t loc; ///< Location + pid_t pid; ///< breakpoint process id + thid_t tid; ///< breakpoint thread id + ea_t ea; ///< Address, if known. For #BPLT_SRC, index into an internal data struct + bpttype_t type; ///< Breakpoint type + int pass_count; ///< Number of times the breakpoint is hit before stopping + ///< (default is 0: stop always) + uint32 flags; ///< \ref BPT_T +/// \defgroup BPT_T Breakpoint property bits +/// Used by bpt_t::flags +///@{ +#define BPT_BRK 0x001 ///< suspend execution upon hit +#define BPT_TRACE 0x002 ///< add trace information upon hit +#define BPT_UPDMEM 0x004 ///< refresh the memory layout and contents before evaluating bpt condition +#define BPT_ENABLED 0x008 ///< enabled? +#define BPT_LOWCND 0x010 ///< condition is calculated at low level (on the server side) +#define BPT_TRACEON 0x020 ///< enable tracing when the breakpoint is reached +#define BPT_TRACE_INSN 0x040 ///< instruction tracing +#define BPT_TRACE_FUNC 0x080 ///< function tracing +#define BPT_TRACE_BBLK 0x100 ///< basic block tracing +#define BPT_TRACE_TYPES (BPT_TRACE_INSN|BPT_TRACE_FUNC|BPT_TRACE_BBLK) + ///< trace insns, functions, and basic blocks. + ///< if any of #BPT_TRACE_TYPES bits are set but #BPT_TRACEON is clear, + ///< then turn off tracing for the specified trace types +#define BPT_ELANG_MASK 0xF0000000u +#define BPT_ELANG_SHIFT 28 ///< index of the extlang (scripting language) of the condition +///@} + + uint32 props; ///< \ref BKPT_ +/// \defgroup BKPT_ Internal breakpoint properties +/// Used by bpt_t::props +///@{ +#define BKPT_BADBPT 0x01 ///< failed to write the bpt to the process memory (at least one location) +#define BKPT_LISTBPT 0x02 ///< include in bpt list (user-defined bpt) +#define BKPT_TRACE 0x04 ///< trace bpt; should not be deleted when the process gets suspended +#define BKPT_ACTIVE 0x08 ///< active? +#define BKPT_PARTIAL 0x10 ///< partially active? (some locations were not written yet) +#define BKPT_CNDREADY 0x20 ///< condition has been compiled +#define BKPT_FAKEPEND 0x40 ///< fake pending bpt: it is inactive but another + ///< bpt of the same type is active at the same address(es) +#define BKPT_PAGE 0x80 ///< written to the process as a page bpt. Available + ///< only after writing the bpt to the process. +///@} + + int size; ///< Size of the breakpoint (0 for software breakpoints) + int cndidx; ///< Internal number of the condition (<0-none) + inode_t bptid; ///< Internal breakpoint id + + bpt_t(void) : cb(sizeof(*this)), pid(NO_PROCESS), tid(NO_THREAD), ea(BADADDR), + type(BPT_SOFT), pass_count(0), flags(BPT_BRK|BPT_ENABLED), + props(0), size(0), cndidx(-1), bptid(0) {} + + bool is_hwbpt(void) const { return type != BPT_SOFT; } ///< Is hardware breakpoint? + bool enabled(void) const { return (flags & BPT_ENABLED) != 0; } ///< Is breakpoint enabled? + bool is_low_level(void) const { return (flags & BPT_LOWCND) != 0; } ///< Is bpt condition calculated at low level? + bool badbpt(void) const { return (props & BKPT_BADBPT) != 0; } ///< Failed to write bpt to process memory? + bool listbpt(void) const { return (props & BKPT_LISTBPT) != 0; } ///< Include in the bpt list? + bool is_compiled(void) const { return (props & BKPT_CNDREADY) != 0; } ///< Condition has been compiled? + /// Written completely to process? + bool is_active(void) const { return (props & (BKPT_PARTIAL|BKPT_ACTIVE)) == BKPT_ACTIVE; } + /// Written partially to process? + bool is_partially_active(void) const { return (props & BKPT_PARTIAL) != 0; } + /// Not written to process at all? + bool is_inactive(void) const { return (props & (BKPT_PARTIAL|BKPT_ACTIVE)) == 0; } + /// Page breakpoint? + bool is_page_bpt(void) const { return (props & BKPT_PAGE) != 0; } + + /// Get bpt size + int get_size(void) const { return is_hwbpt() ? size : 1; } + /// Set bpt location to an absolute address + void set_abs_bpt(ea_t a) { loc.set_abs_bpt(a); ea = a; } + /// Set bpt location to a source line + void set_src_bpt(const char *fn, int lineno) { loc.set_src_bpt(fn, lineno); ea = BADADDR; } + /// Set bpt location to a symbol + void set_sym_bpt(const char *sym, uval_t o) { loc.set_sym_bpt(sym, o); ea = BADADDR; } + /// Set bpt location to a relative address + void set_rel_bpt(const char *mod, uval_t o) { loc.set_rel_bpt(mod, o); ea = BADADDR; } + + bool is_absbpt(void) const { return loc.type() == BPLT_ABS; } ///< Is absolute address breakpoint? + bool is_relbpt(void) const { return loc.type() == BPLT_REL; } ///< Is relative address breakpoint? + bool is_symbpt(void) const { return loc.type() == BPLT_SYM; } ///< Is symbolic breakpoint? + bool is_srcbpt(void) const { return loc.type() == BPLT_SRC; } ///< Is source level breakpoint? + + /// Does breakpoint trace anything? + bool is_tracemodebpt(void) const { return (flags & BPT_TRACE_TYPES) != 0; } + /// Is this a tracing breakpoint, and is tracing enabled? + bool is_traceonbpt(void) const { return is_tracemodebpt() && (flags & BPT_TRACEON) != 0; } + /// Is this a tracing breakpoint, and is tracing disabled? + bool is_traceoffbpt(void) const { return is_tracemodebpt() && (flags & BPT_TRACEON) == 0; } + /// Configure tracing options + bool set_trace_action(bool enable, int trace_types) + { + trace_types &= BPT_TRACE_TYPES; + if ( trace_types == 0 ) + return false; + flags |= trace_types; + setflag(flags, BPT_TRACEON, enable); + return true; + } + + /// Get the scripting language name for the condition string + const char *get_cnd_elang() const; + + /// Set the scripting language name for the condition string + /// \return false if too many languages were used + bool set_cnd_elang(const char *name); + + size_t get_cnd_elang_idx() const { return flags >> BPT_ELANG_SHIFT; } + + void set_cond(const char *cnd); ///< Internal function + bool eval_cond(ea_t ea, bool *fire, const char *bpt_type); ///< Internal function +}; +DECLARE_TYPE_AS_MOVABLE(bpt_t); + +struct movbpt_info_t +{ + bpt_location_t from; + bpt_location_t to; +}; +DECLARE_TYPE_AS_MOVABLE(movbpt_info_t); + +#ifndef __UI__ + + +/// Get number of breakpoints. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline int idaapi get_bpt_qty(void) { return callui(ui_dbg_get_bpt_qty).i; } + + +/// Get the characteristics of a breakpoint. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param n number of breakpoint, is in range 0..get_bpt_qty()-1 +/// \param[out] bpt filled with the characteristics. +/// \return false if no breakpoint exists + +inline bool idaapi getn_bpt(int n, bpt_t *bpt) { return callui(ui_dbg_getn_bpt, n, bpt).cnd; } + + +/// Get the characteristics of a breakpoint. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param ea any address in the breakpoint range +/// \param[out] bpt if not nullptr, is filled with the characteristics. +/// \return false if no breakpoint exists + +inline bool idaapi get_bpt(ea_t ea, bpt_t *bpt) { return callui(ui_dbg_get_bpt, ea, bpt).cnd; } + + +/// Does a breakpoint exist at the given location? + +inline bool exist_bpt(ea_t ea) { return get_bpt(ea, nullptr); } + + +/// Add a new breakpoint in the debugged process. +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +/// \note Only one breakpoint can exist at a given address. +/// \param ea any address in the process memory space. +/// Depending on the architecture, hardware breakpoints +/// always be setup at random address. For example, on x86, +/// hardware breakpoints should be aligned depending on their size. +/// Moreover, on the x86 architecture, it is impossible to setup +/// more than 4 hardware breakpoints. +/// \param size size of the breakpoint (irrelevant for software breakpoints): +/// As for the address, hardware breakpoints can't always be setup +/// with random size. +/// \param type type of the breakpoint (#BPT_SOFT for software breakpoint) +/// special case #BPT_DEFAULT (#BPT_SOFT|#BPT_EXEC): +/// try to add instruction breakpoint of the appropriate type +/// as follows: software bpt if supported, hwbpt otherwise + +inline bool idaapi add_bpt(ea_t ea, asize_t size = 0, bpttype_t type = BPT_DEFAULT) { return callui(ui_dbg_add_oldbpt, ea, size, type).cnd; } + +/// Post an add_bpt(ea_t, asize_t, bpttype_t) request + +inline bool idaapi request_add_bpt(ea_t ea, asize_t size = 0, bpttype_t type = BPT_DEFAULT) { return callui(ui_dbg_request_add_oldbpt, ea, size, type).cnd; } + + +/// Add a new breakpoint in the debugged process. +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +/// \param bpt Breakpoint to add. It describes the break condition, +/// type, flags, location (module relative, source breakpoint +/// or absolute) and other attributes. + +inline bool idaapi add_bpt(const bpt_t &bpt) { return callui(ui_dbg_add_bpt, &bpt).cnd; } + +/// Post an add_bpt(const bpt_t &) request + +inline bool idaapi request_add_bpt(const bpt_t &bpt) { return callui(ui_dbg_request_add_bpt, &bpt).cnd; } + + +/// Delete an existing breakpoint in the debugged process. +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +/// \param ea any address in the breakpoint range + +inline bool idaapi del_bpt(ea_t ea) { return callui(ui_dbg_del_oldbpt, ea).cnd; } + +/// Post a del_bpt(ea_t) request + +inline bool idaapi request_del_bpt(ea_t ea) { return callui(ui_dbg_request_del_oldbpt, ea).cnd; } + + +/// Delete an existing breakpoint in the debugged process. +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +/// \param bptloc Breakpoint location + +inline bool idaapi del_bpt(const bpt_location_t &bptloc) { return callui(ui_dbg_del_bpt, &bptloc).cnd; } + +/// Post a del_bpt(const bpt_location_t &) request + +inline bool idaapi request_del_bpt(const bpt_location_t &bptloc) { return callui(ui_dbg_request_del_bpt, &bptloc).cnd; } + + +/// Update modifiable characteristics of an existing breakpoint. +/// To update the breakpoint location, use change_bptlocs() +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \note Only the following fields can be modified: +/// - bpt_t::cndbody +/// - bpt_t::pass_count +/// - bpt_t::flags +/// - bpt_t::size +/// - bpt_t::type +/// \note Changing some properties will require removing and then re-adding +/// the breakpoint to the process memory (or the debugger backend), which +/// can lead to race conditions (i.e., breakpoint(s) can be missed) in +/// case the process is not suspended. +/// Here are a list of scenarios that will require the breakpoint +/// to be removed & then re-added: +/// - bpt_t::size is modified +/// - bpt_t::type is modified +/// - bpt_t::flags's BPT_ENABLED is modified +/// - bpt_t::flags's BPT_LOWCND is changed +/// - bpt_t::flags's BPT_LOWCND remains set, but cndbody changed + +inline bool idaapi update_bpt(const bpt_t *bpt) { return callui(ui_dbg_update_bpt, bpt).cnd; } + + +/// Find a breakpoint by location. +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +/// \param bptloc Breakpoint location +/// \param bpt bpt is filled if the breakpoint was found + +inline bool idaapi find_bpt(const bpt_location_t &bptloc, bpt_t *bpt) { return callui(ui_dbg_find_bpt, &bptloc, bpt).cnd; } + + +/// Move breakpoint(s) from one location to another +/// \param movinfo what bpts to move and where to +/// \param codes vector of return codes, if detailed error info is required +/// \param del_hindering_bpts should delete hindering breakpoints? +/// \return number of moved bpts + +inline int idaapi change_bptlocs( + const movbpt_infos_t &movinfo, + movbpt_codes_t *codes = nullptr, + bool del_hindering_bpts = true) +{ + return callui(ui_dbg_change_bptlocs, &movinfo, codes, del_hindering_bpts).i; +} + + +/// \name enable/disable breakpoints +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +/// Enable or disable an existing breakpoint. +/// A disabled breakpoint isn't available anymore in the process. +///@{ +inline bool idaapi enable_bpt(ea_t ea, bool enable = true) { return callui(ui_dbg_enable_oldbpt, ea, enable).cnd; } +inline bool idaapi enable_bpt(const bpt_location_t &bptloc, bool enable = true) { return callui(ui_dbg_enable_bpt, &bptloc, enable).cnd; } +inline bool disable_bpt(ea_t ea) { return enable_bpt(ea, false); } +inline bool disable_bpt(const bpt_location_t &bptloc) { return enable_bpt(bptloc, false); } +inline bool idaapi request_enable_bpt(ea_t ea, bool enable = true) { return callui(ui_dbg_request_enable_oldbpt, ea, enable).cnd; } +inline bool idaapi request_enable_bpt(const bpt_location_t &bptloc, bool enable = true) { return callui(ui_dbg_request_enable_bpt, &bptloc, enable).cnd; } +inline bool request_disable_bpt(ea_t ea) { return request_enable_bpt(ea, false); } +inline bool request_disable_bpt(const bpt_location_t &bptloc) { return request_enable_bpt(bptloc, false); } +///@} + + +/// Check the breakpoint at the specified address. +/// \return one of \ref BPTCK_ + +inline int idaapi check_bpt(ea_t ea) { return callui(ui_dbg_check_bpt, ea).i; } + +#endif // __UI__ + +/// \defgroup BPTCK_ Breakpoint status codes +/// Return values for check_bpt() +///@{ +#define BPTCK_NONE -1 ///< breakpoint does not exist +#define BPTCK_NO 0 ///< breakpoint is disabled +#define BPTCK_YES 1 ///< breakpoint is enabled +#define BPTCK_ACT 2 ///< breakpoint is active (written to the process) +///@} + +#ifndef SWIG +/// Visit all breakpoints. +/// To use this class, derive your own class from it and call for_all_bpts(). +/// It is forbidden to add/del bpts from the visit_bpt() function. +/// If bpts are nevertheless modified, the enumeration should be stopped +struct bpt_visitor_t +{ + range_t range; ///< if specified, restricts the address range + const char *name; ///< if specified, restricts bpts to the ones that match the given name + bpt_visitor_t(void) : range(0, BADADDR), name(nullptr) {} + /// Defines action taken when breakpoint is visited + virtual int idaapi visit_bpt(const bpt_t *bpt) = 0; + int idaapi for_all_bpts(int bvflags) + { + return callui(ui_dbg_for_all_bpts, this, bvflags).i; + } +}; + +/// \defgroup BVF_ Breakpoint visitor flags +/// Passed as 'bvflags' parameter to bpt_visitor_t::_for_all_bpts() +/// \note it is forbidden to modify bpt states from the bpt_visitor_t::visit_bpt() +/// function if #BVF_STATE is not #BVFS_ANY +///@{ +#define BVF_ABS 0x0001 ///< include absolute bpts +#define BVF_REL 0x0002 ///< include relative bpts +#define BVF_SYM 0x0004 ///< include symbolic bpts +#define BVF_SRC 0x0008 ///< include source bpts +#define BVF_ALL 0x000F ///< include all bpt location types +#define BVF_STATE 0x0030 ///< bpt state mask +#define BVFS_ANY 0x0000 ///< any state +#define BVFS_INPROC 0x0010 ///< written to process memory +#define BVFS_PENDING 0x0020 ///< pending +#define BVFS_DISABLED 0x0030 ///< disabled. +///@} +#endif // SWIG + + +///@} dbg_funcs_bpts + + +#ifndef __UI__ + +/// \defgroup dbg_funcs_tracing Tracing +/// \ingroup dbg_funcs +/// +/// Trace instructions/functions/basic blocks + +//-------------------------------------------------------------------- +// T R A C I N G B U F F E R +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_trcbuf Tracing buffer +/// \ingroup dbg_funcs_tracing +/// +/// Work with debugger trace buffer. +/// IDA memorizes various types of trace events in a circular buffer: +/// instruction tracing, function call and return, breakpoint access ... +///@{ + + +/// Specify the new size of the circular buffer. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param size if 0, buffer isn't circular and events are never removed. +/// If the new size is smaller than the existing number of trace events, +/// a corresponding number of trace events are removed. +/// \note If you specify 0, all available memory can be quickly used !!! + +inline bool idaapi set_trace_size(int size) { return callui(ui_dbg_set_trace_size, size).cnd; } + + +/// Clear all events in the trace buffer. +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} + +inline void idaapi clear_trace(void) { callui(ui_dbg_clear_trace); } + +/// Post a clear_trace() request + +inline void idaapi request_clear_trace(void) { callui(ui_dbg_request_clear_trace); } + +///@} dbg_funcs_trcbuf + + +//-------------------------------------------------------------------- +// S T E P T R A C I N G +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_strace Step tracing +/// \ingroup dbg_funcs_tracing +/// +/// Plugins can use these functions to implement a custom tracing engine. +/// When enabled, IDA uses single-stepping feature of the debugger +/// and generates a dbg_trace notification after each step in the current thread. +/// Tracing buffer is not maintained in this mode (you need to use one of the +/// higher level tracing types for it) +///@{ + +/// Get current state of step tracing. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline bool idaapi is_step_trace_enabled(void) { return callui(ui_dbg_is_step_trace_enabled).cnd; } + + +/// \name Enable/Disable step tracing +/// Enable or disable the step tracing +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +/// \param enable +/// - 1 : enable step tracing +/// - 0 : disable step tracing +/// - -1 : temporarily disable step tracing +/// (trace-over breakpoints are conserved: +/// these could re-enable step tracing later) +///@{ + +inline bool idaapi enable_step_trace(int enable = 1) { return callui(ui_dbg_enable_step_trace, enable).cnd; } +inline bool disable_step_trace(void) { return enable_step_trace(0); } +inline bool idaapi request_enable_step_trace(int enable = 1) { return callui(ui_dbg_request_enable_step_trace, enable).cnd; } +inline bool request_disable_step_trace(void) { return request_enable_step_trace(false); } +///@} + +#endif // __UI__ + + +/// \defgroup ST_ Step trace options +/// Flags returned by get_step_trace_options() +///@{ +#define ST_OVER_DEBUG_SEG 0x01 ///< step tracing will be disabled when IP is in a debugger segment +#define ST_OVER_LIB_FUNC 0x02 ///< step tracing will be disabled when IP is in a library function +#define ST_ALREADY_LOGGED 0x04 ///< step tracing will be disabled when IP is already logged +#define ST_SKIP_LOOPS 0x08 ///< step tracing will try to skip loops already recorded +#define ST_DIFFERENTIAL 0x10 ///< tracing: log only new instructions (not previously logged) +/// mask of available options, to ensure compatibility with newer IDA versions +#define ST_OPTIONS_MASK (ST_OVER_DEBUG_SEG|ST_OVER_LIB_FUNC|ST_ALREADY_LOGGED|ST_SKIP_LOOPS|ST_DIFFERENTIAL) +#define ST_OPTIONS_DEFAULT (ST_OVER_DEBUG_SEG|ST_OVER_LIB_FUNC) +///@} + +/// specific options for instruction tracing (see set_insn_trace_options()) +#define IT_LOG_SAME_IP 0x01 ///< instruction tracing will log new instructions even when IP doesn't change +/// specific options for function tracing (see set_func_trace_options()) +#define FT_LOG_RET 0x01 ///< function tracing will log returning instructions +/// specific options for basic block tracing (see set_bblk_trace_options()) +#define BT_LOG_INSTS 0x01 ///< log all instructions in the current basic block + +#ifndef __UI__ + +/// Get current step tracing options. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \return \ref ST_ + +inline int idaapi get_step_trace_options(void) { return callui(ui_dbg_get_step_trace_options).i; } + +/// Modify step tracing options. +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} + +inline void idaapi set_step_trace_options(int options) { callui(ui_dbg_set_step_trace_options, options); } + +/// Post a set_step_trace_options() request + +inline void idaapi request_set_step_trace_options(int options) { callui(ui_dbg_request_set_step_trace_options, options); } + +///@} dbg_funcs_strace + + +//-------------------------------------------------------------------- +// I N S T R U C T I O N S T R A C I N G +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_trcins Instruction tracing +/// \ingroup dbg_funcs_tracing +/// +/// When instruction tracing is active, each executed instruction is stored +/// in the tracing buffer. +/// Internally, IDA uses step tracing to record register values after the +/// execution of the instruction. +///@{ + +/// Get current state of instruction tracing. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline bool idaapi is_insn_trace_enabled(void) { return callui(ui_dbg_is_insn_trace_enabled).cnd; } + +/// \name Enable/Disable instruction tracing +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +///@{ +inline bool idaapi enable_insn_trace(bool enable = true) { return callui(ui_dbg_enable_insn_trace, enable).cnd; } +inline bool disable_insn_trace(void) { return enable_insn_trace(false); } +inline bool idaapi request_enable_insn_trace(bool enable = true) { return callui(ui_dbg_request_enable_insn_trace, enable).cnd; } +inline bool request_disable_insn_trace(void) { return request_enable_insn_trace(false); } +///@} + + +/// Get current instruction tracing options. +/// Also see #IT_LOG_SAME_IP +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline int idaapi get_insn_trace_options(void) { return callui(ui_dbg_get_insn_trace_options).i; } + + +/// Modify instruction tracing options. +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} + +inline void idaapi set_insn_trace_options(int options) { callui(ui_dbg_set_insn_trace_options, options); } + +/// Post a set_insn_trace_options() request + +inline void idaapi request_set_insn_trace_options(int options) { callui(ui_dbg_request_set_insn_trace_options, options); } + +///@} dbg_funcs_trcins + + +//-------------------------------------------------------------------- +// F U N C T I O N S T R A C I N G +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_trcfunc Functions tracing +/// \ingroup dbg_funcs_tracing +/// +/// Each call to a function or return from a function is stored +/// in the tracing buffer. +///@{ + +/// Get current state of functions tracing. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline bool idaapi is_func_trace_enabled(void) { return callui(ui_dbg_is_func_trace_enabled).cnd; } + + +/// \name Enable/Disable functions tracing +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +///@{ +inline bool idaapi enable_func_trace(bool enable = true) { return callui(ui_dbg_enable_func_trace, enable).cnd; } +inline bool disable_func_trace(void) { return enable_func_trace(false); } +inline bool idaapi request_enable_func_trace(bool enable = true) { return callui(ui_dbg_request_enable_func_trace, enable).cnd; } +inline bool request_disable_func_trace(void) { return request_enable_func_trace(false); } +///@} + + +/// Get current function tracing options. +/// Also see #FT_LOG_RET +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline int idaapi get_func_trace_options(void) { return callui(ui_dbg_get_func_trace_options).i; } + + +/// Modify function tracing options. +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} + +inline void idaapi set_func_trace_options(int options) { callui(ui_dbg_set_func_trace_options, options); } + +/// Post a set_func_trace_options() request + +inline void idaapi request_set_func_trace_options(int options) { callui(ui_dbg_request_set_func_trace_options, options); } + +///@} dbg_funcs_trcfunc + + +//-------------------------------------------------------------------- +// B A S I C B L O C K T R A C I N G +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_trcbb Basic block tracing +/// \ingroup dbg_funcs_tracing +///@{ + +// Modify basic block tracing options. +// Type: Synchronous function - available as Request +// Notification: none (synchronous function) + + +/// \name Enable/Disable basic blocks tracing +/// \sq{Type, Synchronous function - available as request, +/// Notification, none (synchronous function)} +///@{ +inline bool idaapi enable_bblk_trace(bool enable = true) { return callui(ui_dbg_enable_bblk_trace, enable).cnd; } +inline bool disable_bblk_trace(void) { return enable_bblk_trace(false); } +inline bool idaapi request_enable_bblk_trace(bool enable = true) { return callui(ui_dbg_request_enable_bblk_trace, enable).cnd; } +inline bool request_disable_bblk_trace(void) { return request_enable_bblk_trace(false); } +inline bool idaapi is_bblk_trace_enabled(void) { return callui(ui_dbg_is_bblk_trace_enabled).cnd; } +///@} + +/// Get current basic block tracing options. +/// Also see #BT_LOG_INSTS +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline int idaapi get_bblk_trace_options(void) { return callui(ui_dbg_get_bblk_trace_options).i; } + + +/// Modify basic block tracing options (see #BT_LOG_INSTS) + +inline void idaapi set_bblk_trace_options(int options) { callui(ui_dbg_set_bblk_trace_options, options); } + +/// Post a set_bblk_trace_options() request + +inline void idaapi request_set_bblk_trace_options(int options) { callui(ui_dbg_request_set_bblk_trace_options, options); } + +///@} dbg_funcs_trcbb + +#endif // __UI__ + +//-------------------------------------------------------------------- +// T R A C I N G E V E N T S +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_trcev Tracing events +/// \ingroup dbg_funcs_tracing +///@{ + +/// Trace event types +enum tev_type_t +{ + tev_none = 0, ///< no event + tev_insn, ///< an instruction trace + tev_call, ///< a function call trace + tev_ret, ///< a function return trace + tev_bpt, ///< write, read/write, execution trace + tev_mem, ///< memory layout changed + tev_event, ///< debug event occurred + tev_max, ///< first unused event type +}; + +typedef qvector dbgevt_vec_t; ///< vector of debug events + +/// Common information for all trace events +struct tev_info_t +{ + tev_type_t type; ///< trace event type + thid_t tid; ///< thread where the event was recorded + ea_t ea; ///< address where the event occurred +}; +typedef qvector tevinfo_vec_t; ///< vector of trace event info objects + + +/// Required typedef for get_insn_tev_reg_mem() +struct memreg_info_t +{ + ea_t ea; + bytevec_t bytes; +}; +DECLARE_TYPE_AS_MOVABLE(memreg_info_t); +typedef qvector memreg_infos_t; + + +#ifndef __UI__ + +/// Get number of trace events available in trace buffer. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline int idaapi get_tev_qty(void) { return callui(ui_dbg_get_tev_qty).i; } + + +/// Get main information about a trace event. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param n number of trace event, is in range 0..get_tev_qty()-1. +/// 0 represents the latest added trace event. +/// \param[out] tev_info result +/// \return success + +inline bool idaapi get_tev_info(int n, tev_info_t *tev_info) { return callui(ui_dbg_get_tev_info, n, tev_info).cnd; } + + +/// Read a register value from an instruction trace event. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param n number of trace event, is in range 0..get_tev_qty()-1. +/// 0 represents the latest added trace event. +/// \param regname name of desired register +/// \param[out] regval result +/// \return false if not an instruction event. +/// \note This is the value of the register before the execution of +/// the instruction. + +inline bool idaapi get_insn_tev_reg_val(int n, const char *regname, regval_t *regval) { return callui(ui_dbg_get_insn_tev_reg_val, n, regname, regval).cnd; } + +inline bool idaapi get_insn_tev_reg_val(int n, const char *regname, uint64 *ival) { return callui(ui_dbg_get_insn_tev_reg_val_i, n, regname, ival).cnd; } + + +/// Read the memory pointed by register values from an instruction trace event. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param n number of trace event, is in range 0..get_tev_qty()-1. +/// 0 represents the latest added trace event. +/// \param[out] memmap result +/// \return false if not an instruction event or no memory is available + +inline bool idaapi get_insn_tev_reg_mem(int n, memreg_infos_t *memmap) { return callui(ui_dbg_get_insn_tev_reg_mem, n, memmap).cnd; } + + +/// Read the resulting register value from an instruction trace event. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param n number of trace event, is in range 0..get_tev_qty()-1. +/// 0 represents the latest added trace event. +/// \param regname name of desired register +/// \param[out] regval result +/// \return false if not an instruction trace event or register wasn't modified. + +inline bool idaapi get_insn_tev_reg_result(int n, const char *regname, regval_t *regval) { return callui(ui_dbg_get_insn_tev_reg_result, n, regname, regval).cnd; } + +inline bool idaapi get_insn_tev_reg_result(int n, const char *regname, uint64 *ival) { return callui(ui_dbg_get_insn_tev_reg_result_i, n, regname, ival).cnd; } + + +/// Get the called function from a function call trace event. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param n number of trace event, is in range 0..get_tev_qty()-1. +/// 0 represents the latest added trace event. +/// \return #BADADDR if not a function call event. + +inline ea_t idaapi get_call_tev_callee(int n) { ea_t ea; callui(ui_dbg_get_call_tev_callee, n, &ea); return ea; } + + +/// Get the return address from a function return trace event. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param n number of trace event, is in range 0..get_tev_qty()-1. +/// 0 represents the latest added trace event. +/// \return #BADADDR if not a function return event. + +inline ea_t idaapi get_ret_tev_return(int n) { ea_t ea; callui(ui_dbg_get_ret_tev_return, n, &ea); return ea; } + + +/// Get the address associated to a read, read/write or execution trace event. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param n number of trace event, is in range 0..get_tev_qty()-1. +/// 0 represents the latest added trace event. +/// \return #BADADDR if not a read, read/write or execution trace event. +/// \note Usually, a breakpoint is associated with a read, read/write or execution +/// trace event. However, the returned address could be any address in the +/// range of this breakpoint. +/// If the breakpoint was deleted after the trace event, the address no longer +/// corresponds to a valid breakpoint. + +inline ea_t idaapi get_bpt_tev_ea(int n) { ea_t ea; callui(ui_dbg_get_bpt_tev_ea, n, &ea); return ea; } + + +/// Get the memory layout, if any, for the specified tev object. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param n number of trace event, is in range 0..get_tev_qty()-1. +/// 0 represents the latest added trace event. +/// \param[out] mi result +/// \return false if the tev_t object is not of type ::tev_mem, true otherwise, +/// with the new memory layout in "mi". + +inline bool idaapi get_tev_memory_info(int n, meminfo_vec_t *mi) { return callui(ui_dbg_get_tev_memory_info, n, mi).cnd; } + + +/// Get the corresponding debug event, if any, for the specified tev object. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param n number of trace event, is in range 0..get_tev_qty()-1. +/// 0 represents the latest added trace event. +/// \param[out] d result +/// \return false if the tev_t object doesn't have any associated debug +/// event, true otherwise, with the debug event in "d". + +inline bool idaapi get_tev_event(int n, debug_event_t *d) { return callui(ui_dbg_get_tev_event, n, d).cnd; } + + +/// Get the base address of the current trace. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \return the base address of the currently loaded trace + +inline ea_t idaapi get_trace_base_address(void) { ea_t ea; callui(ui_dbg_get_trace_base_address, &ea); return ea; } + + +/// Set the base address of the current trace. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline void idaapi set_trace_base_address(ea_t ea) { callui(ui_dbg_set_trace_base_address, ea); } + + +/// Add a thread to the current trace. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline void idaapi dbg_add_thread(thid_t tid) { callui(ui_dbg_add_thread, tid); } + + +/// Delete a thread from the current trace. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline void idaapi dbg_del_thread(thid_t tid) { callui(ui_dbg_del_thread, tid); } + + +/// Add a new trace element to the current trace. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline void idaapi dbg_add_tev(tev_type_t type, thid_t tid, ea_t address) { callui(ui_dbg_add_tev, type, tid, address); } + +#endif // __UI__ + +/// Structure used for dbg_add_many_tevs() +struct tev_reg_value_t +{ + regval_t value; + int reg_idx; + + tev_reg_value_t(int _reg_idx = -1, uint64 _value = uint64(-1)) : reg_idx(_reg_idx) + { + value._set_int(_value); + } +}; +DECLARE_TYPE_AS_MOVABLE(tev_reg_value_t); +typedef qvector tev_reg_values_t; ///< vector of trace event reg values + +/// Structure used for dbg_add_many_tevs() +struct tev_info_reg_t +{ + tev_info_t info; + tev_reg_values_t registers; +}; +DECLARE_TYPE_AS_MOVABLE(tev_info_reg_t); +typedef qvector tevinforeg_vec_t; ///< vector of trace elements + + +/// Se dbg_add_insn_tev() +enum save_reg_values_t +{ + SAVE_ALL_VALUES = 0, + SAVE_DIFF, + SAVE_NONE +}; + +#ifndef __UI__ + +/// Add many new trace elements to the current trace. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \return false if the operation failed for any ::tev_info_t object + +inline bool idaapi dbg_add_many_tevs(tevinforeg_vec_t *new_tevs) { return callui(ui_dbg_add_many_tevs, new_tevs).cnd; } + + +/// Add a new instruction trace element to the current trace. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \return false if the operation failed, true otherwise + +inline bool idaapi dbg_add_insn_tev(thid_t tid, ea_t ea, save_reg_values_t save = SAVE_DIFF) { return callui(ui_dbg_add_insn_tev, tid, ea, save).cnd; } + + +/// Add a new breakpoint trace element to the current trace. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \return false if the operation failed, true otherwise + +inline bool idaapi dbg_add_bpt_tev(thid_t tid, ea_t ea, ea_t bp) { return callui(ui_dbg_add_bpt_tev, tid, ea, bp).cnd; } + + +/// Add a new call trace element to the current trace. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline void idaapi dbg_add_call_tev(thid_t tid, ea_t caller, ea_t callee) { callui(ui_dbg_add_call_tev, tid, caller, callee); } + + +/// Add a new return trace element to the current trace. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline void idaapi dbg_add_ret_tev(thid_t tid, ea_t ret_insn, ea_t return_to) { callui(ui_dbg_add_ret_tev, tid, ret_insn, return_to); } + + +/// Add a new debug event to the current trace. +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} + +inline void idaapi dbg_add_debug_event(debug_event_t *event) { callui(ui_dbg_add_debug_event, event); } + +///@} dbg_funcs_trcev + + +//--------------------------------------------------------------------------- +// Trace management functions +//--------------------------------------------------------------------------- +/// \defgroup dbg_funcs_trcm Trace management functions +/// \ingroup dbg_funcs_tracing +///@{ + +/// Load a recorded trace file in the 'Tracing' window. +/// If the call succeeds and 'buf' is not null, the description of the +/// trace stored in the binary trace file will be returned in 'buf' + +inline bool idaapi load_trace_file(qstring *buf, const char *filename) { return callui(ui_dbg_load_trace_file, buf, filename).cnd; } + + +/// Save the current trace in the specified file + +inline bool idaapi save_trace_file(const char *filename, const char *description) { return callui(ui_dbg_save_trace_file, filename, description).cnd; } + + +/// Is the specified file a valid trace file for the current database? + +inline bool idaapi is_valid_trace_file(const char *filename) { return callui(ui_dbg_is_valid_trace_file, filename).cnd; } + + +/// Change the description of the specified trace file + +inline bool idaapi set_trace_file_desc(const char *filename, const char *description) { return callui(ui_dbg_set_trace_file_desc, filename, description).cnd; } + + +/// Get the file header of the specified trace file + +inline bool idaapi get_trace_file_desc(qstring *buf, const char *filename) { return callui(ui_dbg_get_trace_file_desc, buf, filename).cnd; } + + +/// Show the choose trace dialog + +inline bool idaapi choose_trace_file(qstring *buf) { return callui(ui_dbg_choose_trace_file, buf).cnd; } + + +/// Show difference between the current trace and the one from 'filename' + +inline bool idaapi diff_trace_file(const char *NONNULL filename) { return callui(ui_dbg_diff_trace_file, filename).cnd; } + + +/// Show the trace callgraph + +inline bool idaapi graph_trace(void) { return callui(ui_dbg_graph_trace).cnd; } + + +/// Set highlight trace parameters. + +inline void idaapi set_highlight_trace_options( + bool hilight, + bgcolor_t color, + bgcolor_t diff) +{ + callui(ui_dbg_set_highlight_trace_options, hilight, color, diff); +} + + +/// Set platform name of current trace + +inline void idaapi set_trace_platform(const char *platform) { callui(ui_dbg_set_trace_platform, platform); } + + +/// Get platform name of current trace + +inline const char *idaapi get_trace_platform() { return callui(ui_dbg_get_trace_platform).cptr; } + + +/// Set dynamic register set of current trace + +inline void idaapi set_trace_dynamic_register_set(dynamic_register_set_t &idaregs) { callui(ui_dbg_set_trace_dynamic_register_set, &idaregs); } + + +/// Get dynamic register set of current trace + +inline void idaapi get_trace_dynamic_register_set(dynamic_register_set_t *idaregs) { callui(ui_dbg_get_trace_dynamic_register_set, idaregs); } + +///@} dbg_funcs_trcm + +#endif // __UI__ + +//--------------------------------------------------------------------------- +// High level functions (usable from scripts) +//-------------------------------------------------------------------- +/// \defgroup dbg_funcs_high High level functions +/// \ingroup dbg_funcs +/// +/// These functions can be used from scripts +///@{ + +/// Wait for the next debugger event. +/// See also get_process_state() to get info about the current state +/// of the debugged application + +/// Debugger event codes +enum dbg_event_code_t +{ + DEC_NOTASK = -2, ///< process does not exist + DEC_ERROR = -1, ///< error + DEC_TIMEOUT = 0, ///< timeout +}; + +/// \defgroup WFNE_ Wait for debugger event flags +/// Passed as 'wfne' parameter to wait_for_next_event() +///@{ +#define WFNE_ANY 0x0001 ///< return the first event (even if it doesn't suspend the process) +#define WFNE_SUSP 0x0002 ///< wait until the process gets suspended +#define WFNE_SILENT 0x0004 ///< 1: be silent, 0:display modal boxes if necessary +#define WFNE_CONT 0x0008 ///< continue from the suspended state +#define WFNE_NOWAIT 0x0010 ///< do not wait for any event, immediately return ::DEC_TIMEOUT + ///< (to be used with #WFNE_CONT) +#define WFNE_USEC 0x0020 ///< timeout is specified in microseconds + ///< (minimum non-zero timeout is 40000us) +///@} + +/// \defgroup DOPT_ Debugger options +/// Passed as 'options' parameter to set_debugger_options() +///@{ +#define DOPT_SEGM_MSGS 0x00000001 ///< log debugger segments modifications +#define DOPT_START_BPT 0x00000002 ///< break on process start +#define DOPT_THREAD_MSGS 0x00000004 ///< log thread starts/exits +#define DOPT_THREAD_BPT 0x00000008 ///< break on thread start/exit +#define DOPT_BPT_MSGS 0x00000010 ///< log breakpoints +//#define DOPT_BINS_BPT 0x00000020 // break on breakpoint instruction +#define DOPT_LIB_MSGS 0x00000040 ///< log library loads/unloads +#define DOPT_LIB_BPT 0x00000080 ///< break on library load/unload +#define DOPT_INFO_MSGS 0x00000100 ///< log debugging info events +#define DOPT_INFO_BPT 0x00000200 ///< break on debugging information +#define DOPT_REAL_MEMORY 0x00000400 ///< do not hide breakpoint instructions +#define DOPT_REDO_STACK 0x00000800 ///< reconstruct the stack +#define DOPT_ENTRY_BPT 0x00001000 ///< break on program entry point +#define DOPT_EXCDLG 0x00006000 ///< exception dialogs: +# define EXCDLG_NEVER 0x00000000 ///< never display exception dialogs +# define EXCDLG_UNKNOWN 0x00002000 ///< display for unknown exceptions +# define EXCDLG_ALWAYS 0x00006000 ///< always display +#define DOPT_LOAD_DINFO 0x00008000 ///< automatically load debug files (pdb) +#define DOPT_END_BPT 0x00010000 ///< evaluate event condition on process end +#define DOPT_TEMP_HWBPT 0x00020000 ///< when possible use hardware bpts for temp bpts +#define DOPT_FAST_STEP 0x00040000 ///< prevent debugger memory refreshes when single-stepping +#define DOPT_DISABLE_ASLR 0x00080000 ///< disable ASLR +///@} +#ifndef __UI__ + +/// Wait for the next event. +/// +/// This function (optionally) resumes the process execution, +/// and waits for a debugger event until a possible timeout occurs. +/// +/// \param wfne combination of \ref WFNE_ constants +/// \param timeout number of seconds to wait, -1-infinity +/// \return either an event_id_t (if > 0), or a dbg_event_code_t (if <= 0) + +inline dbg_event_code_t idaapi wait_for_next_event(int wfne, int timeout) { return dbg_event_code_t(callui(ui_dbg_wait_for_next_event, wfne, timeout).i); } + + +/// Get the current debugger event + +inline const debug_event_t *idaapi get_debug_event(void) { return (const debug_event_t *)callui(ui_dbg_get_debug_event).vptr; } + + +/// Set debugger options. +/// Replaces debugger options with the specification combination \ref DOPT_ +/// \return the old debugger options + +inline uint idaapi set_debugger_options(uint options) { return callui(ui_dbg_set_debugger_options, options).i; } + + +/// Set remote debugging options. +/// Should be used before starting the debugger. +/// \param host If empty, IDA will use local debugger. +/// If nullptr, the host will not be set. +/// \param pass If nullptr, the password will not be set +/// \param port If -1, the default port number will be used + +inline void idaapi set_remote_debugger(const char *host, const char *pass, int port=-1) { callui(ui_dbg_set_remote_debugger, host, pass, port); } + + +/// Get process options. +/// Any of the arguments may be nullptr + +inline void idaapi get_process_options( + qstring *path, + qstring *args, + launch_env_t *out_envs, + qstring *sdir, + qstring *host, + qstring *pass, + int *port) +{ + callui(ui_dbg_get_process_options, path, args, sdir, host, pass, port, out_envs); +} + + +/// Set process options. +/// Any of the arguments may be nullptr, which means 'do not modify' + +inline void idaapi set_process_options( + const char *path, + const char *args, + const launch_env_t *envs, + const char *sdir, + const char *host, + const char *pass, + int port) +{ + callui(ui_dbg_set_process_options, path, args, sdir, host, pass, port, envs); +} + + +/// Retrieve the exception information. +/// You may freely modify the returned vector and add/edit/delete exceptions +/// You must call store_exceptions() after any modifications +/// Note: exceptions with code zero, multiple exception codes or names are prohibited + +inline excvec_t *idaapi retrieve_exceptions(void) { return (excvec_t *)callui(ui_dbg_retrieve_exceptions).vptr; } + + +/// Update the exception information stored in the debugger module by +/// invoking its dbg->set_exception_info callback + +inline bool idaapi store_exceptions(void) { return callui(ui_dbg_store_exceptions).cnd; } + + +/// Convenience function: define new exception code. +/// \param code exception code (cannot be 0) +/// \param name exception name (cannot be empty or nullptr) +/// \param desc exception description (maybe nullptr) +/// \param flags combination of \ref EXC_ +/// \return failure message or nullptr. +/// You must call store_exceptions() if this function succeeds + +inline const char *idaapi define_exception(uint code, const char *name, const char *desc, int flags) { return callui(ui_dbg_define_exception, code, name, desc, flags).cptr; } + +#endif // __UI__ + + +//-------------------------------------------------------------------- + +/// Is set_dbg_options() implemented in ::debugger_t? + +inline THREAD_SAFE bool have_set_options(const debugger_t *_dbg) +{ + return _dbg != nullptr && _dbg->have_set_options(); +} + +/// Convenience function to set debugger specific options. +/// It checks if the debugger is present and calls function. + +inline const char *idaapi set_dbg_options( + debugger_t *_dbg, + const char *keyword, + int pri, + int value_type, + const void *value) +{ + const char *res = IDPOPT_BADKEY; + if ( have_set_options(_dbg) ) + _dbg->set_dbg_options(&res, keyword, pri, value_type, value); + return res; +} + + +inline const char *idaapi set_dbg_default_options( + debugger_t *_dbg, + const char *keyword, + int value_type, + const void *value) +{ + return set_dbg_options(_dbg, keyword, IDPOPT_PRI_DEFAULT, value_type, value); +} + +inline const char *idaapi set_int_dbg_options( + debugger_t *_dbg, + const char *keyword, + int32 value) +{ + sval_t sv = value; + return set_dbg_default_options(_dbg, keyword, IDPOPT_NUM, &sv); +} + +#ifndef __KERNEL__ +/// Set options for ::dbg + +inline const char *idaapi set_dbg_options( + const char *keyword, + int pri, + int value_type, + const void *value) +{ + return set_dbg_options(dbg, keyword, pri, value_type, value); +} + +/// Set ::dbg options with #IDPOPT_PRI_DEFAULT + +inline const char *idaapi set_dbg_default_options( + const char *keyword, + int value_type, + const void *value) +{ + return set_dbg_options(keyword, IDPOPT_PRI_DEFAULT, value_type, value); +} + +/// Set an integer value option for ::dbg + +inline const char *idaapi set_int_dbg_options( + const char *keyword, + int32 value) +{ + sval_t sv = value; + return set_dbg_default_options(keyword, IDPOPT_NUM, &sv); +} +#endif // __KERNEL__ + +///@} dbg_funcs_high + +//--------------------------------------------------------------------------- +// S O U R C E I N F O R M A T I O N P R O V I D E R S +//--------------------------------------------------------------------------- +/// \defgroup dbg_funcs_srcinfo Source information providers +/// \ingroup dbg_funcs +/// +/// These providers supply information about the source files and lines +/// to the source level debugger. +/// +/// \note objects that inherit from ::qrefcnt_obj_t must be freed +/// using the release() method. do not use the 'delete' operator! +/// See description for qrefcnt_obj_t::release() +/// +/// Currently this interface is not frozen and may change. +/// We will freeze it once we settle things down. +///@{ + +class srcinfo_provider_t; + +class idc_value_t; +class rangeset_t; +class source_item_t; +class argloc_t; + +/// Maintain a reference count for source items +typedef qrefcnt_t source_item_ptr; +/// Iterator for source items +typedef qiterator _source_item_iterator; +/// Maintain a reference count for source item iterators +typedef qrefcnt_t<_source_item_iterator> source_item_iterator; +/// Vector of source items +typedef qvector source_items_t; + +//-------------------------------------------------------------------------- +/// Execution context. Currently not defined in detail. Will probably +/// hold information about the execution context, like: +/// - thread id +/// - current register values +/// - stack frame address + +class eval_ctx_t +{ + int size_cb; +public: + eval_ctx_t(ea_t _ea) : size_cb(sizeof(*this)), ea(_ea) {} + ea_t ea; +}; + + +#ifndef __UI__ +class TWidget; +#endif + +//-------------------------------------------------------------------------- +/// Describes a source file +class source_file_t : public qrefcnt_obj_t +{ +public: + /// Call this function to free source_file_t + virtual void idaapi release() override = 0; + + /// Get source info provider. + /// There is no need to free or release it after using + virtual srcinfo_provider_t *idaapi get_provider(void) const = 0; + + /// Get path to the source file (or a symbolic name). + /// \param[out] errbuf pointer to buffer for the error message + virtual const char *idaapi get_path(qstring *errbuf) = 0; + + /// Open window with source code (optional function). + /// \param[out] strvec pointer to source text. the text should not be destroyed until the form is closed + /// \param[out] pview pointer to view that displays the source text (subview of TWidget) + /// \param lnnum,colnum cursor coordinates + virtual TWidget *open_srcview(strvec_t **strvec, TWidget **pview, int lnnum, int colnum) = 0; + + /// Read entire file (colored lines). + /// \param[out] buf pointer to output buffer + /// \param[out] errbuf pointer to buffer for the error message + virtual bool idaapi read_file(strvec_t *buf, qstring *errbuf) = 0; +}; + +/// Maintain a reference count for source file objects +typedef qrefcnt_t source_file_ptr; +/// Iterator for source files +typedef qiterator _source_file_iterator; +/// Maintain a reference count for source file iterators +typedef qrefcnt_t<_source_file_iterator> source_file_iterator; + +//-------------------------------------------------------------------------- +/// Kinds of source items. +/// Source items are organized into trees. Each tree starts with a MODULE. +/// Each MODULE consists of FUNC and STTVAR global items. +/// Each FUNC consists of STMTs (statements). +/// Each STMT contains some EXPRs (expressions). +enum src_item_kind_t +{ + SRCIT_NONE, ///< unknown + SRCIT_MODULE, ///< module + SRCIT_FUNC, ///< function + SRCIT_STMT, ///< a statement (if/while/for...) + SRCIT_EXPR, ///< an expression (a+b*c) + SRCIT_STTVAR, ///< static variable/code + SRCIT_LOCVAR ///< a stack, register, or register-relative local variable or parameter +}; + +//-------------------------------------------------------------------------- +/// Describes a subdivision of source information +class source_item_t : public qrefcnt_obj_t +{ +public: + /// Call this function to free source_item_t + virtual void idaapi release() override = 0; + + /// Get source files of the item + virtual source_file_iterator idaapi get_source_files() = 0; + + /// Get name of the item + virtual bool idaapi get_name(qstring *buf) const = 0; + + /// Get line number of the item (1-based) + virtual int idaapi get_lnnum() const = 0; + + /// Get ending line number (1-based.) + /// The returned line number is the next + /// line after the expression + virtual int idaapi get_end_lnnum() const = 0; + + /// Get column number of the item. + /// If unknown, return -1 + virtual int idaapi get_colnum() const = 0; + + /// Get ending column number. + /// The returned column number is the next + /// column after the expression. + /// If unknown, return -1 + virtual int idaapi get_end_colnum() const = 0; + + /// Get starting address of the item + virtual ea_t idaapi get_ea() const = 0; + + /// Get size of the item in bytes. + /// If the item is fragmented, return size + /// of the main fragment. + /// if unknown, return 0. + /// On error, return (asize_t) -1. + virtual asize_t idaapi get_size() const = 0; + + /// Get item boundaries as a set of ranges. + /// This function will be used to determine what breakpoints to set for + /// stepping into/stepping over the item. + virtual bool idaapi get_item_bounds(rangeset_t *set) const = 0; + + /// Get parent of the item. + /// \param max_kind maximal source item kind we are interested in. + /// for example, if max_kinds==#SRCIT_STMT, we are not interested + /// in expressions, only in the enclosing statement or function + virtual source_item_ptr idaapi get_parent(src_item_kind_t max_kind) const = 0; + + /// Create an iterator to enumerate all children of the item + virtual source_item_iterator idaapi create_children_iterator() = 0; + + /// Calculate a string to display as a hint. + /// \param hint output buffer for the hint (may by multiline & with colors) + /// \param ctx execution context. nullptr means missing context. + /// \param nlines number of important lines in the hint + virtual bool idaapi get_hint( + qstring *hint, + const eval_ctx_t *ctx, + int *nlines) const = 0; + + /// Evaluate item value (meaningful only for expression items). + /// \param ctx execution context. nullptr means missing context. + /// \param res buffer for the result (or exception if evaluation failed) + /// \param errbuf buffer for the error message + virtual bool idaapi evaluate( + const eval_ctx_t *ctx, + idc_value_t *res, + qstring *errbuf) const = 0; + + /// Do these two items have the same source?. + /// \retval false the source of the underlying data + /// differs between the two items. + /// \retval true when either the source of the underlying + /// data is the same for the two items, or + /// when such information is not available. + /// + /// E.g., A DWARF implementation of source_item_t will + /// return true if the two items are backed by DIEs + /// that have the same file offset. + virtual bool idaapi equals(const source_item_t *other) const = 0; + + /// \name Getters (for modification) + /// The following functions can be used to extract the item information + /// in order to modify it. For example, if the user wants to modify a variable + /// we will find what exactly needs to be modified. + ///@{ + + /// Get item kind + virtual src_item_kind_t idaapi get_item_kind(const eval_ctx_t * /*ctx*/) const newapi { return SRCIT_NONE; } + /// Does this source item represent a statement? + bool is_stmt(const eval_ctx_t *ctx) const { return get_item_kind(ctx) == SRCIT_STMT; } + /// Does this source item represent a module? + bool is_module(const eval_ctx_t *ctx) const { return get_item_kind(ctx) == SRCIT_MODULE; } + /// Does this source item represent a function? + bool is_func(const eval_ctx_t *ctx) const { return get_item_kind(ctx) == SRCIT_FUNC; } + /// Does this source item represent an expression? + bool is_expr(const eval_ctx_t *ctx) const { return get_item_kind(ctx) >= SRCIT_EXPR; } + /// Does this source item represent a stack, register, or register-relative local variable or parameter? + bool is_locvar(const eval_ctx_t *ctx) const { return get_item_kind(ctx) >= SRCIT_LOCVAR; } + /// Does this source item represent a static variable or code? + bool is_sttvar(const eval_ctx_t *ctx) const { return get_item_kind(ctx) == SRCIT_STTVAR; } + + /// Get source info provider. + /// The instance shouldn't be freed or released after using + virtual srcinfo_provider_t *idaapi get_provider(void) const = 0; + + /// Get the location for this source item + virtual bool idaapi get_location(argloc_t * /*out*/, const eval_ctx_t * /*ctx*/) const newapi { return false; } + + /// Get expression type + virtual bool idaapi get_expr_tinfo(tinfo_t *tif) const = 0; + ///@} +}; + +#define SRCDBG_PROV_VERSION 4 + +//-------------------------------------------------------------------------- +/// Describes the mechanism used to retrieve source file information +class srcinfo_provider_t +{ +public: + /// size of this class + size_t cb; + + /// \ref SPF_ + int flags; +/// \defgroup SPF_ Source info provider property bits +/// Used by srcinfo_provider_t::flags +///@{ +#define SPF_DECOMPILER 0x0001 ///< is a decompiler? +#define SPF_ENABLED 0x0002 ///< enabled by the user + ///< (this bit is managed by ida) +#define SPF_ACTIVE 0x0004 ///< is willing to work with the current idb + ///< (this bit is managed by ida) +#define SPF_VERSION_MASK 0xFF000000 ///< Version mask, in the flags. + ///< No other flag should have a bit rank > 23. +///@} + + /// internal (unique) name of srcinfo provider + const char *name; + + /// external (displayable) name of srcinfo provider + const char *display_name; + + srcinfo_provider_t(const char *_name, const char *_display_name, int _flags=0) + : cb(sizeof(*this)), flags(_flags), name(_name), display_name(_display_name) + { + flags |= SRCDBG_PROV_VERSION << 24; + } + + /// Is the source info provider a decompiler? + bool is_decompiler(void) const { return (flags & SPF_DECOMPILER) != 0; } + /// Has the provider been enabled by the user? + bool is_enabled(void) const { return (flags & SPF_ENABLED) != 0; } + /// Is the provider willing to work with the current idb? + bool is_active(void) const { return (flags & SPF_ACTIVE) != 0; } + + /// See #SPF_VERSION_MASK + uint8 get_version() const { return (flags >> 24) & 0xFF; } + + /// Enable or disable the provider. + /// If the provider fails to initialize, it returns false, otherwise true + virtual bool idaapi enable_provider(bool enable) = 0; + + /// Configure srcinfo provider. + /// \param keyword keyword encountered in IDA.CFG/user config file. + /// if nullptr, then an interactive dialog form should be displayed + /// \param value_type type of value of the keyword - one of \ref IDPOPT_T + /// \param value pointer to value + /// \return one of \ref IDPOPT_RET, otherwise a pointer to an error message + virtual const char *idaapi set_options( + const char *keyword, + int value_type, + const void *value) = 0; + + /// Inform the provider that a module got loaded. + /// The provider is not required to read the module information immediately; + /// it may postpone it until really required. + virtual void idaapi add_module(const char *path, ea_t base, asize_t size) = 0; + + /// Inform the provider that a module got unloaded. + virtual void idaapi del_module(ea_t base) = 0; + + /// Inform the provider that we will request for information now. + /// This function must be called before calling all functions below. + /// probably it will be called when the process gets suspended. + virtual void idaapi get_ready(void) = 0; + + /// Ask the provider if the source information has changed + /// and the screen should be refreshed. UI will call this function + /// periodically (when idle). if it returns a flag value other than #SPCH_NONE, + /// then the source information must be refreshed. + virtual int idaapi get_change_flags(void) = 0; +/// \defgroup SPCH_ Source info changed flags +/// Returned by srcinfo_provider_t::get_change_flags() +///@{ +#define SPCH_NONE 0x0000 ///< nothing has changed +#define SPCH_FILES 0x0001 ///< source files have changed +#define SPCH_ITEMS 0x0002 ///< source items have changed +#define SPCH_LINES 0x0004 ///< source line numbers have changed +///@} + + /// Locate source item by address. + /// The kernel will inquire all registered providers and use the best reply. + /// \param ea linear address + /// \param size size of the item + /// \param level the desired item level: + /// - ::SRCIT_STMT: a statement + /// - ::SRCIT_EXPR: an expression + /// - ::SRCIT_FUNC: a global code item + /// - SRCIT_xxxVAR: a global data item (the exact type is not checked) + /// \param may_decompile meaningful only for the decompiler. if set to false + /// and the function at 'ea' has not been decompiled yet, fail. + virtual source_item_iterator idaapi find_source_items( + ea_t ea, + asize_t size, + src_item_kind_t level, + bool may_decompile) = 0; + + /// Locate source item by a position in a source file. + /// if colnum == 0, return the statement that starts at the specified line. + /// if lnnum == 0, return information about all lines (colnum is ignored in this case) + virtual source_item_iterator idaapi find_source_items(source_file_t *sf, int lnnum, int colnum=0) = 0; + + /// Create iterators to enumerate files. + /// \param filename name of the source file to enumerate + virtual source_file_iterator idaapi create_file_iterator(const char *filename=nullptr) = 0; + + /// Create iterators to enumerate items + virtual source_item_iterator idaapi create_item_iterator(const source_file_t *sf) = 0; + + /// Apply the debug information (types, functions, globals) + /// from the module whose path is 'path', to the IDB + virtual bool idaapi apply_module_info(const char * /*path*/) { return false; } + + /// Locate a global variable by its name. + /// \param name The variable name + /// \param ea The current address + /// \return the source item, or nullptr + virtual source_item_ptr idaapi find_static_item(const char *name, ea_t ea) = 0; +}; + + +//-------------------------------------------------------------------------- +/// Register a source information provider. +/// Source information providers will call this function to register themselves +/// with IDA kernel. +/// Returns false: a service provider with this name already exists. + +inline bool idaapi register_srcinfo_provider(srcinfo_provider_t *sp) { return callui(ui_dbg_register_provider, sp).cnd; } + + +/// Unregister a source information provider. +/// Returns false: no such provider. + +inline bool idaapi unregister_srcinfo_provider(srcinfo_provider_t *sp) { return callui(ui_dbg_unregister_provider, sp).cnd; } + + +#ifndef __UI__ +class source_view_t; + +/// Create a source code view + +inline source_view_t *create_source_viewer( + TWidget **out_ccv, + TWidget *parent, + TWidget *custview, + source_file_ptr sf, + strvec_t *lines, + int lnnum, + int colnum, + int flags) +{ + return (source_view_t *) callui( + ui_create_source_viewer, out_ccv, parent, custview, &sf, + lines, lnnum, colnum, flags).vptr; +} + +#endif + +//-------------------------------------------------------------------------- +/// Get one byte of the debugged process memory. +/// \param out pointer to byte value +/// \param ea linear address +/// \return success +/// \retval true success +/// \retval false address inaccessible or debugger not running + +idaman bool ida_export get_dbg_byte(uint32 *out, ea_t ea); + + +/// Change one byte of the debugged process memory. +/// \param ea linear address +/// \param x byte value +/// \return true if the process memory has been modified + +idaman bool ida_export put_dbg_byte(ea_t ea, uint32 x); + +///@} dbg_funcs_srcinfo + +//-------------------------------------------------------------------------- +// D E B U G G E R M E M O R Y F U N C T I O N S F O R U I +//-------------------------------------------------------------------------- +/// \defgroup dbg_funcs_mem Debugger memory functions for UI +/// \ingroup dbg_funcs +/// +/// Inspect debugged process memory. +///@{ + +/// Set the memory information source for IDA kernel. +/// This function allows the kernel to use information coming from somewhere +/// other than the database (from the debugger, for example) +/// \param dbg_get_memory_config returns current memory configuration +/// in the dynamic memory allocated by qalloc(). +/// The kernel will qfree() it automatically. +/// If this argument is nullptr, then the debugged +/// process memory is not used. +/// - n: number of ::range_t elements in the answer +/// \param memory_read read bytes from the debugged process memory +/// \param memory_write write bytes to the debugged process memory +/// (don't forget to call invalidate_dbgmem_contents() from it) + +idaman void ida_export set_dbgmem_source( + range_t *(idaapi*dbg_get_memory_config)(int *n), + int (idaapi*memory_read)(ea_t ea, void *buffer, int size), + int (idaapi*memory_write)(ea_t ea, const void *buffer, int size)); + + +/// Invalidate the debugged process memory configuration. +/// Call this function if the debugged process might have changed its memory +/// layout (allocated more memory, for example) + +idaman void ida_export invalidate_dbgmem_config(void); + + +/// Invalidate the debugged process memory contents. +/// Call this function each time the process has been stopped or the process +/// memory is modified. +/// If ea == #BADADDR, then the whole memory contents will be invalidated + +idaman void ida_export invalidate_dbgmem_contents(ea_t ea, asize_t size); + + +/// Is the debugger currently running? + +idaman bool ida_export is_debugger_on(void); + + +/// Is the address mapped to debugger memory? + +idaman bool ida_export is_debugger_memory(ea_t ea); + +///@} dbg_funcs_mem + +//------------------------------------------------------------------------ +#if !defined(__UI__) && !defined(__KERNEL__) // Not for the UI nor the kernel + + +/// \defgroup dbg_funcs_conv Misc +/// \ingroup dbg_funcs +/// +/// Convenience functions offered by the user interface +///@{ +inline ea_t idaapi get_tev_ea(int n) { ea_t ea; callui(ui_dbg_get_tev_ea, n, &ea); return ea; } +inline int idaapi get_tev_type(int n) { return callui(ui_dbg_get_tev_type, n).i; } +inline int idaapi get_tev_tid(int n) { return callui(ui_dbg_get_tev_tid, n).i; } +inline void idaapi bring_debugger_to_front(void) { callui(ui_dbg_bring_to_front); } +inline void idaapi get_manual_regions(meminfo_vec_t *ranges) { callui(ui_dbg_get_manual_regions, ranges); } +inline void idaapi set_manual_regions(const meminfo_vec_t *ranges) { callui(ui_dbg_set_manual_regions, ranges); } +inline void idaapi edit_manual_regions() { callui(ui_dbg_edit_manual_regions); } +inline void idaapi enable_manual_regions(bool enable) { callui(ui_dbg_enable_manual_regions, enable); } +inline int idaapi handle_debug_event(const debug_event_t *ev, int rqflags) { return callui(ui_dbg_handle_debug_event, ev, rqflags).i; } +inline bool idaapi add_virt_module(const modinfo_t *mod) { return callui(ui_dbg_add_vmod, mod).cnd; } +inline bool idaapi del_virt_module(const ea_t base) { return callui(ui_dbg_del_vmod, base).cnd; } +inline int idaapi set_bptloc_string(const char *s) { return callui(ui_dbg_set_bptloc_string, s).i; } +inline const char *idaapi get_bptloc_string(int i) { return callui(ui_dbg_get_bptloc_string, i).cptr; } +inline int idaapi internal_get_sreg_base(ea_t *answer, thid_t tid, int sreg_value) { return callui(ui_dbg_internal_get_sreg_base, answer, tid, sreg_value).i; } +inline int idaapi internal_ioctl(int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize) { return callui(ui_dbg_internal_ioctl, fn, buf, size, poutbuf, poutsize).i; } +inline int idaapi get_reg_vals(thid_t tid, int clsmask, regval_t *values) { return callui(ui_dbg_read_registers, tid, clsmask, values).i; } +inline int idaapi set_reg_val(thid_t tid, int regidx, const regval_t *value) { return callui(ui_dbg_write_register, tid, regidx, value).i; } +inline int idaapi get_dbg_memory_info(meminfo_vec_t *ranges) { return callui(ui_dbg_get_memory_info, ranges).i; } + +/// Move a bpt into a folder in the breakpoint dirtree +/// if the folder didn't exists, it will be created +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param bpt bpt that will be moved +/// \param grp_name absolute path to the breakpoint dirtree folder +/// \return success +inline bool idaapi set_bpt_group(bpt_t &bpt, const char *grp_name) { return callui(ui_dbg_set_bpt_group, &bpt, grp_name).cnd; } + +/// Move a bpt into a folder in the breakpoint dirtree based on the bpt_location +/// \ref find_bpt is called to retrieve the bpt and then \ref set_bpt_group +/// if the folder didn't exists, it will be created +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param bptloc bptlocation of the bpt that will be moved +/// \param grp_name absolute path to the breakpoint dirtree folder +/// \return success +inline bool idaapi set_bptloc_group(const bpt_location_t &bptloc, const char *grp_name) { return callui(ui_dbg_set_bptloc_group, &bptloc, grp_name).cnd; } + +/// Retrieve the absolute path to the folder of the bpt based on the bpt_location +/// \ref find_bpt is called to retrieve the bpt +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param[out] grp_name absolute path to the breakpoint dirtree folder, can be null +/// \param bptloc bptlocation of the bpt +/// \return success +/// \retval true breakpoint correclty moved to the directory +inline bool idaapi get_bpt_group(qstring *grp_name, const bpt_location_t &bptloc) { return callui(ui_dbg_get_bpt_group, grp_name, &bptloc).cnd; } + +/// Retrieve the list of absolute path of all folders of bpt dirtree +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param[out] bptgrps list of absolute path in the bpt dirtree +/// \return number of folders returned +inline size_t idaapi list_bptgrps(qstrvec_t *bptgrps) { return callui(ui_dbg_list_bptgrps, bptgrps).ssize; } + +/// Rename a folder of bpt dirtree +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param old_name absolute path to the folder to be renamed +/// \param new_name absolute path of the new folder name +/// \return success +inline bool idaapi rename_bptgrp(const char *old_name, const char *new_name) { return callui(ui_dbg_rename_bptgrp, old_name, new_name).cnd; } + +/// Delete a folder, bpt that were part of this folder are moved to the root folder +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param name full path to the folder to be deleted +/// \return success +inline bool idaapi del_bptgrp(const char *name) { return callui(ui_dbg_del_bptgrp, name).cnd; } + +/// Retrieve a copy the bpts stored in a folder +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param[out] bpts : pointer to a vector where the copy of bpts are stored +/// \param grp_name absolute path to the folder +/// \return number of bpts present in the vector +inline ssize_t idaapi get_grp_bpts(bpt_vec_t *bpts, const char *grp_name) { return callui(ui_dbg_get_grp_bpts, bpts, grp_name).ssize; } + +/// Enable (or disable) all bpts in a folder +/// \sq{Type, Synchronous function, +/// Notification, none (synchronous function)} +/// \param bptgrp_name absolute path to the folder +/// \param enable by default true, enable bpts, false disable bpts +/// \retval -1 an error occured +/// \retval 0 no changes +/// \retval >0 nubmers of bpts udpated +inline int idaapi enable_bptgrp(const char *bptgrp_name, bool enable=true) { return callui(ui_dbg_enable_bptgrp, bptgrp_name, enable).i; } +inline bool idaapi get_local_vars(srcinfo_provider_t *prov, ea_t ea, source_items_t *out) { return callui(ui_dbg_get_local_vars, prov, ea, out).cnd; } +inline bool idaapi srcdbg_request_step_into(void) { return callui(ui_dbg_srcdbg_request_step_into).cnd; } +inline bool idaapi srcdbg_request_step_over(void) { return callui(ui_dbg_srcdbg_request_step_over).cnd; } +inline bool idaapi srcdbg_request_step_until_ret(void) { return callui(ui_dbg_srcdbg_request_step_until_ret).cnd; } +///@} dbg_funcs_conv +#endif // !__UI__ && !__KERNEL__ + +#ifndef __UI__ +inline int idaapi internal_cleanup_appcall(thid_t tid) { return callui(ui_dbg_internal_cleanup_appcall, tid).i; } +inline int idaapi hide_all_bpts(void) { return callui(ui_dbg_hide_all_bpts).i; } +inline ssize_t idaapi read_dbg_memory(ea_t ea, void *buffer, size_t size) { return callui(ui_dbg_read_memory, ea, buffer, size).ssize; } +inline bool idaapi get_module_info(ea_t ea, modinfo_t *modinfo) { return callui(ui_dbg_get_module_info, ea, modinfo).cnd; } +inline drc_t idaapi dbg_bin_search(ea_t *out, ea_t start_ea, ea_t end_ea, const compiled_binpat_vec_t &data, int srch_flags, qstring *errbuf) + { return drc_t(callui(ui_dbg_bin_search, out, start_ea, end_ea, &data, srch_flags, errbuf).i); } +inline bool idaapi dbg_can_query(debugger_t *_dbg) +{ + // Debugger can be queried IIF it is set and either currently in + // suspended state, or can be queried while not in suspended state + return _dbg != nullptr && (_dbg->may_disturb() || get_process_state() < DSTATE_NOTASK); +} +inline bool idaapi dbg_can_query(void) +{ + return dbg_can_query(dbg); +} +inline bool idaapi load_debugger(const char *dbgname, bool use_remote) { return callui(ui_dbg_load_debugger, dbgname, use_remote).cnd; } +inline bool idaapi collect_stack_trace(thid_t tid, call_stack_t *trace) { return callui(ui_dbg_collect_stack_trace, tid, trace).cnd; } +inline bool idaapi get_global_var(srcinfo_provider_t *prov, ea_t ea, const char *name, source_item_ptr *out) { return callui(ui_dbg_get_global_var, prov, ea, name, out).cnd; } +inline bool idaapi get_local_var(srcinfo_provider_t *prov, ea_t ea, const char *name, source_item_ptr *out) { return callui(ui_dbg_get_local_var, prov, ea, name, out).cnd; } +inline srcinfo_provider_t *idaapi get_srcinfo_provider(const char *name) { return (srcinfo_provider_t *)callui(ui_dbg_get_srcinfo_provider, name).vptr; } +inline bool idaapi get_current_source_file(qstring *out) { return callui(ui_dbg_get_current_source_file, out).cnd; } +inline int idaapi get_current_source_line(void) { return callui(ui_dbg_get_current_source_line).i; } +inline void idaapi add_path_mapping(const char *src, const char *dst) { callui(ui_dbg_add_path_mapping, src, dst); } +inline bool idaapi srcdbg_step_into(void) { return callui(ui_dbg_srcdbg_step_into).cnd; } +inline bool idaapi srcdbg_step_over(void) { return callui(ui_dbg_srcdbg_step_over).cnd; } +inline bool idaapi srcdbg_step_until_ret(void) { return callui(ui_dbg_srcdbg_step_until_ret).cnd; } +inline ssize_t idaapi write_dbg_memory(ea_t ea, const void *buffer, size_t size) { return callui(ui_dbg_write_memory, ea, buffer, size).ssize; } +inline void idaapi set_debugger_event_cond(const char *NONNULL evcond) { callui(ui_dbg_set_event_cond, evcond); } +inline const char *idaapi get_debugger_event_cond(void) { return callui(ui_dbg_get_event_cond).cptr; } +inline const char *bpt_t::get_cnd_elang() const { return (const char *)(callui(ui_dbg_internal_get_elang, this).cptr); } +inline bool bpt_t::set_cnd_elang(const char *name) { return callui(ui_dbg_internal_set_elang, this, name).cnd; } +#endif // !__UI__ + +#ifdef __KERNEL__ +inline bool idaapi set_bpt_group(bpt_t &bpt, const char *grp_name) { return get_bpt_kernel_interface()->set_bpt_dir(&bpt, grp_name); } +inline int idaapi set_bptloc_string(const char *s) { return get_bpt_kernel_interface()->add_bptloc_string(s); } +inline const char *idaapi get_bptloc_string(int i) { return get_bpt_kernel_interface()->get_bptloc_string(i); } +#endif + +// internal kernel functions to lock the debugger memory configuration updates +// Do not use these functions! They will be removed! +idaman void ida_export lock_dbgmem_config(void); +idaman void ida_export unlock_dbgmem_config(void); + + +#endif + +``` + +`IdaSDK/demangle.hpp`: + +```hpp +/* + * CPP/D/Swift Demangler. + * Copyright (c) 2000-2018 by Iouri Kharon. + * E-mail: yjh@styx.cabel.net + * + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _DEMANGLE_HPP +#define _DEMANGLE_HPP + +// int32 result code +#define ME_INTERR -1 ///< Internal error +#define ME_PARAMERR -2 ///< Input parameters are wrong +#define ME_ILLSTR -3 ///< Incorrectly mangled name +#define ME_SMALLANS -4 ///< Output buffer is too small + ///< This code is possible only with the 'old' calling + ///< form. With the new calling form the output buffer + ///< will have '...' as the last characters and the + ///< result code or'ed with the sign bit +#define ME_FRAME -5 ///< Partial demanging is possible (the input name has + ///< unrecognized suffix) +#define ME_NOCOMP -6 ///< Could not determine the compiler +#define ME_ERRAUTO -7 ///< Specified compiler is impossible for the input name +#define ME_NOHASHMEM -8 ///< Out of internal indexes-most likely bad input name +#define ME_NOSTRMEM -9 ///< Out of internal buffers (can't be!:) + +#define ME_NOERROR_LIMIT -10 ///< Lowest error number. Lower values + ///< signal about the truncated output name + ///< (the output buffer is too small) + +#define M_PRCMSK 0x0000000F ///< If = 0, then data +#define MT_DEFAULT 0x00000001 ///< 1 - default (for watcom/gnu only this) +#define MT_CDECL 0x00000002 ///< 2 - __cdecl +#define MT_PASCAL 0x00000003 ///< 3 - __pascal +#define MT_STDCALL 0x00000004 ///< 4 - __stdcall +#define MT_FASTCALL 0x00000005 ///< 5 - __fastcall +#define MT_THISCALL 0x00000006 ///< 6 - __thiscall [ms & bc => pragma only] +#define MT_FORTRAN 0x00000007 ///< 7 - __fortran +#define MT_SYSCALL 0x00000008 ///< 8 - __syscall [without ms] +#define MT_INTERRUPT 0x00000009 ///< 9 - __interrupt (only with __cdecl!) +#define MT_MSFASTCALL 0x0000000A ///< A - __msfastcall (bc) +#define MT_CLRCALL 0x0000000B ///< B - __clrcall (vc7) +#define MT_DMDCALL 0x0000000C ///< C - __dcall (dm D language abi) +#define MT_VECTORCALL 0x0000000D ///< D - __vectorcall (vc13) +#define MT_REGCALL 0x0000000E ///< E - __regcall (icl, clang) + +// reserved +#define MT_LOCALNAME 0x0000000F ///< f - might be function or data. Currently + ///< is used only for bc - as the + ///< identifier for local pascal labels + +#define M_SAVEREGS 0x00000010 ///< For functions with "__saveregs" + +#define M_CLASS 0x000000E0 ///< 0 - no keyword (not a member field) +#define MT_PUBLIC 0x00000020 ///< 1 - public +#define MT_PRIVATE 0x00000040 ///< 2 - private +#define MT_PROTECT 0x00000060 ///< 3 - protected +#define MT_MEMBER 0x00000080 ///< 4 - undetermined (bc/wat/gcc) +#define MT_VTABLE 0x000000A0 ///< 5 - vtable (bc/gnu) +#define MT_RTTI 0x000000C0 ///< 6 - typeinfo table (gcc3), witness table (Swift) +// reserved + +#define M_PARMSK 0x0000FF00 ///< Parameter number mask (excluding ellipsis) + ///< 255 - >= 255 +#define MT_PARSHF 8 ///< shift to PARMSK +#define MT_PARMAX 0xFF ///< Number limiter + ///< ATT: when CC is __vectorcall and mode is 'C' + ///< real argscount is unknown. This number is + ///< total sizeof of all arguments divided to + ///< sizeof of defptr +#define M_ELLIPSIS 0x00010000 ///< The function _certainly_ has '...' +#define MT_VOIDARG 0x0001FF00 ///< If = 0, the func(void), i.e. no parameters +#define M_STATIC 0x00020000 ///< static + ///< gcc3 - static data in a function + ///< might be encountered in object files and + ///< (possibly) in binaries with debug info +#define M_VIRTUAL 0x00040000 ///< virtual + ///< NOTE: for (D) not virtual -- this (counted!) +#define M_AUTOCRT 0x00080000 ///< Most likely "autogenerated" function (data) + ///< NOTE: +M_STATIC => "__linkproc__" (bc) + +#define M_TYPMASK 0x00700000 ///< Special functions (0-regular function) +#define MT_OPERAT 0x00100000 ///< 1 - operator +#define MT_CONSTR 0x00200000 ///< 2 - constructor +#define MT_DESTR 0x00300000 ///< 3 - destructor +#define MT_CASTING 0x00400000 ///< 4 - type conversion +#define MT_CLRCDTOR 0x00500000 ///< 5 - delphi2010 CLR ctor/dtor for packages +// reserved + +#define M_TRUNCATE 0x00800000 ///< Name was truncated by the compiler (bc/va) +#define M_THUNK 0x01000000 ///< [thunk]: +#define M_ANONNSP 0x02000000 ///< ms => Anonymous Namespace for field + ///< gc3 => Item placed in Anonymous namespace + ///< wat => anonymous_enum + ///< bc => + TMPLNAM = PascalTemplate (for DCC) + ///< If separate - "automatic" except_t + ///< from CBuilder for "external" variables + ///< or a template for global object + ///< constructor/destructor tables (for CBuilder + ///< as well) +#define M_TMPLNAM 0x04000000 ///< ms => template name (?) + ///< wat => + ///< bc => template name => its description table + ///< gc3 => any template funciton/data +#define M_DBGNAME 0x08000000 ///< ms => CV: + ///< wat => xxxx: (T?xxxx-form) + ///< bc => old pascal format (capitalized) + ///< gc3 => unicode symbols or 'vendor-extension' + ///< qualifiers are present + +#define M_COMPILER 0x70000000 ///< Compiler mask (0-unknown) +#define MT_MSCOMP 0x10000000 ///< 1 - microsoft/symantec +#define MT_BORLAN 0x20000000 ///< 2 - borland +#define MT_WATCOM 0x30000000 ///< 3 - watcom +#define MT_OTHER 0x40000000 ///< 4 - digital mars D language (start: _D) + ///< - apple Swift language (start: [_]_T) +// !!! The following definitions must be last and in this order! +#define MT_GNU 0x50000000 ///< 5 - GNU - (over VA for autodetection) +#define MT_GCC3 0x60000000 ///< 6 - gcc-v3 + ///< In the short form this answer is possible + ///< for GNU/VA as well, but gcc3 can be + ///< explicitly requested only with it. + ///< Autodetection works but not very reliable. +#define MT_VISAGE 0x70000000 ///< 7 - Visual Age - never autodetected + ///< In the short form this answer means VA + ///< or GNU. In the automatic mode GNU will + ///< be used! +//--------------------------------------------------------------------------- +// Flags to inhibit different parts of the demangled name +#define MNG_PTRMSK 0x7 ///< Memory model mask +// DO NOT change order in this group (PtrType) +#define MNG_DEFNEAR 0x0 ///< inhibit near, display everything else +#define MNG_DEFNEARANY 0x1 ///< inhibit near/__ptr64, display everything else +#define MNG_DEFFAR 0x2 ///< inhibit far, display everything else +#define MNG_NOPTRTYP16 0x3 ///< inhibit everything (disables vc7-extensions) +#define MNG_DEFHUGE 0x4 ///< inhibit huge, display everything else +#define MNG_DEFPTR64 0x5 ///< inhibit __pt64, display everything else + ///< ATT: in 64bit must be + MNG_NOTYPE|MNG_NOCALLC +#define MNG_DEFNONE 0x6 ///< display everything +#define MNG_NOPTRTYP 0x7 ///< inhibit everything +// +#define MNG_NODEFINIT 0x00000008 ///< Inhibit everything except the main name + ///< This flag is not recommended + ///< for __fastcall/__stdcall GCC3 names + ///< because there is a high probablity of + ///< incorrect demangling. Use it only when + ///< you are sure that the input is a + ///< cygwin/mingw function name +// +#define MNG_NOUNDERSCORE 0x00000010 ///< Inhibit underscores in __ccall, __pascal... + +#define MNG_NOTYPE 0x00000020 ///< Inhibit callc&based +#define MNG_NORETTYPE 0x00000040 ///< Inhibit return type of functions +#define MNG_NOBASEDT 0x00000080 ///< Inhibit base types + ///< NOTE: also inhibits "__linkproc__" + ///< NOTE: -"- 'implicit self types' (Swift) +#define MNG_NOCALLC 0x00000100 ///< Inhibit __pascal/__ccall/etc + ///< NOTE: also inhibits "extern (cc)" (D) +#define MNG_NOPOSTFC 0x00000200 ///< Inhibit postfix const +#define MNG_NOSCTYP 0x00000400 ///< Inhibit public/private/protected + ///< NOTE: also inhibits in/out/lazy for args (D) + ///< NOTE: -"- dynamic/super/override/... (Swift) +#define MNG_NOTHROW 0x00000800 ///< Inhibit throw description + ///< NOTE: also inhibits all funcattr (D) +#define MNG_NOSTVIR 0x00001000 ///< Inhibit "static" & "virtual" + ///< NOTE: also inhibits (D) top-level procs (<=) +#define MNG_NOECSU 0x00002000 ///< Inhibit class/struct/union/enum[/D:typedef] +#define MNG_NOCSVOL 0x00004000 ///< Inhibit const/volatile/restrict + ///< NOTE: also inhibits __unaligned (vc) + ///< NOTE: also inhibits transaction_safe(gcc) + ///< NOTE: also inhibits shared/immutable (D) + ///< NOTE: also inhibits prefix/postfix/infix/inout (Swift) +#define MNG_NOCLOSUR 0x00008000 ///< Inhibit __closure for borland + ///< 'reabstract thunk' description (Swift) +#define MNG_NOUNALG 0x00010000 ///< Inhibit __unaligned (see NOCSVOL) + ///< NOTE: also inhibit transaction_safe (see NOCSVOL) +#define MNG_NOMANAGE 0x00020000 ///< Inhibit __pin/__box/__gc for ms(.net) + ///< NOTE: also inhibit archetype/witness (Swift) + ///< NOTE: also ingibit [abi:xxxx] (gcc3) +#define MNG_NOMODULE 0x00040000 ///< Inhibit module names (Swift) +// 0x00080000 +// +#define MNG_SHORT_S 0x00100000 ///< signed (int) is displayed as s(int) +#define MNG_SHORT_U 0x00200000 ///< unsigned (int) is displayed as u(int) +#define MNG_ZPT_SPACE 0x00400000 ///< Display space after comma in the arglist + ///< NOTE: also spaces in name:type pair (Swift) + ///< and around Swift return clause -> +#define MNG_DROP_IMP 0x00800000 ///< Inhibit __declspec(dllimport) +// +// 0x01000000 +#define MNG_IGN_ANYWAY 0x02000000 ///< Ingore '_nn' at the end of name +#define MNG_IGN_JMP 0x04000000 ///< Ingore 'j_' at the beginning of name +#define MNG_MOVE_JMP 0x08000000 ///< Move 'j_' prefix to the demangled name + // If both MNG_IGN_JMP and MNG_MOVE_JMP + // are set then move the prefix only if + // the name was not truncated +// +#define MNG_COMPILER_MSK 0x70000000 // Compiler mask (0-autodetect) + +#define MNG_SHORT_FORM (MNG_NOTYPE|MNG_NORETTYPE|MNG_NOPOSTFC|MNG_NOPTRTYP \ + | MNG_NOSCTYP|MNG_NOTHROW|MNG_NOSTVIR|MNG_NOECSU|MNG_NOCLOSUR \ + | MNG_SHORT_U|MNG_DROP_IMP|MNG_NOUNALG|MNG_NOMANAGE \ + | MNG_IGN_JMP|MNG_MOVE_JMP|MNG_IGN_ANYWAY) +#define MNG_LONG_FORM (MNG_ZPT_SPACE | MNG_IGN_JMP | MNG_IGN_ANYWAY | MNG_NOPTRTYP) + +// The description of the following symbol is in the notes +#define MNG_CALC_VALID (MNG_COMPILER_MSK|MNG_IGN_JMP|MNG_IGN_ANYWAY) + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +#ifndef H2ASH + +#if !defined(NO_OBSOLETE_FUNCS) || defined(__DEFINE_DEMANGLE__) +typedef int32 ida_export demangler_t( + char *answer, + uint answer_length, + const char *str, + uint32 disable_mask); +idaman demangler_t demangle; +#endif + +// If answer_length == 0 then no demangling is performed neither. The function +// will check if demangling is possible and what compiler is used to mangle +// the name. If the name cannot be demangled then the function will return 0. +// NOTE: the answer MT_MSCOMP+1 means __msfastcall +// (or borland class name with "_4" suffix) and the demangling is possible +// either as MS (__fastcall) or as bc (__msfastcall) +// NOTE: the answer MT_GCC3+1 means POSSIBLE mingw/cygwin with +// __stdcall/__fastcall but it might also mean ms-stdcall. +// In essense it means that the demangler cannot determine the compiler +// precisely. +// It also means that the demangling is possible in the gcc3 mode +// ONLY when the compiler is explicitly set to gcc3 and MNG_NODEFINIT +// bit is not set. + +// If answer == NULL then the demangler will return check if the demangling +// is possible and only return the flags. +// In this case answer_length should be enough to hold the demangled name. + +// NOTE: If int32(answer_length) < 0 then the demangler will calcuate the +// the number of purged bytes for the given name. In this case +// disable_mask may contain only bits included in MNG_CALC_VALID, +// and -answer_length must be equal to the register size (e.g. +// sizeof(uint16/uint32/uint64)). The value of the register size +// is used to check the numeric value in the ms stdcall/fastcall +// encoding (it is used in the gcc mode too). +// if return value <= 0 - no purged bytes or valid information. +// If (value & 1) != 0 - ms stdcall (definite npurged is value-1. +// If (value & 1) == 0 - 'encoded' counter (not implemented yet). + +// If answer != NULL (and answer_length != 0) - perform demangling and fill out. +// NOTE: if int32(answer_length) < 0 then the buffer size will be -answer_length +// but 'answer' is interpreted not as a pointer to the output buffer but +// as a pointer to pointer to the output buffer (char**). +// In this case if the function succeeds,a pointer to the answer end +// will be returned in the pointer (like stpcpy). In this form 'answer' +// cannot be NULL and *(char**)answer cannot be NULL. +// answer_length must be greater than 9 for the 'truncated' answer. + +typedef int mangled_name_type_t; +const mangled_name_type_t MANGLED_CODE = 0; +const mangled_name_type_t MANGLED_DATA = 1; +const mangled_name_type_t MANGLED_UNKNOWN = 2; + +idaman mangled_name_type_t ida_export get_mangled_name_type(const char *name); + +#endif // H2ASH +#endif // _DEMANGLE_HPP + +``` + +`IdaSDK/diff3.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 2005-2025 Hex-Rays SA + * ALL RIGHTS RESERVED. + */ + +#ifndef _DIFF3_HPP +#define _DIFF3_HPP + +/*! \file diff3.hpp + + \brief 3-way diff for anchored info + + NOTE: this functionality is available in IDA Teams (not IDA Pro) + + Since names, comments, functions, etc, are tied to addresses, + we need this kind of diff engine. Other kinds of diff engines + will be necessary too (for example: dirtrees, types, and text) + +*/ + +// The diffing algorithms in this file match information tied to addresses. +// So, if addresses do not match, the information will be considered to be different. + +//------------------------------------------------------------------------- +/// A range of the difference source. +/// This is just a pair of start and end positions. +/// The end position is excluded, as usual. +struct diff_range_t +{ + diffpos_t start; + diffpos_t end; + diff_range_t(diffpos_t s=0, diffpos_t e=0) : start(s), end(e) {} + bool empty() const { return start >= end; } + void clear() { start = end = 0; } + bool contains(diffpos_t p) const { return start <= p && p < end; } + void set_start(diffpos_t p) + { + start = p; + if ( end < start ) + end = start; + } + void set_end(diffpos_t p) + { + end = p; + if ( end < start ) + start = end; + } + void intersect(const diff_range_t &r) + { + if ( start < r.start ) + start = r.start; + if ( end > r.end ) + end = r.end; + if ( end < start ) + end = start; + } + + int compare(const diff_range_t &r) const { return start > r.start ? 1 : start < r.start ? -1 : 0; } + + bool operator ==(const diff_range_t &r) const { return compare(r) == 0; } + bool operator !=(const diff_range_t &r) const { return compare(r) != 0; } +}; +DECLARE_TYPE_AS_MOVABLE(diff_range_t); + +//------------------------------------------------------------------------ +/// A difference degree. +/// Negative values are illegal. +/// 0 means no difference. +/// 1 means a difference. +/// Bigger values mean more important differences, of another nature. +/// INT_MAX is used for the situations where diffpos_t values are different. +/// Example: when comparing instructions and data, 1 is used to denote +/// differences at the operand type level, and 2 is used to denote differences +/// between instructions and data: +/// diff_degree("cmp eax, 20h", "db 83h, 0F8h, 20h") => 2 +/// diff_degree("cmp eax, 20h", "cmp eax, 32") => 1 +/// In the presence of the differences of higher degree the differences of +/// lower degrees should be ignored. +typedef ssize_t diff_degree_t; + +//------------------------------------------------------------------------ +enum diff_action_t +{ + DIFF_NONE, ///< unknown + DIFF_USE1, ///< use information from src1 + DIFF_USE2, ///< use information from src2 + DIFF_BOTH, ///< use information from both (conflict) +}; + +//------------------------------------------------------------------------ +/// A difference region. +/// These regions represent the mismatching ranges in the difference sources. +struct diff_region_t : public diff_range_t +{ + diff_degree_t diff_degree = 0; + diff_action_t action = DIFF_BOTH; + void clear() { diff_range_t::clear(); action = DIFF_NONE; } + bool is_useless() const { return empty() || diff_degree == 0; } + qstring dstr() const; +}; +DECLARE_TYPE_AS_MOVABLE(diff_region_t); +typedef qvector diff_regions_t; + +//------------------------------------------------------------------------ +enum diffpos_check_t +{ + DIFFPOS_CHECK, ///< verify if the position is valid; + ///< if not, return the next valid position + DIFFPOS_FORWARD, ///< advance to the next valid position + DIFFPOS_BACKWARD, ///< back off to the previous valid position +}; + +//------------------------------------------------------------------------ +struct diff_text_t : public qstrvec_t +{ + diffpos_t pos; +}; +DECLARE_TYPE_AS_MOVABLE(diff_text_t); +typedef qvector diff_texts_t; + +//-------------------------------------------------------------------------- +/// standard indexes into dbctx_ids[] and similar arrays +enum diff_source_idx_t +{ + NONE_IDX = -1, + LOCAL_IDX = 0, + REMOTE_IDX = 1, + BASE_IDX = 2 +}; + +//------------------------------------------------------------------------ +#ifndef SWIG +#define DECLARE_DIFF_SOURCE_HELPERS(decl)\ +decl void ida_export diff_source_merge_region(class diff_source_t *destination, class diff_source_t *source, const diff_range_t &dr); +#else +#define DECLARE_DIFF_SOURCE_HELPERS(decl) +#endif // SWIG + +DECLARE_DIFF_SOURCE_HELPERS(idaman) + +//------------------------------------------------------------------------ +/// A difference source. +/// This abstract class provides information that is necessary for comparisons. +/// It can represent list of types, names, structs, enums. +/// It can also represent the program addresses, which can be used to +/// compare the attributes of the disassembly instructions, data, etc. +class diff_source_t +{ + friend class diff3_engine_t; + diffpos_t get_lastpos(const diff_range_t &r) + { + diffpos_t last = check_position(r.end, DIFFPOS_BACKWARD); + if ( last < r.start ) + last = r.end; + if ( last == BADDIFF ) + last = r.start; + return last; + } + + void _merge_region(diff_source_t *source, const diff_range_t &dr); + + DECLARE_DIFF_SOURCE_HELPERS(friend) + +public: + int dbctx_id; + diff_source_idx_t diffidx = NONE_IDX; + diff_range_t range = diff_range_t(0, BADDIFF); + diff_source_t(int id) : dbctx_id(id) {} + + virtual ~diff_source_t() {} + + /// initialize diff source. + /// this function is called immediately before starting to use the diff source. + /// this is the right place to read info from the idb for diffing. + /// doing it earlier (for example, in the diff_source_t ctr) is wrong + /// because a diff_source_t of one kind may depend on a diff_source_t of + /// another kind. For example, crefs_diff_source_t depends on the flags_diff_source_t. + /// It makes to start diffing only after completing work with flags. + /// \note in short, do not perform initialization in the ctr, do it here! + virtual void init_diff_source() {} + + /// set new range of positions + virtual void set_range(const diff_range_t &r) { range = r; } + + /// get current range of positions + virtual const diff_range_t &get_range() const { return range; } + + /// check the position, adjust and move it if requested + /// if the requested position after DIFFPOS_FORWARD/DIFFPOS_BACKWARD does not + /// exist, return a value outside of get_range() + virtual diffpos_t check_position(diffpos_t dpos, diffpos_check_t adj=DIFFPOS_CHECK) const = 0; + + /// compare two difference source at the specified position. + /// returns difference degree. + /// 0 - no difference. non-zero return value mean that the chunks differ. + /// the bigger the number, more important are differences. + /// for simple cases please use 1 to indicate differences. + virtual diff_degree_t compare_chunks(diff_source_t *src2, diffpos_t dpos) const = 0; + + /// find the next difference. + /// this is an optional callback to speed up comparisons. + /// \param dpos1 pointer to position in the current source. + /// out: position of the next difference. + /// \param dpos2 pointer to position in the second source. + /// out: position of the next difference. + /// \param src2 pointer to the second source. + /// \return >= if implemented. the returned value is the difference degree, + /// the same thing as returned by compare_chunks + virtual diff_degree_t find_next_diffpos( + diffpos_t * /*dpos1*/, + diffpos_t * /*dpos2*/, + diff_source_t * /*src2*/) const + { + return -1; // not implemented + } + + /// print the name at the specified position. + /// usually it is the position name or a similar short string. + virtual qstring print_diffpos_name(diffpos_t dpos) const = 0; + + /// print the details at the specified position. + /// usually contains multiple lines, one for each attribute or detail. + /// \note add details to OUT, do not clean the existing content + virtual void print_diffpos_details(qstrvec_t * /*out*/, diffpos_t /*dpos*/) const {} + + /// merge from another diff source. + /// this optional callback provides functionality to copy information + /// from SRC at position DPOS. + virtual void merge_add(diff_source_t * /*src*/, diffpos_t /*dpos*/) {} + + /// delete information at the specified position. + virtual void merge_del(diffpos_t /*dpos*/) {} + + /// replace information at the position DPOS using SRC. + /// the default implementation is provided below. + virtual void merge_replace(diff_source_t *src, diffpos_t dpos) + { + merge_del(dpos); + merge_add(src, dpos); + } + + /// merge a region from another diff source. + /// default implementation that uses one of merge_add/merge_del/merge_replace + /// calls for each item in the region. + /// A derived class may override this function to do that in a more optimal way + virtual void merge_region(diff_source_t *src, const diff_range_t ®ion) + { + diff_source_merge_region(this, src, region); + } + +#ifdef TESTABLE_BUILD + // dump the test results into the provided log file. + // nb: there is no need to switch dbctx, it is already done. + virtual void dump_merge_results(FILE * /*fp*/) const {} +#endif + + diff_texts_t print_range(const diff_range_t *r, bool with_details) const; + void test_diffpos_behavior() const; + bool is_valid_position(diffpos_t dpos) const + { + return get_range().contains(dpos) && check_position(dpos) == dpos; + } + diff_texts_t print_diff_source(bool with_details=true) const + { + return print_range(nullptr, with_details); + } +}; + +//------------------------------------------------------------------------ +enum merge_policy_t ENUM_SIZE(uint8) +{ + MERGE_POLICY_SKIP, ///< do not merge + MERGE_POLICY_USE_LOCAL, ///< merge, resolve conflicts using local data + MERGE_POLICY_USE_REMOTE, ///< merge, resolve conflicts using remote data + MERGE_POLICY_POSTPONE, ///< merge, do not resolve conflicts + MERGE_POLICY_MDIFF, ///< view mode: diff only, do not save the database + MERGE_POLICY_VDIFF, ///< view mode: visual diff only, do not save the database + MERGE_POLICY_LAST, +}; + +//------------------------------------------------------------------------ +/// A difference result. +/// This is the result of comparing 2 difference sources. +/// Essentially it is just a list of difference regions. +struct diff_result_t +{ + diff_source_t *src1; + diff_source_t *src2; + diff_regions_t regions; + diff_result_t(diff_source_t *s1=nullptr, diff_source_t *s2=nullptr) : src1(s1), src2(s2) {} + qstrvec_t print_region(const diff_region_t &b, bool with_details=true) const; + qstrvec_t print_diff_result(bool with_details=true) const; + size_t size() const { return regions.size(); } + bool empty() const { return regions.empty(); } + + /// merge src1 and src2 into src1. + /// removes the resolved regions from diff_result_t. + /// \param merge_policy how to perform the merge + /// \param i1 starting index in REGIONS + /// \param i2 ending index in REGIONS (excluded) + /// \return number of processed regions + size_t merge_diff_sources(merge_policy_t merge_policy, size_t i1=0, size_t i2=SIZE_MAX); +private: + bool merge_one_region(size_t n, merge_policy_t merge_policy) const; +}; + +//------------------------------------------------------------------------ +/// A difference engine. +/// An abstract class that can perform a comparison. +class diff_engine_t +{ + friend class diff3_engine_t; +protected: + diff_source_t *src1; + diff_source_t *src2; + +public: + diff_engine_t(diff_source_t *_src1, diff_source_t *_src2) + : src1(_src1), src2(_src2) + { + } + virtual bool get_diff_regions(diff_regions_t *out) = 0; + diff_result_t perform_diff(); +}; + +//------------------------------------------------------------------------ +/// A 2-way difference engine. +/// It compares 2 difference sources. +/// The result is symmetric wrt to src1 and src2. +/// diff_region_t::action is not provided by this engine. +class diff2_engine_t : public diff_engine_t +{ + // we maintain two positions to optimize calls to adjust_position + // if you decide to remove one of them, please ensure that + // std::lower_bound in testdiff3.cpp is not called too often + diffpos_t pos1; + diffpos_t pos2; + diff_degree_t calc_diff_degree(diff_degree_t cur_degree); +public: + diff2_engine_t(diff_source_t *_src1, diff_source_t *_src2) + : diff_engine_t(_src1, _src2) + { + reset(); + } + void reset(); + virtual bool get_diff_regions(diff_regions_t *out) override; + bool get_diff_region(diff_region_t *out); +}; + +//------------------------------------------------------------------------ +/// A 3-way difference engine. +/// It compares 2 difference sources: src1 and src2 but also uses their +/// common ancestor in order to resolve the situations when src1 and src2 +/// do not match each other. +class diff3_engine_t : public diff2_engine_t +{ + diff2_engine_t de1; ///< between common base and src1 + diff2_engine_t de2; ///< between common base and src2 + diff2_engine_t de3; ///< between src1 and src2 (used to handle conflicts only) + +public: + diff3_engine_t( + diff_source_t *base, + diff_source_t *src1, + diff_source_t *src2); + virtual ~diff3_engine_t() {} + virtual bool get_diff_regions(diff_regions_t *out) override; +}; + +/// Perform 3-way difference +/// \param base the base (common) source. if nullptr, then perform 2-way diff +/// \param src1 the first source (and destination) +/// \param src2 the second source +diff_result_t perform_diff3( + diff_source_t *base, + diff_source_t *src1, + diff_source_t *src2); + +qstrvec_t put_side_by_side( + const char *const *headers, + const diff_texts_t *const *linevecs, + size_t n, + int psbs_flags=0); +#define PSBS_DIFF_STARS 0x01 ///< show stars at diffing lines (***) +#define PSBS_ONLY_DIFFS 0x02 ///< skip lines without diffs + +//------------------------------------------------------------------------- +/// diff result actions, \ref lcsdiff_t::diff \ref lcsdiff_t::diff_mod +enum lcsdiff_result_action_t +{ + TDLA_EQ = 0, ///< items are equal + TDLA_ADD, ///< added items + TDLA_SUB, ///< removed items + TDLA_MOD, ///< updated items (new content) +}; + +//------------------------------------------------------------------------- +/// difference result, +/// stores vectors' items with action +/// to be applied to construct Y from X + +/// result's portion +template +struct lcsdiff_res_part_t +{ + T part; ///< vector's items + T x_part; ///< previous content, only for TDLA_MOD + lcsdiff_result_action_t action; ///< action for items, \ref lcsdiff_result_action_t + + lcsdiff_res_part_t(lcsdiff_result_action_t _a) : action(_a) {} + + void append(const T &v, size_t idx) { part.push_back(v[idx]); } + void reverse() { std::reverse(part.begin(), part.end()); } +}; +// template specialisation for qstring vector +template<> +struct lcsdiff_res_part_t +{ + qstring part; ///< vector's items + qstring x_part; ///< previous content, only for TDLA_MOD + lcsdiff_result_action_t action; ///< action for items, \ref lcsdiff_result_action_t + + lcsdiff_res_part_t(lcsdiff_result_action_t _a) : action(_a) {} + + void append(const qstring &v, size_t idx) { part.append(v[idx]); } + void reverse() { if ( !part.empty() ) std::reverse(part.begin(), std::prev(part.end())); } +}; +DECLARE_TYPE_AS_MOVABLE(lcsdiff_res_part_t); +DECLARE_TYPE_AS_MOVABLE(lcsdiff_res_part_t); + +/// result +template +class lcsdiff_res_t : public qvector> +{ +public: + /// vector's items are equal + void eq(const T &v, size_t idx) + { + lcsdiff_res_part_t &part = set_action(TDLA_EQ); + part.append(v, idx); + } + + /// vector's item should be added + void add(const T &v, size_t idx) + { + lcsdiff_res_part_t &part = set_action(TDLA_ADD); + part.append(v, idx); + } + + /// vector's item should be deleted + void sub(const T &v, size_t idx) + { + lcsdiff_res_part_t &part = set_action(TDLA_SUB); + part.append(v, idx); + } + +private: + lcsdiff_res_part_t &set_action(lcsdiff_result_action_t wanted_action) + { + if ( this->empty() || this->back().action != wanted_action ) + this->push_back(lcsdiff_res_part_t(wanted_action)); + return this->back(); + } +}; + +//------------------------------------------------------------------------- +/// Calculate difference between two vectors. +/// Use Longest Common Subsequences (LCS) approach: +/// https://en.wikipedia.org/wiki/Longest_common_subsequence_problem +template +class lcsdiff_t +{ +protected: + const T &x; ///< left argument + const T &y; ///< right argument + const size_t n = 0; ///< left argument size + const size_t m = 0; ///< right argument size + sizevec_t lcs_table; ///< table to store LCS for each step of the calculation + +public: + using result_t = lcsdiff_res_t; + result_t result; + + //------------------------------------------------------------------ + /// prepare LCS table + lcsdiff_t(const T &_x, const T &_y, size_t _n, size_t _m) + : x(_x), + y(_y), + n(_n), + m(_m) + { + // allocate storage for TABLE + // need an additional row/column at index 0 + const size_t rows = n + 1; + const size_t cols = m + 1; + lcs_table.resize(rows * cols); + + // compute the LCS for substring started from (i,j) indices + for ( size_t i=0; i < rows; ++i ) + { + for ( size_t j=0; j < cols; ++j ) + { + if ( i == 0 || j == 0 ) + { + table(i, j) = 0; + } + else if ( x[i - 1] == y[j - 1] ) + { + table(i, j) = 1 + table(i - 1, j - 1); + } + else + { + size_t l = table(i - 1, j); + size_t u = table(i, j - 1); + table(i, j) = qmax(l, u); + } + } + } + } + + //------------------------------------------------------------------ + /// get a difference between two vectors + /// RESULT will contain only TDLA_EQ, TDLA_ADD, TDLA_SUB actions + void diff() + { + result.clear(); + size_t i = n; + size_t j = m; + while ( i != 0 || j != 0 ) + { + // end of seq reached + if ( i == 0 ) + { + result.add(y, --j); + } + else if ( j == 0 ) + { + result.sub(x, --i); + } + // Otherwise there's still parts of X and Y left. If the + // currently considered parts are equal, then we found an unchanged + // part which belongs to the longest common subsequence. + else if ( x[i - 1] == y[j - 1] ) + { + result.eq(x, --i); + j--; + } + // In any other case, we go in the direction of the longest common subsequence. + else if ( table(i - 1, j) <= table(i, j - 1) ) + { + result.add(y, --j); + } + else + { + result.sub(x, --i); + } + } + std::reverse(result.begin(), result.end()); + for ( auto &part : result ) + part.reverse(); + } + +private: + size_t &table(size_t i, size_t j) + { + const size_t idx = i * (m + 1) + j; + QASSERT(2351, idx < lcs_table.size()); + return lcs_table[idx]; + } +}; + +//------------------------------------------------------------------------- +/// Base class for forming "colored" string (in HTML, for example) +struct txtdiff_printer_t +{ + enum event_t + { + init, ///< start printing + term, ///< end printing + + // print lines + next_line, ///< start to print next line + ///< \param first (::bool) is the first line + + same_line, ///< line is the same in X and Y, \ref TDLA_EQ + ///< \param (const char *) + add_line, ///< line is added to X, \ref TDLA_ADD + ///< \param line (const char *) + del_line, ///< line is removed from X, \ref TDLA_SUB + ///< \param line (const char *) + mod_line, ///< line is changed, \ref TDLA_MOD + ///< \param x_line (const char *) original X line + ///< \param y_line (const char *) new line + }; + + txtdiff_printer_t() {} + virtual ~txtdiff_printer_t() {} + + virtual void on_event(event_t ev, ...) = 0; +}; + +//------------------------------------------------------------------------- +class txtdiff_t; + +#ifndef SWIG +#define DECLARE_TXTDIFF_HELPERS(decl)\ +decl void ida_export txtdiff_t_diff_mod(txtdiff_t *_this);\ +decl void ida_export txtdiff_t_serialize(txtdiff_t *_this, txtdiff_printer_t &printer); +#else +#define DECLARE_TXTDIFF_HELPERS(decl) +#endif // SWIG + +DECLARE_TXTDIFF_HELPERS(idaman) + +//------------------------------------------------------------------------ +/// Calculate difference between two string vectors. +class txtdiff_t : public lcsdiff_t +{ +public: + txtdiff_t(const qstrvec_t &_x, const qstrvec_t &_y) + : lcsdiff_t(_x, _y, _x.size(), _y.size()) {} + + //-------------------------------------------------------- + /// calculate a difference and recognize lines modification + /// If a TDLA_SUB will be followed by the TDLA_ADD, + /// then collapse this sequence to TDLA_MOD and maybe TDLA_SUB/TDLA_ADD + void diff_mod() { txtdiff_t_diff_mod(this); } + + //------------------------------------------------------------------------ + /// Create string with coloring + /// In case of TDLA_MOD call diff() and serialize() for string + void serialize(txtdiff_printer_t &printer) { txtdiff_t_serialize(this, printer); } + + //-------------------------------------------------------- + // Convenience methods + + /// Calculate the difference between two vectors in a visual way + /// The RES lines will be prefixed with: + /// " " line as is + /// "- " line was deleted + /// "+ " line was added + /// "* " line was changed + static void visual_diff(qstrvec_t *res, const qstrvec_t &x, const qstrvec_t &y, bool use_mod=true) + { + txtdiff_t d(x, y); + use_mod ? d.diff_mod() : d.diff(); + for ( const auto &dl : d.result ) + { + char c = dl.action == TDLA_EQ ? ' ' + : dl.action == TDLA_ADD ? '+' + : dl.action == TDLA_SUB ? '-' + : '*'; // TDLA_MOD + for ( auto &p : dl.part ) + res->push_back().sprnt("%c %s", c, p.c_str()); + } + } + +private: + DECLARE_TXTDIFF_HELPERS(friend) + // do not call these functions directly, they are here only for exporting + void _diff_mod(); + void _serialize(txtdiff_printer_t &printer); +}; +#undef DECLARE_TXTDIFF_HELPERS + +//------------------------------------------------------------------------- +/// Base class for forming "colored" string (in HTML, for example) +struct strdiff_printer_t +{ + enum event_t + { + init_chars, ///< start printing + term_chars, ///< end printing + + same_chars, ///< the same chars, \ref TDLA_EQ + ///< \param chars (const char *) + add_chars, ///< added chars to line from X, \ref TDLA_ADD + ///< \param chars (const char *) + del_chars, ///< deleted chars from line X, \ref TDLA_SUB + ///< \param chars (const char *) + }; + + strdiff_printer_t() {} + virtual ~strdiff_printer_t() {} + + virtual void on_event(event_t ev, ...) = 0; +}; + +//------------------------------------------------------------------------- +class strdiff_t; + +#ifndef SWIG +#define DECLARE_STRDIFF_HELPERS(decl)\ +decl void ida_export strdiff_t_serialize(strdiff_t *_this, strdiff_printer_t &printer); +#else +#define DECLARE_STRDIFF_HELPERS(decl) +#endif // SWIG + +DECLARE_STRDIFF_HELPERS(idaman) + +//------------------------------------------------------------------------- +/// Calculate difference between two strings +class strdiff_t : public lcsdiff_t +{ +public: + strdiff_t(const qstring &_x, const qstring &_y) + : lcsdiff_t(_x, _y, _x.length(), _y.length()) {} + + //------------------------------------------------------------------------ + /// Create string with coloring + void serialize(strdiff_printer_t &printer) { strdiff_t_serialize(this, printer); } + +private: + DECLARE_STRDIFF_HELPERS(friend) + // do not call these functions directly, they are here only for exporting + void _serialize(strdiff_printer_t &printer); +}; +#undef DECLARE_STRDIFF_HELPERS +#endif // _DIFF3_HPP + +``` + +`IdaSDK/dirtree.hpp`: + +```hpp + +/*! \file dirtree.hpp + + \brief Types involved in grouping of item into folders + + The dirtree_t class is used to organize a directory tree on top of any + collection that allows for accessing its elements by an id (inode). + + No requirements are imposed on the inodes apart from the forbidden + value -1 (used to denote a bad inode). + + The dirspec_t class is used to specialize the dirtree. + It can be used to introduce a directory structure for: + - local types + - structs + - enums + - functions + - names + - etc + + \note you should be manipulating \ref dirtree_t (and, if implementing a + new tree backend, \ref dirspec_t) instances, not calling top-level + functions in this file directly. +*/ + +#ifndef DIRTREE_HPP +#define DIRTREE_HPP + +//------------------------------------------------------------------------ +typedef qvector inodevec_t; // sequence of inodes + +/// Directory indexes are simple numbers like 0,1,2,3... +/// They are independent of inode numbers. +/// The root directory always exists and has the index 0 (\ref direntry_t::ROOTIDX). +typedef uval_t diridx_t; +typedef qvector dirvec_t; // sequence of directory indexes + +/// Blob index, used for storing/restoring dirtree_t information +typedef ea_t blob_idx_t; +#define BAD_BLOB_IDX blob_idx_t(-1) + +// We use PACKED to save memory, without it we would spend 64 bits instead of +// 8 bits to store a 1-bit value on ida64. +#pragma pack(push, 1) + +/// Directory entry: either a file or directory +struct PACKED direntry_t +{ + uval_t idx; ///< diridx_t or inode_t + bool isdir; ///< is 'idx' a diridx_t, or an inode_t + + static const uval_t BADIDX = uval_t(-1); + static const uval_t ROOTIDX = 0; + + direntry_t(uval_t i=BADIDX, bool d=false) : idx(i), isdir(d) {} + bool valid() const { return idx != BADIDX; } + + bool operator==(const direntry_t &r) const + { + return idx == r.idx && isdir == r.isdir; + } + bool operator!=(const direntry_t &r) const + { + return !(*this == r); + } + bool operator<(const direntry_t &r) const + { + if ( isdir != r.isdir ) + return isdir; // any folder is lesser than any inode + return idx < r.idx; + } +}; +#pragma pack(pop) +DECLARE_TYPE_AS_MOVABLE(direntry_t); +typedef qvector direntry_vec_t; + +/// \defgroup DTN_ bits for get_...name() methods +///@{ +enum +{ + DTN_FULL_NAME = 0x00, ///< use long form of the entry name. + ///< That name is unique. + DTN_DISPLAY_NAME = 0x01, ///< use short, displayable form of the entry name. + ///< for example, 'std::string' instead of + ///< 'std::basic_string'. Note that more + ///< than one "full name" can have the same + ///< displayable name. +}; +///@} + +//------------------------------------------------------------------------ +/// Directory tree specialization. This is an abstract base class that +/// represents 'file items' of our directory structure. +struct dirspec_t +{ + uint32 flags; + enum + { + DSF_INODE_EA = 0x01, // inode is EA, will be handled during segment moving + DSF_PRIVRANGE = 0x02, // inode is tid_t, structure or enum id, will be handled during segment moving + DSF_ORDERABLE = 0x04, // items in a folder are ordered by increasing inode # (unless explicitly reordered) + }; + // netnode name to load/save directory tree + // If not specified the loading/storing operations are not supported + // and the undo functionality is not supported. + qstring id; + + dirspec_t(const char *nm=nullptr, uint32 f=0) : flags(f), id(nm) {} + + virtual ~dirspec_t() {} + + /// get the entry name. for example, the structure name + /// \param[out] out may be nullptr; in this case get_name can be used to validate an inode. + /// \param inode inode number of the entry + /// \param name_flags how exactly the name should be retrieved. + /// combination of \ref DTN_ bits + /// \return false if the entry does not exist. + virtual bool get_name( + qstring *out, + inode_t inode, + uint32 name_flags=DTN_FULL_NAME) = 0; + + /// get the entry inode in the specified directory + /// \param dirpath the absolute directory path with trailing slash + /// \param name the entry name in the directory + /// \return the entry inode + virtual inode_t get_inode(const char *dirpath, const char *name) = 0; + + // print additional attributes of the entry. for example, is union? is mapped? + virtual qstring get_attrs(inode_t inode) const = 0; + + /// rename the entry + /// \param inode + /// \param newname + /// \return success + virtual bool rename_inode(inode_t inode, const char *newname) = 0; + + /// event: unlinked an inode + /// \param inode + virtual void unlink_inode(inode_t inode) { qnotused(inode); } + + bool is_orderable() const { return (flags & DSF_ORDERABLE) != 0; } +}; + +//------------------------------------------------------------------------ +/// Position in the directory tree +struct dirtree_cursor_t +{ + diridx_t parent; ///< the parent directory + size_t rank; ///< the index into the parent directory + dirtree_cursor_t(diridx_t _parent=direntry_t::BADIDX, size_t _rank=size_t(-1)) + : parent(_parent), rank(_rank) {} + bool valid() const { return parent != direntry_t::BADIDX || rank == 0; } + bool is_root_cursor() const { return parent == direntry_t::BADIDX && rank == 0; } + void set_root_cursor(void) { parent = direntry_t::BADIDX; rank = 0; } + + static dirtree_cursor_t root_cursor() + { + dirtree_cursor_t c; + c.set_root_cursor(); + return c; + } + + DECLARE_COMPARISONS(dirtree_cursor_t) + { + if ( parent < r.parent ) return -1; + if ( parent > r.parent ) return 1; + if ( rank < r.rank ) return -1; + if ( rank > r.rank ) return 1; + return 0; + } +}; +DECLARE_TYPE_AS_MOVABLE(dirtree_cursor_t); +typedef qvector dirtree_cursor_vec_t; + +//------------------------------------------------------------------------- +struct dirtree_selection_t : public dirtree_cursor_vec_t {}; + +//------------------------------------------------------------------------ +/// Helper class to iterate over files +struct dirtree_iterator_t +{ + qstring pattern; + dirtree_cursor_t cursor; +}; + +//------------------------------------------------------------------------ +/// Directory tree: error codes +enum dterr_t +{ + DTE_OK, ///< ok + DTE_ALREADY_EXISTS, ///< item already exists + DTE_NOT_FOUND, ///< item not found + DTE_NOT_DIRECTORY, ///< item is not a directory + DTE_NOT_EMPTY, ///< directory is not empty + DTE_BAD_PATH, ///< invalid path + DTE_CANT_RENAME, ///< failed to rename an item + DTE_OWN_CHILD, ///< moving inside subdirectory of itself + DTE_MAX_DIR, ///< maximum directory count achieved + DTE_LAST, +}; + +class dirtree_t; +class dirtree_impl_t; +struct segm_move_infos_t; + +//------------------------------------------------------------------------- +/// A visitor, for use with dirtree_t::traverse +struct dirtree_visitor_t +{ + virtual ~dirtree_visitor_t() {} + /// Will be called for each entry in the dirtree_t + /// If something other than 0 is returned, iteration + /// will stop. + /// \param c the current cursor + /// \param de the current entry + /// \return 0 to keep iterating, or anything else to stop + virtual ssize_t visit( + const dirtree_cursor_t &c, + const direntry_t &de) = 0; +}; + + +/// \cond +//------------------------------------------------------------------------ +// internal functions; use dirtree_t members instead +#ifndef SWIG +idaman dirtree_impl_t *ida_export create_dirtree(dirtree_t *dt, dirspec_t *ds); +idaman void ida_export delete_dirtree(dirtree_impl_t *d); +idaman bool ida_export load_dirtree(dirtree_impl_t *d); +idaman bool ida_export save_dirtree(dirtree_impl_t *d); +void reset_dirtree(dirtree_impl_t *d); +idaman const char *ida_export dirtree_errstr(dterr_t err); +idaman bool ida_export dirtree_is_orderable(const dirtree_impl_t *d); +idaman dterr_t ida_export dirtree_chdir(dirtree_impl_t *d, const char *path); +idaman void ida_export dirtree_getcwd(qstring *out, const dirtree_impl_t *d); +idaman void ida_export dirtree_resolve_path(direntry_t *de, const dirtree_impl_t *d, const char *path); +idaman void ida_export dirtree_resolve_cursor(direntry_t *de, const dirtree_impl_t *d, const dirtree_cursor_t &cursor); +idaman bool ida_export dirtree_get_entry_name(qstring *out, const dirtree_impl_t *d, const direntry_t &de, uint32 name_flags); +idaman void ida_export dirtree_get_entry_attrs(qstring *out, const dirtree_impl_t *d, const direntry_t &de); +idaman bool ida_export dirtree_is_dir_ordered(const dirtree_impl_t *d, diridx_t diridx); +idaman bool ida_export dirtree_set_natural_order(dirtree_impl_t *d, diridx_t diridx, bool enable); +idaman ssize_t ida_export dirtree_get_dir_size(dirtree_impl_t *d, diridx_t diridx); +idaman bool ida_export dirtree_findfirst(dirtree_impl_t *d, dirtree_iterator_t *ff, const char *pattern); +idaman bool ida_export dirtree_findnext(dirtree_impl_t *d, dirtree_iterator_t *ff); +idaman bool ida_export dirtree_get_abspath_by_cursor(qstring *out, const dirtree_impl_t *d, const dirtree_cursor_t &cursor, uint32 name_flags); +idaman bool ida_export dirtree_get_abspath_by_relpath(qstring *out, const dirtree_impl_t *d, const char *relpath); +idaman dterr_t ida_export dirtree_mkdir(dirtree_impl_t *d, const char *path); +idaman dterr_t ida_export dirtree_rmdir(dirtree_impl_t *d, const char *path); +idaman dterr_t ida_export dirtree_link(dirtree_impl_t *d, const char *path, bool do_link); +idaman dterr_t ida_export dirtree_link_inode(dirtree_impl_t *d, inode_t inode, bool do_link); +idaman dterr_t ida_export dirtree_rename(dirtree_impl_t *d, const char *from, const char *to); +idaman ssize_t ida_export dirtree_get_rank(const dirtree_impl_t *d, diridx_t diridx, const direntry_t &de); +idaman dterr_t ida_export dirtree_change_rank(dirtree_impl_t *d, const char *path, ssize_t rank_delta); +idaman void ida_export dirtree_get_parent_cursor(dirtree_cursor_t *out, const dirtree_impl_t *d, const dirtree_cursor_t &cursor); +idaman void ida_export notify_dirtree(dirtree_impl_t *d, bool added, inode_t inode); +idaman const char *ida_export dirtree_get_id(const dirtree_impl_t *d); +idaman void ida_export dirtree_set_id(dirtree_impl_t *d, const char *nm); +idaman const char *ida_export dirtree_get_nodename(const dirtree_impl_t *d); // compat +idaman void ida_export dirtree_set_nodename(dirtree_impl_t *d, const char *nm);// compat +idaman ssize_t ida_export dirtree_traverse(dirtree_impl_t *d, dirtree_visitor_t &v); +idaman dterr_t ida_export dirtree_find_entry(dirtree_cursor_t *out, const dirtree_t *_dt, const direntry_t &_de); +idaman dirtree_t *ida_export dirtree_new_shadow_dirtree(dirtree_impl_t *d); +#endif // SWIG + + + + +/// \endcond + +//------------------------------------------------------------------------ +/// Directory tree. +/// This class organizes a virtual directory tree over items that +/// are represented by dirspec_t. +class dirtree_t +{ + dirtree_impl_t *d; + + +public: + //lint -sem(dirtree_t::dirtree_t, custodial(1)) + dirtree_t(dirspec_t *ds) { d = create_dirtree(this, ds); } + ~dirtree_t() { delete_dirtree(d); } + + /// Get textual representation of the error code + static const char *errstr(dterr_t err) { return dirtree_errstr(err); } + + /// Is dirtree orderable? + /// \return true if the dirtree is orderable + bool is_orderable() const { return dirtree_is_orderable(d); } + + /// Change current directory + /// \param path new current directory + /// \return \ref dterr_t error code + dterr_t chdir(const char *path) { return dirtree_chdir(d, path); } + + /// Get current directory + /// \return the current working directory + qstring getcwd() const + { + qstring out; + dirtree_getcwd(&out, d); + return out; + } + + /// Get absolute path pointed by the cursor + /// \param cursor + /// \param name_flags how exactly the name should be retrieved. + /// combination of \ref DTN_ bits + /// \return path; empty string if error + /// \note see also resolve_cursor() + qstring get_abspath(const dirtree_cursor_t &cursor, uint32 name_flags=DTN_FULL_NAME) const + { + qstring out; + dirtree_get_abspath_by_cursor(&out, d, cursor, name_flags); + return out; + } + + /// Construct an absolute path from the specified relative path. + /// This function verifies the directory part of the specified path. + /// The last component of the specified path is not verified. + /// \param relpath relative path + /// \return path. empty path means wrong directory part of RELPATH + qstring get_abspath(const char *relpath) const + { + qstring out; + dirtree_get_abspath_by_relpath(&out, d, relpath); + return out; + } + + /// Resolve cursor + /// \param cursor to analyze + /// \return directory entry; + /// if the cursor is bad, the resolved entry will be invalid. + /// \note see also get_abspath() + direntry_t resolve_cursor(const dirtree_cursor_t &cursor) const + { + direntry_t de; + dirtree_resolve_cursor(&de, d, cursor); + return de; + } + + /// Resolve path + /// \param path to analyze + /// \return directory entry + direntry_t resolve_path(const char *path) const + { + direntry_t de; + dirtree_resolve_path(&de, d, path); + return de; + } + + static bool isdir(const direntry_t &de) { return de.valid() && de.isdir; } + static bool isfile(const direntry_t &de) { return de.valid() && !de.isdir; } + + /// Is a directory? + /// \param path to analyze + /// \return true if the specified path is a directory + bool isdir(const char *path) const + { + direntry_t de = resolve_path(path); + return isdir(de); + } + + /// Is a file? + /// \param path to analyze + /// \return true if the specified path is a file + bool isfile(const char *path) const + { + direntry_t de = resolve_path(path); + return isfile(de); + } + + /// Get entry name + /// \param de directory entry + /// \param name_flags how exactly the name should be retrieved. + /// combination of \ref DTN_ bits + /// \return name + qstring get_entry_name( + const direntry_t &de, + uint32 name_flags=DTN_FULL_NAME) const + { + qstring out; + dirtree_get_entry_name(&out, d, de, name_flags); + return out; + } + + /// Is dir ordered? + /// \return true if the dirtree has natural ordering + bool is_dir_ordered(diridx_t diridx) const { return dirtree_is_dir_ordered(d, diridx); } + + /// Enable/disable natural inode order in a directory. + /// \param diridx directory index + /// \param enable action to do + /// TRUE - enable ordering: re-order existing entries so that + /// all subdirs are at the to beginning of the list, + /// file entries are sorted and placed after the subdirs + /// FALSE - disable ordering, no changes to existing entries + /// \return SUCCESS + bool set_natural_order(diridx_t diridx, bool enable) const { return dirtree_set_natural_order(d, diridx, enable); } + + /// Get dir size + /// \param diridx directory index + /// \return number of entries under this directory; + /// if error, return -1 + ssize_t get_dir_size(diridx_t diridx) const { return dirtree_get_dir_size(d, diridx); } + + /// Get entry attributes + /// \param de directory entry + /// \return name + qstring get_entry_attrs(const direntry_t &de) const + { + qstring out; + dirtree_get_entry_attrs(&out, d, de); + return out; + } + + /// Start iterating over files in a directory + /// \param ff directory iterator. it will be initialized by the function + /// \param pattern pattern to search for + /// \return success + bool findfirst(dirtree_iterator_t *ff, const char *pattern) const + { + return dirtree_findfirst(d, ff, pattern); + } + + /// Continue iterating over files in a directory + /// \param ff directory iterator + /// \return success + bool findnext(dirtree_iterator_t *ff) const + { + return dirtree_findnext(d, ff); + } + + /// Create a directory. + /// \param path directory to create + /// \return \ref dterr_t error code + dterr_t mkdir(const char *path) { return dirtree_mkdir(d, path); } + + /// Remove a directory. + /// \param path directory to delete + /// \return \ref dterr_t error code + dterr_t rmdir(const char *path) { return dirtree_rmdir(d, path); } + + /// Add a file item into a directory. + /// \param path path to item to add to a directory + /// \return \ref dterr_t error code + dterr_t link(const char *path) { return dirtree_link(d, path, true); } + + /// Remove a file item from a directory. + /// \param path path to item remove from a directory + /// \return \ref dterr_t error code + dterr_t unlink(const char *path) { return dirtree_link(d, path, false); } + + /// Add an inode into the current directory + /// \param inode + /// \return \ref dterr_t error code + dterr_t link(inode_t inode) { return dirtree_link_inode(d, inode, true); } + + /// Remove an inode from the current directory + /// \param inode + /// \return \ref dterr_t error code + dterr_t unlink(inode_t inode) { return dirtree_link_inode(d, inode, false); } + + /// Rename a directory entry. + /// \param from source path + /// \param to destination path + /// \return \ref dterr_t error code + /// \note This function can also rename the item + dterr_t rename(const char *from, const char *to) + { + return dirtree_rename(d, from, to); + } + + /// Get ordering rank of an item. + /// \param diridx index of the parent directory + /// \param de directory entry + /// \return number in a range of [0..n) where n is the number of entries in + /// the parent directory. -1 if error + ssize_t get_rank(diridx_t diridx, const direntry_t &de) const + { + return dirtree_get_rank(d, diridx, de); + } + + /// Change ordering rank of an item. + /// \param path path to the item + /// \param rank_delta the amount of the change. positive numbers mean to + /// move down in the list; negative numbers mean to move up. + /// \return \ref dterr_t error code + /// \note this function may disable natural ordering of the parent folder + /// \sa set_natural_order() + dterr_t change_rank(const char *path, ssize_t rank_delta) + { + return dirtree_change_rank(d, path, rank_delta); + } + + /// Get parent cursor. + /// \param cursor a valid ditree cursor + /// \return cursor's parent + dirtree_cursor_t get_parent_cursor(const dirtree_cursor_t &cursor) const + { + dirtree_cursor_t parent; + dirtree_get_parent_cursor(&parent, d, cursor); + return parent; + } + + /// Load the tree structure from the netnode. + /// If dirspec_t::id is empty, the operation will be considered a success. + /// In addition, calling load() more than once will not do anything, + /// and will be considered a success. + /// \return success + /// \see dirspec_t::id. + bool load() + { + return load_dirtree(d); + } + + /// Save the tree structure to the netnode. + /// \return success + /// \see dirspec_t::id. + bool save() const + { + return save_dirtree(d); + } + + /// netnode name + const char *get_id() const + { + return dirtree_get_id(d); + } + + void set_id(const char *nm) + { + return dirtree_set_id(d, nm); + } + + /// Notify dirtree about a change of an inode. + /// \param added are we adding or deleting an inode? + /// \param inode inode in question + void notify_dirtree(bool added, inode_t inode) + { + ::notify_dirtree(d, added, inode); + } + + /// Traverse dirtree, and be notified at each entry + /// If the the visitor returns anything other than 0, + /// iteration will stop, and that value returned. + /// The tree is traversed using a depth-first algorithm. + /// It is forbidden to modify the dirtree_t during traversal; + /// doing so will result in undefined behavior. + /// \param v the callback + /// \return 0, or whatever the visitor returned + ssize_t traverse(dirtree_visitor_t &v) const + { + return dirtree_traverse(d, v); + } + + /// Find the cursor corresponding to an entry of a directory + /// \param de directory entry + /// \return cursor corresponding to the directory entry + dirtree_cursor_t find_entry(const direntry_t &de) const + { + dirtree_cursor_t c; + dirtree_find_entry(&c, this, de); + return c; + } + +}; + +/// Built-in dirtree specializations: +enum dirtree_id_t +{ + DIRTREE_LOCAL_TYPES, + DIRTREE_FUNCS, + DIRTREE_NAMES, + DIRTREE_IMPORTS, + DIRTREE_IDAPLACE_BOOKMARKS, + DIRTREE_BPTS, + DIRTREE_LTYPES_BOOKMARKS, + DIRTREE_END, +}; +idaman dirtree_t *ida_export get_std_dirtree(dirtree_id_t id); + + +#endif // define DIRTREE_HPP + +``` + +`IdaSDK/diskio.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _DISKIO_HPP +#define _DISKIO_HPP + +#include +#include +#include + +/*! \file diskio.hpp + + \brief File I/O functions for IDA + + You should not use standard C file I/O functions in modules. + Use functions from this header, pro.h and fpro.h instead. + + This file also declares a call_system() function. +*/ + +//------------------------------------------------------------------------- +#define VAULT_CACHE_SUBDIR ".vault" ///< subdir name for cached deltas and old files +#define VAULT_CACHE_FNAME ".vault_cache" ///< to store file caches + +//------------------------------------------------------------------------- +// S E A R C H F O R F I L E S +//------------------------------------------------------------------------- + + +/// Get IDA directory (if subdir==nullptr) +/// or the specified subdirectory (see \ref SUBDIR) + +idaman THREAD_SAFE const char *ida_export idadir(const char *subdir); + + +/// Search for IDA system file. +/// This function searches for a file in: +/// -# each directory specified by %IDAUSR% +/// -# ida directory [+ subdir] +/// +/// and returns the first match. +/// \param[out] buf buffer for file name +/// \param bufsize size of output buffer +/// \param filename name of file to search +/// \param subdir if specified, the file is looked for in the specified subdirectory +/// of the ida directory first (see \ref SUBDIR) +/// \return nullptr if not found, otherwise a pointer to full file name. + +idaman THREAD_SAFE char *ida_export getsysfile( + char *buf, + size_t bufsize, + const char *filename, + const char *subdir); + +/// \defgroup SUBDIR IDA subdirectories +/// Passed as 'subdir' parameter to idadir(), getsysfile(), and others. +///@{ +#define CFG_SUBDIR "cfg" +#define IDC_SUBDIR "idc" +#define IDS_SUBDIR "ids" +#define IDP_SUBDIR "procs" +#define LDR_SUBDIR "loaders" +#define SIG_SUBDIR "sig" +#define TIL_SUBDIR "til" +#define PLG_SUBDIR "plugins" +#define THM_SUBDIR "themes" +///@} + +/// Get user ida related directory. +/// \code +/// - if $IDAUSR is defined: +/// - the first element in $IDAUSR +/// - else +/// - default user directory ($HOME/.idapro or %APPDATA%Hex-Rays/IDA Pro) +/// \endcode + +idaman THREAD_SAFE const char *ida_export get_user_idadir(void); + + +/// Get list of directories in which to find a specific IDA resource +/// (see \ref SUBDIR). The order of the resulting list is as follows: +/// \code +/// - [$IDAUSR/subdir (0..N entries)] +/// - $IDADIR/subdir +/// \endcode +/// \param[out] dirs output vector for directory names +/// \param subdir name of the resource to list (can be nullptr) +/// \param flags \ref IDA_SUBDIR_ bits +/// \return number of directories appended to 'dirs' + +idaman THREAD_SAFE int ida_export get_ida_subdirs(qstrvec_t *dirs, const char *subdir, int flags=0); + +/// \defgroup IDA_SUBDIR_ Subdirectory modification flags +/// Passed as 'flags' parameter to get_ida_subdirs() +///@{ +#define IDA_SUBDIR_IDP 0x0001 ///< append the processor name as a subdirectory +#define IDA_SUBDIR_IDADIR_FIRST 0x0002 ///< $IDADIR/subdir will be first, not last +#define IDA_SUBDIR_ONLY_EXISTING 0x0004 ///< only existing directories will be present +///@} + + +/// Get a folder location by CSIDL (see \ref CSIDL). +/// Path should be of at least MAX_PATH size + +idaman THREAD_SAFE bool ida_export get_special_folder(char *buf, size_t bufsize, int csidl); + +/// \defgroup CSIDL Common CSIDLs +/// Passed as 'csidl' parameter to get_special_folder() +///@{ +#ifndef CSIDL_APPDATA +#define CSIDL_APPDATA 0x001a +#endif +#ifndef CSIDL_LOCAL_APPDATA +#define CSIDL_LOCAL_APPDATA 0x001c +#endif +#ifndef CSIDL_PROGRAM_FILES +#define CSIDL_PROGRAM_FILES 0x0026 +#endif +#ifndef CSIDL_PROGRAM_FILES_COMMON +#define CSIDL_PROGRAM_FILES_COMMON 0x002b +#endif +#ifndef CSIDL_PROGRAM_FILESX86 +#define CSIDL_PROGRAM_FILESX86 0x002a +#endif +///@} + +/// Enumerate files in the specified directory. +/// \param[out] answer buffer to contain the file name for which +/// file_enumerator_t::visit_file returns non-zero value +/// (may be nullptr) +/// \param answer_size size of 'answer' +/// \param path directory to enumerate files in +/// \param fname mask of file names to enumerate +/// \param fv file_enumerator_t::visit_file function called for each file +/// - file: full file name (with path) +/// - if returns non-zero value, the enumeration +/// is stopped and the return code is +/// is returned to the caller. +/// the callback function +/// \return zero or the code returned by 'func' + +struct file_enumerator_t +{ + virtual int visit_file(const char *file) = 0; + virtual ~file_enumerator_t() {} +}; + +idaman THREAD_SAFE int ida_export enumerate_files( + char *answer, + size_t answer_size, + const char *path, + const char *fname, + file_enumerator_t &fv); + + + +//------------------------------------------------------------------------- +// O P E N / R E A D / W R I T E / C L O S E F I L E S +//------------------------------------------------------------------------- + +/// \name Open/Read/Write/Close Files +/// There are two sets of "open file" functions. +/// The first set tries to open a file and returns success or failure. +/// The second set is "open or die": if the file cannot be opened +/// then the function will display an error message and exit. +///@{ + +/// Open a new file for write in text mode, deny write. +/// If a file exists, it will be removed. +/// \return nullptr if failure + +idaman THREAD_SAFE FILE *ida_export fopenWT(const char *file); + + +/// Open a new file for write in binary mode, deny read/write. +/// If a file exists, it will be removed. +/// \return nullptr if failure + +idaman THREAD_SAFE FILE *ida_export fopenWB(const char *file); + + +/// Open a file for read in text mode, deny none. +/// \return nullptr if failure + +idaman THREAD_SAFE FILE *ida_export fopenRT(const char *file); + + +/// Open a file for read in binary mode, deny none. +/// \return nullptr if failure + +idaman THREAD_SAFE FILE *ida_export fopenRB(const char *file); + + +/// Open a file for read/write in binary mode, deny write. +/// \return nullptr if failure + +idaman THREAD_SAFE FILE *ida_export fopenM(const char *file); + + +/// Open a file for append in text mode, deny none. +/// \return nullptr if failure + +idaman THREAD_SAFE FILE *ida_export fopenA(const char *file); + + +/// Open a file for read in binary mode or die, deny none. +/// If a file cannot be opened, this function displays a message and exits. + +idaman THREAD_SAFE FILE *ida_export openR(const char *file); + + +/// Open a file for read in text mode or die, deny none. +/// If a file cannot be opened, this function displays a message and exits. + +idaman THREAD_SAFE FILE *ida_export openRT(const char *file); + + +/// Open a file for read/write in binary mode or die, deny write. +/// If a file cannot be opened, this function displays a message and exits. + +idaman THREAD_SAFE FILE *ida_export openM(const char *file); + + +///@} + +//------------------------------------------------------------------------- +// F I L E S I Z E / D I S K S P A C E +//------------------------------------------------------------------------- + +/// Change size of file or die. +/// If an error occurs, this function displays a message and exits. +/// \param fp pointer to file +/// \param size new size of file + +idaman THREAD_SAFE void ida_export echsize(FILE *fp, uint64 size); + + +/// Get free disk space in bytes. +/// \param path name of any directory on the disk to get information about + +idaman THREAD_SAFE uint64 ida_export get_free_disk_space(const char *path); + + +//------------------------------------------------------------------------- +// I / O P O R T D E F I N I T I O N S F I L E +//------------------------------------------------------------------------- +/// Describes an I/O port bit +struct ioport_bit_t +{ + qstring name; ///< name of the bit + qstring cmt; ///< comment +}; +DECLARE_TYPE_AS_MOVABLE(ioport_bit_t); +typedef qvector ioport_bits_t; + +/// Describes an I/O port +struct ioport_t +{ + ea_t address; ///< address of the port + qstring name; ///< name of the port + qstring cmt; ///< comment + ioport_bits_t bits; ///< bit names + void *userdata; ///< arbitrary data. initialized to nullptr. + + ioport_t() + : address(0), userdata(nullptr) + { + } +}; +DECLARE_TYPE_AS_MOVABLE(ioport_t); +typedef qvector ioports_t; + +/// Read i/o port definitions from a config file. +/// +/// Each device definition in the input file begins with a line like this: +/// +/// \v{.devicename} +/// +/// After it go the port definitions in this format: +/// +/// \v{portname address} +/// +/// The bit definitions (optional) are represented like this: +/// +/// \v{portname.bitname bitnumber} +/// +/// Lines beginning with a space are ignored. +/// comment lines should be started with ';' character. +/// +/// The default device is specified at the start of the file: +/// +/// \v{.default device_name} +/// +/// \note It is permissible to have a symbol mapped to several addresses +/// but all addresses must be unique. +/// \param[out] ports output vector +/// \param device contains device name to load. If default_device[0] == 0 +/// then the default device is determined by .default directive +/// in the config file. +/// \param file config file name +/// \param callback callback to call when the input line can't be parsed normally. +/// \return -1 on error or size of vector + +struct ioports_fallback_t +{ + virtual ~ioports_fallback_t() {} + /// \param[out] errbuf error message + /// \param ports i/o port definitions + /// \param line input line to parse + /// \return success or fills ERRBUF with an error message + virtual bool handle(qstring *errbuf, const ioports_t &ports, const char *line) = 0; +}; + +idaman THREAD_SAFE ssize_t ida_export read_ioports( + ioports_t *ports, + qstring *device, + const char *file, + ioports_fallback_t *callback=nullptr); + + +/// Allow the user to choose the ioport device. +/// \param[in,out] _device in: contains default device name. If default_device[0] == 0 +/// then the default device is determined by .default directive +/// in the config file. +/// out: the selected device name +/// \param file config file name +/// \param parse_params if present (non nullptr), then defines a callback which +/// will be called for all lines not starting with a dot (.) +/// This callback may parse these lines are prepare a simple +/// processor parameter string. This string will be displayed +/// along with the device name. +/// If it returns #IOPORT_SKIP_DEVICE, then the current +/// device will not be included in the list. +/// \retval true the user selected a device, its name is in 'device' +/// \retval false the selection was cancelled. if device=="NONE" upon return, +/// then no devices were found in the configuration file + +idaman THREAD_SAFE bool ida_export choose_ioport_device( + qstring *_device, + const char *file, + const char *(idaapi *parse_params)( + qstring *buf, + const char *line)=nullptr); + +struct choose_ioport_parser_t +{ + virtual ~choose_ioport_parser_t() {} + /// \retval true and fill PARAM with a displayed string + /// \retval false and empty PARAM to skip the current device + /// \retval false and fill PARAM with an error message + virtual bool parse(qstring *param, const char *line) = 0; +}; + +idaman THREAD_SAFE bool ida_export choose_ioport_device2( + qstring *_device, + const char *file, + choose_ioport_parser_t *parse_params); + +/// See 'parse_params' parameter to choose_ioport_device() +#define IOPORT_SKIP_DEVICE ((const char *)(-1)) + + +/// Find ioport in the array of ioports + +idaman THREAD_SAFE const ioport_t *ida_export find_ioport(const ioports_t &ports, ea_t address); + + +/// Find ioport bit in the array of ioports + +idaman THREAD_SAFE const ioport_bit_t *ida_export find_ioport_bit(const ioports_t &ports, ea_t address, size_t bit); + + +//------------------------------------------------------------------------- +// S Y S T E M S P E C I F I C C A L L S +//------------------------------------------------------------------------- + +/// Execute a operating system command. +/// This function suspends the interface (Tvision), runs the command +/// and redraws the screen. +/// \param command command to execute. If nullptr, an interactive shell is activated +/// \return the error code returned by system() call + +idaman THREAD_SAFE int ida_export call_system(const char *command); + + +//------------------------------------------------------------------------- +// L O A D E R I N P U T S O U R C E F U N C T I O N S +//------------------------------------------------------------------------- + +/// \name Loader Input Source +/// Starting with v4.8 IDA can load and run remote files. +/// In order to do that, we replace the FILE* in the loader modules +/// with an abstract input source (linput_t). The source might be linked to +/// a local or remote file. +///@{ + +class linput_t; ///< loader input source + + +/// linput types +enum linput_type_t +{ + LINPUT_NONE, ///< invalid linput + LINPUT_LOCAL, ///< local file + LINPUT_RFILE, ///< remote file (\dbg{open_file}, \dbg{read_file}) + LINPUT_PROCMEM, ///< debugged process memory (read_dbg_memory()) + LINPUT_GENERIC ///< generic linput +}; + + +/// Read the input source. +/// If failed, inform the user and ask him if he wants to continue. +/// If he does not, this function will not return (loader_failure() will be called). +/// This function may be called only from loaders! + +idaman void ida_export lread(linput_t *li, void *buf, size_t size); + +#ifdef TESTABLE_BUILD +#endif + + +/// Read the input source. +/// \return number of read bytes or -1 + +idaman ssize_t ida_export qlread(linput_t *li, void *buf, size_t size); + + +/// Read one line from the input source. +/// \return nullptr if failure, otherwise 's' + +idaman char *ida_export qlgets(char *s, size_t len, linput_t *li); + + +/// Read one character from the input source. +/// \return EOF if failure, otherwise the read character + +idaman int ida_export qlgetc(linput_t *li); + + +/// Read multiple bytes and swap if necessary. +/// \param li input file +/// \param buf pointer to output buffer +/// \param size number of bytes to read +/// \param mf big endian? +/// \retval 0 ok +/// \retval -1 failure + +idaman int ida_export lreadbytes(linput_t *li, void *buf, size_t size, bool mf); + +/// Helper to define lread2bytes(), lread4bytes(), etc +#define DEF_LREADBYTES(read, type, size) \ +/*! \brief Read a value from linput - also see lreadbytes() */ \ +inline int idaapi read(linput_t *li, type *res, bool mf) \ + { return lreadbytes(li, res, size, mf); } +DEF_LREADBYTES(lread2bytes, int16, 2) +DEF_LREADBYTES(lread2bytes, uint16, 2) +DEF_LREADBYTES(lread4bytes, int32, 4) +DEF_LREADBYTES(lread4bytes, uint32, 4) +DEF_LREADBYTES(lread8bytes, int64, 8) +DEF_LREADBYTES(lread8bytes, uint64, 8) +#undef DEF_LREADBYTES + + +/// Read a zero-terminated string from the input. +/// If fpos == -1 then no seek will be performed. + +idaman char *ida_export qlgetz( + linput_t *li, + int64 fpos, + char *buf, + size_t bufsize); + + +/// Get the input source size + +idaman int64 ida_export qlsize(linput_t *li); + + +/// Set input source position. +/// \return the new position (not 0 as fseek!) + +idaman qoff64_t ida_export qlseek(linput_t *li, qoff64_t pos, int whence=SEEK_SET); + + +/// Get input source position + +inline qoff64_t idaapi qltell(linput_t *li) { return qlseek(li, 0, SEEK_CUR); } + + +/// Open loader input + +idaman linput_t *ida_export open_linput(const char *file, bool remote); + + +/// Close loader input + +idaman THREAD_SAFE void ida_export close_linput(linput_t *li); + + +/// Get FILE* from the input source. +/// If the input source is linked to a remote file, then return nullptr. +/// Otherwise return the underlying FILE* +/// Please do not use this function if possible. + +idaman THREAD_SAFE FILE *ida_export qlfile(linput_t *li); + + +/// Convert FILE* to input source. +/// Used for temporary linput_t objects - call unmake_linput() to free +/// the slot without closing FILE*. +/// To destroy linput_t and close FILE*, call close_linput(). +//lint -sem(make_linput,custodial(1)) + +idaman THREAD_SAFE linput_t *ida_export make_linput(FILE *fp); + +/// Free an linput_t object (also see make_linput()) + +idaman THREAD_SAFE void ida_export unmake_linput(linput_t *li); + + +/// Generic linput class - may be used to create a linput_t instance for +/// any data source +struct generic_linput_t +{ + /// \name Warning + /// The following two fields must be filled before calling create_generic_linput() + ///@{ + uint64 filesize; ///< input file size + uint32 blocksize; ///< preferred block size to work with + ///< read/write sizes will be in multiples of this number. + ///< for example, 4096 is a nice value + ///< blocksize 0 means that the filesize is unknown. + ///< the internal cache will be disabled in this case. + ///< also, seeks from the file end will fail. + ///< blocksize=-1 means error. + ///@} + virtual ssize_t idaapi read(qoff64_t off, void *buffer, size_t nbytes) = 0; + virtual ~generic_linput_t() {} +}; + +/// Create a generic linput +/// \param gl linput description. +/// this object will be destroyed by close_linput() +/// using "delete gl;" + +idaman THREAD_SAFE linput_t *ida_export create_generic_linput(generic_linput_t *gl); + +/// Trivial memory linput + +idaman THREAD_SAFE linput_t *ida_export create_bytearray_linput(const uchar *start, size_t size); + + +/// Create a linput for process memory. +/// This linput will use read_dbg_memory() to read data. +/// \param start starting address of the input +/// \param size size of the memory area to represent as linput +/// if unknown, may be passed as 0 + +idaman linput_t *ida_export create_memory_linput(ea_t start, asize_t size); + +/// Get linput type + +inline THREAD_SAFE linput_type_t idaapi get_linput_type(linput_t *li) +{ + return li != nullptr ? *(linput_type_t *)li : LINPUT_NONE; +} + +/// Object that will free an linput_t at destruction-time +typedef janitor_t linput_janitor_t; +/// Free the linput_t +template <> inline linput_janitor_t::~janitor_t() +{ + close_linput(resource); +} + +//--------------------------------------------------------------------------- +/// Helper class - adapts linput to be used in extract_... functions +/// as a data supplier (see kernwin.hpp) +class linput_buffer_t +{ +public: + linput_buffer_t(linput_t *linput, int64 size=0): li(linput), lsize(size) {} + ssize_t read(void *buf, size_t n) + { + return qlread(li, buf, n); + } + bool eof() + { + if ( lsize == 0 ) + lsize = qlsize(li); + return qltell(li) >= lsize; + } +protected: + linput_t *li; +private: + int64 lsize; +}; + +///@} + + +/// Upon closing outer linput, perform one of these actions +enum linput_close_code_t +{ + LOC_CLOSE, ///< close the inner linput + LOC_UNMAKE, ///< unmake the inner linput + LOC_KEEP, ///< do nothing +}; + + +#endif // _DISKIO_HPP + +``` + +`IdaSDK/entry.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _ENTRY_HPP +#define _ENTRY_HPP + +/*! \file entry.hpp + + \brief Functions that deal with entry points + + Exported functions are considered as entry points as well. + + IDA maintains list of entry points to the program. + Each entry point: + - has an address + - has a name + - may have an ordinal number +*/ + +/// Get number of entry points + +idaman size_t ida_export get_entry_qty(void); + +/// \defgroup AEF_ entry flags +/// Passed as 'flags' parameter to add_entry() +///@{ +#define AEF_UTF8 0x0 ///< the name is given in UTF-8 (default) +#define AEF_IDBENC 0x1 ///< the name is given in the IDB encoding; + ///< non-ASCII bytes will be decoded accordingly. + ///< Specifying AEF_IDBENC also implies AEF_NODUMMY +#define AEF_NODUMMY 0x2 ///< automatically prepend the name with '_' if + ///< it begins with a dummy suffix. See also AEF_IDBENC +#define AEF_WEAK 0x4 ///< make name weak +#define AEF_NOFORCE 0x8 ///< if the specified address already has a name, the new name + ///< will be appended to the regular comment, except for the + ///< case when the old name is weak and the new one is not. +///@} + + +/// Add an entry point to the list of entry points. +/// \param ord ordinal number +/// if ordinal number is equal to 'ea' then ordinal is not used +/// \param ea linear address +/// \param name name of entry point. If the specified location already has a name, +/// the old name will be appended to the regular comment. +// This behaviour can be overwritten by AEF_NOFORCE. +// If name == nullptr, then the old name will be retained. +/// \param makecode should the kernel convert bytes at the entry point +/// to instruction(s) +/// \param flags See AEF_* +/// \return success (currently always true) + +idaman bool ida_export add_entry(uval_t ord, ea_t ea, const char *name, bool makecode, int flags=AEF_UTF8); + + +/// Get ordinal number of an entry point. +/// \param idx internal number of entry point. Should be +/// in the range 0..get_entry_qty()-1 +/// \return ordinal number or 0. + +idaman uval_t ida_export get_entry_ordinal(size_t idx); + + +/// Get entry point address by its ordinal +/// \param ord ordinal number of entry point +/// \return address or #BADADDR + +idaman ea_t ida_export get_entry(uval_t ord); + + +/// Get name of the entry point by its ordinal. +/// \param buf output buffer, may be nullptr +/// \param ord ordinal number of entry point +/// \return size of entry name or -1 + +idaman ssize_t ida_export get_entry_name(qstring *buf, uval_t ord); + + +/// Rename entry point. +/// \param ord ordinal number of the entry point +/// \param name name of entry point. If the specified location already +/// has a name, the old name will be appended to a repeatable +/// comment. +/// \param flags See AEF_* +/// \return success + +idaman bool ida_export rename_entry(uval_t ord, const char *name, int flags=AEF_UTF8); + + +/// Set forwarder name for ordinal. +/// \param ord ordinal number of the entry point +/// \param name forwarder name for entry point. +/// \param flags See AEF_* +/// \return success + +idaman bool ida_export set_entry_forwarder(uval_t ord, const char *name, int flags=AEF_UTF8); + + +/// Get forwarder name for the entry point by its ordinal. +/// \param buf output buffer, may be nullptr +/// \param ord ordinal number of entry point +/// \return size of entry forwarder name or -1 + +idaman ssize_t ida_export get_entry_forwarder(qstring *buf, uval_t ord); + + +#endif // _ENTRY_HPP + +``` + +`IdaSDK/err.h`: + +```h +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _ERR_H +#define _ERR_H + +#include + +/*! \file err.h + + \brief Thread safe functions that deal with error codes + +*/ + +/// Print error message to stderr (analog of perror) + +idaman THREAD_SAFE AS_PRINTF(1, 0) void ida_export vqperror(const char *format, va_list va); + + +/// Get error description string. +/// if _qerrno=-1, get_qerrno() will be used + +idaman THREAD_SAFE const char *ida_export qstrerror(error_t _qerrno); + + +/// A convenience function to generate error messages (returns "header: error message") + +idaman THREAD_SAFE char *ida_export get_errdesc(const char *header, error_t _qerrno=-1); + + +/// Get error message for MS Windows error codes +/// \param code errno or GetLastError() depending on the system. + +idaman THREAD_SAFE char *ida_export winerr(int code); + + +/// Get error string. +/// if errno_code == -1, then errno will be used. + +idaman const char *ida_export qerrstr(int errno_code=-1); + + +#ifdef __cplusplus + +/// See vqperror() + +THREAD_SAFE AS_PRINTF(1, 2) inline void qperror(const char *format, ...) +{ + va_list va; + va_start(va, format); + vqperror(format, va); + va_end(va); +} + + +/// See set_qerrno() + +THREAD_SAFE inline void set_errno(int code) +{ + errno = code; + set_qerrno(eOS); +} +#endif + +// Internal functions +/// \cond + +// n=0..3 +idaman THREAD_SAFE void ida_export set_error_data(int n, size_t data); +idaman THREAD_SAFE void ida_export set_error_string(int n, const char *str); +idaman THREAD_SAFE size_t ida_export get_error_data(int n); +idaman THREAD_SAFE const char *ida_export get_error_string(int n); + +#define QPRM_TYPE(t,n,x) set_error_data(n-1, t(x)) +#define QPRM_CHAR(n,x) QPRM_TYPE(char,n,x) +#define QPRM_SHORT(n,x) QPRM_TYPE(short,n,x) +#define QPRM_INT(n,x) QPRM_TYPE(int,n,x) +#define QPRM_INT32(n,x) QPRM_TYPE(int32,n,x) +#define QPRM_UCHAR(n,x) QPRM_TYPE(uchar,n,x) +#define QPRM_USHORT(n,x) QPRM_TYPE(ushort,n,x) +#define QPRM_UINT(n,x) QPRM_TYPE(uint,n,x) +#define QPRM_UINT32(n,x) QPRM_TYPE(uint32,n,x) +#define QPRM(n,x) set_error_string(n-1, x) + +/// \endcond + +#endif + + +``` + +`IdaSDK/exehdr.h`: + +```h +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + * EXE-file header layout + * + */ + +#ifndef __EXEHDR_H +#define __EXEHDR_H +#pragma pack(push, 1) + +struct exehdr +{ + uint16 exe_ident; +#define EXE_ID 0x5A4D // 'MZ' +#define EXE_ID2 0x4D5A // 'ZM' (DOS works with this also) + uint16 PartPag; + uint16 PageCnt; + uint16 ReloCnt; + uint16 HdrSize; + uint16 MinMem; + uint16 MaxMem; + uint16 ReloSS; + uint16 ExeSP; + uint16 ChkSum; + uint16 ExeIP; + uint16 ReloCS; + uint16 TablOff; + uint16 Overlay; +/* + uint16 res[4]; // Reserved words + uint16 oemid; // OEM identifier (for e_oeminfo) + uint16 oeminfo; // OEM information; e_oemid specific + uint16 res2[10]; // Reserved words + uint32 lfanew; // File address of new exe header +*/ + int32 CalcEXE_Length(void) + { + int32 len = PageCnt * 512 - HdrSize * 16; + if ( PartPag != 0 ) + len -= 512 - PartPag; + return len; + } + void CalcEXE_Pages(int32 len) + { + PartPag = uint16(len % 512); + PageCnt = uint16(len / 512); + if ( PartPag != 0 ) + PageCnt++; + } +}; + +struct exehdr_full: exehdr +{ + uint16 res[4]; // Reserved words + uint16 oemid; // OEM identifier (for e_oeminfo) + uint16 oeminfo; // OEM information; e_oemid specific + uint16 res2[10]; // Reserved words + uint32 lfanew; // File address of new exe header +}; + +#define PSPsize 0x100 +#define PE_PTROFF 0x3C +#pragma pack(pop) +#endif + +``` + +`IdaSDK/expr.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _EXPR_H +#define _EXPR_H + +#include +#include + +/*! \file expr.hpp + + \brief Functions that deal with C-like expressions and built-in IDC language. + + Functions marked #THREAD_SAFE may be called from any thread. + No simultaneous calls should be made for the same variable. + We protect only global structures, individual variables must + be protected manually. +*/ + +//------------------------------------------------------------------------ + +// Forward declarations +class idc_value_t; +class idc_class_t; +class idc_object_t; + +/// IDC script extension +#define IDC_LANG_EXT "idc" + +/// Convert IDC variable to a long (32/64bit) number. +/// \return v = 0 if impossible to convert to long + +idaman THREAD_SAFE error_t ida_export idcv_long(idc_value_t *v); + + +/// Convert IDC variable to a 64bit number. +/// \return v = 0 if impossible to convert to int64 + +idaman THREAD_SAFE error_t ida_export idcv_int64(idc_value_t *v); + + +/// Convert IDC variable to a long number. +/// \return +/// - v = 0 if IDC variable = "false" string +/// - v = 1 if IDC variable = "true" string +/// - v = number if IDC variable is number or string containing a number +/// - eTypeConflict if IDC variable = empty string + +idaman THREAD_SAFE error_t ida_export idcv_num(idc_value_t *v); + + +/// Convert IDC variable to a text string + +idaman THREAD_SAFE error_t ida_export idcv_string(idc_value_t *v); + + +/// Convert IDC variable to a floating point + +idaman THREAD_SAFE error_t ida_export idcv_float(idc_value_t *v); + + +/// Create an IDC object. The original value of 'v' is discarded (freed). +/// \param v variable to hold the object. any previous value will be cleaned +/// \param icls ptr to the desired class. nullptr means "object" class +/// this ptr must be returned by add_idc_class() or find_idc_class() +/// \return always eOk + +idaman THREAD_SAFE error_t ida_export idcv_object( + idc_value_t *v, + const idc_class_t *icls=nullptr); + + +/// Move 'src' to 'dst'. +/// This function is more effective than copy_idcv since it never copies big +/// amounts of data. + +idaman THREAD_SAFE error_t ida_export move_idcv( + idc_value_t *dst, + idc_value_t *src); + + +/// Copy 'src' to 'dst'. +/// For idc objects only a reference is copied. + +idaman THREAD_SAFE error_t ida_export copy_idcv( + idc_value_t *dst, + const idc_value_t &src); + + +/// Deep copy an IDC object. +/// This function performs deep copy of idc objects. +/// If 'src' is not an object, copy_idcv() will be called + +idaman THREAD_SAFE error_t ida_export deep_copy_idcv( + idc_value_t *dst, + const idc_value_t &src); + + +/// Free storage used by #VT_STR/#VT_OBJ IDC variables. +/// After this call the variable has a numeric value 0 + +idaman THREAD_SAFE void ida_export free_idcv(idc_value_t *v); + + +/// Swap 2 variables + +idaman THREAD_SAFE void ida_export swap_idcvs( + idc_value_t *v1, + idc_value_t *v2); + + +/// Retrieves the IDC object class name. +/// \param out qstring ptr for the class name. Can be nullptr. +/// \param obj class instance variable +/// \return error code, eOk on success + +idaman THREAD_SAFE error_t ida_export get_idcv_class_name( + qstring *out, + const idc_value_t *obj); + + +/// Get an object attribute. +/// \param res buffer for the attribute value +/// \param obj variable that holds an object reference. +/// if obj is nullptr it searches global variables, then user functions +/// \param attr attribute name +/// \param may_use_getattr may call getattr functions to calculate the attribute if it does not exist +/// \return error code, eOk on success + +idaman THREAD_SAFE error_t ida_export get_idcv_attr( + idc_value_t *res, + const idc_value_t *obj, + const char *attr, + bool may_use_getattr=false); + + +/// Set an object attribute. +/// \param obj variable that holds an object reference. +/// if obj is nullptr then it tries to modify a global variable with the attribute name +/// \param attr attribute name +/// \param value new attribute value +/// \param may_use_setattr may call setattr functions for the class +/// \return error code, eOk on success + +idaman THREAD_SAFE error_t ida_export set_idcv_attr( + idc_value_t *obj, + const char *attr, + const idc_value_t &value, + bool may_use_setattr=false); + + +/// Delete an object attribute. +/// \param obj variable that holds an object reference +/// \param attr attribute name +/// \return error code, eOk on success + +idaman THREAD_SAFE error_t ida_export del_idcv_attr( + idc_value_t *obj, + const char *attr); + + +/// \name Enumerate object attributes +///@{ +idaman THREAD_SAFE const char *ida_export first_idcv_attr(const idc_value_t *obj); +idaman THREAD_SAFE const char *ida_export last_idcv_attr(const idc_value_t *obj); +idaman THREAD_SAFE const char *ida_export next_idcv_attr(const idc_value_t *obj, const char *attr); +idaman THREAD_SAFE const char *ida_export prev_idcv_attr(const idc_value_t *obj, const char *attr); +///@} + + +/// Get text representation of idc_value_t + +idaman bool ida_export print_idcv( + qstring *out, + const idc_value_t &v, + const char *name=nullptr, + int indent=0); + + +/// Get slice. +/// \param res output variable that will contain the slice +/// \param v input variable (string or object) +/// \param i1 slice start index +/// \param i2 slice end index (excluded) +/// \param flags \ref VARSLICE_ or 0 +/// \return eOk if success + +idaman THREAD_SAFE error_t ida_export get_idcv_slice( + idc_value_t *res, + const idc_value_t *v, + uval_t i1, + uval_t i2, + int flags=0); + +/// \defgroup VARSLICE_ IDC variable slice flags +/// Passed as 'flags' parameter to get_idcv_slice() and set_idcv_slice() +///@{ +#define VARSLICE_SINGLE 0x0001 ///< return single index (i2 is ignored) +///@} + +/// Set slice. +/// \param v variable to modify (string or object) +/// \param i1 slice start index +/// \param i2 slice end index (excluded) +/// \param in new value for the slice +/// \param flags \ref VARSLICE_ or 0 +/// \return eOk on success + +idaman THREAD_SAFE error_t ida_export set_idcv_slice( + idc_value_t *v, + uval_t i1, + uval_t i2, + const idc_value_t &in, + int flags=0); + + +//------------------------------------------------------------------------- +// IDC class related functions + +/// Create a new IDC class. +/// \param name name of the new class +/// \param super the base class for the new class. if the new class is not based +/// on any other class, pass nullptr +/// \return pointer to the created class. If such a class already exists, a pointer +/// to it will be returned. +/// Pointers to other existing classes may be invalidated by this call. + +idaman THREAD_SAFE idc_class_t *ida_export add_idc_class( + const char *name, + const idc_class_t *super=nullptr); + + +/// Find an existing IDC class by its name. +/// \param name name of the class +/// \return pointer to the class or nullptr. +/// The returned pointer is valid until a new call to add_idc_class() + +idaman THREAD_SAFE idc_class_t *ida_export find_idc_class(const char *name); + + +/// Set an IDC class method. +/// \param icls pointer to the class +/// \param fullfuncname name of the function to call. use full method name: classname.funcname +/// \retval true success +/// \retval false the function could not be found + +idaman THREAD_SAFE bool ida_export set_idc_method(idc_class_t *icls, const char *fullfuncname); + + +/// \name Set user-defined functions to work with object attributes. +/// If the function name is nullptr, the definitions are removed. +/// \return name of the old attribute function. nullptr means error, "" means no previous attr func +///@{ +idaman THREAD_SAFE const char *ida_export set_idc_getattr(idc_class_t *icls, const char *fullfuncname); +idaman THREAD_SAFE const char *ida_export set_idc_setattr(idc_class_t *icls, const char *fullfuncname); +///@} + +/// Set a destructor for an idc class. +/// The destructor is called before deleting any object of the specified class. +/// Exceptions that escape the destructor are silently ignored, runtime errors too. + +idaman THREAD_SAFE const char *ida_export set_idc_dtor(idc_class_t *icls, const char *fullfuncname); + + +/// Dereference a #VT_REF variable. +/// \param v variable to dereference +/// \param vref_flags \ref VREF_ +/// \return pointer to the dereference result or nullptr. +/// If returns nullptr, qerrno is set to eExecBadRef "Illegal variable reference" + +idaman THREAD_SAFE idc_value_t *ida_export deref_idcv(idc_value_t *v, int vref_flags); + +/// \defgroup VREF_ Dereference IDC variable flags +/// Passed as 'vref_flags' parameter to deref_idcv() +///@{ +#define VREF_LOOP 0x0000 ///< dereference until we get a non #VT_REF +#define VREF_ONCE 0x0001 ///< dereference only once, do not loop +#define VREF_COPY 0x0002 ///< copy the result to the input var (v) +///@} + + +/// Create a variable reference. +/// Currently only references to global variables can be created. +/// \param ref ptr to the result +/// \param v variable to reference +/// \return success + +idaman THREAD_SAFE bool ida_export create_idcv_ref(idc_value_t *ref, const idc_value_t *v); + + +/// Add global IDC variable. +/// \param name name of the global variable +/// \return pointer to the created variable or existing variable. +/// NB: the returned pointer is valid until a new global var is added. + +idaman THREAD_SAFE idc_value_t *ida_export add_idc_gvar(const char *name); + + +/// Find an existing global IDC variable by its name. +/// \param name name of the global variable +/// \return pointer to the variable or nullptr. +/// NB: the returned pointer is valid until a new global var is added. +/// FIXME: it is difficult to use this function in a thread safe manner + +idaman THREAD_SAFE idc_value_t *ida_export find_idc_gvar(const char *name); + + +//------------------------------------------------------------------------- +/// Class to hold idc values +class idc_value_t +{ +public: +/// \defgroup VT_ IDC value types +/// Used by idc_value_t::vtype +///@{ +#define VT_LONG 2 ///< Integer (see idc_value_t::num) +#define VT_FLOAT 3 ///< Floating point (see idc_value_t::e) +#define VT_WILD 4 ///< Function with arbitrary number of arguments. + ///< The actual number of arguments will be passed in idc_value_t::num. + ///< This value should not be used for ::idc_value_t. +#define VT_OBJ 5 ///< Object (see idc_value_t::obj) +#define VT_FUNC 6 ///< Function (see idc_value_t::funcidx) +#define VT_STR 7 ///< String (see qstr() and similar functions) +#define VT_PVOID 8 ///< void * +#define VT_INT64 9 ///< i64 +#define VT_REF 10 ///< Reference +///@} + char vtype = VT_LONG; ///< \ref VT_ + +#ifndef SWIG + // this union must be the last member (see memset below) + union + { +#endif // SWIG + sval_t num; ///< #VT_LONG + fpvalue_t e; ///< #VT_FLOAT + idc_object_t *obj; + int funcidx; ///< #VT_FUNC + void *pvoid; ///< #VT_PVOID + int64 i64; ///< #VT_INT64 + uchar reserve[sizeof(qstring)]; ///< VT_STR +#ifndef SWIG + }; +#endif // SWIG + + /// Create a #VT_LONG value + idc_value_t(sval_t n=0) + { + memset(reserve, 0, sizeof(idc_value_t) - qoffsetof(idc_value_t, reserve)); + num = n; + } + /// Create a $VT_LONG with an existing idc value + idc_value_t(const idc_value_t &r) + { + memset(reserve, 0, sizeof(idc_value_t) - qoffsetof(idc_value_t, reserve)); + copy_idcv(this, r); + } + /// Create a #VT_STR value + idc_value_t(const char *_str) : vtype(VT_STR) { new(&qstr()) qstring(_str); } + /// Create a #VT_STR value + idc_value_t(const qstring &_str) : vtype(VT_STR) { new(&qstr()) qstring(_str); } + /// Destructor + ~idc_value_t(void) { clear(); } + /// See free_idcv() + void clear(void) { free_idcv(this); } // put num 0 + /// Assign this value to an existing value + idc_value_t &operator = (const idc_value_t &r) + { + copy_idcv(this, r); + return *this; + } + qstring &qstr(void) { return *(qstring *)# } ///< #VT_STR + const qstring &qstr(void) const { return *(qstring *)# } ///< #VT_STR + const char *c_str(void) const { return qstr().c_str(); } ///< #VT_STR + const uchar *u_str(void) const { return (const uchar *)c_str(); } ///< #VT_STR + void swap(idc_value_t &v) { swap_idcvs(this, &v); } ///< Set this = r and v = this + bool is_zero(void) const { return vtype == VT_LONG && num == 0; } ///< Does value represent the integer 0? + bool is_integral(void) { return vtype == VT_LONG || vtype == VT_INT64; } ///< Does value represent a whole number? + /// Convertible types are #VT_LONG, #VT_FLOAT, #VT_INT64, and #VT_STR + bool is_convertible(void) const { return (vtype >= 1 && vtype <= VT_FLOAT) || vtype == VT_STR || vtype == VT_INT64; } + + /// \name Warning + /// The following functions do not free the existing data! + /// When the contents are unknown, use the functions without a leading underscore. + ///@{ + void _create_empty_string(void) { vtype = VT_STR; new (&qstr()) qstring; } + void _set_string(const qstring &_str) + { + vtype = VT_STR; + new (&qstr()) qstring(_str); + } + void _set_string(const char *_str, size_t len) + { + vtype = VT_STR; + new (&qstr()) qstring(_str, len); + } + void _set_string(const char *_str) + { + size_t len = _str == nullptr ? 0 : strlen(_str); + _set_string(_str, len); + } + void _set_long(sval_t v) { vtype = VT_LONG; num = v; } + void _set_pvoid(void *p) { vtype = VT_PVOID; pvoid = p; } + void _set_int64(int64 v) { vtype = VT_INT64; i64 = v; } + void _set_float(const fpvalue_t &f) { vtype = VT_FLOAT; e = f; } + ///@} + + /// \name Setters + /// These functions ensure the previous value is cleared + ///@{ + void create_empty_string(void) { clear(); _create_empty_string(); } + void set_string(const char *_str, size_t len) { clear(); _set_string(_str, len); } + void set_string(const char *_str) { clear(); _set_string(_str); } + void set_string(const qstring &_str) { clear(); _set_string(_str); } + void set_long(sval_t v) { clear(); _set_long(v); } + void set_pvoid(void *p) { clear(); vtype = VT_PVOID; pvoid = p; } + void set_int64(int64 v) { clear(); vtype = VT_INT64; i64 = v; } + void set_float(const fpvalue_t &f) { clear(); vtype = VT_FLOAT; e = f; } + ///@} +}; + +/// Global idc variable +struct idc_global_t +{ + qstring name; + idc_value_t value; + idc_global_t(void) {} + idc_global_t(const char *n) : name(n) {} +}; +typedef qvector idc_vars_t; ///< vector of global idc variables + +/// Prototype of an external IDC function (implemented in C). +/// \param argv vector of input arguments. IDA will convert all arguments +/// to types specified by ext_idcfunc_t::args, except for #VT_WILD +/// \param r return value of the function or exception +/// \return 0 if ok, all other values indicate error. +/// the error code must be set with set_qerrno(): +/// - #eExecThrow - a new exception has been generated, see 'r' +/// - other values - runtime error has occurred + +typedef error_t idaapi idc_func_t(idc_value_t *argv, idc_value_t *r); + +#define eExecThrow 90 ///< See return value of ::idc_func_t + +/// Element of functions table. See idcfuncs_t::funcs +struct ext_idcfunc_t +{ + const char *name; ///< Name of function + idc_func_t *fptr; ///< Pointer to the Function + const char *args; ///< Type of arguments. Terminated with 0. + ///< #VT_WILD at the end means a variadic function. + ///< Actual number of arguments will be passed + ///< in res->num in this case. + const idc_value_t *defvals; ///< Default argument values. + ///< Only the rightmost arguments may have + ///< default values. + int ndefvals; ///< Number of default values. + int flags; ///< \ref EXTFUN_ +/// \defgroup EXTFUN_ Function description flags +/// Used by ext_idcfunc_t::flags +///@{ +#define EXTFUN_BASE 0x0001 ///< requires open database. +#define EXTFUN_NORET 0x0002 ///< does not return. the interpreter may + ///< clean up its state before calling it. +#define EXTFUN_SAFE 0x0004 ///< thread safe function. may be called + ///< from any thread. +///@} +}; + +/// Describes an array of IDC functions +struct idcfuncs_t +{ + size_t qnty; ///< Number of functions + ext_idcfunc_t *funcs; ///< Function table + + /// \name IDC Engine + /// IDC engine requires the following functions (all of them may be nullptr) + ///@{ + + /// Start IDC engine. Called before executing any IDC code. + error_t (idaapi *startup)(void); + + /// Stop IDC engine. Called when all IDC engines finish. + /// In other words, nested IDC engines do not call startup/shutdown. + error_t (idaapi *shutdown)(void); + + /// Initialize IDC engine. Called once at the very beginning of work. + /// This callback may create additional IDC classes, methods, etc. + void (idaapi *init_idc)(void); + + /// Terminate IDC engine. Called once at the very end of work. + void (idaapi *term_idc)(void); + + /// Is the database open? (used for #EXTFUN_BASE functions). + /// if this pointer is nullptr, #EXTFUN_BASE is not checked. + bool (idaapi *is_database_open)(void); + + /// Convert an address to a string. + /// if this pointer is nullptr, '%a' will be used. + size_t (idaapi *ea2str)(char *buf, size_t bufsize, ea_t ea); + + /// Should a variable name be accepted without declaration?. + /// When the parser encounters an unrecognized variable, this callback is called. + /// If it returns false, the parser generates the 'undefined variable' error + /// else the parser generates code to call to a set or get function, + /// depending on the current context. + /// If this pointer is nullptr, undeclared variables won't be supported. + /// However, if 'resolver' object is provided to the parser, it will be used + /// to resolve such names to constants at the compilation time. + /// This callback is used by IDA to handle processor register names. + bool (idaapi *undeclared_variable_ok)(const char *name); + + ///@} + + /// \name Indexes + /// Indexes into the 'f' array. non-positive values mean that the function does not exist + ///@{ + + /// Retrieve value of an undeclared variable. + /// Expected prototype: get(#VT_STR varname) + int get_unkvar; + + /// Store a value to an undeclared variable. + /// Expected prototype: set(#VT_WILD new_value, #VT_STR varname) + int set_unkvar; + + /// Execute resolved function. + /// If 'resolver' was used to resolve an unknown name to a constant in a function + /// call context, such a call will be redirected here. + /// Expected prototype: exec_resolved_func(#VT_LONG func, #VT_WILD typeinfo, ...) + /// This callback is used in IDA for Appcall. + int exec_resolved_func; + + /// Calculate sizeof(type). + /// This function is used by the interpreter to calculate sizeof() expressions. + /// Please note that the 'type' argument is an IDC object of typeinfo class. + /// Expected prototype: calc_sizeof(#VT_OBJ typeinfo) + /// This callback requires support of the type system (available only in IDA kernel) + /// It should not be used by standalone IDC interpreters. + int calc_sizeof; + + /// Get address of the specified field using the type information from the idb. + /// This function is used to resolve expressions like 'mystr.field' where + /// mystr does not represent an IDC object but just a plain number. + /// The number is interpreted as an address in the current idb. + /// This function retrieves type information at this address and tried to find + /// the specified 'field'. It returns the address of the 'field' in the idb. + /// This callback should not be used by standalone IDC interpreters. + int get_field_ea; + + ///@} +}; + +// Our idc_value_t and idc_global_t classes are freely movable with memcpy() +DECLARE_TYPE_AS_MOVABLE(idc_value_t); +DECLARE_TYPE_AS_MOVABLE(idc_global_t); + +//------------------------------------------------------------------------ +/// Add an IDC function. +/// This function does not modify the predefined kernel functions. +/// Example: +/// \code +/// static error_t idaapi myfunc5(idc_value_t *argv, idc_value_t *res) +/// { +/// msg("myfunc is called with arg0=%a and arg1=%s\n", argv[0].num, argv[1].str); +/// res->num = 5; // let's return 5 +/// return eOk; +/// } +/// static const char myfunc5_args[] = { VT_LONG, VT_STR, 0 }; +/// static const ext_idcfunc_t myfunc_desc = { "MyFunc5", myfunc5, myfunc5_args, nullptr, 0, EXTFUN_BASE }; +/// +/// // after this: +/// add_idc_func(myfunc_desc); +/// +/// // there is a new IDC function which can be called like this: +/// MyFunc5(0x123, "test"); +/// +/// \endcode +/// \param func function description block. +/// \note If the function already exists, it will be replaced by the new function +/// \return success + +idaman THREAD_SAFE bool ida_export add_idc_func(const ext_idcfunc_t &func); + + +/// Delete an IDC function +/// +idaman THREAD_SAFE bool ida_export del_idc_func(const char *name); + + +// Find an idc function that starts with the given prefix. +// \param out buffer for the output name +// \param prefix prefix to search for +// \param n how many matches to skip +// Returns: success +idaman THREAD_SAFE bool ida_export find_idc_func( + qstring *out, + const char *prefix, + int n=0); + + +/// Possible syntax element highlighting style names +enum syntax_highlight_style +{ + HF_DEFAULT = 0, + HF_KEYWORD1 = 1, + HF_KEYWORD2 = 2, + HF_KEYWORD3 = 3, + HF_STRING = 4, + HF_COMMENT = 5, + HF_PREPROC = 6, + HF_NUMBER = 7, + HF_USER1 = 8, + HF_USER2 = 9, + HF_USER3 = 10, + HF_USER4 = 11, + + HF_MAX, +}; +#define HF_FIRST HF_KEYWORD1 + +struct highlighter_cbs_t +{ + virtual ~highlighter_cbs_t() {} + virtual void idaapi set_style(int32 /*start*/, int32 /*len*/, syntax_highlight_style /*style*/) {} + virtual int32 idaapi prev_block_state() { return 0; } + virtual int32 idaapi cur_block_state() { return 0; } + virtual void idaapi set_block_state(int32 /*state*/) {} +}; + +/// Base class for syntax highligters +struct syntax_highlighter_t +{ + /// Function for extlang syntax highlighting + /// \param context implementation specific context. can be nullptr + /// \param highlighter_cbs structure with set of callbacks + /// \param text part of text to colorize + typedef void idaapi block_highlighter_t( + void *context, + highlighter_cbs_t *highlighter_cbs, + const qstring &text); + syntax_highlighter_t(block_highlighter_t *bh=nullptr) : highlight_block(bh) {} + virtual ~syntax_highlighter_t() {} + block_highlighter_t *highlight_block; +}; + + +//------------------------------------------------------------------------ +/// External language (to support third party language interpreters) +struct extlang_t +{ + size_t size; ///< Size of this structure + uint32 flags; ///< Language features +#define EXTLANG_IDC 0x01 +#define EXTLANG_NS_AWARE 0x02 ///< Namespace-aware (see above.) + int32 refcnt; ///< Reference count + const char *name; ///< Language name + const char *fileext; ///< File name extension for the language + syntax_highlighter_t *highlighter; // Language syntax highlighter + + /// Compile an expression. + /// \param name name of the function which will + /// hold the compiled expression + /// \param current_ea current address. if unknown then #BADADDR + /// \param expr expression to compile + /// \param[out] errbuf error message if compilation fails + /// \return success + bool (idaapi *compile_expr)( + const char *name, + ea_t current_ea, + const char *expr, + qstring *errbuf); + + /// Compile (load) a file. + /// + /// If an extlang_t object claims to be namespace-aware, it means its + /// 'compile_file()' will receive a requested namespace to compile a file + /// under. + /// + /// For example, compile_file() might receive a file + /// '.../loaders/myloader.py', with the corresponding namespace: + /// '__loaders__myloader' + /// + /// Accordingly, call_func() has to be prepared to receive a function name that + /// is namespace-qualified: "__loaders__myloader.accept_file()". + /// + /// \param file file name + /// \param requested_namespace requested namespace, may be ignored if not namespace-aware + /// \param[out] errbuf error message if compilation fails + bool (idaapi *compile_file)(const char *file, const char *requested_namespace, qstring *errbuf); + + /// Evaluate a previously compiled expression. + /// \param[out] result function result or exception + /// \param name function to call + /// \param nargs number of input arguments + /// \param args input arguments + /// \param[out] errbuf error message if evaluation fails + /// \return success + bool (idaapi *call_func)( + idc_value_t *result, + const char *name, + const idc_value_t args[], + size_t nargs, + qstring *errbuf); + + /// Compile and evaluate an expression. + /// \param[out] rv expression value or exception + /// \param current_ea current address. if unknown then BADADDR + /// \param expr expression to evaluate + /// \param[out] errbuf error message if evaluation fails + /// \return success + bool (idaapi *eval_expr)( + idc_value_t *rv, + ea_t current_ea, + const char *expr, + qstring *errbuf); + + /// Compile and execute a string with statements. + /// (see also: eval_expr() which works with expressions) + /// \param str input string to execute + /// \param[out] errbuf error message + /// \return success + bool (idaapi *eval_snippet)( + const char *str, + qstring *errbuf); + + /// Create an object instance. + /// \param result created object or exception + /// \param name object class name + /// \param args input arguments + /// \param nargs number of input arguments + /// \param errbuf error message if evaluation fails + /// \return success + bool (idaapi *create_object)( + idc_value_t *result, + const char *name, + const idc_value_t args[], + size_t nargs, + qstring *errbuf); + + /// Returns the attribute value of a given object from the global scope. + /// \param[out] result attribute value + /// \param obj object (may be nullptr) + /// \param attr attribute name. + /// if nullptr or empty string then the object instance name + /// (i.e. class name) should be returned. + /// \return success + bool (idaapi *get_attr)( + idc_value_t *result, + const idc_value_t *obj, + const char *attr); + + /// Sets the attribute value of a given object in the global scope. + /// \param obj object (may be nullptr) + /// \param attr attribute name + /// \param value attribute value + /// \return success + bool (idaapi *set_attr)( + idc_value_t *obj, + const char *attr, + const idc_value_t &value); + + /// Calls a member function. + /// \param[out] result function result or exception + /// \param obj object instance + /// \param name method name to call + /// \param args input arguments + /// \param nargs number of input arguments + /// \param[out] errbuf error message if evaluation fails + /// \return success + bool (idaapi *call_method)( + idc_value_t *result, + const idc_value_t *obj, + const char *name, + const idc_value_t args[], + size_t nargs, + qstring *errbuf); + + /// Compile (load) a file with processor module. + /// + /// See the note about namespace-awareness in compile_file() + /// + /// \param[out] procobj created object or exception + /// \param path processor module file name + /// \param[out] errbuf error message if compilation fails + /// \retval true success + /// \retval false if errbuf is empty then file has been + /// loaded (compiled) successfully but + /// it doesn't contain processor module + bool (idaapi *load_procmod)( + idc_value_t *procobj, + const char *path, + qstring *errbuf); + + /// Unload previously loaded processor module. + /// \param path processor module file name + /// \param[out] errbuff error message if compilation fails + /// \return success + bool (idaapi *unload_procmod)( + const char *path, + qstring *errbuf); + + bool is_idc(void) const { return (flags & EXTLANG_IDC) != 0; } + bool is_namespace_aware(void) const { return (flags & EXTLANG_NS_AWARE) != 0; } + void release(void) {} +}; + +typedef qvector extlangs_t; ///< vector of external language descriptions +typedef qrefcnt_t extlang_object_t; + +/// Get current active external language. + +idaman void *ida_export get_current_extlang(void); // do not use + +inline const extlang_object_t get_extlang(void) // use this function +{ + return extlang_object_t((extlang_t *)get_current_extlang()); +} + + +/// Install an external language interpreter. +/// Any previously registered interpreter will be automatically unregistered. +/// The installed extlang can be used in select_extlang(). +/// \param el description of the new language. must point to static storage. +/// \return extlang id; -1 means failure and will happen if the extlang has +/// already been installed + +idaman ssize_t ida_export install_extlang(extlang_t *el); + + +/// Uninstall an external language interpreter. +/// \return success + +idaman bool ida_export remove_extlang(extlang_t *el); + + +/// Selects the external language interpreter. +/// The specified extlang must be registered before selecting it. +/// It will be used to evaluate expressions entered in dialog boxes. +/// It will also replace the eval_expr() and eval_expr_long() functions. +/// \return success + +idaman bool ida_export select_extlang(extlang_t *el); + + +struct extlang_visitor_t +{ + virtual ssize_t idaapi visit_extlang(extlang_t *extlang) = 0; +}; + +/// Process all registered extlangs +// \param ev visitor object +// \param select temporarily select extlang for the duration of the visit +// \return 0 or the first non-zero value returned by visit_extlang() + +idaman ssize_t ida_export for_all_extlangs(extlang_visitor_t &ev, bool select=false); + + +// Helper function to search for extlang +enum find_extlang_kind_t +{ + FIND_EXTLANG_BY_EXT, + FIND_EXTLANG_BY_NAME, + FIND_EXTLANG_BY_IDX, +}; + +// do not use +idaman void *ida_export find_extlang(const void *str, find_extlang_kind_t kind); + + +/// Get the extlang that can handle the given file extension + +inline extlang_object_t find_extlang_by_ext(const char *ext) +{ + return extlang_object_t((extlang_t *)find_extlang(ext, FIND_EXTLANG_BY_EXT)); +} + +/// Find an extlang by name + +inline extlang_object_t find_extlang_by_name(const char *name) +{ + return extlang_object_t((extlang_t *)find_extlang(name, FIND_EXTLANG_BY_NAME)); +} + +/// Find an extlang by index + +inline extlang_object_t find_extlang_by_index(size_t idx) +{ + return extlang_object_t((extlang_t *)find_extlang(&idx, FIND_EXTLANG_BY_IDX)); +} + + + +//------------------------------------------------------------------------ +/// Set or append a header path. +/// IDA looks for the include files in the appended header paths, +/// then in the ida executable directory. +/// \param path list of directories to add (separated by ';') +/// may be nullptr, in this case nothing is added +/// \param add true: append. +/// false: remove old paths. +/// \retval true success +/// \retval false no memory + +idaman THREAD_SAFE bool ida_export set_header_path(const char *path, bool add); + + +/// Get full name of IDC file name. +/// Search for file in list of include directories, IDCPATH directory +/// and system directories. +/// \param buf buffer for the answer +/// \param bufsize size of buffer +/// \param file file name without full path +/// \return nullptr is file not found. +/// otherwise returns pointer to buf + +idaman THREAD_SAFE char *ida_export get_idc_filename( + char *buf, + size_t bufsize, + const char *file); + + +/// Compile and execute "main" function from system file. +/// \param file file name with IDC function(s). +/// The file will be searched using get_idc_filename(). +/// \param complain_if_no_file +/// - 1: display warning if the file is not found +/// - 0: don't complain if file doesn't exist +/// \retval 1 ok, file is compiled and executed +/// \retval 0 failure, compilation or execution error, warning is displayed + +idaman THREAD_SAFE bool ida_export exec_system_script( + const char *file, + bool complain_if_no_file=true); + + +/// Compile and calculate an expression. +/// \param res pointer to result. The result will be converted +/// to 32/64bit number. Use eval_expr() if you +/// need the result of another type. +/// \param where the current linear address in the addressing space of the +/// program being disassembled. it will be used to resolve +/// names of local variables, etc. +/// if not applicable, then should be #BADADDR +/// \param line a text line with IDC expression +/// \param[out] errbuf buffer for the error message +/// \retval true ok +/// \retval false error, see errbuf + +idaman bool ida_export eval_expr_long( + sval_t *res, + ea_t where, + const char *line, + qstring *errbuf=nullptr); + +/// See eval_expr_long() + +inline bool idaapi eval_expr_long( + uval_t *res, + ea_t where, + const char *line, + qstring *errbuf=nullptr) +{ + return eval_expr_long((sval_t *)res, where, line, errbuf); +} + + +/// Compile and calculate an expression. +/// \param rv pointer to the result +/// \param where the current linear address in the addressing space of the +/// program being disassembled. If will be used to resolve +/// names of local variables etc. +/// if not applicable, then should be #BADADDR. +/// \param line the expression to evaluate +/// \param[out] errbuf buffer for the error message +/// \retval true ok +/// \retval false error, see errbuf + +idaman bool ida_export eval_expr( + idc_value_t *rv, + ea_t where, + const char *line, + qstring *errbuf=nullptr); + + +/// Same as eval_expr(), but will always use the IDC interpreter regardless of the +/// currently installed extlang. + +idaman bool ida_export eval_idc_expr( + idc_value_t *rv, + ea_t where, + const char *buf, + qstring *errbuf=nullptr); + + +/// Compile a text file with IDC function(s). +/// \param file name of file to compile +/// if nullptr, then "File not found" is returned. +/// \param[out] errbuf buffer for the error message +/// \param cpl_flags \ref CPL_ or 0 +/// \retval true ok +/// \retval false error, see errbuf + +/// \defgroup CPL_ Flags for compile_idc_file() +///@{ +#define CPL_DEL_MACROS 0x0001 ///< delete macros at the end of compilation +#define CPL_USE_LABELS 0x0002 ///< allow program labels in the script +#define CPL_ONLY_SAFE 0x0004 ///< allow calls of only thread-safe functions +///@} + +idaman THREAD_SAFE bool ida_export compile_idc_file( + const char *file, + qstring *errbuf=nullptr, + int cpl_flags = CPL_DEL_MACROS|CPL_USE_LABELS); + + +/// Compile text with IDC function(s). +/// \param line line with IDC function(s) (can't be nullptr!) +/// \param[out] errbuf buffer for the error message +/// \param resolver callback object to get values of undefined variables +/// This object will be called if IDC function contains +/// references to undefined variables. May be nullptr. +/// \param only_safe_funcs if true, any calls to functions without #EXTFUN_SAFE flag +/// will lead to a compilation error. +/// \retval true ok +/// \retval false error, see errbuf + +struct idc_resolver_t +{ + virtual uval_t idaapi resolve_name(const char *name) = 0; +}; + +idaman THREAD_SAFE bool ida_export compile_idc_text( + const char *line, + qstring *errbuf=nullptr, + idc_resolver_t *resolver=nullptr, + bool only_safe_funcs=false); + + +/// Compile text with IDC statements. +/// \param func name of the function to create out of the snippet +/// \param text text to compile +/// \param[out] errbuf buffer for the error message +/// \param resolver callback object to get values of undefined variables +/// This object will be called if IDC function contains +/// references to undefined variables. May be nullptr. +/// \param only_safe_funcs if true, any calls to functions without #EXTFUN_SAFE flag +/// will lead to a compilation error. +/// \retval true ok +/// \retval false error, see errbuf + +idaman bool ida_export compile_idc_snippet( + const char *func, + const char *text, + qstring *errbuf=nullptr, + idc_resolver_t *resolver=nullptr, + bool only_safe_funcs=false); + + +// Execution of IDC code can generate exceptions. Exception objects +// will have the following attributes: +// file - the source file name +// line - the line number that was executing when the exception occurred +// func - the function name +// pc - bytecode program counter +// For runtime errors, the following additional attributes exist: +// qerrno - runtime error code +// description - text description of the runtime error + +/// Execute an IDC function. +/// \param[out] result pointer to idc_value_t to hold the return value of the function. +/// If execution fails, this variable will contain +/// the exception information. +/// Can be nullptr if return value is not required. +/// \param fname function name. User-defined functions, built-in functions, +/// and plugin-defined functions are accepted. +/// \param args array of parameters +/// \param argsnum number of parameters to pass to 'fname'. +/// This number should be equal to number of parameters +/// the function expects. +/// \param[out] errbuf buffer for the error message +/// \param resolver callback object to get values of undefined variables +/// This object will be called if IDC function contains +/// references to undefined variables. May be nullptr. +/// \retval true ok +/// \retval false error, see errbuf + +idaman bool ida_export call_idc_func( + idc_value_t *result, + const char *fname, + const idc_value_t args[], + size_t argsnum, + qstring *errbuf=nullptr, + idc_resolver_t *resolver=nullptr); + + +/// Compile and execute IDC function(s) from file. +/// \param result ptr to idc_value_t to hold result of the function. +/// If execution fails, this variable will contain +/// the exception information. +/// You may pass nullptr if you are not interested in the returned +/// value. +/// \param path text file containing text of IDC functions +/// \param func function name to execute +/// \param args array of parameters +/// \param argsnum number of parameters to pass to 'fname' +/// This number should be equal to number of parameters +/// the function expects. +/// \param[out] errbuf buffer for the error message +/// \retval true ok +/// \retval false error, see errbuf + +THREAD_SAFE inline bool exec_idc_script( + idc_value_t *result, + const char *path, + const char *func, + const idc_value_t args[], + size_t argsnum, + qstring *errbuf=nullptr) +{ + if ( !compile_idc_file(path, errbuf) ) + return false; + return call_idc_func(result, func, args, argsnum, errbuf); +} + + +/// Compile and execute IDC statements or expressions. +/// \param result ptr to idc_value_t to hold result of the function. +/// If execution fails, this variable will contain +/// the exception information. +/// You may pass nullptr if you are not interested in the returned +/// value. +/// \param line body of IDC the function +/// \param[out] errbuf buffer for the error message +/// \param resolver callback object to get values of undefined variables +/// This object will be called if IDC function contains +/// references to undefined variables. May be nullptr. +/// \return success +/// \note see also eval_idc_expr() + + +idaman bool ida_export eval_idc_snippet( + idc_value_t *result, + const char *line, + qstring *errbuf=nullptr, + idc_resolver_t *resolver=nullptr); + + +//------------------------------------------------------------------------ +/// Setup lowcnd callbacks to read/write registers. +/// These callbacks will be used by the idc engine to read/write registers +/// while calculating low level breakpoint conditions for local debuggers. + +idaman void ida_export setup_lowcnd_regfuncs(idc_func_t *getreg, idc_func_t *setreg); + +//------------------------------------------------------------------------ +/// Extract type & data from the idc_value_t instance that +/// was passed to parse_config_value(). +/// +/// \param vtype pointer to storage that will hold the type (\ref IDPOPT_T) +/// \param vdata pointer to storage that contains the value (see \ref IDPOPT_T +/// for what type of data is pointed to.) +/// \param v the value holder +/// \return true in case of success, false if 'v' is of unexpected type +inline THREAD_SAFE bool get_idptype_and_data(int *vtype, const void **vdata, const idc_value_t &v) +{ + switch ( v.vtype ) + { + case VT_STR: *vtype = IDPOPT_STR, *vdata = v.c_str(); break; + case VT_LONG: *vtype = IDPOPT_NUM; *vdata = &v.num; break; + case VT_WILD: *vtype = IDPOPT_BIT; *vdata = &v.num; break; + case VT_INT64: *vtype = IDPOPT_I64; *vdata = &v.i64; break; + case VT_PVOID: *vtype = IDPOPT_CST; *vdata = v.pvoid; break; + default: return false; + } + return true; +} + +/// Create an idc execution exception object. +/// This helper function can be used to return an exception from C++ code to IDC. +/// In other words this function can be called from idc_func_t() callbacks. +/// Sample usage: if ( !ok ) return throw_idc_exception(r, "detailed error msg"); +/// \param r object to hold the exception object +/// \param desc exception description +/// \return eExecThrow + +idaman error_t ida_export throw_idc_exception(idc_value_t *r, const char *desc); + + +#endif /* _EXPR_H */ + +``` + +`IdaSDK/fixup.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef FIXUP_HPP +#define FIXUP_HPP + +#include +#include + +/*! \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 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 + +``` + +`IdaSDK/fpro.h`: + +```h +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef __FPRO_H +#define __FPRO_H + +#include +#include + +/*! \file fpro.h + + \brief System independent counterparts of FILE* related functions from Clib + + You should not use C standard I/O functions in your modules. + The reason: Each module compiled with Borland + (and statically linked to Borland's library) will host a copy of + the FILE * information. + + So, if you open a file in the plugin and pass the handle to the + kernel, the kernel will not be able to use it. + + If you really need to use the standard functions, define USE_STANDARD_FILE_FUNCTIONS. + In this case do not mix them with q... functions. +*/ + +#if !defined(USE_STANDARD_FILE_FUNCTIONS) && !defined(__CODE_CHECKER__) +#undef stdin +#undef stdout +#undef stderr +#undef fgetc +#undef fputc + +#endif + + +/// \name File I/O +/// The following functions work just like their counterparts from Clib, +/// only they are safer, system independent, and they set qerrno (see get_qerrno()). +///@{ +idaman THREAD_SAFE FILE *ida_export qfopen(const char *file, const char *mode); +idaman THREAD_SAFE ssize_t ida_export qfread(FILE *fp, void *buf, size_t n); +idaman THREAD_SAFE ssize_t ida_export qfwrite(FILE *fp, const void *buf, size_t n); +idaman THREAD_SAFE qoff64_t ida_export qftell(FILE *fp); +idaman THREAD_SAFE int ida_export qfseek(FILE *fp, qoff64_t offset, int whence); +idaman THREAD_SAFE int ida_export qfclose(FILE *fp); +idaman THREAD_SAFE int ida_export qflush(FILE *fp); +idaman THREAD_SAFE int ida_export qfputc(int chr, FILE *fp); +idaman THREAD_SAFE int ida_export qfgetc(FILE *fp); +idaman THREAD_SAFE char *ida_export qfgets(char *s, size_t len, FILE *fp); +idaman THREAD_SAFE int ida_export qfputs(const char *s, FILE *fp); +idaman FILE *ida_export qtmpfile(void); +idaman THREAD_SAFE int ida_export qunlink(const char *fname); +idaman THREAD_SAFE int ida_export qaccess(const char *fname, int mode); +idaman THREAD_SAFE char *ida_export qgets(char *line, size_t linesize); +idaman THREAD_SAFE uint64 ida_export qfsize(FILE *fp); + +idaman THREAD_SAFE AS_PRINTF(2, 0) int ida_export qvfprintf(FILE *fp, const char *format, va_list va); +idaman THREAD_SAFE AS_PRINTF(1, 0) int ida_export qvprintf(const char *format, va_list va); +idaman THREAD_SAFE AS_PRINTF(1, 0) int ida_export qveprintf(const char *format, va_list va); +idaman THREAD_SAFE AS_SCANF(2, 0) int ida_export qvfscanf(FILE *fp, const char *format, va_list va); + +#ifdef __cplusplus +THREAD_SAFE AS_PRINTF(2, 3) inline int qfprintf(FILE *fp, const char *format, ...) +{ + va_list va; + va_start(va, format); + int code = qvfprintf(fp, format, va); + va_end(va); + return code; +} + +THREAD_SAFE AS_PRINTF(1, 2) inline int qprintf(const char *format, ...) +{ + va_list va; + va_start(va, format); + int code = qvprintf(format, va); + va_end(va); + return code; +} + +THREAD_SAFE AS_PRINTF(1, 2) inline int qeprintf(const char *format, ...) +{ + va_list va; + va_start(va, format); + int code = qveprintf(format, va); + va_end(va); + return code; +} + +THREAD_SAFE AS_SCANF(2, 3) inline int qfscanf(FILE *fp, const char *format, ...) +{ + va_list va; + va_start(va, format); + int code = qvfscanf(fp, format, va); + va_end(va); + return code; +} +#endif +///@} + + +/// Read line from file (the newline is removed from the output buffer) +/// \param buf output buffer +/// \param fp pointer to file +/// \return -1 or length of line + +idaman THREAD_SAFE ssize_t ida_export qgetline(qstring *buf, FILE *fp); + +/// Rename a file: 'newname' may exist, and will be deleted + +idaman THREAD_SAFE int ida_export qrename(const char *oldfname, const char *newfname); + +/// Move a file - more powerful version of qrename +/// \retval 0 success +/// \retval -1 system error +/// \retval else a combination of flags to be given for successful move +idaman THREAD_SAFE int ida_export qmove(const char *oldfname, const char *newfname, uint32 flags); +enum +{ + QMOVE_CROSS_FS = 0x01, // UNIX: allow moving between different filesystem + QMOVE_OVERWRITE = 0x02, // Overwrite existing file + QMOVE_OVR_RO = 0x04, // Overwrite file even if it is write-protected +}; + +/// Copy a file. +/// \param from source file name +/// \param to destination file name +/// \param overwrite overwrite output if it exists? +/// \param cb user callback. return false to abort the copy loop +/// \param ud user data passed back to cb +/// \param flags reserved (should be zero) +/// \retval -1 input file not found +/// \retval -2 output file not writable +/// \retval -3 output file already exists while overwrite is false +/// \retval -4 write failure +/// \retval -5 interrupted from the callback + +idaman THREAD_SAFE int ida_export qcopyfile( + const char *from, + const char *to, + bool overwrite = true, + bool (idaapi *cb)(uint64 pos, uint64 total, void *ud)=nullptr, + void *ud = nullptr, + int flags = 0); + + +/// Get temporary directory name +/// Returns absolute path + +idaman char *ida_export qtmpdir(char *buf, size_t bufsize); + +/// Get temporary file name. +/// Returns absolute path (includes directory) + +idaman char *ida_export qtmpnam(char *buf, size_t bufsize); + + +/// File janitor: will close a file at destruction-time +typedef janitor_t file_janitor_t; +template <> inline file_janitor_t::~janitor_t() +{ + qfclose(resource); +} + +/// \name readbytes/writebytes +/// Add-ins for 2..32 byte read/writes. +/// \param fp pointer to file +/// \param res value read from file +/// \param size size of value in bytes (1..32) +/// \param mostfirst is MSB first? (0/1) +/// \retval 0 All these functions return 0 on success +///@{ +idaman THREAD_SAFE int ida_export freadbytes(FILE *fp, void *res, int size, int mostfirst); +idaman THREAD_SAFE int ida_export fwritebytes(FILE *fp, const void *l, int size, int mostfirst); + +#ifdef __cplusplus +#define DEF_FREADBYTES(read, write, type, size) \ + inline THREAD_SAFE int read(FILE *fp, type *res, bool mostfirst) \ + { return freadbytes(fp, res, size, mostfirst); } \ + inline THREAD_SAFE int write(FILE *fp, const type *res, bool mostfirst) \ + { return fwritebytes(fp, res, size, mostfirst); } +DEF_FREADBYTES(fread2bytes, fwrite2bytes, int16, 2) +DEF_FREADBYTES(fread2bytes, fwrite2bytes, uint16, 2) +DEF_FREADBYTES(fread4bytes, fwrite4bytes, int32, 4) +DEF_FREADBYTES(fread4bytes, fwrite4bytes, uint32, 4) +DEF_FREADBYTES(fread8bytes, fwrite8bytes, int64, 8) +DEF_FREADBYTES(fread8bytes, fwrite8bytes, uint64, 8) +#else +#define fread2bytes(fp,v,mf) freadbytes(fp,v,2,mf) +#define fwrite2bytes(fp,v,mf) fwritebytes(fp,v,2,mf) +#define fread4bytes(fp,v,mf) freadbytes(fp,v,4,mf) +#define fwrite4bytes(fp,v,mf) fwritebytes(fp,v,4,mf) +#define fread8bytes(fp,v,mf) freadbytes(fp,v,8,mf) +#define fwrite8bytes(fp,v,mf) fwritebytes(fp,v,8,mf) +#endif +///@} + +#if !defined(feof) || !defined(ferror) +// If feof() and ferror() are not macros, we cannot use them +// Fortunately, for borland and vc they are macros, so there is no problem +// GCC defines them as functions: I have no idea whether they will work or not +// Anyway we remove the error directive from this file +// so the plugins can be compiled with gcc +//#error feof or ferror are not macros! +#endif + + +#endif + +``` + +`IdaSDK/frame.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _FRAME_HPP +#define _FRAME_HPP +#include + +/*! \file frame.hpp + + \brief Routines to manipulate function stack frames, stack + variables, register variables and local labels. + + The frame is represented as a structure: +
+    +------------------------------------------------+
+    | function arguments                             |
+    +------------------------------------------------+
+    | return address (isn't stored in func_t)        |
+    +------------------------------------------------+
+    | saved registers (SI, DI, etc - func_t::frregs) |
+    +------------------------------------------------+ <- typical BP
+    |                                                |  |
+    |                                                |  | func_t::fpd
+    |                                                |  |
+    |                                                | <- real BP
+    | local variables (func_t::frsize)               |
+    |                                                |
+    |                                                |
+    +------------------------------------------------+ <- SP
+  
+ + To access the structure of a function frame and stack variables, use: + - tinfo_t::get_func_frame(const func_t *pfn) (the preferred way) + - get_func_frame(tinfo_t *out, const func_t *pfn) + - tinfo_t::get_udt_details() gives info about stack variables: their type, + names, offset, etc +*/ + +// name of stkvar to denote the return address slot +#define FRAME_UDM_NAME_R "__return_address" +// name of stkvar to denote the saved register slots +#define FRAME_UDM_NAME_S "__saved_registers" + +class op_t; + +// We need to trace value of SP register. For this we introduce +// an array of SP register change points. + +// SP register change point +// +// NOTE: To manipulate/modify stack points, please use the specialized +// functions provided below in this file (stack pointer change points) + +struct stkpnt_t +{ + ea_t ea; // linear address + sval_t spd; // here we keep a cumulative difference from [BP-frsize] + + DECLARE_COMPARISONS(stkpnt_t) + { + if ( ea < r.ea ) + return -1; + if ( ea > r.ea ) + return 1; + return 0; + } +}; +DECLARE_TYPE_AS_MOVABLE(stkpnt_t); +// we declare a struct to be able to forward declare it in other files +struct stkpnts_t : public qvector +{ + DECLARE_COMPARISONS(stkpnts_t) { return compare_containers(*this, r); } +}; + +//-------------------------------------------------------------------------- +// F R A M E M A N I P U L A T I O N +//-------------------------------------------------------------------------- + +/// Add function frame. +/// \param pfn pointer to function structure +/// \param frsize size of function local variables +/// \param frregs size of saved registers +/// \param argsize size of function arguments range which will be purged upon return. +/// this parameter is used for __stdcall and __pascal calling conventions. +/// for other calling conventions please pass 0. +/// \retval 1 ok +/// \retval 0 failed (no function, frame already exists) + +idaman bool ida_export add_frame( + func_t *pfn, + sval_t frsize, + ushort frregs, + asize_t argsize); + + +/// Delete a function frame. +/// \param pfn pointer to function structure +/// \return success + +idaman bool ida_export del_frame(func_t *pfn); + + +/// Set size of function frame. +/// Note: The returned size may not include all stack arguments. It does so +/// only for __stdcall and __fastcall calling conventions. To get the entire +/// frame size for all cases use frame.get_func_frame(pfn).get_size() +/// \param pfn pointer to function structure +/// \param frsize size of function local variables +/// \param frregs size of saved registers +/// \param argsize size of function arguments that will be purged +/// from the stack upon return +/// \return success + +idaman bool ida_export set_frame_size( + func_t *pfn, + asize_t frsize, + ushort frregs, + asize_t argsize); + + +/// Get full size of a function frame. +/// This function takes into account size of local variables + size of +/// saved registers + size of return address + number of purged bytes. +/// The purged bytes correspond to the arguments of the functions with +/// __stdcall and __fastcall calling conventions. +/// \param pfn pointer to function structure, may be nullptr +/// \return size of frame in bytes or zero + +idaman asize_t ida_export get_frame_size(const func_t *pfn); + + +/// Get size of function return address. +/// \param pfn pointer to function structure, can't be nullptr + +idaman int ida_export get_frame_retsize(const func_t *pfn); + +/// Parts of a frame +enum frame_part_t +{ + FPC_ARGS, + FPC_RETADDR, + FPC_SAVREGS, + FPC_LVARS, +}; + +/// Get offsets of the frame part in the frame. +/// \param range pointer to the output buffer with the frame part +/// start/end(exclusive) offsets, can't be nullptr +/// \param pfn pointer to function structure, can't be nullptr +/// \param part frame part + +idaman void ida_export get_frame_part(range_t *range, const func_t *pfn, frame_part_t part); + +/// Get starting address of arguments section + +inline ea_t frame_off_args(const func_t *pfn) +{ + range_t range; + get_frame_part(&range, pfn, FPC_ARGS); + return range.start_ea; +} + +/// Get starting address of return address section + +inline ea_t frame_off_retaddr(const func_t *pfn) +{ + range_t range; + get_frame_part(&range, pfn, FPC_RETADDR); + return range.start_ea; +} + +/// Get starting address of saved registers section + +inline ea_t frame_off_savregs(const func_t *pfn) +{ + range_t range; + get_frame_part(&range, pfn, FPC_SAVREGS); + return range.start_ea; +} + +/// Get start address of local variables section + +inline ea_t frame_off_lvars(const func_t *pfn) +{ + range_t range; + get_frame_part(&range, pfn, FPC_LVARS); + return range.start_ea; +} + +/// Does the given offset lie within the arguments section? + +inline bool processor_t::is_funcarg_off(const func_t *pfn, uval_t frameoff) const +{ + range_t args; + get_frame_part(&args, pfn, FPC_ARGS); + return stkup() + ? frameoff < args.end_ea + : frameoff >= args.start_ea; +} + +/// Does the given offset lie within the local variables section? + +inline sval_t processor_t::lvar_off(const func_t *pfn, uval_t frameoff) const +{ + range_t lvars; + get_frame_part(&lvars, pfn, FPC_LVARS); + return stkup() + ? frameoff - lvars.start_ea + : lvars.end_ea - frameoff; +} + +/// Get type of function frame +/// \param[out] out type info +/// \param pfn pointer to function structure +/// \return success + +idaman bool ida_export get_func_frame(tinfo_t *out, const func_t *pfn); + +inline bool get_func_frame(tinfo_t *tif, ea_t ea) { return get_func_frame(tif, get_func(ea)); } + +/// Convert struct offsets into fp-relative offsets. +/// This function converts the offsets inside the udt_type_data_t object +/// into the frame pointer offsets (for example, EBP-relative). + +inline sval_t soff_to_fpoff(func_t *pfn, uval_t soff) +{ + return pfn != nullptr ? soff - pfn->frsize + pfn->fpd : soff; +} + + +/// Update frame pointer delta. +/// \param pfn pointer to function structure +/// \param fpd new fpd value. +/// cannot be bigger than the local variable range size. +/// \return success + +idaman bool ida_export update_fpd(func_t *pfn, asize_t fpd); + + +/// Set the number of purged bytes for a function or data item (funcptr). +/// This function will update the database and plan to reanalyze items +/// referencing the specified address. It works only for processors +/// with #PR_PURGING bit in 16 and 32 bit modes. +/// \param ea address of the function of item +/// \param nbytes number of purged bytes +/// \param override_old_value may overwrite old information about purged bytes +/// \return success + +idaman bool ida_export set_purged(ea_t ea, int nbytes, bool override_old_value); + + +//-------------------------------------------------------------------------- +// S T A C K V A R I A B L E S +//-------------------------------------------------------------------------- + +/// Automatically add stack variable. +/// Processor modules should use insn_t::create_stkvar(). +/// \param insn the instruction +/// \param x reference to instruction operand +/// \param v immediate value in the operand (usually x.addr) +/// \param flags \ref STKVAR_1 +/// \return success + +idaman bool ida_export add_stkvar(const insn_t &insn, const op_t &x, sval_t v, int flags); + +/// \defgroup STKVAR_1 Add stkvar flags +/// Passed as 'flags' parameter to add_stkvar() +///@{ +#define STKVAR_VALID_SIZE 0x0001 ///< x.dtyp contains correct variable type + ///< (for insns like 'lea' this bit must be off). + ///< In general, dr_O references do not allow + ///< to determine the variable size +#define STKVAR_KEEP_EXISTING 0x0002 ///< if a stack variable for this operand already + ///< exists then we do not create a new variable +///@} + +/// Define/redefine a stack variable. +/// \param pfn pointer to function +/// \param name variable name, nullptr means autogenerate a name +/// \param off offset of the stack variable in the frame. +/// negative values denote local variables, positive - function arguments. +/// \param tif variable type +/// \param repr variable representation +/// \return success + +idaman bool ida_export define_stkvar( + func_t *pfn, + const char *name, + sval_t off, + const tinfo_t &tif, + const struct value_repr_t *repr=nullptr); + + +/// Add member to the frame type +/// \param pfn pointer to function +/// \param name variable name, nullptr means autogenerate a name +/// \param offset member offset in the frame structure, in bytes +/// \param tif variable type +/// \param repr variable representation +/// \param etf_flags \see ETF_ +/// \return success + +idaman bool ida_export add_frame_member( + const func_t *pfn, + const char *name, + uval_t offset, + const tinfo_t &tif, + const struct value_repr_t *repr=nullptr, + uint etf_flags=0); + + +/// Is member name prefixed with "anonymous"? + +inline THREAD_SAFE bool is_anonymous_member_name(const char *name) +{ + return name == nullptr + || strncmp(name, "anonymous", 9) == 0; +} + + +/// Is member name an auto-generated name? + +inline THREAD_SAFE bool is_dummy_member_name(const char *name) +{ + return name == nullptr + || strncmp(name, "arg_", 4) == 0 + || strncmp(name, "var_", 4) == 0 + || is_anonymous_member_name(name); +} + + +/// Is stkvar with TID the return address slot or the saved registers slot ? +/// \param tid frame member type id +/// return address or saved registers member? + +idaman bool ida_export is_special_frame_member(tid_t tid); + + +/// Change type of the frame member +/// \param pfn pointer to function +/// \param offset member offset in the frame structure, in bytes +/// \param tif variable type +/// \param repr variable representation +/// \param etf_flags \see ETF_ +/// \return success + +idaman bool ida_export set_frame_member_type( + const func_t *pfn, + uval_t offset, + const tinfo_t &tif, + const struct value_repr_t *repr=nullptr, + uint etf_flags=0); + + +/// Delete frame members +/// \param pfn pointer to function +/// \param start_offset member offset to start deletion from, in bytes +/// \param end_offset member offset which not included in the deletion, in bytes +/// \return success + +idaman bool ida_export delete_frame_members( + const func_t *pfn, + uval_t start_offset, + uval_t end_offset); + + +/// Build automatic stack variable name. +/// \param buf pointer to buffer +/// \param pfn pointer to function (can't be nullptr!) +/// \param v value of variable offset +/// \return length of stack variable name or -1 + +idaman ssize_t ida_export build_stkvar_name( + qstring *buf, + const func_t *pfn, + sval_t v); + + +/// Calculate offset of stack variable in the frame structure. +/// \param pfn pointer to function (cannot be nullptr) +/// \param insn the instruction +/// \param n 0..#UA_MAXOP-1 operand number +/// -1 if error, return #BADADDR +/// \return #BADADDR if some error (issue a warning if stack frame is bad) + +idaman ea_t ida_export calc_stkvar_struc_offset( + func_t *pfn, + const insn_t &insn, + int n); + + +/// Calculate the offset of stack variable in the frame. +/// \param pfn pointer to function (cannot be nullptr) +/// \param off the offset relative to stack pointer or frame pointer +/// \param insn the instruction +/// \param op the operand +/// \return the offset in the frame + +idaman sval_t ida_export calc_frame_offset( + func_t *pfn, + sval_t off, + const insn_t *insn = nullptr, + const op_t *op = nullptr); + + +/// Find and delete wrong frame info. +/// Namely, we delete: +/// - unreferenced stack variable definitions +/// - references to dead stack variables (i.e. operands displayed in red) +/// these operands will be untyped and most likely displayed in hex. +/// +/// We also plan to reanalyze instruction with the stack frame references +/// \param pfn pointer to the function +/// \param should_reanalyze callback to determine which instructions to reanalyze +/// \return number of deleted definitions + +idaman int ida_export delete_wrong_frame_info( + func_t *pfn, + bool idaapi should_reanalyze(const insn_t &insn)); + + +//-------------------------------------------------------------------------- +// R E G I S T E R V A R I A B L E S +//-------------------------------------------------------------------------- +/// \defgroup regvar Register variables +/// Definition of ::regvar_t and related functions +///@{ + +idaman void ida_export free_regvar(struct regvar_t *v); + +/// A register variable allows the user to rename a general processor register +/// to a meaningful name. +/// IDA doesn't check whether the target assembler supports the register renaming. +/// All register definitions will appear at the beginning of the function. +struct regvar_t : public range_t +{ + char *canon = nullptr; ///< canonical register name (case-insensitive) + char *user = nullptr; ///< user-defined register name + char *cmt = nullptr; ///< comment to appear near definition + + regvar_t() {} + regvar_t(const regvar_t &r) : range_t(r) + { + canon = ::qstrdup(r.canon); + user = ::qstrdup(r.user); + cmt = ::qstrdup(r.cmt); + } + ~regvar_t() { free_regvar(this); } + regvar_t &operator=(const regvar_t &r) + { + if ( this != &r ) + { + free_regvar(this); + new (this) regvar_t(r); + } + return *this; + } + void swap(regvar_t &r) + { + uchar buf[sizeof(*this)]; + memcpy(buf, &r, sizeof(buf)); + memcpy(&r, this, sizeof(buf)); + memcpy(this, buf, sizeof(buf)); + } +#ifndef SWIG + DECLARE_COMPARISONS(regvar_t); +#endif +}; +DECLARE_TYPE_AS_MOVABLE(regvar_t); + +/// Define a register variable. +/// \param pfn function in which the definition will be created +/// \param ea1,ea2 range of addresses within the function where the definition +/// will be used +/// \param canon name of a general register +/// \param user user-defined name for the register +/// \param cmt comment for the definition +/// \return \ref REGVAR_ERROR_ + +idaman int ida_export add_regvar( + func_t *pfn, + ea_t ea1, + ea_t ea2, + const char *canon, + const char *user, + const char *cmt); +/// \defgroup REGVAR_ERROR_ Register variable error codes +/// Return values for functions in described in \ref regvar +///@{ +#define REGVAR_ERROR_OK 0 ///< all ok +#define REGVAR_ERROR_ARG (-1) ///< function arguments are bad +#define REGVAR_ERROR_RANGE (-2) ///< the definition range is bad +#define REGVAR_ERROR_NAME (-3) ///< the provided name(s) can't be accepted +///@} + +/// Find a register variable definition (powerful version). +/// One of 'canon' and 'user' should be nullptr. +/// If both 'canon' and 'user' are nullptr it returns the first regvar +/// definition in the range. +/// \param pfn function in question +/// \param ea1,ea2 range of addresses to search. +/// ea1==BADADDR means the entire function +/// \param canon name of a general register +/// \param user user-defined name for the register +/// \return nullptr-not found, otherwise ptr to regvar_t + +idaman regvar_t *ida_export find_regvar(func_t *pfn, ea_t ea1, ea_t ea2, const char *canon, const char *user); + + +/// Find a register variable definition. +/// \param pfn function in question +/// \param ea current address +/// \param canon name of a general register +/// \return nullptr-not found, otherwise ptr to regvar_t + +inline regvar_t *find_regvar(func_t *pfn, ea_t ea, const char *canon) +{ + return find_regvar(pfn, ea, ea+1, canon, nullptr); +} + + +/// Is there a register variable definition? +/// \param pfn function in question +/// \param ea current address + +inline bool has_regvar(func_t *pfn, ea_t ea) +{ + return find_regvar(pfn, ea, ea+1, nullptr, nullptr) != nullptr; +} + + +/// Rename a register variable. +/// \param pfn function in question +/// \param v variable to rename +/// \param user new user-defined name for the register +/// \return \ref REGVAR_ERROR_ + +idaman int ida_export rename_regvar(func_t *pfn, regvar_t *v, const char *user); + + +/// Set comment for a register variable. +/// \param pfn function in question +/// \param v variable to rename +/// \param cmt new comment +/// \return \ref REGVAR_ERROR_ + +idaman int ida_export set_regvar_cmt(func_t *pfn, regvar_t *v, const char *cmt); + + +/// Delete a register variable definition. +/// \param pfn function in question +/// \param ea1,ea2 range of addresses within the function where the definition holds +/// \param canon name of a general register +/// \return \ref REGVAR_ERROR_ + +idaman int ida_export del_regvar(func_t *pfn, ea_t ea1, ea_t ea2, const char *canon); + +///@} regvar + +//-------------------------------------------------------------------------- +// S P R E G I S T E R C H A N G E P O I N T S +//-------------------------------------------------------------------------- + +/// Add automatic SP register change point. +/// \param pfn pointer to the function. may be nullptr. +/// \param ea linear address where SP changes. +/// usually this is the end of the instruction which +/// modifies the stack pointer (\cmd{ea}+\cmd{size}) +/// \param delta difference between old and new values of SP +/// \return success + +idaman bool ida_export add_auto_stkpnt(func_t *pfn, ea_t ea, sval_t delta); + + +/// Add user-defined SP register change point. +/// \param ea linear address where SP changes +/// \param delta difference between old and new values of SP +/// \return success + +idaman bool ida_export add_user_stkpnt(ea_t ea, sval_t delta); + + +/// Delete SP register change point. +/// \param pfn pointer to the function. may be nullptr. +/// \param ea linear address +/// \return success + +idaman bool ida_export del_stkpnt(func_t *pfn, ea_t ea); + + +/// Get difference between the initial and current values of ESP. +/// \param pfn pointer to the function. may be nullptr. +/// \param ea linear address of the instruction +/// \return 0 or the difference, usually a negative number. +/// returns the sp-diff before executing the instruction. + +idaman sval_t ida_export get_spd(func_t *pfn, ea_t ea); + + +/// Get effective difference between the initial and current values of ESP. +/// This function returns the sp-diff used by the instruction. +/// The difference between get_spd() and get_effective_spd() is present only +/// for instructions like "pop [esp+N]": they modify sp and use the modified value. +/// \param pfn pointer to the function. may be nullptr. +/// \param ea linear address +/// \return 0 or the difference, usually a negative number + +idaman sval_t ida_export get_effective_spd(func_t *pfn, ea_t ea); + + +/// Get modification of SP made at the specified location +/// \param pfn pointer to the function. may be nullptr. +/// \param ea linear address +/// \return 0 if the specified location doesn't contain a SP change point. +/// otherwise return delta of SP modification. + +idaman sval_t ida_export get_sp_delta(func_t *pfn, ea_t ea); + + +/// Add such an automatic SP register change point so that at EA the new +/// cumulative SP delta (that is, the difference between the initial and +/// current values of SP) would be equal to NEW_SPD. +/// \param pfn pointer to the function. may be nullptr. +/// \param ea linear address of the instruction +/// \param new_spd new value of the cumulative SP delta +/// \return success + +idaman bool ida_export set_auto_spd(func_t *pfn, ea_t ea, sval_t new_spd); + + +/// Recalculate SP delta for an instruction that stops execution. +/// The next instruction is not reached from the current instruction. +/// We need to recalculate SP for the next instruction. +/// +/// This function will create a new automatic SP register change +/// point if necessary. It should be called from the emulator (emu.cpp) +/// when auto_state == ::AU_USED if the current instruction doesn't pass +/// the execution flow to the next instruction. +/// \param cur_ea linear address of the current instruction +/// \retval 1 new stkpnt is added +/// \retval 0 nothing is changed + +idaman bool ida_export recalc_spd(ea_t cur_ea); + + +/// Recalculate SP delta for the current instruction. +/// The typical code snippet to calculate SP delta in a proc module is: +/// +///
+/// if ( may_trace_sp() && pfn != nullptr )
+///   if ( !recalc_spd_for_basic_block(pfn, insn.ea) )
+///     trace_sp(pfn, insn);
+/// 
+/// +/// where trace_sp() is a typical name for a function +/// that emulates the SP change of an instruction. +/// +/// \param pfn pointer to the function +/// \param cur_ea linear address of the current instruction +/// \retval true the cumulative SP delta is set +/// \retval false the instruction at CUR_EA passes flow to the next +/// instruction. SP delta must be set as a result of +/// emulating the current instruction. + +idaman bool ida_export recalc_spd_for_basic_block(func_t *pfn, ea_t cur_ea); + + +/// An xref to an argument or variable located in a function's stack frame +struct xreflist_entry_t +{ + ea_t ea; ///< Location of the insn referencing the stack frame member + uchar opnum; ///< Number of the operand of that instruction + uchar type; ///< The type of xref (::cref_t & ::dref_t) + + DECLARE_COMPARISONS(xreflist_entry_t) + { + int code = ::compare(ea, r.ea); + if ( code == 0 ) + { + code = ::compare(type, r.type); + if ( code == 0 ) + code = ::compare(opnum, r.opnum); + } + return code; + } +}; +DECLARE_TYPE_AS_MOVABLE(xreflist_entry_t); +typedef qvector xreflist_t; ///< vector of xrefs to variables in a function's stack frame + +/// Fill 'out' with a list of all the xrefs made from function 'pfn' to +/// specified range of the pfn's stack frame. +/// \param out the list of xrefs to fill. +/// \param pfn the function to scan. +/// \param start_offset start frame structure offset, in bytes +/// \param end_offset end frame structure offset, in bytes + +idaman void ida_export build_stkvar_xrefs(xreflist_t *out, func_t *pfn, uval_t start_offset, uval_t end_offset); + + +#endif // _FRAME_HPP + +``` + +`IdaSDK/funcs.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef FUNCS_HPP +#define FUNCS_HPP +#include +#include + +/*! \file funcs.hpp + + \brief Routines for working with functions within the disassembled program. + + This file also contains routines for working with library signatures + (e.g. FLIRT). + + Each function consists of function chunks. At least one function chunk + must be present in the function definition - the function entry chunk. + Other chunks are called function tails. There may be several of them + for a function. + + A function tail is a continuous range of addresses. + It can be used in the definition of one or more functions. + One function using the tail is singled out and called the tail owner. + This function is considered as 'possessing' the tail. + get_func() on a tail address will return the function possessing the tail. + You can enumerate the functions using the tail by using ::func_parent_iterator_t. + + Each function chunk in the disassembly is represented as an "range" (a range + of addresses, see range.hpp for details) with characteristics. + + A function entry must start with an instruction (code) byte. +*/ + +struct stkpnt_t; // #include +struct regvar_t; // #include +struct llabel_t; // #include +class insn_t; // #include + +idaman void ida_export free_regarg(struct regarg_t *v); + +/// Register argument description. +/// regargs are destroyed when the full function type is determined. +struct regarg_t +{ + int reg = 0; + type_t *type = nullptr; + char *name = nullptr; + + regarg_t() {} + regarg_t(const regarg_t &r) : reg(r.reg) + { + type = (type_t *)::qstrdup((char *)r.type); + name = ::qstrdup(r.name); + } + ~regarg_t() { free_regarg(this); } + regarg_t &operator=(const regarg_t &r) + { + if ( this != &r ) + { + free_regarg(this); + new (this) regarg_t(r); + } + return *this; + } + void swap(regarg_t &r) + { + std::swap(reg, r.reg); + std::swap(type, r.type); + std::swap(name, r.name); + } +#ifndef SWIG + DECLARE_COMPARISONS(regarg_t); +#endif + +}; +DECLARE_TYPE_AS_MOVABLE(regarg_t); + +//------------------------------------------------------------------------ +/// A function is a set of continuous ranges of addresses with characteristics +class func_t : public range_t +{ +public: + uint64 flags; ///< \ref FUNC_ +/// \defgroup FUNC_ Function flags +/// Used by func_t::flags +///@{ +#define FUNC_NORET 0x00000001 ///< Function doesn't return +#define FUNC_FAR 0x00000002 ///< Far function +#define FUNC_LIB 0x00000004 ///< Library function + +#define FUNC_STATICDEF 0x00000008 ///< Static function + +#define FUNC_FRAME 0x00000010 ///< Function uses frame pointer (BP) +#define FUNC_USERFAR 0x00000020 ///< User has specified far-ness + ///< of the function +#define FUNC_HIDDEN 0x00000040 ///< A hidden function chunk +#define FUNC_THUNK 0x00000080 ///< Thunk (jump) function +#define FUNC_BOTTOMBP 0x00000100 ///< BP points to the bottom of the stack frame +#define FUNC_NORET_PENDING 0x00200 ///< Function 'non-return' analysis must be performed. + ///< This flag is verified upon func_does_return() +#define FUNC_SP_READY 0x00000400 ///< SP-analysis has been performed. + ///< If this flag is on, the stack + ///< change points should not be not + ///< modified anymore. Currently this + ///< analysis is performed only for PC +#define FUNC_FUZZY_SP 0x00000800 ///< Function changes SP in untraceable way, + ///< for example: and esp, 0FFFFFFF0h +#define FUNC_PROLOG_OK 0x00001000 ///< Prolog analysis has been performed + ///< by last SP-analysis +#define FUNC_PURGED_OK 0x00004000 ///< 'argsize' field has been validated. + ///< If this bit is clear and 'argsize' + ///< is 0, then we do not known the real + ///< number of bytes removed from + ///< the stack. This bit is handled + ///< by the processor module. +#define FUNC_TAIL 0x00008000 ///< This is a function tail. + ///< Other bits must be clear + ///< (except #FUNC_HIDDEN). +#define FUNC_LUMINA 0x00010000 ///< Function info is provided by Lumina. +#define FUNC_OUTLINE 0x00020000 ///< Outlined code, not a real function. +#define FUNC_REANALYZE 0x00040000 ///< Function frame changed, request to + ///< reanalyze the function after the last + ///< insn is analyzed. +#define FUNC_UNWIND 0x00080000 ///< function is an exception unwind handler +#define FUNC_CATCH 0x00100000 ///< function is an exception catch handler + +#define FUNC_RESERVED 0x8000000000000000LL ///< Reserved (for internal usage) +///@} + + /// Is a far function? + bool is_far(void) const { return (flags & FUNC_FAR) != 0; } + /// Does function return? + bool does_return(void) const { return (flags & FUNC_NORET) == 0; } + /// Has SP-analysis been performed? + bool analyzed_sp(void) const { return (flags & FUNC_SP_READY) != 0; } + /// Needs prolog analysis? + bool need_prolog_analysis(void) const { return (flags & FUNC_PROLOG_OK) == 0; } +#ifndef SWIG + union + { + /// attributes of a function entry chunk + struct + { +#endif // SWIG + // + // Stack frame of the function. It is represented as a structure: + // + // +------------------------------------------------+ + // | function arguments | + // +------------------------------------------------+ + // | return address (isn't stored in func_t) | + // +------------------------------------------------+ + // | saved registers (SI, DI, etc - func_t::frregs) | + // +------------------------------------------------+ <- typical BP + // | | | + // | | | func_t::fpd + // | | | + // | | <- real BP + // | local variables (func_t::frsize) | + // | | + // | | + // +------------------------------------------------+ <- SP + // + uval_t frame; ///< netnode id of frame structure - see frame.hpp + asize_t frsize; ///< size of local variables part of frame in bytes. + ///< If #FUNC_FRAME is set and #fpd==0, the frame pointer + ///< (EBP) is assumed to point to the top of the local + ///< variables range. + ushort frregs; ///< size of saved registers in frame. This range is + ///< immediately above the local variables range. + asize_t argsize; ///< number of bytes purged from the stack + ///< upon returning + asize_t fpd; ///< frame pointer delta. (usually 0, i.e. realBP==typicalBP) + ///< use update_fpd() to modify it. + + bgcolor_t color; ///< user defined function color + + // the following fields should not be accessed directly: + + uint32 pntqty; ///< number of SP change points + stkpnt_t *points; ///< array of SP change points. + ///< use ...stkpnt...() functions to access this array. + + int regvarqty; ///< number of register variables (-1-not read in yet) + ///< use find_regvar() to read register variables + regvar_t *regvars; ///< array of register variables. + ///< this array is sorted by: start_ea. + ///< use ...regvar...() functions to access this array. + + int llabelqty; ///< number of local labels + llabel_t *llabels; ///< local labels array. + ///< this array shouldn't be modified directly; name.hpp's + ///< SN_LOCAL should be used instead. + + int regargqty; ///< number of register arguments. + ///< During analysis IDA tries to guess the register + ///< arguments. It stores store the guessing outcome + ///< in this field. As soon as it determines the final + ///< function prototype, regargqty is set to zero. + regarg_t *regargs; ///< unsorted array of register arguments. + ///< use ...regarg...() functions to access this array. + ///< regargs are destroyed when the full function + ///< type is determined. + + int tailqty; ///< number of function tails + range_t *tails; ///< array of tails, sorted by ea. + ///< use func_tail_iterator_t to access function tails. +#ifndef SWIG + }; + /// attributes of a function tail chunk + struct + { +#endif // SWIG + ea_t owner; ///< the address of the main function possessing this tail + int refqty; ///< number of referers + ea_t *referers; ///< array of referers (function start addresses). + ///< use func_parent_iterator_t to access the referers. +#ifndef SWIG + }; + }; +#endif // SWIG + + func_t(ea_t start=0, ea_t end=0, flags64_t f=0) + : range_t(start, end), flags(f|FUNC_NORET_PENDING), frame(BADNODE), + frsize(0), frregs(0), argsize(0), fpd(0), color(DEFCOLOR), + pntqty(0), points(nullptr), + regvarqty(0), regvars(nullptr), + llabelqty(0), llabels(nullptr), + regargqty(0), regargs(nullptr), + tailqty(0), tails(nullptr) + { + } +#ifndef SWIG + DECLARE_COMPARISONS(func_t); +#endif +}; +DECLARE_TYPE_AS_MOVABLE(func_t); + +/// Does function describe a function entry chunk? +inline bool is_func_entry(const func_t *pfn) { return pfn != nullptr && (pfn->flags & FUNC_TAIL) == 0; } +/// Does function describe a function tail chunk? +inline bool is_func_tail(const func_t *pfn) { return pfn != nullptr && (pfn->flags & FUNC_TAIL) != 0; } + + +/// Lock function pointer +/// Locked pointers are guaranteed to remain valid until they are unlocked. +/// Ranges with locked pointers cannot be deleted or moved. + +idaman void ida_export lock_func_range(const func_t *pfn, bool lock); + +/// Helper class to lock a function pointer so it stays valid +class lock_func +{ + const func_t *pfn; +public: + lock_func(const func_t *_pfn) : pfn(_pfn) + { + lock_func_range(pfn, true); + } + ~lock_func(void) + { + lock_func_range(pfn, false); + } +}; + +//lint -esym(1788, lock_func_with_tails_t) referenced only by ctr/dtr +class lock_func_with_tails_t +{ + rangeset_t func_ranges; + void lock_ranges(bool lock); + +public: + lock_func_with_tails_t(func_t *pfn); + ~lock_func_with_tails_t() { lock_ranges(false); } +}; + +/// Is the function pointer locked? + +idaman bool ida_export is_func_locked(const func_t *pfn); + +//-------------------------------------------------------------------- +// F U N C T I O N S +//-------------------------------------------------------------------- +/// Get pointer to function structure by address. +/// \param ea any address in a function +/// \return ptr to a function or nullptr. +/// This function returns a function entry chunk. + +idaman func_t *ida_export get_func(ea_t ea); + + +/// Get the containing tail chunk of 'ea'. +/// \retval -1 means 'does not contain ea' +/// \retval 0 means the 'pfn' itself contains ea +/// \retval >0 the number of the containing function tail chunk + +idaman int ida_export get_func_chunknum(func_t *pfn, ea_t ea); + +/// Does the given function contain the given address? + +inline bool func_contains(func_t *pfn, ea_t ea) +{ + return get_func_chunknum(pfn, ea) >= 0; +} + +/// Do two addresses belong to the same function? +inline bool is_same_func(ea_t ea1, ea_t ea2) +{ + func_t *pfn = get_func(ea1); + return pfn != nullptr && func_contains(pfn, ea2); +} + +/// Get pointer to function structure by number. +/// \param n number of function, is in range 0..get_func_qty()-1 +/// \return ptr to a function or nullptr. +/// This function returns a function entry chunk. + +idaman func_t *ida_export getn_func(size_t n); + + +/// Get total number of functions in the program + +idaman size_t ida_export get_func_qty(void); + + +/// Get ordinal number of a function. +/// \param ea any address in the function +/// \return number of function (0..get_func_qty()-1). +/// -1 means 'no function at the specified address'. + +idaman int ida_export get_func_num(ea_t ea); + + +/// Get pointer to the previous function. +/// \param ea any address in the program +/// \return ptr to function or nullptr if previous function doesn't exist + +idaman func_t *ida_export get_prev_func(ea_t ea); + + +/// Get pointer to the next function. +/// \param ea any address in the program +/// \return ptr to function or nullptr if next function doesn't exist + +idaman func_t *ida_export get_next_func(ea_t ea); + + +/// Get function ranges. +/// \param ranges buffer to receive the range info +/// \param pfn ptr to function structure +/// \return end address of the last function range (BADADDR-error) + +idaman ea_t ida_export get_func_ranges(rangeset_t *ranges, func_t *pfn); + + +/// Get function comment. +/// \param buf buffer for the comment +/// \param pfn ptr to function structure +/// \param repeatable get repeatable comment? +/// \return size of comment or -1 +/// In fact this function works with function chunks too. + +idaman ssize_t ida_export get_func_cmt(qstring *buf, const func_t *pfn, bool repeatable); + + +/// Set function comment. +/// This function works with function chunks too. +/// \param pfn ptr to function structure +/// \param cmt comment string, may be multiline (with '\n'). +/// Use empty str ("") to delete comment +/// \param repeatable set repeatable comment? + +idaman bool ida_export set_func_cmt(const func_t *pfn, const char *cmt, bool repeatable); + + +/// Update information about a function in the database (::func_t). +/// You must not change the function start and end addresses using this function. +/// Use set_func_start() and set_func_end() for it. +/// \param pfn ptr to function structure +/// \return success + +idaman bool ida_export update_func(func_t *pfn); + + +/// Add a new function. +/// If the fn->end_ea is #BADADDR, then IDA will try to determine the +/// function bounds by calling find_func_bounds(..., #FIND_FUNC_DEFINE). +/// \param pfn ptr to filled function structure +/// \return success + +idaman bool ida_export add_func_ex(func_t *pfn); + + +/// Add a new function. +/// If the function end address is #BADADDR, then IDA will try to determine +/// the function bounds by calling find_func_bounds(..., #FIND_FUNC_DEFINE). +/// \param ea1 start address +/// \param ea2 end address +/// \return success + +inline bool add_func(ea_t ea1, ea_t ea2=BADADDR) +{ + func_t fn(ea1, ea2); + return add_func_ex(&fn); +} + + +/// Delete a function. +/// \param ea any address in the function entry chunk +/// \return success + +idaman bool ida_export del_func(ea_t ea); + + +/// Move function chunk start address. +/// \param ea any address in the function +/// \param newstart new end address of the function +/// \return \ref MOVE_FUNC_ + +idaman int ida_export set_func_start(ea_t ea, ea_t newstart); +/// \defgroup MOVE_FUNC_ Function move result codes +/// Return values for set_func_start() +///@{ +#define MOVE_FUNC_OK 0 ///< ok +#define MOVE_FUNC_NOCODE 1 ///< no instruction at 'newstart' +#define MOVE_FUNC_BADSTART 2 ///< bad new start address +#define MOVE_FUNC_NOFUNC 3 ///< no function at 'ea' +#define MOVE_FUNC_REFUSED 4 ///< a plugin refused the action +///@} + + +/// Move function chunk end address. +/// \param ea any address in the function +/// \param newend new end address of the function +/// \return success + +idaman bool ida_export set_func_end(ea_t ea, ea_t newend); + + +/// Reanalyze a function. +/// This function plans to analyzes all chunks of the given function. +/// Optional parameters (ea1, ea2) may be used to narrow the analyzed range. +/// \param pfn pointer to a function +/// \param ea1 start of the range to analyze +/// \param ea2 end of range to analyze +/// \param analyze_parents meaningful only if pfn points to a function tail. +/// if true, all tail parents will be reanalyzed. +/// if false, only the given tail will be reanalyzed. + +idaman void ida_export reanalyze_function( + func_t *pfn, + ea_t ea1=0, + ea_t ea2=BADADDR, + bool analyze_parents=false); + + +/// Determine the boundaries of a new function. +/// This function tries to find the start and end addresses of a new function. +/// It calls the module with \ph{func_bounds} in order to fine tune +/// the function boundaries. +/// \param nfn structure to fill with information +/// \ nfn->start_ea points to the start address of the new function. +/// \param flags \ref FIND_FUNC_F +/// \return \ref FIND_FUNC_R + +idaman int ida_export find_func_bounds(func_t *nfn, int flags); + +/// \defgroup FIND_FUNC_F Find function bounds flags +/// Passed as 'flags' parameter to find_func_bounds() +///@{ +#define FIND_FUNC_NORMAL 0x0000 ///< stop processing if undefined byte is encountered +#define FIND_FUNC_DEFINE 0x0001 ///< create instruction if undefined byte is encountered +#define FIND_FUNC_IGNOREFN 0x0002 ///< ignore existing function boundaries. + ///< by default the function returns function boundaries + ///< if ea belongs to a function. +#define FIND_FUNC_KEEPBD 0x0004 ///< do not modify incoming function boundaries, + ///< just create instructions inside the boundaries. +///@} + +/// \defgroup FIND_FUNC_R Find function bounds result codes +/// Return values for find_func_bounds() +///@{ +#define FIND_FUNC_UNDEF 0 ///< function has instructions that pass execution flow to unexplored bytes. + ///< nfn->end_ea will have the address of the unexplored byte. +#define FIND_FUNC_OK 1 ///< ok, 'nfn' is ready for add_func() +#define FIND_FUNC_EXIST 2 ///< function exists already. + ///< its bounds are returned in 'nfn'. +///@} + + +/// Get function name. +/// \param out buffer for the answer +/// \param ea any address in the function +/// \return length of the function name + +idaman ssize_t ida_export get_func_name(qstring *out, ea_t ea); + + +/// Calculate function size. +/// This function takes into account all fragments of the function. +/// \param pfn ptr to function structure + +idaman asize_t ida_export calc_func_size(func_t *pfn); + + +/// Get function bitness (which is equal to the function segment bitness). +/// pfn==nullptr => returns 0 +/// \retval 0 16 +/// \retval 1 32 +/// \retval 2 64 + +idaman int ida_export get_func_bitness(const func_t *pfn); + +/// Get number of bits in the function addressing +inline int idaapi get_func_bits(const func_t *pfn) { return 1 << (get_func_bitness(pfn)+4); } + +/// Get number of bytes in the function addressing +inline int idaapi get_func_bytes(const func_t *pfn) { return get_func_bits(pfn)/8; } + + +/// Is the function visible (not hidden)? + +inline bool is_visible_func(func_t *pfn) { return pfn != nullptr && (pfn->flags & FUNC_HIDDEN) == 0; } + +/// Is the function visible (event after considering #SCF_SHHID_FUNC)? +inline bool is_finally_visible_func(func_t *pfn) +{ + return (inf_get_cmtflg() & SCF_SHHID_FUNC) != 0 || is_visible_func(pfn); +} + +/// Set visibility of function + +idaman void ida_export set_visible_func(func_t *pfn, bool visible); + + +/// Give a meaningful name to function if it consists of only 'jump' instruction. +/// \param pfn pointer to function (may be nullptr) +/// \param oldname old name of function. +/// if old name was in "j_..." form, then we may discard it +/// and set a new name. +/// if oldname is not known, you may pass nullptr. +/// \return success + +idaman int ida_export set_func_name_if_jumpfunc(func_t *pfn, const char *oldname); + + +/// Calculate target of a thunk function. +/// \param pfn pointer to function (may not be nullptr) +/// \param fptr out: will hold address of a function pointer (if indirect jump) +/// \return the target function or #BADADDR + +idaman ea_t ida_export calc_thunk_func_target(func_t *pfn, ea_t *fptr); + + +/// Does the function return?. +/// To calculate the answer, #FUNC_NORET flag and is_noret() are consulted +/// The latter is required for imported functions in the .idata section. +/// Since in .idata we have only function pointers but not functions, we have +/// to introduce a special flag for them. + +idaman bool ida_export func_does_return(ea_t callee); + + +/// Plan to reanalyze noret flag. +/// This function does not remove FUNC_NORET if it is already present. +/// It just plans to reanalysis. + +idaman bool ida_export reanalyze_noret_flag(ea_t ea); + + +/// Signal a non-returning instruction. +/// This function can be used by the processor module to tell the kernel +/// about non-returning instructions (like call exit). The kernel will +/// perform the global function analysis and find out if the function +/// returns at all. This analysis will be done at the first call to func_does_return() +/// \return true if the instruction 'noret' flag has been changed + +idaman bool ida_export set_noret_insn(ea_t insn_ea, bool noret); + + +//-------------------------------------------------------------------- +// F U N C T I O N C H U N K S +//-------------------------------------------------------------------- +/// Get pointer to function chunk structure by address. +/// \param ea any address in a function chunk +/// \return ptr to a function chunk or nullptr. +/// This function may return a function entry as well as a function tail. + +idaman func_t *ida_export get_fchunk(ea_t ea); + + +/// Get pointer to function chunk structure by number. +/// \param n number of function chunk, is in range 0..get_fchunk_qty()-1 +/// \return ptr to a function chunk or nullptr. +/// This function may return a function entry as well as a function tail. + +idaman func_t *ida_export getn_fchunk(int n); + + +/// Get total number of function chunks in the program + +idaman size_t ida_export get_fchunk_qty(void); + + +/// Get ordinal number of a function chunk in the global list of function chunks. +/// \param ea any address in the function chunk +/// \return number of function chunk (0..get_fchunk_qty()-1). +/// -1 means 'no function chunk at the specified address'. + +idaman int ida_export get_fchunk_num(ea_t ea); + + +/// Get pointer to the previous function chunk in the global list. +/// \param ea any address in the program +/// \return ptr to function chunk or nullptr if previous function chunk doesn't exist + +idaman func_t *ida_export get_prev_fchunk(ea_t ea); + + +/// Get pointer to the next function chunk in the global list. +/// \param ea any address in the program +/// \return ptr to function chunk or nullptr if next function chunk doesn't exist + +idaman func_t *ida_export get_next_fchunk(ea_t ea); + + +//-------------------------------------------------------------------- +// Functions to manipulate function chunks + +/// Append a new tail chunk to the function definition. +/// If the tail already exists, then it will simply be added to the function tail list +/// Otherwise a new tail will be created and its owner will be set to be our function +/// If a new tail cannot be created, then this function will fail. +/// \param pfn pointer to the function +/// \param ea1 start of the tail. If a tail already exists at the specified address +/// it must start at 'ea1' +/// \param ea2 end of the tail. If a tail already exists at the specified address +/// it must end at 'ea2'. If specified as BADADDR, IDA will determine +/// the end address itself. + +idaman bool ida_export append_func_tail(func_t *pfn, ea_t ea1, ea_t ea2); + + +/// Remove a function tail. +/// If the tail belongs only to one function, it will be completely removed. +/// Otherwise if the function was the tail owner, the first function using +/// this tail becomes the owner of the tail. +/// \param pfn pointer to the function +/// \param tail_ea any address inside the tail to remove + +idaman bool ida_export remove_func_tail(func_t *pfn, ea_t tail_ea); + + +/// Set a new owner of a function tail. +/// The new owner function must be already referring to the tail (after append_func_tail). +/// \param fnt pointer to the function tail +/// \param new_owner the entry point of the new owner function + +idaman bool ida_export set_tail_owner(func_t *fnt, ea_t new_owner); + + +// Auxiliary function(s) to be used in func_..._iterator_t + +class func_parent_iterator_t; +class func_tail_iterator_t; +class func_item_iterator_t; + +/// Declare helper functions for ::func_item_iterator_t +#define DECLARE_FUNC_ITERATORS(prefix) \ +prefix bool ida_export func_tail_iterator_set(func_tail_iterator_t *fti, func_t *pfn, ea_t ea);\ +prefix bool ida_export func_tail_iterator_set_ea(func_tail_iterator_t *fti, ea_t ea);\ +prefix bool ida_export func_parent_iterator_set(func_parent_iterator_t *fpi, func_t *pfn);\ +prefix bool ida_export func_item_iterator_next(func_item_iterator_t *fii, testf_t *testf, void *ud);\ +prefix bool ida_export func_item_iterator_prev(func_item_iterator_t *fii, testf_t *testf, void *ud);\ +prefix bool ida_export func_item_iterator_decode_prev_insn(func_item_iterator_t *fii, insn_t *out); \ +prefix bool ida_export func_item_iterator_decode_preceding_insn(func_item_iterator_t *fii, eavec_t *visited, bool *p_farref, insn_t *out); \ +prefix bool ida_export func_item_iterator_succ(func_item_iterator_t *fii, testf_t *testf, void *ud); +DECLARE_FUNC_ITERATORS(idaman) + +/// Helper function to accept any address +inline THREAD_SAFE bool idaapi f_any(flags64_t, void *) { return true; } + +/// Class to enumerate all function tails sorted by addresses. +/// Enumeration is started with main(), first(), or last(). +/// If first() is used, the function entry chunk will be excluded from the enumeration. +/// Otherwise it will be included in the enumeration (for main() and last()). +/// The loop may continue until the next() or prev() function returns false. +/// These functions return false when the enumeration is over. +/// The tail chunks are always sorted by their addresses. +/// +/// Sample code: +/// \code +/// func_tail_iterator_t fti(pfn); +/// for ( bool ok=fti.first(); ok; ok=fti.next() ) +/// const range_t &a = fti.chunk(); +/// .... +/// \endcode +/// +/// If the 'ea' parameter is used in the constructor, then the iterator is positioned +/// at the chunk containing the specified 'ea'. Otherwise it is positioned at the +/// function entry chunk. +/// If 'pfn' is specified as nullptr then the set() function will fail, +/// but it is still possible to use the class. In this case the iteration will be +/// limited by the segment boundaries. +/// The function main chunk is locked during the iteration. +/// It is also possible to enumerate one single arbitrary range using set_range() +/// This function is mainly designed to be used from ::func_item_iterator_t. +class func_tail_iterator_t +{ + func_t *pfn; + int idx; + range_t seglim; // valid and used only if pfn == nullptr +public: + func_tail_iterator_t(void) : pfn(nullptr), idx(-1) {} + func_tail_iterator_t(func_t *_pfn, ea_t ea=BADADDR) : pfn(nullptr) { set(_pfn, ea); } + ~func_tail_iterator_t(void) + { + // if was iterating over function chunks, unlock the main chunk + if ( pfn != nullptr ) + lock_func_range(pfn, false); + } + bool set(func_t *_pfn, ea_t ea=BADADDR) { return func_tail_iterator_set(this, _pfn, ea); } + bool set_ea(ea_t ea) { return func_tail_iterator_set_ea(this, ea); } + // set an arbitrary range + bool set_range(ea_t ea1, ea_t ea2) + { + this->~func_tail_iterator_t(); + pfn = nullptr; + idx = -1; + seglim = range_t(ea1, ea2); + return !seglim.empty(); + } + const range_t &chunk(void) const + { + if ( pfn == nullptr ) + return seglim; + return idx >= 0 && idx < pfn->tailqty ? pfn->tails[idx] : *(range_t*)pfn; + } + bool first(void) { if ( pfn != nullptr ) { idx = 0; return pfn->tailqty > 0; } return false; } // get only tail chunks + bool last(void) { if ( pfn != nullptr ) { idx = pfn->tailqty - 1; return true; } return false; } // get all chunks (the entry chunk last) + bool next(void) { if ( pfn != nullptr && idx+1 < pfn->tailqty ) { idx++; return true; } return false; } + bool prev(void) { if ( idx >= 0 ) { idx--; return true; } return false; } + bool main(void) { idx = -1; return pfn != nullptr; } // get all chunks (the entry chunk first) +}; + + +/// Function to iterate function chunks (all of them including the entry chunk) +/// \param pfn pointer to the function +/// \param func function to call for each chunk +/// \param ud user data for 'func' +/// \param include_parents meaningful only if pfn points to a function tail. +/// if true, all tail parents will be iterated. +/// if false, only the given tail will be iterated. + +idaman void ida_export iterate_func_chunks( + func_t *pfn, + void (idaapi *func)(ea_t ea1, ea_t ea2, void *ud), + void *ud=nullptr, + bool include_parents=false); + + +/// Class to enumerate all function instructions and data sorted by addresses. +/// The function entry chunk items are enumerated first regardless of their addresses +/// +/// Sample code: +/// \code +/// func_item_iterator_t fii; +/// for ( bool ok=fii.set(pfn, ea); ok; ok=fii.next_addr() ) +/// ea_t ea = fii.current(); +/// .... +/// \endcode +/// +/// If 'ea' is not specified in the call to set(), then the enumeration starts at +/// the function entry point. +/// If 'pfn' is specified as nullptr then the set() function will fail, +/// but it is still possible to use the class. In this case the iteration will be +/// limited by the segment boundaries. +/// It is also possible to enumerate addresses in an arbitrary range using set_range(). +class func_item_iterator_t +{ + func_tail_iterator_t fti; + ea_t ea; +public: + func_item_iterator_t(void) : ea(BADADDR) {} + func_item_iterator_t(func_t *pfn, ea_t _ea=BADADDR) { set(pfn, _ea); } + /// Set a function range. if pfn == nullptr then a segment range will be set. + bool set(func_t *pfn, ea_t _ea=BADADDR) + { + ea = (_ea != BADADDR || pfn == nullptr) ? _ea : pfn->start_ea; + return fti.set(pfn, _ea); + } + /// Set an arbitrary range + bool set_range(ea_t ea1, ea_t ea2) { ea = ea1; return fti.set_range(ea1, ea2); } + bool first(void) { if ( !fti.main() ) return false; ea=fti.chunk().start_ea; return true; } + bool last(void) { if ( !fti.last() ) return false; ea=fti.chunk().end_ea; return true; } + ea_t current(void) const { return ea; } + bool set_ea(ea_t _ea) + { + if ( !fti.set_ea(_ea) ) + return false; + ea = _ea; + return true; + } + const range_t &chunk(void) const { return fti.chunk(); } + bool next(testf_t *func, void *ud) { return func_item_iterator_next(this, func, ud); } + bool prev(testf_t *func, void *ud) { return func_item_iterator_prev(this, func, ud); } + bool next_addr(void) { return next(f_any, nullptr); } + bool next_head(void) { return next(f_is_head, nullptr); } + bool next_code(void) { return next(f_is_code, nullptr); } + bool next_data(void) { return next(f_is_data, nullptr); } + bool next_not_tail(void) { return next(f_is_not_tail, nullptr); } + bool prev_addr(void) { return prev(f_any, nullptr); } + bool prev_head(void) { return prev(f_is_head, nullptr); } + bool prev_code(void) { return prev(f_is_code, nullptr); } + bool prev_data(void) { return prev(f_is_data, nullptr); } + bool prev_not_tail(void) { return prev(f_is_not_tail, nullptr); } + bool decode_prev_insn(insn_t *out) { return func_item_iterator_decode_prev_insn(this, out); } + bool decode_preceding_insn(eavec_t *visited, bool *p_farref, insn_t *out) + { return func_item_iterator_decode_preceding_insn(this, visited, p_farref, out); } + /// Similar to next(), but succ() iterates the chunks from low to high + /// addresses, while next() iterates through chunks starting at the + /// function entry chunk + bool succ(testf_t *func, void *ud) { return func_item_iterator_succ(this, func, ud); } + bool succ_code(void) { return succ(f_is_code, nullptr); } +}; + +/// Class to enumerate all function parents sorted by addresses. +/// Enumeration is started with first() or last(). +/// The loop may continue until the next() or prev() function returns false. +/// The parent functions are always sorted by their addresses. +/// The tail chunk is locked during the iteration. +/// +/// Sample code: +/// \code +/// func_parent_iterator_t fpi(fnt); +/// for ( bool ok=fpi.first(); ok; ok=fpi.next() ) +/// ea_t parent = fpi.parent(); +/// .... +/// \endcode +class func_parent_iterator_t +{ + func_t *fnt; + int idx; +public: + func_parent_iterator_t(void) : fnt(nullptr), idx(0) {} + func_parent_iterator_t(func_t *_fnt) : fnt(nullptr) { set(_fnt); } + ~func_parent_iterator_t(void) + { + if ( fnt != nullptr ) + lock_func_range(fnt, false); + } + bool set(func_t *_fnt) { return func_parent_iterator_set(this, _fnt); } + ea_t parent(void) const { return fnt->referers[idx]; } + bool first(void) { idx = 0; return is_func_tail(fnt) && fnt->refqty > 0; } + bool last(void) { idx = fnt->refqty - 1; return idx >= 0; } + bool next(void) { if ( idx+1 < fnt->refqty ) { idx++; return true; } return false; } + bool prev(void) { if ( idx > 0 ) { idx--; return true; } return false; } + void reset_fnt(func_t *_fnt) { fnt = _fnt; } // for internal use only! +}; + + +/// \name Get prev/next address in function +/// Unlike func_item_iterator_t which always enumerates the main function +/// chunk first, these functions respect linear address ordering. +///@{ +idaman ea_t ida_export get_prev_func_addr(func_t *pfn, ea_t ea); +idaman ea_t ida_export get_next_func_addr(func_t *pfn, ea_t ea); +///@} + +//-------------------------------------------------------------------- +/// \name +/// Functions to work with temporary register argument definitions +///@{ +idaman void ida_export read_regargs(func_t *pfn); +idaman void ida_export add_regarg(func_t *pfn, int reg, const tinfo_t &tif, const char *name); +///@} + +//-------------------------------------------------------------------- +// L I B R A R Y M O D U L E S I G N A T U R E S +//-------------------------------------------------------------------- + +/// \defgroup IDASGN_ Error codes for signature functions: +/// See calc_idasgn_state() and del_idasgn() +///@{ +#define IDASGN_OK 0 ///< ok +#define IDASGN_BADARG 1 ///< bad number of signature +#define IDASGN_APPLIED 2 ///< signature is already applied +#define IDASGN_CURRENT 3 ///< signature is currently being applied +#define IDASGN_PLANNED 4 ///< signature is planned to be applied +///@} + +/// Add a signature file to the list of planned signature files. +/// \param fname file name. should not contain directory part. +/// \return 0 if failed, otherwise number of planned (and applied) signatures + +idaman int ida_export plan_to_apply_idasgn(const char *fname); // plan to use library + + +/// Apply a signature file to the specified address. +/// \param signame short name of signature file (the file name without path) +/// \param ea address to apply the signature +/// \param is_startup if set, then the signature is treated as a startup one +/// for startup signature ida doesn't rename the first +/// function of the applied module. +/// \return \ref LIBFUNC_ + +idaman int ida_export apply_idasgn_to(const char *signame, ea_t ea, bool is_startup); + + +/// Get number of signatures in the list of planned and applied signatures. +/// \return 0..n + +idaman int ida_export get_idasgn_qty(void); + + +/// Get number of the the current signature. +/// \return 0..n-1 + +idaman int ida_export get_current_idasgn(void); + + +/// Get state of a signature in the list of planned signatures +/// \param n number of signature in the list (0..get_idasgn_qty()-1) +/// \return state of signature or #IDASGN_BADARG + +idaman int ida_export calc_idasgn_state(int n); + + +/// Remove signature from the list of planned signatures. +/// \param n number of signature in the list (0..get_idasgn_qty()-1) +/// \return #IDASGN_OK, #IDASGN_BADARG, #IDASGN_APPLIED + +idaman int ida_export del_idasgn(int n); + + +/// Get information about a signature in the list. +/// \param signame buffer for the name of the signature. +/// (short form, only base name without the directory part +/// will be stored). +/// if signame == nullptr, then the name won't be returned. +/// \param optlibs buffer for the names of the optional libraries +/// if optlibs == nullptr, then the optional libraries are not returned +/// \param n number of signature in the list (0..get_idasgn_qty()-1) +/// \return number of successfully recognized modules using this signature. +/// -1 means the 'n' is a bad argument, i.e. no signature with this +/// number exists.. + +idaman int32 ida_export get_idasgn_desc( + qstring *signame, + qstring *optlibs, + int n); + +/// Get full description of the signature by its short name. +/// \param buf the output buffer +/// \param name short name of a signature +/// \return size of signature description or -1 + +idaman ssize_t ida_export get_idasgn_title( + qstring *buf, + const char *name); + +/// Determine compiler/vendor using the startup signatures. +/// If determined, then appropriate signature files are included into +/// the list of planned signature files. + +idaman void ida_export determine_rtl(void); + + +/// Apply a startup signature file to the specified address. +/// \param ea address to apply the signature to; usually \inf{start_ea} +/// \param startup the name of the signature file without path and extension +/// \return true if successfully applied the signature + +idaman bool ida_export apply_startup_sig(ea_t ea, const char *startup); + + +/// Apply the currently loaded signature file to the specified address. +/// If a library function is found, then create a function and name +/// it accordingly. +/// \param ea any address in the program +/// \return \ref LIBFUNC_ + +idaman int ida_export try_to_add_libfunc(ea_t ea); + + +/// \defgroup LIBFUNC_ Library function codes +/// Return values for try_to_add_libfunc() and apply_idasgn_to() +///@{ +#define LIBFUNC_FOUND 0 ///< ok, library function is found +#define LIBFUNC_NONE 1 ///< no, this is not a library function +#define LIBFUNC_DELAY 2 ///< no decision because of lack of information +///@} + +inline lock_func_with_tails_t::lock_func_with_tails_t(func_t *pfn) +{ + get_func_ranges(&func_ranges, pfn); + lock_ranges(true); +} + +inline void lock_func_with_tails_t::lock_ranges(bool lock) +{ + for ( const auto &r : func_ranges ) + lock_func_range(get_fchunk(r.start_ea), lock); +} + + +#endif + +``` + +`IdaSDK/gdl.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + * + * Graph drawing support + * + */ + +#ifndef __GDLDRAW_HPP +#define __GDLDRAW_HPP + +#include + +/*! \file gdl.hpp + + \brief Low level graph drawing operations + +*/ + +//------------------------------------------------------------------------- +// forward declarations: +class node_iterator; +class qflow_chart_t; +class gdl_graph_t; + +/// Flow chart block types +enum fc_block_type_t +{ + fcb_normal, ///< normal block + fcb_indjump, ///< block ends with indirect jump + fcb_ret, ///< return block + fcb_cndret, ///< conditional return block + fcb_noret, ///< noreturn block + fcb_enoret, ///< external noreturn block (does not belong to the function) + fcb_extern, ///< external normal block + fcb_error, ///< block passes execution past the function end +}; + +#ifndef SWIG +#define DECLARE_HELPER(decl) \ +decl node_iterator *ida_export node_iterator_goup(node_iterator *); \ +decl void ida_export create_qflow_chart(qflow_chart_t &); \ +decl bool ida_export append_to_flowchart(qflow_chart_t &, ea_t, ea_t); \ +decl fc_block_type_t ida_export fc_calc_block_type(const qflow_chart_t &, size_t); \ +decl bool ida_export create_multirange_qflow_chart(qflow_chart_t &, const rangevec_t &); +#else +#define DECLARE_HELPER(decl) +#endif // SWIG + +DECLARE_HELPER(idaman) + +//------------------------------------------------------------------------- +/// Set of integer constants +class intset_t : public std::set +{ +public: + DEFINE_MEMORY_ALLOCATION_FUNCS() + size_t idaapi print(char *buf, size_t bufsize) const; + const char *idaapi dstr(void) const; + bool has(int value) const + { + const_iterator p = find(value); + const_iterator q = end(); + return p != q; + } +}; + +typedef qvector array_of_intvec_t; + +/// Map of integer constants to integer constants +class intmap_t : public std::map +{ +public: + DEFINE_MEMORY_ALLOCATION_FUNCS() + size_t idaapi print(char *buf, size_t bufsize) const; + const char *idaapi dstr(void) const; +}; + +typedef qvector array_of_intmap_t; + +//------------------------------------------------------------------------- +/// Edge connecting two graph nodes +struct edge_t +{ + int src = 0; ///< source node number + int dst = 0; ///< destination node number + edge_t(int x=0, int y=0) : src(x), dst(y) {} + bool operator < (const edge_t &y) const + { return src < y.src || (src == y.src && dst < y.dst); } + bool operator == (const edge_t &y) const + { return src == y.src && dst == y.dst; } + bool operator != (const edge_t &y) const + { return src != y.src || dst != y.dst; } +}; +DECLARE_TYPE_AS_MOVABLE(edge_t); + +struct edgevec_t : public qvector +{ +}; + +struct edgeset_t; +struct edge_segs_vec_t; +struct edge_infos_t; +struct destset_t; + +enum edge_type_t +{ + EDGE_NONE = 0, + EDGE_TREE = 1, + EDGE_FORWARD = 2, + EDGE_BACK = 3, + EDGE_CROSS = 4, + EDGE_SUBGRAPH = 5 // edge of a subgraph (used in collapse) +}; + +//------------------------------------------------------------------------- +/// Set of graph nodes +class node_set_t : public intset_t +{ +public: + idaapi node_set_t(void) {} + idaapi node_set_t(int node) { insert(node); } + idaapi node_set_t(const gdl_graph_t *g); + bool idaapi add(int node) { return insert(node).second; } + void idaapi sub(int node) { erase(node); } + void idaapi sub(const node_set_t &r); + void idaapi add(const node_set_t &r); + void idaapi intersect(const node_set_t &r); + void idaapi extract(intvec_t &out) const; + int idaapi first(void) const { return empty() ? -1 : *begin(); } +}; + +typedef qvector array_of_node_set_t; + +//------------------------------------------------------------------------- +/// Node ordering in a graph. +/// Maps a node number to a number describing its +/// order in the graph (and vice versa). +class node_ordering_t +{ + intvec_t node_by_order; ///< ordered sequence of node numbers + intvec_t order_by_node; ///< node number => index in #node_by_order + + void ensure_order_by_node() + { + if ( order_by_node.empty() ) + { + size_t n = size(); + order_by_node.resize(n, -1); + for ( size_t i = 0; i < n; i++ ) + { + int idx = node_by_order[i]; + if ( idx != -1 ) + order_by_node[idx] = i; + } + } + } + +public: + DEFINE_MEMORY_ALLOCATION_FUNCS() + void idaapi clear(void) + { + node_by_order.clear(); + order_by_node.clear(); + } + + void idaapi resize(int n) + { + clear(); + if ( n >= 0 ) + node_by_order.resize(n, -1); + } + + size_t idaapi size(void) const + { + return node_by_order.size(); + } + + void idaapi set(int _node, int num) + { + ensure_order_by_node(); + if ( num >= 0 && num < node_by_order.size() + && _node >= 0 && _node < order_by_node.size() ) + { + node_by_order[num] = _node; + order_by_node[_node] = num; + } + } + + bool idaapi clr(int _node) + { + if ( _node < 0 ) + return false; + ensure_order_by_node(); + if ( _node >= order_by_node.size() ) + return false; + int old = order_by_node[_node]; + if ( old < 0 || old >= node_by_order.size() ) + return false; + order_by_node[_node] = -1; + node_by_order[old] = -1; + // shift all order numbers higher than the deleted order number by one + size_t n = size(); + for ( size_t i = 0; i < n; i++ ) + if ( order_by_node[i] > old ) + order_by_node[i]--; + int rest = n - old - 1; + if ( rest > 0 ) + memmove(&node_by_order[old], &node_by_order[old+1], rest*sizeof(int)); + return true; + } + + int idaapi node(size_t _order) const + { + return _order < node_by_order.size() ? node_by_order[_order] : -1; + } + + int idaapi order(int _node) + { + ensure_order_by_node(); + return (_node >= 0 && _node < order_by_node.size()) ? order_by_node[_node] : -1; + } +}; + +//------------------------------------------------------------------------- +/// Node iterator (used to draw graphs) +class node_iterator +{ + DECLARE_HELPER(friend) + friend class gdl_graph_t; + const gdl_graph_t *g; + int i; + node_iterator &_goup(void); + node_iterator &goup(void) { return *node_iterator_goup(this); } +public: + node_iterator(const gdl_graph_t *_g, int n) : g(_g), i(n) {} + node_iterator &operator++(void) { i++; return goup(); } + bool operator==(const node_iterator &n) const { return i == n.i && g == n.g; } + bool operator!=(const node_iterator &n) const { return !(*this == n); } + int operator*(void) const { return i; } +}; + +//------------------------------------------------------------------------- +/// gdl graph interface - includes only functions required to draw it +class gdl_graph_t +{ + // does a path from 'm' to 'n' exist? + bool idaapi path(node_set_t &visited, int m, int n) const; +public: + DEFINE_MEMORY_ALLOCATION_FUNCS() + virtual ~gdl_graph_t() {} + virtual char *idaapi get_node_label(char *iobuf, int iobufsize, int n) const { qnotused(iobufsize); qnotused(n); iobuf[0] = '\0'; return iobuf; } + virtual void idaapi print_graph_attributes(FILE *fp) const { qnotused(fp); } + virtual bool idaapi print_node(FILE *fp, int n) const { qnotused(fp); qnotused(n); return false; } + virtual bool idaapi print_edge(FILE *fp, int i, int j) const { qnotused(fp); qnotused(i); qnotused(j); return false; } + virtual void idaapi print_node_attributes(FILE *fp, int n) const { qnotused(fp); qnotused(n); } + virtual int idaapi size(void) const = 0; // number of the max node number + virtual int idaapi node_qty(void) const { return size(); } // number of alive nodes + virtual bool idaapi exists(int node) const { qnotused(node); return true; } + virtual int idaapi entry(void) const { return 0; } + virtual int idaapi exit(void) const { return size()-1; } + virtual int idaapi nsucc(int node) const = 0; + virtual int idaapi npred(int node) const = 0; + virtual int idaapi succ(int node, int i) const = 0; + virtual int idaapi pred(int node, int i) const = 0; + virtual bool idaapi empty(void) const { return node_qty() == 0; } + virtual bgcolor_t idaapi get_node_color(int n) const { qnotused(n); return DEFCOLOR; } + virtual bgcolor_t idaapi get_edge_color(int i, int j) const { qnotused(i); qnotused(j); return DEFCOLOR; } + void idaapi gen_gdl(FILE *fp) const; + void idaapi gen_gdl(const char *file) const; + size_t idaapi nedge(int node, bool ispred) const { return ispred ? npred(node) : nsucc(node); } + int idaapi edge(int node, int i, bool ispred) const { return ispred ? pred(node, i) : succ(node, i); } + int idaapi front(void) { return *begin(); } + node_iterator idaapi begin(void) const { return node_iterator(this, 0).goup(); } + node_iterator idaapi end(void) const { return node_iterator(this, size()); } + // does a path from 'm' to 'n' exist? + bool idaapi path_exists(int m, int n) const { node_set_t v; return path(v, m, n); } + + void idaapi gen_dot(FILE *fp) const; + void idaapi gen_dot(const char *file) const; +}; + + +/// Create GDL file for graph + +idaman void ida_export gen_gdl(const gdl_graph_t *g, const char *fname); + + +/// Display GDL file by calling wingraph32. +/// The exact name of the grapher is taken from the configuration file +/// and set up by setup_graph_subsystem(). +/// The path should point to a temporary file: when wingraph32 +/// succeeds showing the graph, the input file will be deleted. +/// \return error code from os, 0 if ok + +idaman int ida_export display_gdl(const char *fname); + + +//------------------------------------------------------------------------- +// Build and display program graphs + +/// Build and display a flow graph. +/// \param filename output file name. the file extension is not used. maybe nullptr. +/// \param title graph title +/// \param pfn function to graph +/// \param ea1, ea2 if pfn == nullptr, then the address range +/// \param gflags combination of \ref CHART_1. +/// if none of #CHART_GEN_DOT, #CHART_GEN_GDL, #CHART_WINGRAPH +/// is specified, the function will return false +/// \return success. if fails, a warning message is displayed on the screen + +idaman bool ida_export gen_flow_graph( + const char *filename, + const char *title, + func_t *pfn, + ea_t ea1, + ea_t ea2, + int gflags); + +/// \defgroup CHART_1 Flow graph building flags +/// Passed as flags parameter to: +/// - gen_flow_graph() +/// - gen_simple_call_chart() +/// - gen_complex_call_chart() +///@{ +#define CHART_PRINT_NAMES 0x1000 ///< print labels for each block? +#define CHART_GEN_DOT 0x2000 ///< generate .dot file (file extension is forced to .dot) +#define CHART_GEN_GDL 0x4000 ///< generate .gdl file (file extension is forced to .gdl) +#define CHART_WINGRAPH 0x8000 ///< call grapher to display the graph +///@} + + +/// Build and display a simple function call graph. +/// \param filename output file name. the file extension is not used. maybe nullptr. +/// \param wait message to display during graph building +/// \param title graph title +/// \param gflags combination of #CHART_NOLIBFUNCS and \ref CHART_1. +/// if none of #CHART_GEN_DOT, #CHART_GEN_GDL, #CHART_WINGRAPH +/// is specified, the function will return false. +/// \return success. if fails, a warning message is displayed on the screen + +idaman bool ida_export gen_simple_call_chart( + const char *filename, + const char *wait, + const char *title, + int gflags); + + +/// Build and display a complex xref graph. +/// \param filename output file name. the file extension is not used. maybe nullptr. +/// \param wait message to display during graph building +/// \param title graph title +/// \param ea1, ea2 address range +/// \param flags combination of \ref CHART_2 and \ref CHART_1. +/// if none of #CHART_GEN_DOT, #CHART_GEN_GDL, #CHART_WINGRAPH +/// is specified, the function will return false. +/// \param recursion_depth optional limit of recursion +/// \return success. if fails, a warning message is displayed on the screen + +idaman bool ida_export gen_complex_call_chart( + const char *filename, + const char *wait, + const char *title, + ea_t ea1, + ea_t ea2, + int flags, + int32 recursion_depth=-1); + +/// \defgroup CHART_2 Call chart building flags +/// Passed as flags parameter to gen_complex_call_chart() +///@{ +#define CHART_NOLIBFUNCS 0x0400 ///< don't include library functions in the graph +#define CHART_REFERENCING 0x0001 ///< references to the addresses in the list +#define CHART_REFERENCED 0x0002 ///< references from the addresses in the list +#define CHART_RECURSIVE 0x0004 ///< analyze added blocks +#define CHART_FOLLOW_DIRECTION 0x0008 ///< analyze references to added blocks only in the direction of the reference who discovered the current block +#define CHART_IGNORE_XTRN 0x0010 +#define CHART_IGNORE_DATA_BSS 0x0020 +#define CHART_IGNORE_LIB_TO 0x0040 ///< ignore references to library functions +#define CHART_IGNORE_LIB_FROM 0x0080 ///< ignore references from library functions +#define CHART_PRINT_COMMENTS 0x0100 +#define CHART_PRINT_DOTS 0x0200 ///< print dots if xrefs exist outside of the range recursion depth +///@} + + +/// Setup the user-defined graph colors and graph viewer program. +/// This function is called by the GUI at the beginning, so no need to call +/// it again. + +idaman void ida_export setup_graph_subsystem(const char *_grapher, bgcolor_t (idaapi *get_graph_color)(int color)); + + +class cancellable_graph_t : public gdl_graph_t +{ +public: + mutable bool cancelled = false; + char padding[3]; // make the class nicely aligned. otherwise we have + // problems with gcc in qflow_chart_t. + virtual ~cancellable_graph_t() {} + bool idaapi check_cancel(void) const; +}; + +//-------------------------------------------------------------------------- +/// Information about a basic block of a \ref qflow_chart_t +struct qbasic_block_t : public range_t +{ + intvec_t succ; ///< list of node successors + intvec_t pred; ///< list of node predecessors +}; + +/// Does this block never return? +inline THREAD_SAFE bool is_noret_block(fc_block_type_t btype) +{ + return btype == fcb_noret || btype == fcb_enoret; +} + +/// Does this block return? +inline THREAD_SAFE bool is_ret_block(fc_block_type_t btype) +{ + return btype == fcb_ret || btype == fcb_cndret; +} + +/// \defgroup FC_ Flow chart flags +/// Passed as 'flags' parameter to qflow_chart_t +///@{ +#define FC_PRINT 0x0001 ///< print names (used only by display_flow_chart()) +#define FC_NOEXT 0x0002 ///< do not compute external blocks. Use this to prevent jumps leaving the + ///< function from appearing in the flow chart. Unless specified, the + ///< targets of those outgoing jumps will be present in the flow + ///< chart under the form of one-instruction blocks +#define FC_RESERVED 0x0004 ///< former FC_PREDS +#define FC_APPND 0x0008 ///< multirange flowchart (set by append_to_flowchart) +#define FC_CHKBREAK 0x0010 ///< build_qflow_chart() may be aborted by user +#define FC_CALL_ENDS 0x0020 ///< call instructions terminate basic blocks +#define FC_NOPREDS 0x0040 ///< do not compute predecessor lists +#define FC_OUTLINES 0x0080 ///< include outlined code (with FUNC_OUTLINE) +///@} + +/// A flow chart for a function, or a set of address ranges +class qflow_chart_t : public cancellable_graph_t +{ +public: + typedef qvector blocks_t; + DECLARE_HELPER(friend) + qstring title; + range_t bounds; ///< overall bounds of the qflow_chart_t instance + func_t *pfn = nullptr; ///< the function this instance was built upon + int flags = 0; ///< flags. See \ref FC_ + blocks_t blocks; ///< basic blocks + int nproper = 0; ///< number of basic blocks belonging to the specified range + + idaapi qflow_chart_t(void) {} + idaapi qflow_chart_t(const char *_title, func_t *_pfn, ea_t _ea1, ea_t _ea2, int _flags) + : title(_title), bounds(_ea1, _ea2), pfn(_pfn), flags(_flags) + { + refresh(); + } + virtual ~qflow_chart_t() {} + void idaapi create(const char *_title, func_t *_pfn, ea_t _ea1, ea_t _ea2, int _flags) + { + title = _title; + pfn = _pfn; + bounds = range_t(_ea1, _ea2); + flags = _flags; + refresh(); + } + void idaapi create(const char *_title, const rangevec_t &ranges, int _flags) + { + title = _title; + flags = _flags; + create_multirange_qflow_chart(*this, ranges); + } + void idaapi append_to_flowchart(ea_t ea1, ea_t ea2) { ::append_to_flowchart(*this, ea1, ea2); } + void idaapi refresh(void) { create_qflow_chart(*this); } + fc_block_type_t calc_block_type(size_t blknum) const + { return fc_calc_block_type(*this, blknum); } + bool is_ret_block(size_t blknum) const { return ::is_ret_block(calc_block_type(blknum)); } + bool is_noret_block(size_t blknum) const { return ::is_noret_block(calc_block_type(blknum)); } + virtual void idaapi print_node_attributes(FILE *fp, int n) const override { qnotused(fp); qnotused(n); } + virtual int idaapi nsucc(int node) const override { return int(blocks[node].succ.size()); } + virtual int idaapi npred(int node) const override { return int(blocks[node].pred.size()); } + virtual int idaapi succ(int node, int i) const override { return blocks[node].succ[i]; } + virtual int idaapi pred(int node, int i) const override { return blocks[node].pred[i]; } + virtual char *idaapi get_node_label(char *iobuf, int iobufsize, int n) const override { qnotused(iobuf); qnotused(iobufsize); qnotused(n); return nullptr; } + virtual int idaapi size(void) const override { return int(blocks.size()); } + bool idaapi print_names(void) const { return (flags & FC_PRINT) != 0; } +}; + +#endif // __GDLDRAW_HPP + +``` + +`IdaSDK/graph.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * Graph type definitions + * + * Due to the use of STL and virtual functions, some parts of this + * interface might be incompatible with compilers other than Visual Studio + * on Windows and gcc on Unix systems + */ + +#ifndef __GRAPH_DEF_HPP +#define __GRAPH_DEF_HPP + +#include +#include +#include +#include +#include + +/*! \file graph.hpp + + \brief Graph view management + +*/ + +//------------------------------------------------------------------------- +/// \defgroup NIF_ Node info flags +/// Passed as 'flags' parameter to set_node_info(). +/// Also see node_info_t::get_flags_for_valid(). +///@{ +#define NIF_BG_COLOR (1 << 0) ///< node_info_t::bg_color +#define NIF_FRAME_COLOR (1 << 1) ///< node_info_t::frame_color +#define NIF_EA (1 << 2) ///< node_info_t::ea +#define NIF_TEXT (1 << 3) ///< node_info_t::text +#define NIF_FLAGS (1 << 4) ///< node_info_t::flags +#define NIF_ALL (NIF_BG_COLOR | NIF_FRAME_COLOR | NIF_EA | NIF_TEXT | NIF_FLAGS) +///@} + +//------------------------------------------------------------------------- +/// \defgroup GLICTL_ graph_location_info_t control flags +/// Passed as 'flags' parameter to viewer_set_gli() and viewer_get_gli(). +///@{ +#define GLICTL_CENTER (1 << 0) ///< the gli should be set/get as center +///@} + +/// Information about a node in a graph +struct node_info_t +{ +private: + size_t cb = sizeof(node_info_t); ///< size of this structure +public: + bgcolor_t bg_color = DEFCOLOR; ///< background color + bgcolor_t frame_color = DEFCOLOR; ///< color of enclosing frame +#define NIFF_SHOW_CONTENTS (1 << 0) //< force show contents of huge node + uint32 flags = 0; ///< flags + ea_t ea = BADADDR; ///< address + qstring text; ///< node contents + + /// Has valid #bg_color? + inline bool valid_bg_color() const { return bg_color != DEFCOLOR; } + /// Has valid #frame_color? + inline bool valid_frame_color() const { return frame_color != DEFCOLOR; } + /// Has valid #ea? + inline bool valid_ea() const { return ea != BADADDR; } + /// Has non-empty #text? + inline bool valid_text() const { return !text.empty(); } + /// Has valid #flags? + inline bool valid_flags() const { return flags != 0; } + + /// Get combination of \ref NIF_ describing which attributes are valid + inline uint32 get_flags_for_valid() const + { + uint32 F = 0; + if ( valid_bg_color() ) + F |= NIF_BG_COLOR; + if ( valid_frame_color() ) + F |= NIF_FRAME_COLOR; + if ( valid_ea() ) + F |= NIF_EA; + if ( valid_text() ) + F |= NIF_TEXT; + if ( valid_flags() ) + F |= NIF_FLAGS; + return F; + } +}; + +/// Graph instances have a unique id (see interactive_graph_t::gid) +typedef ea_t graph_id_t; + + +/// Get node info. +/// \param out result +/// \param gid id of desired graph +/// \param node node number +/// \return success + +idaman bool ida_export get_node_info(node_info_t *out, graph_id_t gid, int node); + + +/// Set node info. +/// \param gid id of desired graph +/// \param node node number +/// \param ni node info to use +/// \param flags combination of \ref NIF_, +/// identifying which fields of 'ni' will be used + +idaman void ida_export set_node_info(graph_id_t gid, int node, const node_info_t &ni, uint32 flags); + + +/// Delete the ::node_info_t for the given node + +idaman void ida_export del_node_info(graph_id_t gid, int node); + + +/// Clear node info for the given node. +/// \param gid id of desired graph +/// \param node node number +/// \param flags combination of \ref NIF_, +/// identifying which fields of ::node_info_t +/// will be cleared + +idaman void ida_export clr_node_info(graph_id_t gid, int node, uint32 flags); + + +//------------------------------------------------------------------------- +/// Node visitor - see drawable_graph_t::visit_nodes() +class graph_node_visitor_t +{ + node_set_t visited; ///< set of previously visited nodes +public: + /// Reset visited nodes + void idaapi reinit(void) { visited.clear(); } + /// Mark node as visited + void idaapi set_visited(int n) { visited.add(n); } + /// Have we already visited the given node? + bool idaapi is_visited(int n) const { return visited.has(n); } + + /// Implements action to take when a node is visited + virtual int idaapi visit_node(int /*node*/) { return 0; } + /// Should the edge between 'n' and 'm' be ignored? + virtual bool idaapi is_forbidden_edge(int /*n*/, int /*m*/) const { return false; } + + virtual ~graph_node_visitor_t() {} +}; + +//------------------------------------------------------------------------- +/// Path visitor - see drawable_graph_t::visit_paths() +struct graph_path_visitor_t +{ + intvec_t path; ///< current path + bool prune = false; ///< walk_forward(): prune := true + ///< means to stop the current path + + + virtual int idaapi walk_forward(int /*node*/) { return 0; } + virtual int idaapi walk_backward(int /*node*/) { return 0; } + + virtual ~graph_path_visitor_t() {} +}; + +//------------------------------------------------------------------------- +/// Coordinate in a graph view +struct point_t +{ + int x = 0; + int y = 0; + point_t() {} + point_t(int _x, int _y) : x(_x), y(_y) {} + point_t &add(const point_t &r) + { + x += r.x; + y += r.y; + return *this; + } + point_t &sub(const point_t &r) + { + x -= r.x; + y -= r.y; + return *this; + } + template void div(T d) + { + x /= d; + y /= d; + } + void negate(void) + { + x = -x; + y = -y; + } +#ifdef VCL_H + point_t(const TPoint &p) : x(p.x), y(p.y) {} +#endif + bool operator ==(const point_t &r) const { return x == r.x && y == r.y; } + bool operator !=(const point_t &r) const { return !(*this == r); } + const char *idaapi dstr(void) const; + size_t idaapi print(char *buf, size_t bufsize) const; +}; +DECLARE_TYPE_AS_MOVABLE(point_t); +typedef qvector pointvec_t; + +/// Calculate distance between p and q +inline THREAD_SAFE double calc_dist(point_t p, point_t q) +{ + double dx = q.x - p.x; + double dy = q.y - p.y; + return sqrt(dx*dx+dy*dy); +} + +/// Ordered sequence of points +class pointseq_t : public pointvec_t +{ +public: + const char *idaapi dstr(void) const; + size_t idaapi print(char *buf, size_t bufsize) const; +}; + +/// A rectangle in a graph view. +/// Please note that a correct rectangle has left < right and top < bottom. +/// NB: top less than bottom, which means that the vertical axis grows to the +/// bottom, a bigger value means verically lower. +struct rect_t +{ + int left = 0; + int top = 0; + int right = 0; + int bottom = 0; + rect_t() {} + rect_t(int l, int t, int r, int b) : left(l), top(t), right(r), bottom(b) { verify(); } + rect_t(const point_t &p0, const point_t &p1) + : left (qmin(p0.x, p1.x)), + top (qmin(p0.y, p1.y)), + right (qmax(p0.x, p1.x)), + bottom(qmax(p0.y, p1.y)) { verify(); } + void verify(void) + { + if ( left > right ) + qswap(left, right); + if ( top > bottom ) + qswap(top, bottom); + } + int width(void) const { return right - left; } + int height(void) const { return bottom - top; } + void move_to(const point_t &p) + { + int dx = p.x - left; + int dy = p.y - top; + move_by(point_t(dx, dy)); + } + void move_by(const point_t &p) + { + left += p.x; + right += p.x; + top += p.y; + bottom += p.y; + } + point_t center(void) const + { + return point_t((left+right)/2, (top+bottom)/2); + } + point_t topleft(void) const + { + return point_t(left, top); + } + point_t bottomright(void) const + { + return point_t(right, bottom); + } + void grow(int delta) + { + left -= delta; + right += delta; + top -= delta; + bottom += delta; + } + void intersect(const rect_t &r) + { + if ( left < r.left ) + left = r.left; + if ( right > r.right ) + right = r.right; + if ( top < r.top ) + top = r.top; + if ( bottom > r.bottom ) + bottom = r.bottom; + } + void make_union(const rect_t &r) + { + if ( left > r.left ) + left = r.left; + if ( right < r.right ) + right = r.right; + if ( top > r.top ) + top = r.top; + if ( bottom < r.bottom ) + bottom = r.bottom; + } + bool empty(void) const + { + return left >= right || top >= bottom; + } + bool is_intersection_empty(const rect_t &r) const + { + return left >= r.right + || right <= r.left + || top >= r.bottom + || bottom <= r.top; + } + bool contains(const point_t &p) const + { + return left <= p.x + && right > p.x + && top <= p.y + && bottom > p.y; + } + int area(void) const { return width()*height(); } + bool idaapi operator == (const rect_t &r) const + { + return left == r.left + && right == r.right + && top == r.top + && bottom == r.bottom; + } + bool idaapi operator != (const rect_t &r) const { return !(*this == r); } + bool idaapi operator < (const rect_t &r) const; +#ifdef VCL_H + const TRect &operator()(void) const { return *(TRect *)this; } + TRect &operator()(void) { return *(TRect *)this; } + rect_t(const TRect &r) : left(r.left), top(r.top), right(r.right), bottom(r.bottom) {} +#endif +}; +DECLARE_TYPE_AS_MOVABLE(rect_t); + +//--------------------------------------------------------------------------- +/// Coordinate in a graph view (double precision) +struct TPointDouble +{ + double x = 0.0; + double y = 0.0; + TPointDouble() {} + TPointDouble(double a, double b) : x(a), y(b) {} + TPointDouble(const point_t &r) : x(r.x), y(r.y) {} + void add(const TPointDouble &r) + { + x += r.x; + y += r.y; + } + void sub(const TPointDouble &r) + { + x -= r.x; + y -= r.y; + } + void negate(void) + { + x = -x; + y = -y; + } + template void div(T d) + { + x /= d; + y /= d; + } + bool operator ==(const TPointDouble &r) const { return x == r.x && y == r.y; } + bool operator !=(const TPointDouble &r) const { return !(*this == r); } +}; + +//--------------------------------------------------------------------------- +typedef int layout_type_t; ///< see \ref layout_ +/// \defgroup layout_ Proximity view layouts +///@{ +const layout_type_t + layout_none = 0, + layout_digraph = 1, + layout_tree = 2, + layout_circle = 3, + layout_polar_tree = 4, + layout_orthogonal = 5, + layout_radial_tree = 6; +///@} + +/// Attributes of a graph edge +struct edge_info_t +{ + bgcolor_t color = DEFCOLOR; ///< edge color + int width = 1; ///< edge width + int srcoff = -1; ///< source: edge port offset from the left + int dstoff = -1; ///< destination: edge port offset from the left + pointseq_t layout; ///< describes geometry of edge +#if !defined(_MSC_VER) || _MSC_VER >= 1600 + void idaapi reverse_layout(void) { std::reverse(layout.begin(), layout.end()); } +#endif + void idaapi add_layout_point(point_t p); +}; + +/// Edge layout point +struct edge_layout_point_t +{ + int pidx = -1; ///< index into edge_info_t::layout + edge_t e = edge_t(-1, -1); ///< parent edge + idaapi edge_layout_point_t() {} + idaapi edge_layout_point_t(const edge_t &_e, int _pidx) : pidx(_pidx), e(_e) {} + int idaapi compare(const edge_layout_point_t &r) const + { + if ( e < r.e ) + return -1; + if ( r.e < e ) + return 1; + return ::compare(pidx, r.pidx); + } + bool idaapi operator == (const edge_layout_point_t &r) const + { + return pidx == r.pidx && e == r.e; + } + bool idaapi operator != (const edge_layout_point_t &r) const + { + return !(*this == r); + } +}; + +/// Element of a graph selection - could be a node or edge layout point. +/// See ::screen_graph_selection_t. +struct selection_item_t +{ + bool is_node = false; ///< represents a selected node? + int node = -1; ///< node number (is_node = true) + edge_layout_point_t elp; ///< edge layout point (is_node = false) + idaapi selection_item_t() {} + idaapi selection_item_t(int n) : is_node(true), node(n) {} + idaapi selection_item_t(edge_layout_point_t &_elp) + : is_node(false), node(-1), elp(_elp) {} + idaapi selection_item_t(edge_t e, int idx) + : is_node(false), node(-1), elp(e, idx) {} + idaapi selection_item_t(class graph_item_t &); + int idaapi compare(const selection_item_t &r) const + { + if ( is_node != r.is_node ) + return is_node - r.is_node; + if ( is_node ) + return ::compare(node, r.node); + return elp.compare(r.elp); + } + bool idaapi operator == (const selection_item_t &r) const + { return compare(r) == 0; } + bool idaapi operator != (const selection_item_t &r) const + { return compare(r) != 0; } + bool idaapi operator < (const selection_item_t &r) const + { return compare(r) < 0; } +}; + + +/// Selection in a graph. +/// (A list of nodes and edge layout points). +/// This selection is used to move a subgraph on the screen. +typedef qvector screen_graph_selection_base_t; + +struct screen_graph_selection_t : public screen_graph_selection_base_t +{ + bool idaapi has(const selection_item_t &item) const + { return (const_iterator)find(item) != end(); } + void idaapi add(const screen_graph_selection_t &s) + { + for ( screen_graph_selection_t::const_iterator p=s.begin(); p != s.end(); ++p ) + add_unique(*p); + } + void idaapi sub(const screen_graph_selection_t &s) + { + for ( screen_graph_selection_t::const_iterator p=s.begin(); p != s.end(); ++p ) + del(*p); + } + void idaapi add_node(int node) { add_unique(selection_item_t(node)); } + void idaapi del_node(int node) { del(selection_item_t(node)); } + void idaapi add_point(edge_t e, int idx) { add_unique(selection_item_t(e, idx)); } + void idaapi del_point(edge_t e, int idx) { del(selection_item_t(e, idx)); } + + size_t idaapi nodes_count() const { return items_count(true); } + size_t idaapi points_count() const { return items_count(false); } + size_t idaapi items_count(bool look_for_nodes) const + { + size_t cnt = 0; + for ( size_t i = 0, sz = size(); i < sz; ++i ) + if ( at(i).is_node == look_for_nodes ) + ++cnt; + return cnt; + } +}; + +/// Sub-segment of a graph edge +struct edge_segment_t +{ + edge_t e; + int nseg; + int x0, x1; + size_t idaapi length() const { return abs(x1-x0); } + bool idaapi toright() const { return x1 > x0; } // horizontal segment to the right + bool idaapi operator < (const edge_segment_t &r) const + { + return e < r.e; +/* // longest edges first + int ll = x1 - x0; if ( ll < 0 ) ll = -ll; + int rl = r.x1 - r.x0; if ( rl < 0 ) rl = -rl; + if ( rl < ll ) + return true; + if ( rl == ll ) + return e < r.e; + return false;*/ + } +}; + +//--------------------------------------------------------------------------- +/// See ::graph_item_t +enum graph_item_type_t +{ // valid graph_item_t fields: + git_none, ///< nothing + git_edge, ///< edge (graph_item_t::e, graph_item_t::n. n is farthest edge endpoint) + git_node, ///< node title (graph_item_t::n) + git_tool, ///< node title button (graph_item_t::n, graph_item_t::b) + git_text, ///< node text (graph_item_t::n, graph_item_t::p) + git_elp, ///< edge layout point (graph_item_t::elp) +}; + +/// Describes a sub-item of a graph +class graph_item_t +{ +public: + graph_item_type_t type; ///< type + edge_t e; ///< edge source and destination + int n; ///< node number + int b; ///< button number + point_t p; ///< text coordinates in the node + edge_layout_point_t elp; ///< edge layout point + bool operator == (const graph_item_t &r) const; + bool is_node(void) const { return type >= git_node && type <= git_text; } + bool is_edge(void) const { return type == git_edge || type == git_elp; } +}; + +//------------------------------------------------------------------------- +/// One dimensional range +struct interval_t +{ + int x0 = 0, x1 = 0; // x0 always <= x1, otherwise the interval is empty + bool empty(void) const { return x0 < x1; } + void intersect(const interval_t &r) + { + if ( x0 < r.x0 ) + x0 = r.x0; + if ( x1 > r.x1 ) + x1 = r.x1; + } + void make_union(const interval_t &r) + { + if ( x0 > r.x0 ) + x0 = r.x0; + if ( x1 < r.x1 ) + x1 = r.x1; + } + void move_by(int shift) + { + x0 += shift; + x1 += shift; + } + interval_t() {} + interval_t(int y0, int y1) + { + x0 = qmin(y0, y1); + x1 = qmax(y0, y1); + } + interval_t(const edge_segment_t &s) + { + x0 = qmin(s.x0, s.x1); + x1 = qmax(s.x0, s.x1); + } + int length(void) const { return x1 - x0; } + bool contains(int x) const { return x0 <= x && x <= x1; } + bool operator ==(const interval_t &r) const { return x0 == r.x0 && x1 == r.x1; } + bool operator !=(const interval_t &r) const { return !(*this == r); } +}; + +//------------------------------------------------------------------------- +/// Organize graph nodes by row +struct row_info_t +{ + intvec_t nodes; ///< list of nodes at the row + int top = 0; ///< top y coord of the row + int bottom = 0; ///< bottom y coord of the row + + int height(void) const { return bottom - top; } +}; +typedef qvector graph_row_info_t; ///< vector of row infos + +static const int ygap = 30; +static const int xgap = 10; +static const int arrow_height = 10; +static const int arrow_width = 8; + +struct graph_location_info_t; +class graph_visitor_t; +class edge_typer_t; + +//------------------------------------------------------------------------- +class drawable_graph_t : public gdl_graph_t +{ + void idaapi find_entries(node_set_t &entries) const; + void idaapi depth_first(int root, struct depth_first_info_t &di) const; + size_t idaapi remove_reachable(int n, node_set_t *s) const; + int idaapi longest_path(int n, intvec_t &tops, int row_height) const; + size_t idaapi sort_layer_nodes( + const row_info_t &r1, + const intmap_t &lpi1, + row_info_t &r2, + intmap_t &lpi2, + bool ispred) const; + size_t idaapi calc_cross_num( + const intvec_t &r1, + const intvec_t &r2, + const intmap_t &lpi1, + bool ispred) const; + size_t idaapi num_crossings(const graph_row_info_t &gri, const array_of_intmap_t &nodepi) const; + int idaapi calc_x_coord(const row_info_t &ri, int idx, bool ispred, int first_added_node) const; + void idaapi try_move_down(intvec_t &tops, int n, int row_height) const; + +protected: + /// Returns one entry point for each connected component + void idaapi get_connected_components(intvec_t &entries) const; + + /// Find longest paths from the entries. take into account node heights. + /// if row_height > 0, then use it instead of real node heights. + /// return max distance found + int idaapi calc_longest_pathes( + const node_set_t &entries, + intvec_t &tops, + int row_height) const; + + /// Move entry nodes down as much as possible + void idaapi move_nodes_down( + intvec_t &tops, + const node_ordering_t &post, + int first_reverser_node, + int row_height) const; + + /// Create graph row info from 'tops' + void idaapi create_graph_row_info( + const intvec_t &tops, + graph_row_info_t &gri, + int graph_height) const; + + /// Calculate height of each row + void idaapi calc_row_heights(graph_row_info_t &gri) const; + + /// Minimize crossings + void idaapi minimize_crossings(graph_row_info_t &gri) const; + + /// Calculate x coords of all nodes + void idaapi set_x_coords( + const graph_row_info_t &gri, + const node_set_t &selfrefs, + int first_added_node); + + /// Gather information about all edge segments + void idaapi gather_edge_segments( + const graph_row_info_t &gri, + edge_segs_vec_t &ges) const; + + /// Make all edges rectangular + void idaapi make_rect_edges( + graph_row_info_t &gri, + const edge_segs_vec_t &ges, + int first_reverser_node); + + /// Assign ports to edges + void idaapi assign_edge_ports( + const graph_row_info_t &gri, + const node_set_t &selfrefs); + + /// Recalculate width of all edges + void idaapi recalc_edge_widths( + const edgeset_t &back_edges, + const edge_infos_t &self_edges); + + /// Clear layout information in the graph + void idaapi clear_layout_info(void); + + void idaapi depth_first( + node_ordering_t *pre, + node_ordering_t *post, + edge_typer_t *et) const; + + void idaapi create_spanning_tree( + edge_typer_t *et, + node_set_t *entries, + edgeset_t *back_edges, + node_ordering_t *pre, + node_ordering_t *post) const; + + void idaapi tree_layout(edge_typer_t &et, const node_set_t &entries); + + /// Is there a path from M to N which terminates with a back edge to N? + bool idaapi path_back(const array_of_node_set_t &domin, int m, int n) const; + bool idaapi path_back(edge_typer_t &et, int m, int n) const; + + /// Visit nodes starting from 'node', depth first + int idaapi visit_nodes(int node, graph_node_visitor_t &gv) const; + + /// Visit all possible paths starting from 'node'. + /// A path cannot contain the same node twice. + int idaapi visit_paths(int node, graph_path_visitor_t &gv) const; + +public: + qstring title; ///< graph title + bool rect_edges_made = false; ///< have create rectangular edges? + layout_type_t current_layout = layout_none; ///< see \ref layout_ + point_t circle_center; ///< for layout_circle + int circle_radius = 0; ///< for layout_circle + hook_cb_t *callback = nullptr; ///< user-defined callback + void *callback_ud = nullptr; ///< user data for #callback + + virtual ~drawable_graph_t() {} + void idaapi clear(void); + void idaapi dump_graph(const char *header) const; + bool idaapi calc_bounds(rect_t *r); + void idaapi calc_fitting_params( + const rect_t &area, + const rect_t &r, + graph_location_info_t *gli, + double max_zoom); + bool idaapi calc_fitting_params( + const rect_t &area, + graph_location_info_t *gli, + double max_zoom); + int idaapi for_all_nodes_edges(graph_visitor_t &nev, bool visit_nodes=true); + // get edge ports - fills s, d arguments and returns edge_info_t + const edge_info_t *idaapi get_edge_ports( + edge_t e, + point_t &s, + point_t &d) const; + // add edges from/to the node + void idaapi add_node_edges(edgevec_t &dlist, int node); + const rect_t &idaapi nrect(int n) const + { return (CONST_CAST(drawable_graph_t *)(this))->nrect(n); } + const edge_info_t *idaapi get_edge(edge_t e) const + { return (CONST_CAST(drawable_graph_t *)(this))->get_edge(e); } + virtual rect_t &idaapi nrect(int n) = 0; + virtual edge_info_t *idaapi get_edge(edge_t e) = 0; + virtual drawable_graph_t *idaapi clone(void) const = 0; + + bool idaapi create_tree_layout(void); + bool idaapi create_circle_layout(point_t p, int radius); + bool idaapi create_polar_tree_layout(point_t p, int radius); + bool idaapi create_radial_tree_layout(point_t p, int radius); + bool idaapi create_orthogonal_layout(void); + + void set_callback(hook_cb_t *_callback, void *_ud) + { + callback = _callback; + callback_ud = _ud; + } + ssize_t vgrcall(int code, va_list va) + { + if ( callback != nullptr ) + return callback(callback_ud, code, va); + return 0; + } + ssize_t grcall(int code, ...) + { + va_list va; + va_start(va, code); + ssize_t result = vgrcall(code, va); + va_end(va); + return result; + } +}; + +/// For some reason GCC insists on putting the vtable into object files, +/// even though we only use ::interactive_graph_t by pointer. +/// This looks like a linker bug. We fix it by declaring functions as pure virtual +/// when plugins are compiled. +#if defined(__GNUC__) && (defined(__KERNEL__) || !defined(__UI__)) // compiling a plugin or the kernel with gcc? +#define GCC_PUREVIRT = 0 +#else +#define GCC_PUREVIRT +#endif + +//------------------------------------------------------------------------- +struct edge_infos_wrapper_t +{ + edge_infos_wrapper_t(); + edge_infos_wrapper_t(const edge_infos_wrapper_t &other); + edge_infos_wrapper_t &idaapi operator=(const edge_infos_wrapper_t &other); + ~edge_infos_wrapper_t() { clear(); } + void clear(); + + edge_infos_t *ptr; +}; + +#ifdef _DEBUG +#define CHKNODEIDX(n) QASSERT(1385, int(n) >= 0) +#else +#define CHKNODEIDX(n) +#endif + +/// The base class used to display graphs in IDA. +/// +/// The interactive_graph_t introduces the following notions on top of +/// the ones that parent classes already provide: +/// * ability to add/remove nodes +/// * ability to add/remove edges +/// * grouping/ungrouping nodes +/// * expanding/collapsing existing groups +/// +/// While the adding/removing of nodes & edges is, in itself, a fairly +/// straightforward notion, 'groups' can be a bit more tricky to +/// figure out. +/// +/// For the purpose of illustrating what 'groups' are, and how they +/// are handled, let's assume that we are analyzing a binary that +/// contains multiple bits of connected information scattered all over +/// the place in a read-only '.rodata' segment. +/// +/// In order to simplify analysis that scattered-but-connected data, +/// the user might choose to write a small plugin that represents the +/// data in a graph form: +/// +/// +-----------------------------+ +/// | Driver object #1 | +/// | address: 0x400100 | +/// | size: 0x200 bytes | +/// +-------------+---------------+ +/// | +/// +------------------------+ +/// | +/// +-----------+-----------------+ +/// | Driver object #2 | +/// | address: 0x407380 | +/// | size: 0x180 bytes | +/// | (seems to maintain state of | +/// | the I/O ports) | +/// +----+-------+----------------+ +/// | | +/// +---------------+ +----------------+ +/// | | +/// +-------------+-----------+ +-------+-------------+ +/// | Unknown object #1 | | Driver object #3 | +/// | address: 0x404100 | | address: 0x402000 | +/// | size: 0x80 bytes | | size: 4KB | +/// | (credentials?) | | (mostly mutexes) | +/// +-------------------------+ +---------------------+ +/// +/// In this case, the graph has 4 nodes, each showing some information +/// that is relevant to the analyst. +/// In order to implement this, the plugin must keep, somewhere in +/// memory, the relevant information about the nodes. In this case, it +/// would have a set of 4 items, from which the nodes texts can be +/// generated and provided back to IDA's UI. +/// +/// If one was to call the following methods on the interactive_graph_t +/// instance that is being displayed by IDA, one would get: +/// +/// interactive_graph_t::size() -> 4 +/// interactive_graph_t::node_qty() -> 4 +/// +/// Let's now assume the user decides the 2 first nodes are not +/// that interesting after all, and groups them together, in a node +/// labeled "irrelevant". +/// +/// Then, IDA will modify the interactive_graph_t by adding it a 5th node, +/// which represents that group +/// It's worth pointing out that, it's the interactive_graph_t itself +/// that's doing all the bookkeeping about the groups: the user plugin +/// doesn't need to do anything at all +/// +/// In addition, calling the methods above now yields: +// +/// interactive_graph_t::size() -> 5 +/// interactive_graph_t::node_qty() -> 3 +/// +class interactive_graph_t : public drawable_graph_t +{ + typedef drawable_graph_t inherited; + int idaapi _find_subgraph_node(int group, int n) const; + void idaapi collapse_edges(const intvec_t &nodes, int group); + void idaapi del_node_keep_edges(int n); + void idaapi add_dest(destset_t &ne, edge_t e, int g); + void idaapi reverse_edges( + const edgeset_t &back_edges, + edge_infos_t &self_edges, + node_set_t &entries); + void idaapi layout_self_reference_edges(const edge_infos_t &selfrefs); + void idaapi restore_edges(int first_reserver_node, bool failed); + + void idaapi add_layer_nodes(graph_row_info_t &gri, intvec_t &tops); + void idaapi del_layer_nodes(graph_row_info_t &gri, int first_added_node); + void idaapi fix_collapsed_group_edges(void); + +public: + uval_t gid; ///< graph id - unique for the database + ///< for flowcharts it is equal to the function start_ea + intvec_t belongs; ///< the subgraph the node belongs to + ///< INT_MAX means that the node doesn't exist + ///< sign bit means collapsed node + bytevec_t node_flags; ///< node flags +#define MTG_GROUP_NODE 0x01 ///< is group node? +#define MTG_DOT_NODE 0x02 ///< is dot node? +#define MTG_NON_DISPLAYABLE_NODE 0x08 ///< for disassembly graphs - non-displayable nodes have a visible + ///< area that is too large to generate disassembly lines for without + ///< IDA slowing down significantly (see MAX_VISIBLE_NODE_AREA) + + // groups: original edges without considering any group info + array_of_intvec_t org_succs; + array_of_intvec_t org_preds; + + array_of_intvec_t succs; + array_of_intvec_t preds; + typedef qvector node_layout_t; + node_layout_t nodes; + edge_infos_wrapper_t edges; + + idaapi interactive_graph_t(uval_t id); + idaapi interactive_graph_t(const drawable_graph_t &g, uval_t id); + virtual ~interactive_graph_t() {} + + /// Get the total number of nodes (including group nodes, and + /// including hidden nodes.) + /// + /// See also node_qty() + /// + /// \return the total number of nodes in the graph + virtual int idaapi size(void) const override { return int(succs.size()); } + + /// Get the number of visible nodes (the list can be retrieved using + /// gdl.hpp's node_iterator) + /// + /// See also size() + /// + /// \return the number of visible nodes + virtual int idaapi node_qty(void) const override; + + /// Clears all nodes & edges information in this instance + /// (does not remove node_info_t stored in the database.) + void idaapi clear(void); + + /// Is the graph (visually) empty? + /// \return true if there are no visible nodes + virtual bool idaapi empty(void) const override; + + /// Is the node visible? + /// + /// \param node the node number + /// \return success + virtual bool idaapi exists(int node) const override { return is_visible_node(node); } +#define COLLAPSED_NODE 0x80000000 + + /// Get the node that currently visually represents 'node'. + /// This will find the "closest" parent group node that's visible, + /// by attempting to walk up the group nodes that contain + /// 'node', and will stop when it finds a node that is currently + /// visible. + /// + /// See also get_group_node() + // + /// \param node the node + /// \return the node that represents 'node', or 'node' if it's not + /// part of any group + int idaapi get_node_representative(int node); + + int idaapi get_node_group(int node) const { CHKNODEIDX(node); return (belongs[node] & ~COLLAPSED_NODE); } + void idaapi set_node_group(int node, int group) { CHKNODEIDX(node); belongs[node] = group | (belongs[node] & COLLAPSED_NODE); } + bool idaapi is_deleted_node(int node) const { CHKNODEIDX(node); return belongs[node] == INT_MAX; } + void idaapi set_deleted_node(int node) { CHKNODEIDX(node); belongs[node] = INT_MAX; } + bool idaapi is_subgraph_node(int node) const { return get_node_group(node) != node; } + bool idaapi is_dot_node(int node) const { CHKNODEIDX(node); return (node_flags[node] & MTG_DOT_NODE) != 0; } + bool idaapi is_group_node(int node) const { CHKNODEIDX(node); return (node_flags[node] & MTG_GROUP_NODE) != 0; } + bool idaapi is_displayable_node(int node) const { CHKNODEIDX(node); return (node_flags[node] & MTG_NON_DISPLAYABLE_NODE) == 0; } + bool idaapi is_simple_node(int node) const { return !is_group_node(node); } + bool idaapi is_collapsed_node(int node) const { CHKNODEIDX(node); return (belongs[node] & COLLAPSED_NODE) != 0; } + bool idaapi is_uncollapsed_node(int node) const { return is_group_node(node) && !is_collapsed_node(node); } + + /// Is the node currently visible? + /// + /// An invisible node is a node that's part of a group that's + /// currently collapsed. + /// + /// \param node the node + /// \return success + bool idaapi is_visible_node(int node) const; + + /// Is there any group node in the graph? + /// + /// \return success + bool idaapi groups_are_present(void) const; + + // iterate subgraph nodes, return -1 at the end + int idaapi get_first_subgraph_node(int group) const { return _find_subgraph_node(group, 0); } + int idaapi get_next_subgraph_node(int group, int current) const { return _find_subgraph_node(group, current+1); } + void idaapi insert_visible_nodes(intvec_t &nodes, int group) const; + void idaapi insert_simple_nodes(intvec_t &nodes, int group) const; + bool idaapi check_new_group(const intvec_t &nodes, intvec_t &refined); + + /// Create a new group node, that will contain all the nodes in + /// 'nodes'. + /// + /// \param nodes the nodes that will be part of the group + /// \return the group node, or -1 in case of error + int idaapi create_group(const intvec_t &nodes); + + /// Delete a group node. + /// + /// This deletes the group node only; it does not delete nodes that + /// are part of the group. + /// + /// \param group the group node + /// \return success + bool idaapi delete_group(int group); + + /// Expand/collapse a group node + /// + /// \param group the group node + /// \param expand whether to expand or collapse + /// \return success + bool idaapi change_group_visibility(int group, bool expand); + + /// Change visibility of multiple group nodes + /// + /// \param nodes the group nodes + /// \param expand whether to expand or collapse + /// \return success (true if any group node was modified) + bool idaapi change_visibility(const intvec_t &nodes, bool expand); + + virtual int idaapi nsucc(int b) const override { CHKNODEIDX(b); return (int)succs[b].size(); } + virtual int idaapi npred(int b) const override { CHKNODEIDX(b); return (int)preds[b].size(); } + virtual int idaapi succ(int b, int i) const override { CHKNODEIDX(b); return succs[b][i]; } + virtual int idaapi pred(int b, int i) const override { CHKNODEIDX(b); return preds[b][i]; } + const intvec_t &idaapi succset(int b) const { CHKNODEIDX(b); return succs[b]; } + const intvec_t &idaapi predset(int b) const { CHKNODEIDX(b); return preds[b]; } + + void idaapi reset(void) { resize(0); } + + /// Recompute the layout, according to the value of + /// 'current_layout'. + /// + /// \return success + virtual bool idaapi redo_layout(void) GCC_PUREVIRT; + + /// Resize the graph to 'n' nodes + /// + /// \param n the new size + virtual void idaapi resize(int n) GCC_PUREVIRT; + + /// Add a node, possibly with a specific geometry + /// + /// \param r the node geometry (can be nullptr) + /// \return the new node + virtual int idaapi add_node(const rect_t *r) GCC_PUREVIRT; + + /// Delete a node + /// + /// \param n the node to delete + /// \return the number of deleted edges + virtual ssize_t idaapi del_node(int n) GCC_PUREVIRT; // returns number of deleted edges + + virtual bool idaapi add_edge(int i, int j, const edge_info_t *ei) GCC_PUREVIRT; + virtual bool idaapi del_edge(int i, int j) GCC_PUREVIRT; // true: found and deleted the edge + virtual bool idaapi replace_edge(int i, int j, int x, int y) GCC_PUREVIRT; + + /// Refresh the graph + /// + /// A graph needs refreshing when it's "backing data". E.g., if the + /// number (or contents) of the objects in the above example, + /// change. + /// + /// Let's say the user's plugin ends up finding a 5th piece of + /// scattered data. It should then add it to its internal list + /// of known objects, and tell IDA that the graph needs to be + /// refreshed, using refresh_viewer(). + /// This will cause IDA to: + /// - discard all its internal rendering information, + /// - call interactive_graph_t::refresh() on the graph so that the + /// user's plugin has a chance to "sync" the number of nodes & + /// edges that this graph contains, to the information that the + /// plugin has collected so far + /// - re-create internal rendering information, and + /// - repaint the view + /// + /// \return success + virtual bool idaapi refresh(void) GCC_PUREVIRT; + + virtual interactive_graph_t *idaapi clone(void) const override GCC_PUREVIRT; + + // get node rectangle + const rect_t &idaapi nrect(int n) const + { return (CONST_CAST(interactive_graph_t *)(this))->nrect(n); } + virtual rect_t &idaapi nrect(int n) override; + virtual edge_info_t *idaapi get_edge(edge_t e) override GCC_PUREVIRT; + + virtual bool idaapi set_nrect(int n, const rect_t &r) GCC_PUREVIRT; + virtual bool idaapi set_edge(edge_t e, const edge_info_t *ei) GCC_PUREVIRT; + + bool idaapi create_digraph_layout(void); + + void idaapi del_custom_layout(void); + bool idaapi get_custom_layout(void); + void idaapi set_custom_layout(void) const; + bool idaapi get_graph_groups(void); + void idaapi set_graph_groups(void) const; + virtual ea_t idaapi calc_group_ea(const intvec_t & /*nodes*/) newapi { return BADADDR; } + + point_t idaapi calc_center_of(const intvec_t &nodes) const; + void idaapi move_to_same_place(const intvec_t &collapsing_nodes, point_t p); + void idaapi move_grouped_nodes(const intvec_t &groups, const interactive_graph_t *ng); + + virtual bool idaapi is_user_graph() newapi { return false; } +}; + +//------------------------------------------------------------------------- +/// Visit all nodes and edges in a graph +class graph_visitor_t +{ +public: + virtual ~graph_visitor_t() {} +protected: + drawable_graph_t *g; + virtual int idaapi visit_node(int n, rect_t &r) = 0; + virtual int idaapi visit_edge(edge_t e, edge_info_t *ei) = 0; + friend int idaapi drawable_graph_t::for_all_nodes_edges(graph_visitor_t &nev, bool visit_nodes); +}; + +//------------------------------------------------------------------------- +/// Graph notification codes +enum graph_notification_t +{ + // Callbacks called by IDA (plugins can hook to them): + + grcode_calculating_layout, ///< calculating user-defined graph layout. + ///< \param g (::interactive_graph_t *) + ///< \retval 0 not implemented + ///< \retval 1 graph layout calculated by the plugin + + grcode_layout_calculated, ///< graph layout calculated. + ///< \param g (::interactive_graph_t *) + ///< \param layout_succeeded (bool) + ///< \retval 0 must return 0 + + grcode_changed_graph, ///< new graph has been set. + ///< \param g (::interactive_graph_t *) + ///< \retval 0 must return 0 + + grcode_reserved, + + grcode_clicked, ///< graph is being clicked. + ///< this callback allows you to ignore some clicks. + ///< it occurs too early, internal graph variables are not updated yet. + ///< current_item1, current_item2 point to the same thing. + ///< item2 has more information. + ///< see also: ::custom_viewer_click_t + ///< \param gv (::graph_viewer_t *) + ///< \param current_item1 (::selection_item_t *) + ///< \param current_item2 (::graph_item_t *) + ///< \retval 0 ok + ///< \retval 1 ignore click + + grcode_dblclicked, ///< a graph node has been double clicked. + ///< \param gv (::graph_viewer_t *) + ///< \param current_item (::selection_item_t *) + ///< \retval 0 ok + ///< \retval 1 ignore click + + grcode_creating_group, ///< a group is being created. + ///< this provides an opportunity for the + ///< graph to forbid creation of the group. + ///< Note that groups management is done by the + ///< interactive_graph_t instance itself: there is + ///< no need to modify the graph in this callback. + ///< \param g (::interactive_graph_t *) + ///< \param nodes (::intvec_t *) + ///< \retval 0 ok + ///< \retval 1 forbid group creation + + grcode_deleting_group, ///< a group is being deleted. + ///< this provides an opportunity for the + ///< graph to forbid deletion of the group. + ///< Note that groups management is done by the + ///< interactive_graph_t instance itself: there is + ///< no need to modify the graph in this callback. + ///< \param g (::interactive_graph_t *) + ///< \param old_group (int) + ///< \retval 0 ok + ///< \retval 1 forbid group deletion + + grcode_group_visibility, ///< a group is being collapsed/uncollapsed + ///< this provides an opportunity for the graph + ///< to forbid changing the visibility of the group. + ///< Note that groups management is done by the + ///< interactive_graph_t instance itself: there is + ///< no need to modify the graph in this callback. + ///< \param g (::interactive_graph_t *) + ///< \param group (int) + ///< \param expand (bool) + ///< \retval 0 ok + ///< \retval 1 forbid group modification + + grcode_gotfocus, ///< a graph viewer got focus. + ///< \param gv (::graph_viewer_t *) + ///< \retval 0 must return 0 + + grcode_lostfocus, ///< a graph viewer lost focus. + ///< \param gv (::graph_viewer_t *) + ///< \retval 0 must return 0 + + grcode_user_refresh, ///< refresh user-defined graph nodes and edges + ///< This is called when the UI considers that it is + ///< necessary to recreate the graph layout, and thus + ///< has to ensure that the 'interactive_graph_t' instance + ///< it is using, is up-to-date. + ///< For example: + ///< - at graph creation-time + ///< - if a refresh_viewer() call was made + ///< + ///< \param g (::interactive_graph_t *) + ///< \return success + + grcode_reserved2, + + grcode_user_text, ///< retrieve text for user-defined graph node. + ///< NB: do not use anything calling GDI! + ///< \param g (::interactive_graph_t *) + ///< \param node (int) + ///< \param result (const char **) + ///< \param bg_color (::bgcolor_t *) may be nullptr + ///< \return success, result must be filled + + grcode_user_size, ///< calculate node size for user-defined graph. + ///< \param g (::interactive_graph_t *) + ///< \param node (int) + ///< \param cx (int *) + ///< \param cy (int *) + ///< \retval 0 did not calculate. ida will use node text size + ///< \retval 1 calculated. ida will add node title to the size + + grcode_user_title, ///< render node title of a user-defined graph. + ///< \param g (::interactive_graph_t *) + ///< \param node (int) + ///< \param title_rect (::rect_t *) + ///< \param title_bg_color (int) + ///< \param dc (HDC) + ///< \retval 0 did not render, ida will fill it with title_bg_color + ///< \retval 1 rendered node title + + grcode_user_draw, ///< render node of a user-defined graph. + ///< NB: draw only on the specified DC and nowhere else! + ///< \param g (::interactive_graph_t *) + ///< \param node (int) + ///< \param node_rect (::rect_t *) + ///< \param dc (HDC) + ///< \retval 0 not rendered + ///< \retval 1 rendered + + grcode_user_hint, ///< retrieve hint for the user-defined graph. + ///< \param g (::interactive_graph_t *) + ///< \param mousenode (int) + ///< \param mouseedge_src (int) + ///< \param mouseedge_dst (int) + ///< \param hint (char **) must be allocated by qalloc() or qstrdup() + ///< \retval 0 use default hint + ///< \retval 1 use proposed hint + + grcode_destroyed, ///< graph is being destroyed. Note that this doesn't mean + ///< the graph viewer is being destroyed; this only means + ///< that the graph that is being displayed by it is being + ///< destroyed, and that, e.g., any possibly cached data should + ///< be invalidated (this event can happen when, for example, + ///< the user decides to group nodes together: that operation + ///< will effectively create a new graph, that will replace + ///< the old one.) + ///< To be notified when the graph viewer itself is being destroyed, + ///< please see notification 'view_close', in kernwin.hpp + ///< \param g (::interactive_graph_t *) + ///< \retval 0 must return 0 + + //------------------------------------------------------------------------- + // Callbacks callable from plugins (see inline functions below): + //------------------------------------------------------------------------- + + // graph_viewer_t (or IDA View graph) manipulation. + grcode_create_graph_viewer = 256, ///< use create_graph_viewer() + grcode_get_graph_viewer, ///< use get_graph_viewer() + grcode_get_viewer_graph, ///< use get_viewer_graph() + grcode_create_interactive_graph, ///< use create_interactive_graph() + grcode_set_viewer_graph, ///< use set_viewer_graph() + grcode_refresh_viewer, ///< use refresh_viewer() + grcode_fit_window, ///< use viewer_fit_window() + grcode_get_curnode, ///< use viewer_get_curnode() + grcode_center_on, ///< use viewer_center_on() + grcode_get_selection, ///< use viewer_get_selection() + + // interactive_graph_t (and drawable_graph_t) manipulation. + grcode_del_custom_layout, ///< use interactive_graph_t::del_custom_layout() + grcode_set_custom_layout, ///< use interactive_graph_t::set_custom_layout() + grcode_set_graph_groups, ///< use interactive_graph_t::set_graph_groups() + grcode_clear, ///< use interactive_graph_t::clear() + grcode_create_digraph_layout, ///< use interactive_graph_t::create_digraph_layout() + grcode_create_tree_layout, ///< use drawable_graph_t::create_tree_layout() + grcode_create_circle_layout, ///< use drawable_graph_t::create_circle_layout() + grcode_get_node_representative, ///< use interactive_graph_t::get_node_representative() + grcode_find_subgraph_node, ///< use interactive_graph_t::_find_subgraph_node() + grcode_create_group, ///< use interactive_graph_t::create_group() + grcode_get_custom_layout, ///< use interactive_graph_t::get_custom_layout() + grcode_get_graph_groups, ///< use interactive_graph_t::get_graph_groups() + grcode_empty, ///< use interactive_graph_t::empty() + grcode_is_visible_node, ///< use interactive_graph_t::is_visible_node() + grcode_delete_group, ///< use interactive_graph_t::delete_group() + grcode_change_group_visibility, ///< use interactive_graph_t::change_group_visibility() + grcode_set_edge, ///< use interactive_graph_t::set_edge() + grcode_node_qty, ///< use interactive_graph_t::node_qty() + grcode_nrect, ///< use interactive_graph_t::nrect() + + // More graph_viewer_t manipulation. + grcode_set_titlebar_height, ///< use viewer_set_titlebar_height() + grcode_create_user_graph_place, ///< use create_user_graph_place() + grcode_create_disasm_graph1, ///< use create_disasm_graph(ea_t ea) + grcode_create_disasm_graph2, ///< use create_disasm_graph(const rangevec_t &ranges) + grcode_set_node_info, ///< use viewer_set_node_info() + grcode_get_node_info, ///< use viewer_get_node_info() + grcode_del_node_info, ///< use viewer_del_node_info() + + // Deprecated. Those were meant to work with intset_t, that we now got rid of. + grcode_viewer_create_groups, + grcode_viewer_delete_groups, + grcode_viewer_groups_visibility, + + // + grcode_viewer_create_groups_vec, ///< use viewer_create_groups() + grcode_viewer_delete_groups_vec, ///< use viewer_delete_groups() + grcode_viewer_groups_visibility_vec,///< use viewer_set_groups_visibility() + grcode_delete_interactive_graph, ///< use delete_interactive_graph() + grcode_edge_infos_wrapper_copy, ///< use edge_infos_wrapper_t::operator=() + grcode_edge_infos_wrapper_clear, ///< use edge_infos_wrapper_t::clear() + + // + grcode_attach_menu_item, + + // + grcode_set_gli, ///< use viewer_set_gli() + grcode_get_gli, ///< use viewer_get_gli() +}; + + +//------------------------------------------------------------------------- +#ifndef SWIG +/// \name grentry +/// IDA designates a function for handling graph operations (::grentry). +/// This function accepts a notification code (::graph_notification_t), +/// along with a list of relevant arguments, and handles the action appropriately +/// (similar to ::callui in kernwin.hpp). +///@{ + + +inline ssize_t grentry(graph_notification_t event_code, ...) +{ + va_list va; + va_start(va, event_code); + ssize_t code = invoke_callbacks(HT_GRAPH, event_code, va); + va_end(va); + return code; +} +///@} +#endif + + +//------------------------------------------------------------------------- +struct group_crinfo_t +{ + intvec_t nodes; + qstring text; +}; +typedef qvector groups_crinfos_t; + +#ifndef __UI__ + +typedef TWidget graph_viewer_t; ///< graph view opaque structure + + +/// Create a custom graph viewer. +/// \param title the widget title +/// \param id graph id +/// \param callback callback to handle graph notifications +/// (::graph_notification_t) +/// \param ud user data passed to callback +/// \param title_height node title height +/// \param parent the parent widget of the graph viewer +/// \return new viewer + +inline graph_viewer_t *idaapi create_graph_viewer( + const char *title, + uval_t id, + hook_cb_t *callback, + void *ud, + int title_height, + TWidget *parent=nullptr) +{ + graph_viewer_t *gv = nullptr; + grentry(grcode_create_graph_viewer, title, &gv, id, callback, ud, title_height, parent); + return gv; +} + + +/// Get custom graph viewer for given form + +inline graph_viewer_t *idaapi get_graph_viewer(TWidget *parent) { graph_viewer_t *gv = nullptr; grentry(grcode_get_graph_viewer, parent, &gv); return gv; } + + +/// Create a new empty graph with given id + +inline interactive_graph_t *idaapi create_interactive_graph(uval_t id) { interactive_graph_t *g = nullptr; grentry(grcode_create_interactive_graph, id, &g); return g; } + + +/// Create a graph for the function that contains 'ea' + +inline interactive_graph_t *idaapi create_disasm_graph(ea_t ea) { interactive_graph_t *g = nullptr; grentry(grcode_create_disasm_graph1, ea, &g); return g; } + + +/// Create a graph using an arbitrary set of ranges + +inline interactive_graph_t *idaapi create_disasm_graph(const rangevec_t &ranges) { interactive_graph_t *g = nullptr; grentry(grcode_create_disasm_graph2, &ranges, &g); return g; } + + +/// Get graph object for given custom graph viewer + +inline interactive_graph_t *idaapi get_viewer_graph(graph_viewer_t *gv) { interactive_graph_t *g = nullptr; grentry(grcode_get_viewer_graph, gv, &g); return g; } + + +/// Set the underlying graph object for the given viewer + +inline void idaapi set_viewer_graph(graph_viewer_t *gv, interactive_graph_t *g) { grentry(grcode_set_viewer_graph, gv, g); } + + +/// Redraw the graph in the given view + +inline void idaapi refresh_viewer(graph_viewer_t *gv) { grentry(grcode_refresh_viewer, gv); } + + +/// Fit graph viewer to its parent form + +inline void idaapi viewer_fit_window(graph_viewer_t *gv) { grentry(grcode_fit_window, gv); } + + +/// Get number of currently selected node (-1 if none) + +inline int idaapi viewer_get_curnode(graph_viewer_t *gv) { return grentry(grcode_get_curnode, gv); } + + +/// Center the graph view on the given node + +inline void idaapi viewer_center_on(graph_viewer_t *gv, int node) { grentry(grcode_center_on, gv, node); } + +/// Set location info for given graph view +/// If flags contains GLICTL_CENTER, then the gli will be set to be +/// the center of the view. Otherwise it will be the top-left. + +inline void idaapi viewer_set_gli( + graph_viewer_t *gv, + const graph_location_info_t *gli, + uint32 flags = 0) +{ + grentry(grcode_set_gli, gv, gli, flags); +} + + +/// Get location info for given graph view +/// If flags contains GLICTL_CENTER, then the gli that will be retrieved, will +/// be the one at the center of the view. Otherwise it will be the top-left. + +inline bool idaapi viewer_get_gli( + graph_location_info_t *out, + graph_viewer_t *gv, + uint32 flags = 0) +{ + return grentry(grcode_get_gli, out, gv, flags) == 0; +} + + +/// Set node info for node in given viewer (see set_node_info()) + +inline void idaapi viewer_set_node_info( + graph_viewer_t *gv, + int n, + const node_info_t &ni, + uint32 flags) +{ + grentry(grcode_set_node_info, gv, n, &ni, flags); +} + + +/// Get node info for node in given viewer (see get_node_info()) + +inline bool idaapi viewer_get_node_info( + graph_viewer_t *gv, + node_info_t *out, + int n) +{ + return grentry(grcode_get_node_info, gv, out, n) == 1; +} + + +/// Delete node info for node in given viewer (see del_node_info()) + +inline void idaapi viewer_del_node_info(graph_viewer_t *gv, int n) +{ + grentry(grcode_del_node_info, gv, n); +} + + +/// This will perform an operation similar to what happens when +/// a user manually selects a set of nodes, right-clicks and selects +/// "Create group". This is a wrapper around interactive_graph_t::create_group +/// that will, in essence: +/// - clone the current graph +/// - for each group_crinfo_t, attempt creating group in that new graph +/// - if all were successful, animate to that new graph. +/// +/// \note this accepts parameters that allow creating +/// of multiple groups at once; which means only one graph +/// animation will be triggered. + +inline bool idaapi viewer_create_groups( + graph_viewer_t *gv, + intvec_t *out_group_nodes, + const groups_crinfos_t &gi) +{ + return grentry(grcode_viewer_create_groups_vec, gv, out_group_nodes, &gi) == 1; +} + + +/// Wrapper around interactive_graph_t::delete_group. +/// This function will: +/// - clone the current graph +/// - attempt deleting the groups in that new graph +/// - if successful, animate to that new graph. + +inline bool idaapi viewer_delete_groups( + graph_viewer_t *gv, + const intvec_t &groups, + int new_current = -1) +{ + return grentry(grcode_viewer_delete_groups_vec, gv, &groups, new_current) == 1; +} + + +/// Wrapper around interactive_graph_t::change_visibility. +/// This function will: +/// - clone the current graph +/// - attempt changing visibility of the groups in that new graph +/// - if successful, animate to that new graph. + +inline bool idaapi viewer_set_groups_visibility( + graph_viewer_t *gv, + const intvec_t &groups, + bool expand, + int new_current = -1) +{ + return grentry(grcode_viewer_groups_visibility_vec, gv, &groups, + expand, new_current) == 1; +} + + +/// Attach a previously-registered action to the view's context menu. +/// See kernwin.hpp for how to register actions. +/// \param g graph viewer +/// \param name action name +/// \return success + +inline bool idaapi viewer_attach_menu_item(graph_viewer_t *g, const char *name) +{ + return grentry(grcode_attach_menu_item, g, name) != 0; +} + + +/// Get currently selected items for graph viewer + +inline bool idaapi viewer_get_selection( + graph_viewer_t *gv, + screen_graph_selection_t *sgs) +{ + return grentry(grcode_get_selection, gv, sgs) != 0; +} + + +/// Set height of node title bars (::grcode_set_titlebar_height) + +inline int idaapi viewer_set_titlebar_height(graph_viewer_t *gv, int height) +{ + return grentry(grcode_set_titlebar_height, gv, height); +} + + +/// Delete graph object. +/// \warning use this only if you are dealing with ::interactive_graph_t instances +/// that have not been used together with a ::graph_viewer_t. +/// If you have called set_viewer_graph() with your graph, the graph's +/// lifecycle will be managed by the viewer, and you shouldn't +/// interfere with it + +inline void idaapi delete_interactive_graph(interactive_graph_t *g) +{ + grentry(grcode_delete_interactive_graph, g); +} + + +inline void idaapi interactive_graph_t::del_custom_layout(void) { grentry(grcode_del_custom_layout, this); } +inline void idaapi interactive_graph_t::set_custom_layout(void) const { grentry(grcode_set_custom_layout, this); } +inline void idaapi interactive_graph_t::set_graph_groups(void) const { grentry(grcode_set_graph_groups, this); } +inline void idaapi interactive_graph_t::clear(void) { grentry(grcode_clear, this); } +inline bool idaapi interactive_graph_t::create_digraph_layout(void) { return grentry(grcode_create_digraph_layout, this) != 0; } +inline bool idaapi drawable_graph_t::create_tree_layout(void) { return grentry(grcode_create_tree_layout, this) != 0; } +inline bool idaapi drawable_graph_t::create_circle_layout(point_t c, int radius) { return grentry(grcode_create_circle_layout, this, c.x, c.y, radius) != 0; } +inline int idaapi interactive_graph_t::get_node_representative(int node) { return grentry(grcode_get_node_representative, this, node); } +inline int idaapi interactive_graph_t::_find_subgraph_node(int gr, int n) const { return grentry(grcode_find_subgraph_node, this, gr, n); } +inline int idaapi interactive_graph_t::create_group(const intvec_t &_nodes) { return grentry(grcode_create_group, this, &_nodes); } +inline bool idaapi interactive_graph_t::get_custom_layout(void) { return grentry(grcode_get_custom_layout, this) != 0; } +inline bool idaapi interactive_graph_t::get_graph_groups(void) { return grentry(grcode_get_graph_groups, this) != 0; } +inline bool idaapi interactive_graph_t::empty(void) const { return grentry(grcode_empty, this) != 0; } +inline bool idaapi interactive_graph_t::is_visible_node(int node) const { return grentry(grcode_is_visible_node, this, node) != 0; } +inline bool idaapi interactive_graph_t::delete_group(int group) { return grentry(grcode_delete_group, this, group) != 0; } +inline bool idaapi interactive_graph_t::change_group_visibility(int gr, bool exp) { return grentry(grcode_change_group_visibility, this, gr, exp) != 0; } +inline bool idaapi interactive_graph_t::set_edge(edge_t e, const edge_info_t *ei) { return grentry(grcode_set_edge, this, e.src, e.dst, ei) != 0; } +inline int idaapi interactive_graph_t::node_qty(void) const { return grentry(grcode_node_qty, this); } +inline rect_t &idaapi interactive_graph_t::nrect(int n) { rect_t *r; grentry(grcode_nrect, this, n, &r); return *r; } + +inline edge_infos_wrapper_t &idaapi edge_infos_wrapper_t::operator=( + const edge_infos_wrapper_t &other) +{ + grentry(grcode_edge_infos_wrapper_copy, this, &other); return *this; +} + +inline void edge_infos_wrapper_t::clear() +{ + grentry(grcode_edge_infos_wrapper_clear, this); +} + + +/// Structure returned by get_custom_viewer_place() if the first +/// parameter is a graph viewer. +struct user_graph_place_t : public place_t +{ + int node; +}; + + +/// Get a copy of a ::user_graph_place_t (returns a pointer to static storage) + +inline user_graph_place_t *create_user_graph_place(int node, int lnnum) { user_graph_place_t *r; grentry(grcode_create_user_graph_place, node, lnnum, &r); return r; } + +#endif // UI + +#endif // __GRAPH_DEF_HPP + +``` + +`IdaSDK/help.h`: + +```h +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _HELP_H +#define _HELP_H + +typedef int help_t; /* Help messages are referred by ints */ + +// Get pointer to message text by its message id +// The message texts are read from ida.hlp at the beginning +// Returns: pointer to message text (nullptr is never returned by IDA) + +idaman THREAD_SAFE const char *ida_export itext(help_t msg_id); + +#ifdef __KERNWIN_HPP +GCC_DIAG_OFF(format-nonliteral); +NORETURN inline void Err(help_t format, ...) +{ + va_list va; + va_start(va, format); + verror(itext(format), va); + // NOTREACHED +} + +inline void Warn(help_t format, ...) +{ + va_list va; + va_start(va, format); + vwarning(itext(format), va); + va_end(va); +} + +inline void Info(help_t format, ...) +{ + va_list va; + va_start(va, format); + vinfo(itext(format), va); + va_end(va); +} + +inline int Message(help_t format, ...) +{ + va_list va; + va_start(va, format); + int nbytes = vmsg(itext(format), va); + va_end(va); + return nbytes; +} + +inline int vask_yn(int deflt, help_t format, va_list va) +{ + return vask_yn(deflt, itext(format), va); +} + +inline int ask_yn(int deflt, help_t format, ...) +{ + va_list va; + va_start(va, format); + int code = vask_yn(deflt, itext(format), va); + va_end(va); + return code; +} +GCC_DIAG_ON(format-nonliteral); +#endif + +#ifndef NO_OBSOLETE_FUNCS +#endif + +#endif /* _HELP_H */ + +``` + +`IdaSDK/hexrays.hpp`: + +```hpp +/*! + * Hex-Rays Decompiler project + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + */ + +#ifndef __HEXRAYS_HPP +#define __HEXRAYS_HPP + +/*! + * \file hexrays.hpp + * \brief There are 2 representations of the binary code in the decompiler: + * + * - microcode: processor instructions are translated into it and then + * the decompiler optimizes and transforms it + * - ctree: ctree is built from the optimized microcode and represents + * AST-like tree with C statements and expressions. It can + * be printed as C code. + * + * Microcode is represented by the following classes: + * - mba_t keeps general info about the decompiled code and + * array of basic blocks. usually mba_t is named 'mba' + * - mblock_t a basic block. includes list of instructions + * - minsn_t an instruction. contains 3 operands: left, right, and + * destination + * - mop_t an operand. depending on its type may hold various info + * like a number, register, stack variable, etc. + * - mlist_t list of memory or register locations; can hold vast areas + * of memory and multiple registers. this class is used + * very extensively in the decompiler. it may represent + * list of locations accessed by an instruction or even + * an entire basic block. it is also used as argument of + * many functions. for example, there is a function + * that searches for an instruction that refers to a mlist_t. + * + * See https://hex-rays.com/blog/microcode-in-pictures for a few pictures. + * + * Ctree is represented by: + * - cfunc_t keeps general info about the decompiled code, including a + * pointer to mba_t. deleting cfunc_t will delete + * mba_t too (however, decompiler returns cfuncptr_t, + * which is a reference counting object and deletes the + * underlying function as soon as all references to it go + * out of scope). cfunc_t has 'body', which represents the + * decompiled function body as cinsn_t. + * - cinsn_t a C statement. can be a compound statement or any other + * legal C statements (like if, for, while, return, + * expression-statement, etc). depending on the statement + * type has pointers to additional info. for example, the + * 'if' statement has poiner to cif_t, which holds the + * 'if' condition, 'then' branch, and optionally 'else' + * branch. Please note that despite of the name cinsn_t + * we say "statements", not "instructions". For us + * instructions are part of microcode, not ctree. + * - cexpr_t a C expression. is used as part of a C statement, when + * necessary. cexpr_t has 'type' field, which keeps the + * expression type. + * - citem_t a base class for cinsn_t and cexpr_t, holds common info + * like the address, label, and opcode. + * - cnumber_t a constant 64-bit number. in addition to its value also + * holds information how to represent it: decimal, hex, or + * as a symbolic constant (enum member). please note that + * numbers are represented by another class (mnumber_t) + * in microcode. + + * See https://hex-rays.com/blog/hex-rays-decompiler-primer + * for more pictures and more details. + * + * Both microcode and ctree use the following class: + * - lvar_t a local variable. may represent a stack or register + * variable. a variable has a name, type, location, etc. + * the list of variables is stored in mba->vars. + * - lvar_locator_t holds a variable location (vdloc_t) and its definition + * address. + * - vdloc_t describes a variable location, like a register number, + * a stack offset, or, in complex cases, can be a mix of + * register and stack locations. very similar to argloc_t, + * which is used in ida. the differences between argloc_t + * and vdloc_t are: + * - vdloc_t never uses ARGLOC_REG2 + * - vdloc_t uses micro register numbers instead of + * processor register numbers + * - the stack offsets are never negative in vdloc_t, while + * in argloc_t there can be negative offsets + * + * The above are the most important classes in this header file. There are + * many auxiliary classes, please see their definitions in the header file. + * + * See also the description of \ref vmpage. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*! + * \page vmpage Virtual Machine used by Microcode + * We can imagine a virtual micro machine that executes microcode. + * This virtual micro machine has many registers. + * Each register is 8 bits wide. During translation of processor + * instructions into microcode, multibyte processor registers are mapped + * to adjacent microregisters. Processor condition codes are also + * represented by microregisters. The microregisters are grouped + * into following groups: + * - 0..7: condition codes + * - 8..n: all processor registers (including fpu registers, if necessary) + * this range may also include temporary registers used during + * the initial microcode generation + * - n.. : so called kernel registers; they are used during optimization + * see is_kreg() + * + * Each micro-instruction (minsn_t) has zero to three operands. + * Some of the possible operands types are: + * - immediate value + * - register + * - memory reference + * - result of another micro-instruction + * + * The operands (mop_t) are l (left), r (right), d (destination). + * An example of a microinstruction: + * + * add r0.4, #8.4, r2.4 + * + * which means 'add constant 8 to r0 and place the result into r2'. + * where + * - the left operand is 'r0', its size is 4 bytes (r0.4) + * - the right operand is a constant '8', its size is 4 bytes (#8.4) + * - the destination operand is 'r2', its size is 4 bytes (r2.4) + * Note that 'd' is almost always the destination but there are exceptions. + * See mcode_modifies_d(). For example, stx does not modify 'd'. + * See the opcode map below for the list of microinstructions and their + * operands. Most instructions are very simple and do not need + * detailed explanations. There are no side effects in microinstructions. + * + * Each operand has a size specifier. The following sizes can be used in + * practically all contexts: 1, 2, 4, 8, 16 bytes. Floating types may have + * other sizes. Functions may return objects of arbitrary size, as well as + * operations upon UDT's (user-defined types, i.e. are structs and unions). + * + * Memory is considered to consist of several segments. + * A memory reference is made using a (selector, offset) pair. + * A selector is always 2 bytes long. An offset can be 4 or 8 bytes long, + * depending on the bitness of the target processor. + * Currently the selectors are not used very much. The decompiler tries to + * resolve (selector, offset) pairs into direct memory references at each + * opportunity and then operates on mop_v operands. In other words, + * while the decompiler can handle segmented memory models, internally + * it still uses simple linear addresses. + * + * The following memory regions are recognized: + * - GLBLOW global memory: low part, everything below the stack + * - LVARS stack: local variables + * - RETADDR stack: return address + * - SHADOW stack: shadow arguments + * - ARGS stack: regular stack arguments + * - GLBHIGH global memory: high part, everything above the stack + * Any stack region may be empty. Objects residing in one memory region + * are considered to be completely distinct from objects in other regions. + * We allocate the stack frame in some memory region, which is not + * allocated for any purposes in IDA. This permits us to use linear addresses + * for all memory references, including the stack frame. + * + * If the operand size is bigger than 1 then the register + * operand references a block of registers. For example: + * + * ldc #1.4, r8.4 + * + * loads the constant 1 to registers 8, 9, 10, 11: + * + * #1 -> r8 + * #0 -> r9 + * #0 -> r10 + * #0 -> r11 + * + * This example uses little-endian byte ordering. + * Big-endian byte ordering is supported too. Registers are always little- + * endian, regardless of the memory endianness. + * + * Each instruction has 'next' and 'prev' fields that are used to form + * a doubly linked list. Such lists are present for each basic block (mblock_t). + * Basic blocks have other attributes, including: + * - dead_at_start: list of dead locations at the block start + * - maybuse: list of locations the block may use + * - maybdef: list of locations the block may define (or spoil) + * - mustbuse: list of locations the block will certainly use + * - mustbdef: list of locations the block will certainly define + * - dnu: list of locations the block will certainly define + * but will not use (registers or non-aliasable stkack vars) + * + * These lists are represented by the mlist_t class. It consists of 2 parts: + * - rlist_t: list of microregisters (possibly including virtual stack locations) + * - ivlset_t: list of memory locations represented as intervals + * we use linear addresses in this list. + * The mlist_t class is used quite often. For example, to find what an operand + * can spoil, we build its 'maybe-use' list. Then we can find out if this list + * is accessed using the is_accessed() or is_accessed_globally() functions. + * + * All basic blocks of the decompiled function constitute an array called + * mba_t (array of microblocks). This is a huge class that has too + * many fields to describe here (some of the fields are not visible in the sdk) + * The most importants ones are: + * - stack frame: frregs, stacksize, etc + * - memory: aliased, restricted, and other ranges + * - type: type of the current function, its arguments (argidx) and + * local variables (vars) + * - natural: array of pointers to basic blocks. the basic blocks + * are also accessible as a doubly linked list starting from 'blocks'. + * - bg: control flow graph. the graph gives access to the use-def + * chains that describe data dependencies between basic blocks + * + * Facilities for debugging decompiler plugins: + * Many decompiler objects have a member function named dstr(). + * These functions create a text representation of the object and return + * a pointer to it. They are very convenient to use in a debugger instead of + * inspecting class fields manually. The mba_t object does not have the + * dstr() function because its text representation very long. Instead, we + * provide the mba_t::dump_mba() and mba_t::dump() functions. + * + * To ensure that your plugin manipulates the microcode in a correct way, + * please call mba_t::verify() before returning control to the decompiler. + * + */ + +#ifdef __NT__ +#pragma warning(push) +#pragma warning(disable:4062) // enumerator 'x' in switch of enum 'y' is not handled +#pragma warning(disable:4265) // virtual functions without virtual destructor +#endif + +#define hexapi ///< Public functions are marked with this keyword + +// Lint suppressions: +//lint -sem(mop_t::_make_cases, custodial(1)) +//lint -sem(mop_t::_make_pair, custodial(1)) +//lint -sem(mop_t::_make_callinfo, custodial(1)) +//lint -sem(mop_t::_make_insn, custodial(1)) +//lint -sem(mop_t::make_insn, custodial(1)) + +// Microcode level forward definitions: +class mop_t; // microinstruction operand +class mop_pair_t; // pair of operands. example, :(edx.4,eax.4).8 +class mop_addr_t; // address of an operand. example: &global_var +class mcallinfo_t; // function call info. example: .8 +class mcases_t; // jump table cases. example: {0 => 12, 1 => 13} +class minsn_t; // microinstruction +class mblock_t; // basic block +class mba_t; // array of blocks, represents microcode for a function +class codegen_t; // helper class to generate the initial microcode +class mbl_graph_t; // control flow graph of microcode +class control_graph_t; // the result of structural analysis +class edge_mapper_t; +struct vdui_t; // widget representing the pseudocode window +struct hexrays_failure_t; // decompilation failure object, is thrown by exceptions +struct mba_stats_t; // statistics about decompilation of a function +struct mlist_t; // list of memory and register locations +struct voff_t; // value offset (microregister number or stack offset) +typedef std::set voff_set_t; +struct vivl_t; // value interval (register or stack range) +typedef int mreg_t; ///< Micro register + +// Ctree level forward definitions: +struct cfunc_t; // result of decompilation, the highest level object +struct citem_t; // base class for cexpr_t and cinsn_t +struct cexpr_t; // C expression +struct cinsn_t; // C statement +struct cblock_t; // C statement block (sequence of statements) +struct cswitch_t; // C switch statement +struct carg_t; // call argument +struct carglist_t; // vector of call arguments +struct ctry_t; // C++ try-statement +struct cthrow_t; // C++ throw-statement + +typedef std::set easet_t; +typedef std::set minsn_ptr_set_t; +typedef std::set strings_t; +typedef qvector minsnptrs_t; +typedef qvector mopptrs_t; +typedef qvector mopvec_t; +typedef qvector uint64vec_t; +typedef qvector mregvec_t; +typedef qrefcnt_t cfuncptr_t; + +// Function frames must be smaller than this value, otherwise +// the decompiler will bail out with MERR_HUGESTACK +#define MAX_SUPPORTED_STACK_SIZE 0x100000 // 1MB + +//------------------------------------------------------------------------- +// Original version of macro DEFINE_MEMORY_ALLOCATION_FUNCS +// (uses decompiler-specific memory allocation functions) +#define HEXRAYS_PLACEMENT_DELETE void operator delete(void *, void *) {} +#define HEXRAYS_MEMORY_ALLOCATION_FUNCS() \ + void *operator new (size_t _s) { return hexrays_alloc(_s); } \ + void *operator new[](size_t _s) { return hexrays_alloc(_s); } \ + void *operator new(size_t /*size*/, void *_v) { return _v; } \ + void operator delete (void *_blk) { hexrays_free(_blk); } \ + void operator delete[](void *_blk) { hexrays_free(_blk); } \ + HEXRAYS_PLACEMENT_DELETE + +void *hexapi hexrays_alloc(size_t size); +void hexapi hexrays_free(void *ptr); + +typedef uint64 uvlr_t; +typedef int64 svlr_t; +enum { MAX_VLR_SIZE = sizeof(uvlr_t) }; +const uvlr_t MAX_VLR_VALUE = uvlr_t(-1); +const svlr_t MAX_VLR_SVALUE = svlr_t(uvlr_t(-1) >> 1); +const svlr_t MIN_VLR_SVALUE = ~MAX_VLR_SVALUE; + +//------------------------------------------------------------------------- +inline uvlr_t max_vlr_value(int size) +{ + return size == MAX_VLR_SIZE + ? MAX_VLR_VALUE + : (uvlr_t(1) << (size * 8)) - 1; +} +inline uvlr_t min_vlr_svalue(int size) +{ + return size == MAX_VLR_SIZE + ? MIN_VLR_SVALUE + : (uvlr_t(1) << (size * 8 - 1)); +} +inline uvlr_t max_vlr_svalue(int size) +{ + return size == MAX_VLR_SIZE + ? MAX_VLR_SVALUE + : (uvlr_t(1) << (size * 8 - 1)) - 1; +} + +enum cmpop_t +{ // the order of comparisons is the same as in microcode opcodes + CMP_NZ, + CMP_Z, + CMP_AE, + CMP_B, + CMP_A, + CMP_BE, + CMP_GT, + CMP_GE, + CMP_LT, + CMP_LE, +}; +inline bool is_unsigned_cmpop(cmpop_t cmpop) +{ + return cmpop >= CMP_AE && cmpop <= CMP_BE; +} +inline bool is_signed_cmpop(cmpop_t cmpop) +{ + return cmpop >= CMP_GT && cmpop <= CMP_LE; +} +inline bool is_cmpop_with_eq(cmpop_t cmpop) +{ + return cmpop == CMP_AE + || cmpop == CMP_BE + || cmpop == CMP_GE + || cmpop == CMP_LE; +} +inline bool is_cmpop_without_eq(cmpop_t cmpop) +{ + return cmpop == CMP_A + || cmpop == CMP_B + || cmpop == CMP_GT + || cmpop == CMP_LT; +} + +//------------------------------------------------------------------------- +// value-range class to keep possible operand value(s). +class valrng_t +{ +protected: + int flags; +#define VLR_TYPE 0x0F // valrng_t type +#define VLR_NONE 0x00 // no values +#define VLR_ALL 0x01 // all values +#define VLR_IVLS 0x02 // union of disjoint intervals +#define VLR_RANGE 0x03 // strided range +#define VLR_SRANGE 0x04 // strided range with signed bound +#define VLR_BITS 0x05 // known bits +#define VLR_SECT 0x06 // intersection of sub-ranges + // each sub-range should be simple or union +#define VLR_UNION 0x07 // union of sub-ranges + // each sub-range should be simple or + // intersection +#define VLR_UNK 0x08 // unknown value (like 'null' in SQL) + int size; // operand size: 1..8 bytes + // all values must fall within the size + union + { + struct // VLR_RANGE/VLR_SRANGE + { // values that are between VALUE and LIMIT + // and conform to: value+stride*N + uvlr_t value; // initial value + uvlr_t limit; // final value + // we adjust LIMIT to be on the STRIDE lattice + svlr_t stride; // stride between values + }; + struct // VLR_BITS + { + uvlr_t zeroes; // bits known to be clear + uvlr_t ones; // bits known to be set + }; + char reserved[sizeof(qvector)]; + // VLR_IVLS/VLR_SECT/VLR_UNION + }; + void hexapi clear(); + void hexapi copy(const valrng_t &r); + valrng_t &hexapi assign(const valrng_t &r); + +public: + explicit valrng_t(int size_ = MAX_VLR_SIZE) + : flags(VLR_NONE), size(size_), value(0), limit(0), stride(0) {} + valrng_t(const valrng_t &r) { copy(r); } + ~valrng_t() { clear(); } + valrng_t &operator=(const valrng_t &r) { return assign(r); } + void swap(valrng_t &r) { qswap(*this, r); } + DECLARE_COMPARISONS(valrng_t); + DEFINE_MEMORY_ALLOCATION_FUNCS() + + void set_none() { clear(); } + void set_all() { clear(); flags = VLR_ALL; } + void set_unk() { clear(); flags = VLR_UNK; } + void hexapi set_eq(uvlr_t v); + void hexapi set_cmp(cmpop_t cmp, uvlr_t _value); + + // reduce size + // it takes the low part of size NEW_SIZE + // it returns "true" if size is changed successfully. + // e.g.: valrng_t vr(2); vr.set_eq(0x1234); + // vr.reduce_size(1); + // uvlr_t v; vr.cvt_to_single_value(&v); + // assert(v == 0x34); + bool hexapi reduce_size(int new_size); + + // Perform intersection or union or inversion. + // \return did we change something in THIS? + bool hexapi intersect_with(const valrng_t &r); + bool hexapi unite_with(const valrng_t &r); + void hexapi inverse(); // works for VLR_IVLS only + + bool empty() const { return flags == VLR_NONE; } + bool all_values() const { return flags == VLR_ALL; } + bool is_unknown() const { return flags == VLR_UNK; } + bool hexapi has(uvlr_t v) const; + + void hexapi print(qstring *vout) const; + const char *hexapi dstr() const; + + bool hexapi cvt_to_single_value(uvlr_t *v) const; + bool hexapi cvt_to_cmp(cmpop_t *cmp, uvlr_t *val) const; + + int get_size() const { return size; } + uvlr_t max_value() const { return max_vlr_value(size); } + uvlr_t min_svalue() const { return min_vlr_svalue(size); } + uvlr_t max_svalue() const { return max_vlr_svalue(size); } +}; +DECLARE_TYPE_AS_MOVABLE(valrng_t); + +//------------------------------------------------------------------------- +// Are we looking for 'must access' or 'may access' information? +// 'must access' means that the code will always access the specified location(s) +// 'may access' means that the code may in some cases access the specified location(s) +// Example: ldx cs.2, r0.4, r1.4 +// MUST_ACCESS: r0.4 and r1.4, usually displayed as r0.8 because r0 and r1 are adjacent +// MAY_ACCESS: r0.4 and r1.4, and all aliasable memory, because +// ldx may access any part of the aliasable memory +typedef int maymust_t; +const maymust_t + // One of the following two bits should be specified: + MUST_ACCESS = 0x00, // access information we can count on + MAY_ACCESS = 0x01, // access information we should take into account + // Optionally combined with the following bits: + MAYMUST_ACCESS_MASK = 0x01, + + ONE_ACCESS_TYPE = 0x20, // for find_first_use(): + // use only the specified maymust access type + // (by default it inverts the access type for def-lists) + INCLUDE_SPOILED_REGS = 0x40, // for build_def_list() with MUST_ACCESS: + // include spoiled registers in the list + EXCLUDE_PASS_REGS = 0x80, // for build_def_list() with MAY_ACCESS: + // exclude pass_regs from the list + FULL_XDSU = 0x100, // for build_def_list(): + // if xds/xdu source and targets are the same + // treat it as if xdsu redefines the entire destination + WITH_ASSERTS = 0x200, // for find_first_use(): + // do not ignore assertions + EXCLUDE_VOLATILE = 0x400, // for build_def_list(): + // exclude volatile memory from the list + INCLUDE_UNUSED_SRC = 0x800, // for build_use_list(): + // do not exclude unused source bytes for m_and/m_or insns + INCLUDE_DEAD_RETREGS = 0x1000, // for build_def_list(): + // include dead returned registers in the list + INCLUDE_RESTRICTED = 0x2000,// for MAY_ACCESS: include restricted memory + CALL_SPOILS_ONLY_ARGS = 0x4000;// for build_def_list() & MAY_ACCESS: + // do not include global memory into the + // spoiled list of a call + +inline THREAD_SAFE bool is_may_access(maymust_t maymust) +{ + return (maymust & MAYMUST_ACCESS_MASK) != MUST_ACCESS; +} + +//------------------------------------------------------------------------- +/// \defgroup MERR_ Microcode error code +///@{ +enum merror_t +{ + MERR_OK = 0, ///< ok + MERR_BLOCK = 1, ///< no error, switch to new block + MERR_INTERR = -1, ///< internal error + MERR_INSN = -2, ///< cannot convert to microcode + MERR_MEM = -3, ///< not enough memory + MERR_BADBLK = -4, ///< bad block found + MERR_BADSP = -5, ///< positive sp value has been found + MERR_PROLOG = -6, ///< prolog analysis failed + MERR_SWITCH = -7, ///< wrong switch idiom + MERR_EXCEPTION = -8, ///< exception analysis failed + MERR_HUGESTACK = -9, ///< stack frame is too big + MERR_LVARS = -10, ///< local variable allocation failed + MERR_BITNESS = -11, ///< 16-bit functions cannot be decompiled + MERR_BADCALL = -12, ///< could not determine call arguments + MERR_BADFRAME = -13, ///< function frame is wrong + MERR_UNKTYPE = -14, ///< undefined type %s (currently unused error code) + MERR_BADIDB = -15, ///< inconsistent database information + MERR_SIZEOF = -16, ///< wrong basic type sizes in compiler settings + MERR_REDO = -17, ///< redecompilation has been requested + MERR_CANCELED = -18, ///< decompilation has been cancelled + MERR_RECDEPTH = -19, ///< max recursion depth reached during lvar allocation + MERR_OVERLAP = -20, ///< variables would overlap: %s + MERR_PARTINIT = -21, ///< partially initialized variable %s + MERR_COMPLEX = -22, ///< too complex function + MERR_LICENSE = -23, ///< no license available + MERR_ONLY32 = -24, ///< only 32-bit functions can be decompiled for the current database + MERR_ONLY64 = -25, ///< only 64-bit functions can be decompiled for the current database + MERR_BUSY = -26, ///< already decompiling a function + MERR_FARPTR = -27, ///< far memory model is supported only for pc + MERR_EXTERN = -28, ///< special segments cannot be decompiled + MERR_FUNCSIZE = -29, ///< too big function + MERR_BADRANGES = -30, ///< bad input ranges + MERR_BADARCH = -31, ///< current architecture is not supported + MERR_DSLOT = -32, ///< bad instruction in the delay slot + MERR_STOP = -33, ///< no error, stop the analysis + MERR_CLOUD = -34, ///< cloud: %s + MERR_EMULATOR = -35, ///< emulator: %s + MERR_MAX_ERR = 35, + MERR_LOOP = -36, ///< internal code: redo last loop (never reported) +}; +///@} + +/// Get textual description of an error code +/// \param out the output buffer for the error description +/// \param code \ref MERR_ +/// \param mba the microcode array +/// \return the error address + +ea_t hexapi get_merror_desc(qstring *out, merror_t code, mba_t *mba); + +//------------------------------------------------------------------------- +// List of microinstruction opcodes. +// The order of setX and jX insns is important, it is used in the code. + +// Instructions marked with *F may have the FPINSN bit set and operate on fp values +// Instructions marked with +F must have the FPINSN bit set. They always operate on fp values +// Other instructions do not operate on fp values. + +enum mcode_t +{ + m_nop = 0x00, // nop // no operation + m_stx = 0x01, // stx l, {r=sel, d=off} // store register to memory *F + m_ldx = 0x02, // ldx {l=sel,r=off}, d // load register from memory *F + m_ldc = 0x03, // ldc l=const, d // load constant + m_mov = 0x04, // mov l, d // move *F + m_neg = 0x05, // neg l, d // negate + m_lnot = 0x06, // lnot l, d // logical not + m_bnot = 0x07, // bnot l, d // bitwise not + m_xds = 0x08, // xds l, d // extend (signed) + m_xdu = 0x09, // xdu l, d // extend (unsigned) + m_low = 0x0A, // low l, d // take low part + m_high = 0x0B, // high l, d // take high part + m_add = 0x0C, // add l, r, d // l + r -> dst + m_sub = 0x0D, // sub l, r, d // l - r -> dst + m_mul = 0x0E, // mul l, r, d // l * r -> dst + m_udiv = 0x0F, // udiv l, r, d // l / r -> dst + m_sdiv = 0x10, // sdiv l, r, d // l / r -> dst + m_umod = 0x11, // umod l, r, d // l % r -> dst + m_smod = 0x12, // smod l, r, d // l % r -> dst + m_or = 0x13, // or l, r, d // bitwise or + m_and = 0x14, // and l, r, d // bitwise and + m_xor = 0x15, // xor l, r, d // bitwise xor + m_shl = 0x16, // shl l, r, d // shift logical left + m_shr = 0x17, // shr l, r, d // shift logical right + m_sar = 0x18, // sar l, r, d // shift arithmetic right + m_cfadd = 0x19, // cfadd l, r, d=carry // calculate carry bit of (l+r) + m_ofadd = 0x1A, // ofadd l, r, d=overf // calculate overflow bit of (l+r) + m_cfshl = 0x1B, // cfshl l, r, d=carry // calculate carry bit of (l<>r) + m_sets = 0x1D, // sets l, d=byte SF=1 Sign + m_seto = 0x1E, // seto l, r, d=byte OF=1 Overflow of (l-r) + m_setp = 0x1F, // setp l, r, d=byte PF=1 Unordered/Parity *F + m_setnz = 0x20, // setnz l, r, d=byte ZF=0 Not Equal *F + m_setz = 0x21, // setz l, r, d=byte ZF=1 Equal *F + m_setae = 0x22, // setae l, r, d=byte CF=0 Unsigned Above or Equal *F + m_setb = 0x23, // setb l, r, d=byte CF=1 Unsigned Below *F + m_seta = 0x24, // seta l, r, d=byte CF=0 & ZF=0 Unsigned Above *F + m_setbe = 0x25, // setbe l, r, d=byte CF=1 | ZF=1 Unsigned Below or Equal *F + m_setg = 0x26, // setg l, r, d=byte SF=OF & ZF=0 Signed Greater + m_setge = 0x27, // setge l, r, d=byte SF=OF Signed Greater or Equal + m_setl = 0x28, // setl l, r, d=byte SF!=OF Signed Less + m_setle = 0x29, // setle l, r, d=byte SF!=OF | ZF=1 Signed Less or Equal + m_jcnd = 0x2A, // jcnd l, d // d is mop_v or mop_b + m_jnz = 0x2B, // jnz l, r, d // ZF=0 Not Equal *F + m_jz = 0x2C, // jz l, r, d // ZF=1 Equal *F + m_jae = 0x2D, // jae l, r, d // CF=0 Unsigned Above or Equal *F + m_jb = 0x2E, // jb l, r, d // CF=1 Unsigned Below *F + m_ja = 0x2F, // ja l, r, d // CF=0 & ZF=0 Unsigned Above *F + m_jbe = 0x30, // jbe l, r, d // CF=1 | ZF=1 Unsigned Below or Equal *F + m_jg = 0x31, // jg l, r, d // SF=OF & ZF=0 Signed Greater + m_jge = 0x32, // jge l, r, d // SF=OF Signed Greater or Equal + m_jl = 0x33, // jl l, r, d // SF!=OF Signed Less + m_jle = 0x34, // jle l, r, d // SF!=OF | ZF=1 Signed Less or Equal + m_jtbl = 0x35, // jtbl l, r=mcases // Table jump + m_ijmp = 0x36, // ijmp {r=sel, d=off} // indirect unconditional jump + m_goto = 0x37, // goto l // l is mop_v or mop_b + m_call = 0x38, // call l d // l is mop_v or mop_b or mop_h + m_icall = 0x39, // icall {l=sel, r=off} d // indirect call + m_ret = 0x3A, // ret + m_push = 0x3B, // push l + m_pop = 0x3C, // pop d + m_und = 0x3D, // und d // undefine + m_ext = 0x3E, // ext in1, in2, out1 // external insn, not microcode *F + m_f2i = 0x3F, // f2i l, d int(l) => d; convert fp -> integer +F + m_f2u = 0x40, // f2u l, d uint(l)=> d; convert fp -> uinteger +F + m_i2f = 0x41, // i2f l, d fp(l) => d; convert integer -> fp +F + m_u2f = 0x42, // i2f l, d fp(l) => d; convert uinteger -> fp +F + m_f2f = 0x43, // f2f l, d l => d; change fp precision +F + m_fneg = 0x44, // fneg l, d -l => d; change sign +F + m_fadd = 0x45, // fadd l, r, d l + r => d; add +F + m_fsub = 0x46, // fsub l, r, d l - r => d; subtract +F + m_fmul = 0x47, // fmul l, r, d l * r => d; multiply +F + m_fdiv = 0x48, // fdiv l, r, d l / r => d; divide +F +#define m_max 0x49 // first unused opcode +}; + +/// Must an instruction with the given opcode be the last one in a block? +/// Such opcodes are called closing opcodes. +/// \param mcode instruction opcode +/// \param including_calls should m_call/m_icall be considered as the closing opcodes? +/// If this function returns true, the opcode cannot appear in the middle +/// of a block. Calls are a special case: unknown calls (\ref is_unknown_call) +/// are considered as closing opcodes. + +THREAD_SAFE bool hexapi must_mcode_close_block(mcode_t mcode, bool including_calls); + + +/// May opcode be propagated? +/// Such opcodes can be used in sub-instructions (nested instructions) +/// There is a handful of non-propagatable opcodes, like jumps, ret, nop, etc +/// All other regular opcodes are propagatable and may appear in a nested +/// instruction. + +THREAD_SAFE bool hexapi is_mcode_propagatable(mcode_t mcode); + + +// Is add or sub instruction? +inline THREAD_SAFE bool is_mcode_addsub(mcode_t mcode) { return mcode == m_add || mcode == m_sub; } +// Is xds or xdu instruction? We use 'xdsu' as a shortcut for 'xds or xdu' +inline THREAD_SAFE bool is_mcode_xdsu(mcode_t mcode) { return mcode == m_xds || mcode == m_xdu; } +// Is a 'set' instruction? (an instruction that sets a condition code) +inline THREAD_SAFE bool is_mcode_set(mcode_t mcode) { return mcode >= m_sets && mcode <= m_setle; } +// Is a 1-operand 'set' instruction? Only 'sets' is in this group +inline THREAD_SAFE bool is_mcode_set1(mcode_t mcode) { return mcode == m_sets; } +// Is a 1-operand conditional jump instruction? Only 'jcnd' is in this group +inline THREAD_SAFE bool is_mcode_j1(mcode_t mcode) { return mcode == m_jcnd; } +// Is a conditional jump? +inline THREAD_SAFE bool is_mcode_jcond(mcode_t mcode) { return mcode >= m_jcnd && mcode <= m_jle; } +// Is a 'set' instruction that can be converted into a conditional jump? +inline THREAD_SAFE bool is_mcode_convertible_to_jmp(mcode_t mcode) { return mcode >= m_setnz && mcode <= m_setle; } +// Is a conditional jump instruction that can be converted into a 'set'? +inline THREAD_SAFE bool is_mcode_convertible_to_set(mcode_t mcode) { return mcode >= m_jnz && mcode <= m_jle; } +// Is a call instruction? (direct or indirect) +inline THREAD_SAFE bool is_mcode_call(mcode_t mcode) { return mcode == m_call || mcode == m_icall; } +// Must be an FPU instruction? +inline THREAD_SAFE bool is_mcode_fpu(mcode_t mcode) { return mcode >= m_f2i; } +// Is a commutative instruction? +inline THREAD_SAFE bool is_mcode_commutative(mcode_t mcode) +{ + return mcode == m_add + || mcode == m_mul + || mcode == m_or + || mcode == m_and + || mcode == m_xor + || mcode == m_setz + || mcode == m_setnz + || mcode == m_cfadd + || mcode == m_ofadd; +} +// Is a shift instruction? +inline THREAD_SAFE bool is_mcode_shift(mcode_t mcode) +{ + return mcode == m_shl + || mcode == m_shr + || mcode == m_sar; +} +// Is a kind of div or mod instruction? +inline THREAD_SAFE bool is_mcode_divmod(mcode_t op) +{ + return op == m_udiv || op == m_sdiv || op == m_umod || op == m_smod; +} +// Is an instruction with the selector/offset pair? +inline THREAD_SAFE bool has_mcode_seloff(mcode_t op) +{ + return op == m_ldx || op == m_stx || op == m_icall || op == m_ijmp; +} + +// Convert setX opcode into corresponding jX opcode +// This function relies on the order of setX and jX opcodes! +inline THREAD_SAFE mcode_t set2jcnd(mcode_t code) +{ + return mcode_t(code - m_setnz + m_jnz); +} + +// Convert setX opcode into corresponding jX opcode +// This function relies on the order of setX and jX opcodes! +inline THREAD_SAFE mcode_t jcnd2set(mcode_t code) +{ + return mcode_t(code + m_setnz - m_jnz); +} + +// Negate a conditional opcode. +// Conditional jumps can be negated, example: jle -> jg +// 'Set' instruction can be negated, example: seta -> setbe +// If the opcode cannot be negated, return m_nop +THREAD_SAFE mcode_t hexapi negate_mcode_relation(mcode_t code); + + +// Swap a conditional opcode. +// Only conditional jumps and set instructions can be swapped. +// The returned opcode the one required for swapped operands. +// Example "x > y" is the same as "y < x", therefore swap(m_jg) is m_jl. +// If the opcode cannot be swapped, return m_nop + +THREAD_SAFE mcode_t hexapi swap_mcode_relation(mcode_t code); + +// Return the opcode that performs signed operation. +// Examples: jae -> jge; udiv -> sdiv +// If the opcode cannot be transformed into signed form, simply return it. + +THREAD_SAFE mcode_t hexapi get_signed_mcode(mcode_t code); + + +// Return the opcode that performs unsigned operation. +// Examples: jl -> jb; xds -> xdu +// If the opcode cannot be transformed into unsigned form, simply return it. + +THREAD_SAFE mcode_t hexapi get_unsigned_mcode(mcode_t code); + +// Does the opcode perform a signed operation? +inline THREAD_SAFE bool is_signed_mcode(mcode_t code) { return get_unsigned_mcode(code) != code; } +// Does the opcode perform a unsigned operation? +inline THREAD_SAFE bool is_unsigned_mcode(mcode_t code) { return get_signed_mcode(code) != code; } + + +// Does the 'd' operand gets modified by the instruction? +// Example: "add l,r,d" modifies d, while instructions +// like jcnd, ijmp, stx does not modify it. +// Note: this function returns 'true' for m_ext but it may be wrong. +// Use minsn_t::modifies_d() if you have minsn_t. + +THREAD_SAFE bool hexapi mcode_modifies_d(mcode_t mcode); + + +// Processor condition codes are mapped to the first microregisters +// The order is important, see mop_t::is_cc() +const mreg_t mr_none = mreg_t(-1); +const mreg_t mr_cf = mreg_t(0); // carry bit +const mreg_t mr_zf = mreg_t(1); // zero bit +const mreg_t mr_sf = mreg_t(2); // sign bit +const mreg_t mr_of = mreg_t(3); // overflow bit +const mreg_t mr_pf = mreg_t(4); // parity bit +const int cc_count = mr_pf - mr_cf + 1; // number of condition code registers +const mreg_t mr_cc = mreg_t(5); // synthetic condition code, used internally +const mreg_t mr_first = mreg_t(8); // the first processor specific register + +//------------------------------------------------------------------------- +/// Operand locator. +/// It is used to denote a particular operand in the ctree, for example, +/// when the user right clicks on a constant and requests to represent it, say, +/// as a hexadecimal number. +struct operand_locator_t +{ +private: + // forbid the default constructor, force the user to initialize objects of this class. + operand_locator_t() {} +public: + ea_t ea; ///< address of the original processor instruction + int opnum; ///< operand number in the instruction + operand_locator_t(ea_t _ea, int _opnum) : ea(_ea), opnum(_opnum) {} + DECLARE_COMPARISONS(operand_locator_t); + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; + +//------------------------------------------------------------------------- +/// Number representation. +/// This structure holds information about a number format. +struct number_format_t +{ + flags_t flags32 = 0; ///< low 32bit of flags (for compatibility) + char opnum; ///< operand number: 0..UA_MAXOP + char props = 0; ///< properties: combination of NF_ bits (\ref NF_) +/// \defgroup NF_ Number format property bits +/// Used in number_format_t::props +///@{ +#define NF_FIXED 0x01 ///< number format has been defined by the user +#define NF_NEGDONE 0x02 ///< temporary internal bit: negation has been performed +#define NF_BINVDONE 0x04 ///< temporary internal bit: inverting bits is done +#define NF_NEGATE 0x08 ///< The user asked to negate the constant +#define NF_BITNOT 0x10 ///< The user asked to invert bits of the constant +#define NF_VALID 0x20 ///< internal bit: stroff or enum is valid + ///< for enums: this bit is set immediately + ///< for stroffs: this bit is set at the end of decompilation +///@} + uchar serial = 0; ///< for enums: constant serial number + char org_nbytes = 0; ///< original number size in bytes + qstring type_name; ///< for stroffs: structure for offsetof()\n + ///< for enums: enum name + flags64_t flags = 0; ///< ida flags, which describe number radix, enum, etc + /// Contructor + number_format_t(int _opnum=0) : opnum(char(_opnum)) {} + /// Get number radix + /// \return 2,8,10, or 16 + int get_radix() const { return ::get_radix(flags, opnum); } + /// Is number representation fixed? + /// Fixed representation cannot be modified by the decompiler + bool is_fixed() const { return props != 0; } + /// Is a hexadecimal number? + bool is_hex() const { return ::is_numop(flags, opnum) && get_radix() == 16; } + /// Is a decimal number? + bool is_dec() const { return ::is_numop(flags, opnum) && get_radix() == 10; } + /// Is a octal number? + bool is_oct() const { return ::is_numop(flags, opnum) && get_radix() == 8; } + /// Is a symbolic constant? + bool is_enum() const { return ::is_enum(flags, opnum); } + /// Is a character constant? + bool is_char() const { return ::is_char(flags, opnum); } + /// Is a structure field offset? + bool is_stroff() const { return ::is_stroff(flags, opnum); } + /// Is a number? + bool is_numop() const { return !is_enum() && !is_char() && !is_stroff(); } + /// Does the number need to be negated or bitwise negated? + /// Returns true if the user requested a negation but it is not done yet + bool needs_to_be_inverted() const + { + return (props & (NF_NEGATE|NF_BITNOT)) != 0 // the user requested it + && (props & (NF_NEGDONE|NF_BINVDONE)) == 0; // not done yet + } + // symbolic constants and struct offsets cannot easily change + // their sign or size without a cast. only simple numbers can do that. + // for example, by modifying the expression type we can convert: + // 10u -> 10 + // but replacing the type of a symbol constant would lead to an inconsistency. + bool has_unmutable_type() const + { + return (props & NF_VALID) != 0 && (is_stroff() || is_enum()); + } + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; + +// Number formats are attached to (ea,opnum) pairs +typedef std::map user_numforms_t; + +//------------------------------------------------------------------------- +/// Base helper class to convert binary data structures into text. +/// Other classes are derived from this class. +struct vd_printer_t +{ + qstring tmpbuf; + int hdrlines = 0; ///< number of header lines (prototype+typedef+lvars) + ///< valid at the end of print process + /// Print. + /// This function is called to generate a portion of the output text. + /// The output text may contain color codes. + /// \param indent number of spaces to generate as prefix + /// \param format printf-style format specifier + /// \return length of printed string + AS_PRINTF(3, 4) virtual int hexapi print(int indent, const char *format, ...); + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; + +/// Helper class to convert cfunc_t into text. +struct vc_printer_t : public vd_printer_t +{ + const cfunc_t *func; ///< cfunc_t to generate text for + char lastchar = 0; ///< internal: last printed character + /// Constructor + vc_printer_t(const cfunc_t *f) : func(f) {} + /// Are we generating one-line text representation? + /// \return \c true if the output will occupy one line without line breaks + virtual bool idaapi oneliner() const newapi { return false; } +}; + +/// Helper class to convert binary data structures into text and put into a file. +struct file_printer_t : public vd_printer_t +{ + FILE *fp; ///< Output file pointer + /// Print. + /// This function is called to generate a portion of the output text. + /// The output text may contain color codes. + /// \param indent number of spaces to generate as prefix + /// \param format printf-style format specifier + /// \return length of printed string + AS_PRINTF(3, 4) int hexapi print(int indent, const char *format, ...) override; + /// Constructor + file_printer_t(FILE *_fp) : fp(_fp) {} +}; + +/// Helper class to convert cfunc_t into a text string +struct qstring_printer_t : public vc_printer_t +{ + bool with_tags; ///< Generate output with color tags + qstring &s; ///< Reference to the output string + /// Constructor + qstring_printer_t(const cfunc_t *f, qstring &_s, bool tags) + : vc_printer_t(f), with_tags(tags), s(_s) {} + /// Print. + /// This function is called to generate a portion of the output text. + /// The output text may contain color codes. + /// \param indent number of spaces to generate as prefix + /// \param format printf-style format specifier + /// \return length of the printed string + AS_PRINTF(3, 4) int hexapi print(int indent, const char *format, ...) override; +}; + +//------------------------------------------------------------------------- +/// \defgroup type Type string related declarations +/// Type related functions and class. +///@{ + +/// Print the specified type info. +/// This function can be used from a debugger by typing "tif->dstr()" + +const char *hexapi dstr(const tinfo_t *tif); + + +/// Verify a type string. +/// \return true if type string is correct + +bool hexapi is_type_correct(const type_t *ptr); + + +/// Is a small structure or union? +/// \return true if the type is a small UDT (user defined type). +/// Small UDTs fit into a register (or pair or registers) as a rule. + +bool hexapi is_small_udt(const tinfo_t &tif); + + +/// Is definitely a non-boolean type? +/// \return true if the type is a non-boolean type (non bool and well defined) + +bool hexapi is_nonbool_type(const tinfo_t &type); + + +/// Is a boolean type? +/// \return true if the type is a boolean type + +bool hexapi is_bool_type(const tinfo_t &type); + + +/// Is a pointer or array type? +inline THREAD_SAFE bool is_ptr_or_array(type_t t) +{ + return is_type_ptr(t) || is_type_array(t); +} + +/// Is a pointer, array, or function type? +inline THREAD_SAFE bool is_paf(type_t t) +{ + return is_ptr_or_array(t) || is_type_func(t); +} + +/// Is struct/union/enum definition (not declaration)? +inline THREAD_SAFE bool is_inplace_def(const tinfo_t &type) +{ + return type.is_decl_complex() && !type.is_typeref(); +} + +/// Calculate number of partial subtypes. +/// \return number of partial subtypes. The bigger is this number, the uglier is the type. + +int hexapi partial_type_num(const tinfo_t &type); + + +/// Get a type of a floating point value with the specified width +/// \return type info object +/// \param width width of the desired type + +tinfo_t hexapi get_float_type(int width); + + +/// Create a type info by width and sign. +/// Returns a simple type (examples: int, short) with the given width and sign. +/// \param srcwidth size of the type in bytes +/// \param sign sign of the type + +tinfo_t hexapi get_int_type_by_width_and_sign(int srcwidth, type_sign_t sign); + + +/// Create a partial type info by width. +/// Returns a partially defined type (examples: _DWORD, _BYTE) with the given width. +/// \param size size of the type in bytes + +tinfo_t hexapi get_unk_type(int size); + + +/// Generate a dummy pointer type +/// \param ptrsize size of pointed object +/// \param isfp is floating point object? + +tinfo_t hexapi dummy_ptrtype(int ptrsize, bool isfp); + + +/// Create a pointer type. +/// This function performs the following conversion: "type" -> "type*" +/// \param type object type. +/// \return "type*". for example, if 'char' is passed as the argument, +// the function will return 'char *' + +tinfo_t hexapi make_pointer(const tinfo_t &type); + + +/// Create a reference to a named type. +/// \param name type name +/// \return type which refers to the specified name. For example, if name is "DWORD", +/// the type info which refers to "DWORD" is created. + +tinfo_t hexapi create_typedef(const char *name); + + +/// Create a reference to an ordinal type. +/// \param n ordinal number of the type +/// \return type which refers to the specified ordinal. For example, if n is 1, +/// the type info which refers to ordinal type 1 is created. + +inline tinfo_t create_typedef(int n) +{ + tinfo_t tif; + tif.create_typedef(nullptr, n); + return tif; +} + +/// Type source (where the type information comes from) +enum type_source_t +{ + GUESSED_NONE, // not guessed, specified by the user + GUESSED_WEAK, // not guessed, comes from idb + GUESSED_FUNC, // guessed as a function + GUESSED_DATA, // guessed as a data item + TS_NOELL = 0x8000000, // can be used in set_type() to avoid merging into ellipsis + TS_SHRINK = 0x4000000, // can be used in set_type() to prefer smaller arguments + TS_DONTREF = 0x2000000, // do not mark type as referenced (referenced_types) + TS_MASK = 0xE000000, // all high bits +}; + + +/// Get a global type. +/// Global types are types of addressable objects and struct/union/enum types +/// \param id address or id of the object +/// \param tif buffer for the answer +/// \param guess what kind of types to consider +/// \return success + +bool hexapi get_type(uval_t id, tinfo_t *tif, type_source_t guess); + + +/// Set a global type. +/// \param id address or id of the object +/// \param tif new type info +/// \param source where the type comes from +/// \param force true means to set the type as is, false means to merge the +/// new type with the possibly existing old type info. +/// \return success + +bool hexapi set_type(uval_t id, const tinfo_t &tif, type_source_t source, bool force=false); + +///@} + +//------------------------------------------------------------------------- +// We use our own class to store argument and variable locations. +// It is called vdloc_t that stands for 'vd location'. +// 'vd' is the internal name of the decompiler, it stands for 'visual decompiler'. +// The main differences between vdloc and argloc_t: +// ALOC_REG1: the offset is always 0, so it is not used. the register number +// uses the whole ~VLOC_MASK field. +// ALOC_STACK: stack offsets are always positive because they are based on +// the lowest value of sp in the function. +class vdloc_t : public argloc_t +{ + int regoff(); // inaccessible & undefined: regoff() should not be used +public: + // Get the register number. + // This function works only for ALOC_REG1 and ALOC_REG2 location types. + // It uses all available bits for register number for ALOC_REG1 + int reg1() const { return atype() == ALOC_REG2 ? argloc_t::reg1() : get_reginfo(); } + + // Set vdloc to point to the specified register without cleaning it up. + // This is a dangerous function, use set_reg1() instead unless you understand + // what it means to cleanup an argloc. + void _set_reg1(int r1) { argloc_t::_set_reg1(r1, r1>>16); } + + // Set vdloc to point to the specified register. + void set_reg1(int r1) { cleanup_argloc(this); _set_reg1(r1); } + + // Use member functions of argloc_t for other location types. + + // Return textual representation. + // Note: this and all other dstr() functions can be used from a debugger. + // It is much easier than to inspect the memory contents byte by byte. + const char *hexapi dstr(int width=0) const; + DECLARE_COMPARISONS(vdloc_t); + bool hexapi is_aliasable(const mba_t *mb, int size) const; +}; + +/// Print vdloc. +/// Since vdloc does not always carry the size info, we pass it as NBYTES.. +void hexapi print_vdloc(qstring *vout, const vdloc_t &loc, int nbytes); + +//------------------------------------------------------------------------- +/// Do two arglocs overlap? +bool hexapi arglocs_overlap(const vdloc_t &loc1, size_t w1, const vdloc_t &loc2, size_t w2); + +/// Local variable locator. +/// Local variables are located using definition ea and location. +/// Each variable must have a unique locator, this is how we tell them apart. +struct lvar_locator_t +{ + vdloc_t location; ///< Variable location. + ea_t defea = BADADDR; ///< Definition address. Usually, this is the address + ///< of the instruction that initializes the variable. + ///< In some cases it can be a fictional address. + + lvar_locator_t() {} + lvar_locator_t(const vdloc_t &loc, ea_t ea) : location(loc), defea(ea) {} + /// Get offset of the varialbe in the stack frame. + /// \return a non-negative value for stack variables. The value is + /// an offset from the bottom of the stack frame in terms of + /// vd-offsets. + /// negative values mean error (not a stack variable) + sval_t get_stkoff() const + { + return location.is_stkoff() ? location.stkoff() : -1; + } + /// Is variable located on one register? + bool is_reg1() const { return location.is_reg1(); } + /// Is variable located on two registers? + bool is_reg2() const { return location.is_reg2(); } + /// Is variable located on register(s)? + bool is_reg_var() const { return location.is_reg(); } + /// Is variable located on the stack? + bool is_stk_var() const { return location.is_stkoff(); } + /// Is variable scattered? + bool is_scattered() const { return location.is_scattered(); } + /// Get the register number of the variable + mreg_t get_reg1() const { return location.reg1(); } + /// Get the number of the second register (works only for ALOC_REG2 lvars) + mreg_t get_reg2() const { return location.reg2(); } + /// Get information about scattered variable + const scattered_aloc_t &get_scattered() const { return location.scattered(); } + scattered_aloc_t &get_scattered() { return location.scattered(); } + DECLARE_COMPARISONS(lvar_locator_t); + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + // Debugging: get textual representation of a lvar locator. + const char *hexapi dstr() const; +}; + +/// Definition of a local variable (register or stack) #var #lvar +class lvar_t : public lvar_locator_t +{ + friend class mba_t; + int flags; ///< \ref CVAR_ +/// \defgroup CVAR_ Local variable property bits +/// Used in lvar_t::flags +///@{ +#define CVAR_USED 0x00000001 ///< is used in the code? +#define CVAR_TYPE 0x00000002 ///< the type is defined? +#define CVAR_NAME 0x00000004 ///< has nice name? +#define CVAR_MREG 0x00000008 ///< corresponding mregs were replaced? +#define CVAR_NOWD 0x00000010 ///< width is unknown +#define CVAR_UNAME 0x00000020 ///< user-defined name +#define CVAR_UTYPE 0x00000040 ///< user-defined type +#define CVAR_RESULT 0x00000080 ///< function result variable +#define CVAR_ARG 0x00000100 ///< function argument +#define CVAR_FAKE 0x00000200 ///< fake variable (return var or va_list) +#define CVAR_OVER 0x00000400 ///< overlapping variable +#define CVAR_FLOAT 0x00000800 ///< used in a fpu insn +#define CVAR_SPOILED 0x00001000 ///< internal flag, do not use: spoiled var +#define CVAR_MAPDST 0x00002000 ///< other variables are mapped to this var +#define CVAR_PARTIAL 0x00004000 ///< variable type is partialy defined +#define CVAR_THISARG 0x00008000 ///< 'this' argument of c++ member functions +#define CVAR_SPLIT 0x00010000 ///< variable was created by an explicit request + ///< otherwise we could reuse an existing var +#define CVAR_REGNAME 0x00020000 ///< has a register name (like _RAX): if lvar + ///< is used by an m_ext instruction +#define CVAR_NOPTR 0x00040000 ///< variable cannot be a pointer (user choice) +#define CVAR_DUMMY 0x00080000 ///< dummy argument (added to fill a hole in + ///< the argument list) +#define CVAR_NOTARG 0x00100000 ///< variable cannot be an input argument +#define CVAR_AUTOMAP 0x00200000 ///< variable was automatically mapped +#define CVAR_BYREF 0x00400000 ///< the address of the variable was taken +#define CVAR_INASM 0x00800000 ///< variable is used in instructions translated + ///< into __asm {...} +#define CVAR_UNUSED 0x01000000 ///< user-defined __unused attribute + ///< meaningful only if: is_arg_var() && !mba->final_type +#define CVAR_SHARED 0x02000000 ///< variable is mapped to several chains +#define CVAR_SCARG 0x04000000 ///< variable is a stack argument that was + ///< transformed from a scattered one +///@} + +public: + qstring name; ///< variable name. + ///< use mba_t::set_nice_lvar_name() and + ///< mba_t::set_user_lvar_name() to modify it + qstring cmt; ///< variable comment string + tinfo_t tif; ///< variable type + int width = 0; ///< variable size in bytes + int defblk = -1; ///< first block defining the variable. + ///< 0 for args, -1 if unknown + uint64 divisor = 0; ///< max known divisor of the variable + + lvar_t() : flags(CVAR_USED) {} + lvar_t(const qstring &n, const vdloc_t &l, ea_t e, const tinfo_t &t, int w, int db) + : lvar_locator_t(l, e), flags(CVAR_USED), name(n), tif(t), width(w), defblk(db) + { + } + // Debugging: get textual representation of a local variable. + const char *hexapi dstr() const; + + /// Is the variable used in the code? + bool used() const { return (flags & CVAR_USED) != 0; } + /// Has the variable a type? + bool typed() const { return (flags & CVAR_TYPE) != 0; } + /// Have corresponding microregs been replaced by references to this variable? + bool mreg_done() const { return (flags & CVAR_MREG) != 0; } + /// Does the variable have a nice name? + bool has_nice_name() const { return (flags & CVAR_NAME) != 0; } + /// Do we know the width of the variable? + bool is_unknown_width() const { return (flags & CVAR_NOWD) != 0; } + /// Has any user-defined information? + bool has_user_info() const + { + return (flags & (CVAR_UNAME|CVAR_UTYPE|CVAR_NOPTR|CVAR_UNUSED)) != 0 + || !cmt.empty(); + } + /// Has user-defined name? + bool has_user_name() const { return (flags & CVAR_UNAME) != 0; } + /// Has user-defined type? + bool has_user_type() const { return (flags & CVAR_UTYPE) != 0; } + /// Is the function result? + bool is_result_var() const { return (flags & CVAR_RESULT) != 0; } + /// Is the function argument? + bool is_arg_var() const { return (flags & CVAR_ARG) != 0; } + /// Is the promoted function argument? + bool hexapi is_promoted_arg() const; + /// Is fake return variable? + bool is_fake_var() const { return (flags & CVAR_FAKE) != 0; } + /// Is overlapped variable? + bool is_overlapped_var() const { return (flags & CVAR_OVER) != 0; } + /// Used by a fpu insn? + bool is_floating_var() const { return (flags & CVAR_FLOAT) != 0; } + /// Is spoiled var? (meaningful only during lvar allocation) + bool is_spoiled_var() const { return (flags & CVAR_SPOILED) != 0; } + /// Variable type should be handled as a partial one + bool is_partialy_typed() const { return (flags & CVAR_PARTIAL) != 0; } + /// Variable type should not be a pointer + bool is_noptr_var() const { return (flags & CVAR_NOPTR) != 0; } + /// Other variable(s) map to this var? + bool is_mapdst_var() const { return (flags & CVAR_MAPDST) != 0; } + /// Is 'this' argument of a C++ member function? + bool is_thisarg() const { return (flags & CVAR_THISARG) != 0; } + /// Is a split variable? + bool is_split_var() const { return (flags & CVAR_SPLIT) != 0; } + /// Has a register name? (like _RAX) + bool has_regname() const { return (flags & CVAR_REGNAME) != 0; } + /// Is variable used in an instruction translated into __asm? + bool in_asm() const { return (flags & CVAR_INASM) != 0; } + /// Is a dummy argument (added to fill a hole in the argument list) + bool is_dummy_arg() const { return (flags & CVAR_DUMMY) != 0; } + /// Is a local variable? (local variable cannot be an input argument) + bool is_notarg() const { return (flags & CVAR_NOTARG) != 0; } + /// Was the variable automatically mapped to another variable? + bool is_automapped() const { return (flags & CVAR_AUTOMAP) != 0; } + /// Was the address of the variable taken? + bool is_used_byref() const { return (flags & CVAR_BYREF) != 0; } + /// Was declared as __unused by the user? See CVAR_UNUSED + bool is_decl_unused() const { return (flags & CVAR_UNUSED) != 0; } + /// Is lvar mapped to several chains + bool is_shared() const { return (flags & CVAR_SHARED) != 0; } + /// Was lvar transformed from a scattered argument? + bool was_scattered_arg() const { return (flags & CVAR_SCARG) != 0; } + void set_used() { flags |= CVAR_USED; } + void clear_used() { flags &= ~CVAR_USED; } + void set_typed() { flags |= CVAR_TYPE; clr_noptr_var(); } + void set_non_typed() { flags &= ~CVAR_TYPE; } + void clr_user_info() { flags &= ~(CVAR_UNAME|CVAR_UTYPE|CVAR_NOPTR); } + void set_user_name() { flags |= CVAR_NAME|CVAR_UNAME; } + void set_user_type() { flags |= CVAR_TYPE|CVAR_UTYPE; } + void clr_user_type() { flags &= ~CVAR_UTYPE; } + void clr_user_name() { flags &= ~CVAR_UNAME; } + void set_mreg_done() { flags |= CVAR_MREG; } + void clr_mreg_done() { flags &= ~CVAR_MREG; } + void set_unknown_width() { flags |= CVAR_NOWD; } + void clr_unknown_width() { flags &= ~CVAR_NOWD; } + void set_arg_var() { flags |= CVAR_ARG; } + void clr_arg_var() { flags &= ~(CVAR_ARG|CVAR_THISARG); } + void set_fake_var() { flags |= CVAR_FAKE; } + void clr_fake_var() { flags &= ~CVAR_FAKE; } + void set_overlapped_var() { flags |= CVAR_OVER; } + void clr_overlapped_var() { flags &= ~CVAR_OVER; } + void set_floating_var() { flags |= CVAR_FLOAT; } + void clr_floating_var() { flags &= ~CVAR_FLOAT; } + void set_spoiled_var() { flags |= CVAR_SPOILED; } + void clr_spoiled_var() { flags &= ~CVAR_SPOILED; } + void set_mapdst_var() { flags |= CVAR_MAPDST; } + void clr_mapdst_var() { flags &= ~CVAR_MAPDST; } + void set_partialy_typed() { flags |= CVAR_PARTIAL; } + void clr_partialy_typed() { flags &= ~CVAR_PARTIAL; } + void set_noptr_var() { flags |= CVAR_NOPTR; } + void clr_noptr_var() { flags &= ~CVAR_NOPTR; } + void set_thisarg() { flags |= CVAR_THISARG; } + void clr_thisarg() { flags &= ~CVAR_THISARG; } + void set_split_var() { flags |= CVAR_SPLIT; } + void clr_split_var() { flags &= ~CVAR_SPLIT; } + void set_dummy_arg() { flags |= CVAR_DUMMY; } + void clr_dummy_arg() { flags &= ~CVAR_DUMMY; } + void set_notarg() { clr_arg_var(); flags |= CVAR_NOTARG; } + void clr_notarg() { flags &= ~CVAR_NOTARG; } + void set_automapped() { flags |= CVAR_AUTOMAP; } + void clr_automapped() { flags &= ~CVAR_AUTOMAP; } + void set_used_byref() { flags |= CVAR_BYREF; } + void clr_used_byref() { flags &= ~CVAR_BYREF; } + void set_decl_unused() { flags |= CVAR_UNUSED; } + void clr_decl_unused() { flags &= ~CVAR_UNUSED; } + void set_shared() { flags |= CVAR_SHARED; } + void clr_shared() { flags &= ~CVAR_SHARED; } + void set_scattered_arg() { flags |= CVAR_SCARG; } + void clr_scattered_arg() { flags &= ~CVAR_SCARG; } + + /// Do variables overlap? + bool has_common(const lvar_t &v) const + { + return arglocs_overlap(location, width, v.location, v.width); + } + /// Does the variable overlap with the specified location? + bool has_common_bit(const vdloc_t &loc, asize_t width2) const + { + return arglocs_overlap(location, width, loc, width2); + } + /// Get variable type + const tinfo_t &type() const { return tif; } + tinfo_t &type() { return tif; } + + /// Check if the variable accept the specified type. + /// Some types are forbidden (void, function types, wrong arrays, etc) + bool hexapi accepts_type(const tinfo_t &t, bool may_change_thisarg=false); + /// Set variable type + /// Note: this function does not modify the idb, only the lvar instance + /// in the memory. For permanent changes see modify_user_lvars() + /// Also, the variable type is not considered as final by the decompiler + /// and may be modified later by the type derivation. + /// In some cases set_final_var_type() may work better, but it does not + /// do persistent changes to the database neither. + /// \param t new type + /// \param may_fail if false and type is bad, interr + /// \return success + bool hexapi set_lvar_type(const tinfo_t &t, bool may_fail=false); + + /// Set final variable type. + void set_final_lvar_type(const tinfo_t &t) + { + set_lvar_type(t); + set_typed(); + } + + /// Change the variable width. + /// We call the variable size 'width', it is represents the number of bytes. + /// This function may change the variable type using set_lvar_type(). + /// \param w new width + /// \param svw_flags combination of SVW_... bits + /// \return success + bool hexapi set_width(int w, int svw_flags=0); +#define SVW_INT 0x00 // integer value +#define SVW_FLOAT 0x01 // floating point value +#define SVW_SOFT 0x02 // may fail and return false; + // if this bit is not set and the type is bad, interr + + /// Append local variable to mlist. + /// \param mba ptr to the current mba_t + /// \param lst list to append to + /// \param pad_if_scattered if true, append padding bytes in case of scattered lvar + void hexapi append_list(const mba_t *mba, mlist_t *lst, bool pad_if_scattered=false) const; + + /// Is the variable aliasable? + /// \param mba ptr to the current mba_t + /// Aliasable variables may be modified indirectly (through a pointer) + bool is_aliasable(const mba_t *mba) const + { + return location.is_aliasable(mba, width); + } + +}; +DECLARE_TYPE_AS_MOVABLE(lvar_t); + +/// Vector of local variables +struct lvars_t : public qvector +{ + /// Find an input variable at the specified location. + /// \param argloc variable location + /// \param _size variable size in bytes + /// \return -1 if failed, otherwise an index into 'vars' + int find_input_lvar(const vdloc_t &argloc, int _size) { return find_lvar(argloc, _size, 0); } + + + /// Find an input register variable. + /// \param reg register to find + /// \param _size variable size in bytes + /// \return -1 if failed, otherwise an index into 'vars' + int find_input_reg(int reg, int _size=1) + { + vdloc_t rloc; + rloc._set_reg1(reg); + return find_input_lvar(rloc, _size); + } + + + /// Find a stack variable at the specified location. + /// \param spoff offset from the minimal sp + /// \param width variable size in bytes + /// \return -1 if failed, otherwise an index into 'vars' + int hexapi find_stkvar(sval_t spoff, int width); + + + /// Find a variable at the specified location. + /// \param ll variable location + /// \return pointer to variable or nullptr + lvar_t *hexapi find(const lvar_locator_t &ll); + + + /// Find a variable at the specified location. + /// \param location variable location + /// \param width variable size in bytes + /// \param defblk definition block of the lvar. -1 means any block + /// \return -1 if failed, otherwise an index into 'vars' + int hexapi find_lvar(const vdloc_t &location, int width, int defblk=-1) const; +}; + +/// Saved user settings for local variables: name, type, comment. +struct lvar_saved_info_t +{ + lvar_locator_t ll; ///< Variable locator + qstring name; ///< Name + tinfo_t type; ///< Type + qstring cmt; ///< Comment + ssize_t size = BADSIZE; ///< Type size (if not initialized then -1) + int flags = 0; ///< \ref LVINF_ +/// \defgroup LVINF_ saved user lvar info property bits +/// Used in lvar_saved_info_t::flags +///@{ +#define LVINF_KEEP 0x0001 ///< preserve saved user settings regardless of vars + ///< for example, if a var loses all its + ///< user-defined attributes or even gets + ///< destroyed, keep its lvar_saved_info_t. + ///< this is used for ephemeral variables that + ///< get destroyed by macro recognition. +#define LVINF_SPLIT 0x0002 ///< split allocation of a new variable. + ///< forces the decompiler to create a new + ///< variable at ll.defea +#define LVINF_NOPTR 0x0004 ///< variable type should not be a pointer +#define LVINF_NOMAP 0x0008 ///< forbid automatic mapping of the variable +#define LVINF_UNUSED 0x0010 ///< unused argument, corresponds to CVAR_UNUSED +///@} + bool has_info() const + { + return !name.empty() + || !type.empty() + || !cmt.empty() + || is_split_lvar() + || is_noptr_lvar() + || is_nomap_lvar(); + } + bool operator==(const lvar_saved_info_t &r) const + { + return name == r.name + && cmt == r.cmt + && ll == r.ll + && type == r.type; + } + bool operator!=(const lvar_saved_info_t &r) const { return !(*this == r); } + bool is_kept() const { return (flags & LVINF_KEEP) != 0; } + void clear_keep() { flags &= ~LVINF_KEEP; } + void set_keep() { flags |= LVINF_KEEP; } + bool is_split_lvar() const { return (flags & LVINF_SPLIT) != 0; } + void set_split_lvar() { flags |= LVINF_SPLIT; } + void clr_split_lvar() { flags &= ~LVINF_SPLIT; } + bool is_noptr_lvar() const { return (flags & LVINF_NOPTR) != 0; } + void set_noptr_lvar() { flags |= LVINF_NOPTR; } + void clr_noptr_lvar() { flags &= ~LVINF_NOPTR; } + bool is_nomap_lvar() const { return (flags & LVINF_NOMAP) != 0; } + void set_nomap_lvar() { flags |= LVINF_NOMAP; } + void clr_nomap_lvar() { flags &= ~LVINF_NOMAP; } + bool is_unused_lvar() const { return (flags & LVINF_UNUSED) != 0; } + void set_unused_lvar() { flags |= LVINF_UNUSED; } + void clr_unused_lvar() { flags &= ~LVINF_UNUSED; } +}; +DECLARE_TYPE_AS_MOVABLE(lvar_saved_info_t); +typedef qvector lvar_saved_infos_t; + +/// Local variable mapping (is used to merge variables) +typedef std::map lvar_mapping_t; + +/// All user-defined information about local variables +struct lvar_uservec_t +{ + /// User-specified names, types, comments for lvars. Variables without + /// user-specified info are not present in this vector. + lvar_saved_infos_t lvvec; + + /// Local variable mapping (used for merging variables) + lvar_mapping_t lmaps; + + /// Delta to add to IDA stack offset to calculate Hex-Rays stack offsets. + /// Should be set by the caller before calling save_user_lvar_settings(); + uval_t stkoff_delta = 0; + +/// \defgroup ULV_ lvar_uservec_t property bits +/// Used in lvar_uservec_t::ulv_flags +///@{ +#define ULV_PRECISE_DEFEA 0x0001 ///< Use precise defea's for lvar locations +///@} + /// Various flags. Possible values are from \ref ULV_ + int ulv_flags = ULV_PRECISE_DEFEA; + + void swap(lvar_uservec_t &r) + { + lvvec.swap(r.lvvec); + lmaps.swap(r.lmaps); + std::swap(stkoff_delta, r.stkoff_delta); + std::swap(ulv_flags, r.ulv_flags); + } + void clear() + { + lvvec.clear(); + lmaps.clear(); + stkoff_delta = 0; + ulv_flags = ULV_PRECISE_DEFEA; + } + bool empty() const + { + return lvvec.empty() + && lmaps.empty() + && stkoff_delta == 0 + && ulv_flags == ULV_PRECISE_DEFEA; + } + + /// find saved user settings for given var + lvar_saved_info_t *find_info(const lvar_locator_t &vloc) + { + for ( lvar_saved_infos_t::iterator p=lvvec.begin(); p != lvvec.end(); ++p ) + { + if ( p->ll == vloc ) + return p; + } + return nullptr; + } + + /// Preserve user settings for given var + void keep_info(const lvar_t &v) + { + lvar_saved_info_t *p = find_info(v); + if ( p != nullptr ) + p->set_keep(); + } +}; + +/// Restore user defined local variable settings in the database. +/// \param func_ea entry address of the function +/// \param lvinf ptr to output buffer +/// \return success + +bool hexapi restore_user_lvar_settings(lvar_uservec_t *lvinf, ea_t func_ea); + + +/// Save user defined local variable settings into the database. +/// \param func_ea entry address of the function +/// \param lvinf user-specified info about local variables + +void hexapi save_user_lvar_settings(ea_t func_ea, const lvar_uservec_t &lvinf); + + +/// Helper class to modify saved local variable settings. +struct user_lvar_modifier_t +{ + virtual ~user_lvar_modifier_t() {} + /// Modify lvar settings. + /// Returns: true-modified + virtual bool idaapi modify_lvars(lvar_uservec_t *lvinf) = 0; +}; + +/// Modify saved local variable settings. +/// \param entry_ea function start address +/// \param mlv local variable modifier +/// \return true if modified variables + +bool hexapi modify_user_lvars(ea_t entry_ea, user_lvar_modifier_t &mlv); + + +/// Modify saved local variable settings of one variable. +/// \param func_ea function start address +/// \param info local variable info attrs +/// \param mli_flags bits that specify which attrs defined by INFO are to be set +/// \return true if modified, false if invalid MLI_FLAGS passed + +bool hexapi modify_user_lvar_info( + ea_t func_ea, + uint mli_flags, + const lvar_saved_info_t &info); + +/// \defgroup MLI_ user info bits +///@{ +#define MLI_NAME 0x01 ///< apply lvar name +#define MLI_TYPE 0x02 ///< apply lvar type +#define MLI_CMT 0x04 ///< apply lvar comment +#define MLI_SET_FLAGS 0x08 ///< set LVINF_... bits +#define MLI_CLR_FLAGS 0x10 ///< clear LVINF_... bits +///@} + + +/// Find a variable by name. +/// \param out output buffer for the variable locator +/// \param func_ea function start address +/// \param varname variable name +/// \return success +/// Since VARNAME is not always enough to find the variable, it may decompile +/// the function. + +bool hexapi locate_lvar( + lvar_locator_t *out, + ea_t func_ea, + const char *varname); + + +/// Rename a local variable. +/// \param func_ea function start address +/// \param oldname old name of the variable +/// \param newname new name of the variable +/// \return success +/// This is a convenience function. +/// For bulk renaming consider using modify_user_lvars. + +inline bool rename_lvar( + ea_t func_ea, + const char *oldname, + const char *newname) +{ + lvar_saved_info_t info; + if ( !locate_lvar(&info.ll, func_ea, oldname) ) + return false; + info.name = newname; + return modify_user_lvar_info(func_ea, MLI_NAME, info); +} + +//------------------------------------------------------------------------- +/// User-defined function calls +struct udcall_t +{ + qstring name; // name of the function + tinfo_t tif; // function prototype + DECLARE_COMPARISONS(udcall_t) + { + int code = ::compare(name, r.name); + if ( code == 0 ) + code = ::compare(tif, r.tif); + return code; + } + + bool empty() const { return name.empty() && tif.empty(); } +}; + +// All user-defined function calls (map address -> udcall) +typedef std::map udcall_map_t; + +/// Restore user defined function calls from the database. +/// \param udcalls ptr to output buffer +/// \param func_ea entry address of the function +/// \return success + +bool hexapi restore_user_defined_calls(udcall_map_t *udcalls, ea_t func_ea); + + +/// Save user defined local function calls into the database. +/// \param func_ea entry address of the function +/// \param udcalls user-specified info about user defined function calls + +void hexapi save_user_defined_calls(ea_t func_ea, const udcall_map_t &udcalls); + + +/// Convert function type declaration into internal structure +/// \param udc - pointer to output structure +/// \param decl - function type declaration +/// \param silent - if TRUE: do not show warning in case of incorrect type +/// \return success + +bool hexapi parse_user_call(udcall_t *udc, const char *decl, bool silent); + + +/// try to generate user-defined call for an instruction +/// \return \ref MERR_ code: +/// MERR_OK - user-defined call generated +/// else - error (MERR_INSN == inacceptable udc.tif) + +merror_t hexapi convert_to_user_call(const udcall_t &udc, codegen_t &cdg); + + +//------------------------------------------------------------------------- +/// Generic microcode generator class. +/// An instance of a derived class can be registered to be used for +/// non-standard microcode generation. Before microcode generation for an +/// instruction all registered object will be visited by the following way: +/// if ( filter->match(cdg) ) +/// code = filter->apply(cdg); +/// if ( code == MERR_OK ) +/// continue; // filter generated microcode, go to the next instruction +struct microcode_filter_t +{ + virtual ~microcode_filter_t() {} + /// check if the filter object is to be applied + /// \return success + virtual bool match(codegen_t &cdg) = 0; + + /// generate microcode for an instruction + /// \return MERR_... code: + /// MERR_OK - user-defined microcode generated, go to the next instruction + /// MERR_INSN - not generated - the caller should try the standard way + /// else - error + virtual merror_t apply(codegen_t &cdg) = 0; +}; + +/// register/unregister non-standard microcode generator +/// \param filter - microcode generator object +/// \param install - TRUE - register the object, FALSE - unregister +/// \return success +bool hexapi install_microcode_filter(microcode_filter_t *filter, bool install=true); + +//------------------------------------------------------------------------- +/// Abstract class: User-defined call generator +/// derived classes should implement method 'match' +class udc_filter_t : public microcode_filter_t +{ + udcall_t udc; + +public: + ~udc_filter_t() { cleanup(); } + + /// Cleanup the filter + /// This function properly clears type information associated to this filter. + void hexapi cleanup(); + + /// return true if the filter object should be applied to given instruction + virtual bool match(codegen_t &cdg) override = 0; + + bool hexapi init(const char *decl); + virtual merror_t hexapi apply(codegen_t &cdg) override; + + bool empty() const { return udc.empty(); } +}; + +//------------------------------------------------------------------------- +typedef size_t mbitmap_t; +const size_t bitset_width = sizeof(mbitmap_t) * CHAR_BIT; +const size_t bitset_align = bitset_width - 1; +const size_t bitset_shift = 6; + +/// Bit set class. See https://en.wikipedia.org/wiki/Bit_array +class bitset_t +{ + mbitmap_t *bitmap = nullptr; ///< pointer to bitmap + size_t high = 0; ///< highest bit+1 (multiply of bitset_width) + +public: + bitset_t() {} + hexapi bitset_t(const bitset_t &m); // copy constructor + ~bitset_t() + { + qfree(bitmap); + bitmap = nullptr; + } + void swap(bitset_t &r) + { + std::swap(bitmap, r.bitmap); + std::swap(high, r.high); + } + bitset_t &operator=(const bitset_t &m) { return copy(m); } + bitset_t &hexapi copy(const bitset_t &m); // assignment operator + bool hexapi add(int bit); // add a bit + bool hexapi add(int bit, int width); // add bits + bool hexapi add(const bitset_t &ml); // add another bitset + bool hexapi sub(int bit); // delete a bit + bool hexapi sub(int bit, int width); // delete bits + bool hexapi sub(const bitset_t &ml); // delete another bitset + bool hexapi cut_at(int maxbit); // delete bits >= maxbit + void hexapi shift_down(int shift); // shift bits down + bool hexapi has(int bit) const; // test presence of a bit + bool hexapi has_all(int bit, int width) const; // test presence of bits + bool hexapi has_any(int bit, int width) const; // test presence of bits + void print( + qstring *vout, + int (*get_bit_name)(qstring *out, int bit, int width, void *ud)=nullptr, + void *ud=nullptr) const; + const char *hexapi dstr() const; + bool hexapi empty() const; // is empty? + int hexapi count() const; // number of set bits + int hexapi count(int bit) const; // get number set bits starting from 'bit' + int hexapi last() const; // get the number of the last bit (-1-no bits) + void clear() { high = 0; } // make empty + void hexapi fill_with_ones(int maxbit); + bool hexapi fill_gaps(int total_nbits); + bool hexapi has_common(const bitset_t &ml) const; // has common elements? + bool hexapi intersect(const bitset_t &ml); // intersect sets. returns true if changed + bool hexapi is_subset_of(const bitset_t &ml) const; // is subset of? + bool includes(const bitset_t &ml) const { return ml.is_subset_of(*this); } + void extract(intvec_t &out) const; + DECLARE_COMPARISONS(bitset_t); + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + class iterator + { + friend class bitset_t; + int i; + public: + iterator(int n=-1) : i(n) {} + bool operator==(const iterator &n) const { return i == n.i; } + bool operator!=(const iterator &n) const { return i != n.i; } + int operator*() const { return i; } + }; + typedef iterator const_iterator; + iterator itat(int n) const { return iterator(goup(n)); } + iterator begin() const { return itat(0); } + iterator end() const { return iterator(high); } + int front() const { return *begin(); } + int back() const { return *end(); } + void inc(iterator &p, int n=1) const { p.i = goup(p.i+n); } +private: + int hexapi goup(int reg) const; +}; +DECLARE_TYPE_AS_MOVABLE(bitset_t); +typedef qvector array_of_bitsets; + +//------------------------------------------------------------------------- +// set of graph nodes as a bitset +class node_bitset_t : public bitset_t +{ +public: + node_bitset_t() {} + node_bitset_t(int node) { add(node); } +}; +DECLARE_TYPE_AS_MOVABLE(node_bitset_t); + +class array_of_node_bitset_t : public qvector {}; + +//------------------------------------------------------------------------- +template +struct ivl_tpl // an interval +{ + ivl_tpl() = delete; +public: + T off; + T size; + ivl_tpl(T _off, T _size) : off(_off), size(_size) {} + bool valid() const { return last() >= off; } + T end() const { return off + size; } + T last() const { return off + size - 1; } + + DEFINE_MEMORY_ALLOCATION_FUNCS() +}; + +//------------------------------------------------------------------------- +typedef ivl_tpl uval_ivl_t; +struct ivl_t : public uval_ivl_t +{ +private: + typedef ivl_tpl inherited; + +public: + ivl_t(uval_t _off=0, uval_t _size=0) : inherited(_off,_size) {} + bool empty() const { return size == 0; } + void clear() { size = 0; } + void print(qstring *vout) const; + const char *hexapi dstr() const; + + bool extend_to_cover(const ivl_t &r) // extend interval to cover 'r' + { + uval_t new_end = end(); + bool changed = false; + if ( off > r.off ) + { + off = r.off; + changed = true; + } + if ( new_end < r.end() ) + { + new_end = r.end(); + changed = true; + } + if ( changed ) + size = new_end - off; + return changed; + } + void intersect(const ivl_t &r) + { + uval_t new_off = qmax(off, r.off); + uval_t new_end = end(); + if ( new_end > r.end() ) + new_end = r.end(); + if ( new_off < new_end ) + { + off = new_off; + size = new_end - off; + } + else + { + size = 0; + } + } + + // do *this and ivl overlap? + bool overlap(const ivl_t &ivl) const + { + return interval::overlap(off, size, ivl.off, ivl.size); + } + // does *this include ivl? + bool includes(const ivl_t &ivl) const + { + return interval::includes(off, size, ivl.off, ivl.size); + } + // does *this contain off2? + bool contains(uval_t off2) const + { + return interval::contains(off, size, off2); + } + + DECLARE_COMPARISONS(ivl_t); + static const ivl_t allmem; +#define ALLMEM ivl_t::allmem +}; +DECLARE_TYPE_AS_MOVABLE(ivl_t); + +//------------------------------------------------------------------------- +struct ivl_with_name_t +{ + ivl_t ivl; + const char *whole; // name of the whole interval + const char *part; // prefix to use for parts of the interval (e.g. sp+4) + ivl_with_name_t(): ivl(0, BADADDR), whole(""), part(nullptr) {} + DEFINE_MEMORY_ALLOCATION_FUNCS() +}; + +//------------------------------------------------------------------------- +template +class ivlset_tpl // set of intervals +{ +public: + typedef qvector bag_t; + +protected: + bag_t bag; + bool verify() const; + // we do not store the empty intervals in bag so size == 0 denotes + // MAX_VALUE+1, e.g. 0x100000000 for uint32 + static bool ivl_all_values(const Ivl &ivl) { return ivl.off == 0 && ivl.size == 0; } + +public: + ivlset_tpl() {} + ivlset_tpl(const Ivl &ivl) { if ( ivl.valid() ) bag.push_back(ivl); } + DEFINE_MEMORY_ALLOCATION_FUNCS() + + void swap(ivlset_tpl &r) { bag.swap(r.bag); } + const Ivl &getivl(int idx) const { return bag[idx]; } + const Ivl &lastivl() const { return bag.back(); } + size_t nivls() const { return bag.size(); } + bool empty() const { return bag.empty(); } + void clear() { bag.clear(); } + void qclear() { bag.qclear(); } + bool all_values() const { return nivls() == 1 && ivl_all_values(bag[0]); } + void set_all_values() { clear(); bag.push_back(Ivl(0, 0)); } + bool single_value() const { return nivls() == 1 && bag[0].size == 1; } + bool single_value(T v) const { return single_value() && bag[0].off == v; } + + bool operator==(const Ivl &v) const { return nivls() == 1 && bag[0] == v; } + bool operator!=(const Ivl &v) const { return !(*this == v); } + + typedef typename bag_t::iterator iterator; + typedef typename bag_t::const_iterator const_iterator; + const_iterator begin() const { return bag.begin(); } + const_iterator end() const { return bag.end(); } + iterator begin() { return bag.begin(); } + iterator end() { return bag.end(); } +}; + +//------------------------------------------------------------------------- +/// Set of address intervals. +/// Bit arrays are efficient only for small sets. Potentially huge +/// sets, like memory ranges, require another representation. +/// ivlset_t is used for a list of memory locations in our decompiler. +typedef ivlset_tpl uval_ivl_ivlset_t; +struct ivlset_t : public uval_ivl_ivlset_t +{ + typedef ivlset_tpl inherited; + ivlset_t() {} + ivlset_t(const ivl_t &ivl) : inherited(ivl) {} + bool hexapi add(const ivl_t &ivl); + bool add(ea_t ea, asize_t size) { return add(ivl_t(ea, size)); } + bool hexapi add(const ivlset_t &ivs); + bool hexapi addmasked(const ivlset_t &ivs, const ivl_t &mask); + bool hexapi sub(const ivl_t &ivl); + bool sub(ea_t ea, asize_t size) { return sub(ivl_t(ea, size)); } + bool hexapi sub(const ivlset_t &ivs); + bool hexapi has_common(const ivl_t &ivl, bool strict=false) const; + void hexapi print(qstring *vout) const; + const char *hexapi dstr() const; + asize_t hexapi count() const; + bool hexapi has_common(const ivlset_t &ivs) const; + bool hexapi contains(uval_t off) const; + bool hexapi includes(const ivlset_t &ivs) const; + bool hexapi intersect(const ivlset_t &ivs); + + DECLARE_COMPARISONS(ivlset_t); + +}; +DECLARE_TYPE_AS_MOVABLE(ivlset_t); +typedef qvector array_of_ivlsets; +//------------------------------------------------------------------------- +// We use bitset_t to keep list of registers. +// This is the most optimal storage for them. +class rlist_t : public bitset_t +{ +public: + rlist_t() {} + rlist_t(const rlist_t &m) : bitset_t(m) {} + rlist_t(mreg_t reg, int width) { add(reg, width); } + ~rlist_t() {} + rlist_t &operator=(const rlist_t &) = default; + void hexapi print(qstring *vout) const; + const char *hexapi dstr() const; +}; +DECLARE_TYPE_AS_MOVABLE(rlist_t); + +//------------------------------------------------------------------------- +// Microlist: list of register and memory locations +struct mlist_t +{ + rlist_t reg; // registers + ivlset_t mem; // memory locations + + mlist_t() {} + mlist_t(const ivl_t &ivl) : mem(ivl) {} + mlist_t(mreg_t r, int size) : reg(r, size) {} + + void swap(mlist_t &r) { reg.swap(r.reg); mem.swap(r.mem); } + bool hexapi addmem(ea_t ea, asize_t size); + bool add(mreg_t r, int size) { return add(mlist_t(r, size)); } // also see append_def_list() + bool add(const rlist_t &r) { return reg.add(r); } + bool add(const ivl_t &ivl) { return add(mlist_t(ivl)); } + bool add(const mlist_t &lst) + { + bool changed = reg.add(lst.reg); + if ( mem.add(lst.mem) ) + changed = true; + return changed; + } + bool sub(mreg_t r, int size) { return sub(mlist_t(r, size)); } + bool sub(const ivl_t &ivl) { return sub(mlist_t(ivl)); } + bool sub(const mlist_t &lst) + { + bool changed = reg.sub(lst.reg); + if ( mem.sub(lst.mem) ) + changed = true; + return changed; + } + asize_t count() const { return reg.count() + mem.count(); } + void hexapi print(qstring *vout) const; + const char *hexapi dstr() const; + bool empty() const { return reg.empty() && mem.empty(); } + void clear() { reg.clear(); mem.clear(); } + bool has(mreg_t r) const { return reg.has(r); } + bool has_all(mreg_t r, int size) const { return reg.has_all(r, size); } + bool has_any(mreg_t r, int size) const { return reg.has_any(r, size); } + bool has_memory() const { return !mem.empty(); } + bool has_allmem() const { return mem == ALLMEM; } + bool has_common(const mlist_t &lst) const { return reg.has_common(lst.reg) || mem.has_common(lst.mem); } + bool includes(const mlist_t &lst) const { return reg.includes(lst.reg) && mem.includes(lst.mem); } + bool intersect(const mlist_t &lst) + { + bool changed = reg.intersect(lst.reg); + if ( mem.intersect(lst.mem) ) + changed = true; + return changed; + } + bool is_subset_of(const mlist_t &lst) const { return lst.includes(*this); } + + DECLARE_COMPARISONS(mlist_t); + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; +DECLARE_TYPE_AS_MOVABLE(mlist_t); +typedef qvector mlistvec_t; +DECLARE_TYPE_AS_MOVABLE(mlistvec_t); + +//------------------------------------------------------------------------- +/// Get list of temporary registers. +/// Tempregs are temporary registers that are used during code generation. +/// They do not map to regular processor registers. They are used only to +/// store temporary values during execution of one instruction. +/// Tempregs may not be used to pass a value from one block to another. +/// In other words, at the end of a block all tempregs must be dead. +const mlist_t &hexapi get_temp_regs(); + +/// Is a kernel register? +/// Kernel registers are temporary registers that can be used freely. +/// They may be used to store values that cross instruction or basic block +/// boundaries. Kernel registers do not map to regular processor registers. +/// See also \ref mba_t::alloc_kreg() +bool hexapi is_kreg(mreg_t r); + +/// Map a processor register to a microregister. +/// \param reg processor register number +/// \return microregister register id or mr_none +mreg_t hexapi reg2mreg(int reg); + +/// Map a microregister to a processor register. +/// \param reg microregister number +/// \param width size of microregister in bytes +/// \return processor register id or -1 +int hexapi mreg2reg(mreg_t reg, int width); + +/// Get the microregister name. +/// \param out output buffer, may be nullptr +/// \param reg microregister number +/// \param width size of microregister in bytes. may be bigger than the real +/// register size. +/// \param ud reserved, must be nullptr +/// \return width of the printed register. this value may be less than +/// the WIDTH argument. + +int hexapi get_mreg_name(qstring *out, mreg_t reg, int width, void *ud=nullptr); + +//------------------------------------------------------------------------- +/// User defined callback to optimize individual microcode instructions +struct optinsn_t +{ + virtual ~optinsn_t() {} + /// Optimize an instruction. + /// \param blk current basic block. maybe nullptr, which means that + /// the instruction must be optimized without context + /// \param ins instruction to optimize; it is always a top-level instruction. + /// the callback may not delete the instruction but may + /// convert it into nop (see mblock_t::make_nop). to optimize + /// sub-instructions, visit them using minsn_visitor_t. + /// sub-instructions may not be converted into nop but + /// can be converted to "mov x,x". for example: + /// add x,0,x => mov x,x + /// this callback may change other instructions in the block, + /// but should do this with care, e.g. to no break the + /// propagation algorithm if called with OPTI_NO_LDXOPT. + /// \param optflags combination of \ref OPTI_ bits + /// \return number of changes made to the instruction. + /// if after this call the instruction's use/def lists have changed, + /// you must mark the block level lists as dirty (see mark_lists_dirty) + virtual int idaapi func(mblock_t *blk, minsn_t *ins, int optflags) = 0; +}; + +/// Install an instruction level custom optimizer +/// \param opt an instance of optinsn_t. cannot be destroyed before calling +/// remove_optinsn_handler(). +void hexapi install_optinsn_handler(optinsn_t *opt); + +/// Remove an instruction level custom optimizer +bool hexapi remove_optinsn_handler(optinsn_t *opt); + +/// User defined callback to optimize microcode blocks +struct optblock_t +{ + virtual ~optblock_t() {} + /// Optimize a block. + /// This function usually performs the optimizations that require analyzing + /// the entire block and/or its neighbors. For example it can recognize + /// patterns and perform conversions like: + /// b0: b0: + /// ... ... + /// jnz x, 0, @b2 => jnz x, 0, @b2 + /// b1: b1: + /// add x, 0, y mov x, y + /// ... ... + /// \param blk Basic block to optimize as a whole. + /// \return number of changes made to the block. See also mark_lists_dirty. + virtual int idaapi func(mblock_t *blk) = 0; +}; + +/// Install a block level custom optimizer. +/// \param opt an instance of optblock_t. cannot be destroyed before calling +/// remove_optblock_handler(). +void hexapi install_optblock_handler(optblock_t *opt); + +/// Remove a block level custom optimizer +bool hexapi remove_optblock_handler(optblock_t *opt); + + +//------------------------------------------------------------------------- +// abstract graph interface +class simple_graph_t : public gdl_graph_t +{ + // does a path from 'm' to 'n' exist? + bool path_exists(node_bitset_t &visited, int m, int n) const; +protected: + void calc_outgoing_edges(const intvec_t &sub, edgevec_t &el) const; + void compute_dominator_info(struct dominator_info_t &di); + bool is_connected_without(const edge_t &forbidden_edge, const intvec_t &dead_nodes) const; +public: + qstring title; + bool colored_gdl_edges = false; + + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + // this call is used to exclude edges in worklist_iterate... functions() + virtual bool ignore_edge(int /*src*/, int /*dst*/ ) const newapi { return false; } + + void hexapi compute_dominators(array_of_node_bitset_t &domin, bool post=false) const; + void hexapi compute_immediate_dominators( + const array_of_node_bitset_t &domin, + intvec_t &idomin, + bool post=false) const; + int hexapi depth_first_preorder(node_ordering_t *pre) const; // returns number of visited nodes + int hexapi depth_first_postorder(node_ordering_t *post) const; // returns number of visited nodes +#ifndef SWIG + void depth_first_postorder(node_ordering_t *post, edge_mapper_t *et) const; + void depth_first_postorder_for_all_entries(node_ordering_t *post) const; + intvec_t find_dead_nodes() const; + + // find nodes reaching 'n' + void find_reaching_nodes(int n, node_bitset_t &reaching) const; + + // does a path from 'm' to 'n' exist? + bool path_exists(int m, int n) const; + + // is there a path from M to N which terminates with a back edge to N? + bool path_back(const array_of_node_bitset_t &domin, int m, int n) const; + bool path_back(const edge_mapper_t &et, int m, int n) const; +#endif + + class iterator + { + friend class simple_graph_t; + int i; + iterator(int n) : i(n) {} + public: + bool operator==(const iterator &n) const { return i == n.i; } + bool operator!=(const iterator &n) const { return i != n.i; } + int operator*() const { return i; } + }; + typedef iterator const_iterator; + iterator begin() const { return iterator(goup(0)); } + iterator end() const { return iterator(size()); } + int front() const { return *begin(); } + void inc(iterator &p, int n=1) const { p.i = goup(p.i+n); } + virtual int hexapi goup(int node) const newapi; +}; + +//------------------------------------------------------------------------- +// Since our data structures are quite complex, we use the visitor pattern +// in many of our algorthims. This functionality is available for plugins too. +// https://en.wikipedia.org/wiki/Visitor_pattern + +// All our visitor callbacks return an integer value. +// Visiting is interrupted as soon an the return value is non-zero. +// This non-zero value is returned as the result of the for_all_... function. +// If for_all_... returns 0, it means that it successfully visited all items. + +/// The context info used by visitors +struct op_parent_info_t +{ + mba_t *mba; // current microcode + mblock_t *blk; // current block + minsn_t *topins; // top level instruction (parent of curins or curins itself) + minsn_t *curins; // currently visited instruction + + op_parent_info_t( + mba_t *_mba=nullptr, + mblock_t *_blk=nullptr, + minsn_t *_topins=nullptr) + : mba(_mba), blk(_blk), topins(_topins), curins(nullptr) {} + virtual ~op_parent_info_t() {} + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + bool really_alloc() const; +}; + +/// Micro instruction visitor. +/// See mba_t::for_all_topinsns, minsn_t::for_all_insns, +/// mblock_::for_all_insns, mba_t::for_all_insns +struct minsn_visitor_t : public op_parent_info_t +{ + minsn_visitor_t( + mba_t *_mba=nullptr, + mblock_t *_blk=nullptr, + minsn_t *_topins=nullptr) + : op_parent_info_t(_mba, _blk, _topins) {} + virtual int idaapi visit_minsn() = 0; +}; + +/// Micro operand visitor. +/// See mop_t::for_all_ops, minsn_t::for_all_ops, mblock_t::for_all_insns, +/// mba_t::for_all_insns +struct mop_visitor_t : public op_parent_info_t +{ + /// Should skip sub-operands of the current operand? + /// visit_mop() may set 'prune=true' for that. + bool prune = false; + + mop_visitor_t( + mba_t *_mba=nullptr, + mblock_t *_blk=nullptr, + minsn_t *_topins=nullptr) + : op_parent_info_t(_mba, _blk, _topins) {} + virtual int idaapi visit_mop(mop_t *op, const tinfo_t *type, bool is_target) = 0; +}; + +/// Scattered mop: visit each of the scattered locations as a separate mop. +/// See mop_t::for_all_scattered_submops +struct scif_visitor_t +{ + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + virtual ~scif_visitor_t() {} + virtual int idaapi visit_scif_mop(const mop_t &r, int off) = 0; +}; + +// Used operand visitor. +// See mblock_t::for_all_uses +struct mlist_mop_visitor_t +{ + minsn_t *topins = nullptr; + minsn_t *curins = nullptr; + bool changed = false; + mlist_t *list = nullptr; + /// Should skip sub-operands of the current operand? + /// visit_mop() may set 'prune=true' for that. + bool prune = false; + + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + virtual ~mlist_mop_visitor_t() {} + virtual int idaapi visit_mop(mop_t *op) = 0; +}; + +//------------------------------------------------------------------------- +/// Instruction operand types + +typedef uint8 mopt_t; +const mopt_t + mop_z = 0, ///< none + mop_r = 1, ///< register (they exist until MMAT_LVARS) + mop_n = 2, ///< immediate number constant + mop_str = 3, ///< immediate string constant (user representation) + mop_d = 4, ///< result of another instruction + mop_S = 5, ///< local stack variable (they exist until MMAT_LVARS) + mop_v = 6, ///< global variable + mop_b = 7, ///< micro basic block (mblock_t) + mop_f = 8, ///< list of arguments + mop_l = 9, ///< local variable + mop_a = 10, ///< mop_addr_t: address of operand (mop_l, mop_v, mop_S, mop_r) + mop_h = 11, ///< helper function + mop_c = 12, ///< mcases + mop_fn = 13, ///< floating point constant + mop_p = 14, ///< operand pair + mop_sc = 15; ///< scattered + +const int NOSIZE = -1; ///< wrong or unexisting operand size + +//------------------------------------------------------------------------- +/// Reference to a local variable. Used by mop_l +struct lvar_ref_t +{ + /// Pointer to the parent mba_t object. + /// Since we need to access the 'mba->vars' array in order to retrieve + /// the referenced variable, we keep a pointer to mba_t here. + /// Note: this means this class and consequently mop_t, minsn_t, mblock_t + /// are specific to a mba_t object and cannot migrate between + /// them. fortunately this is not something we need to do. + /// second, lvar_ref_t's appear only after MMAT_LVARS. + mba_t *const mba; + sval_t off; ///< offset from the beginning of the variable + int idx; ///< index into mba->vars + lvar_ref_t(mba_t *m, int i, sval_t o=0) : mba(m), off(o), idx(i) {} + lvar_ref_t(const lvar_ref_t &r) : mba(r.mba), off(r.off), idx(r.idx) {} + lvar_ref_t &operator=(const lvar_ref_t &r) + { + off = r.off; + idx = r.idx; + return *this; + } + DECLARE_COMPARISONS(lvar_ref_t); + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + void swap(lvar_ref_t &r) + { + std::swap(off, r.off); + std::swap(idx, r.idx); + } + lvar_t &hexapi var() const; ///< Retrieve the referenced variable +}; + +//------------------------------------------------------------------------- +/// Reference to a stack variable. Used for mop_S +struct stkvar_ref_t +{ + /// Pointer to the parent mba_t object. + /// We need it in order to retrieve the referenced stack variable. + /// See notes for lvar_ref_t::mba. + mba_t *const mba; + + /// Offset to the stack variable from the bottom of the stack frame. + /// It is called 'decompiler stkoff' and it is different from IDA stkoff. + /// See a note and a picture about 'decompiler stkoff' below. + sval_t off; + + stkvar_ref_t(mba_t *m, sval_t o) : mba(m), off(o) {} + DECLARE_COMPARISONS(stkvar_ref_t); + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + void swap(stkvar_ref_t &r) + { + std::swap(off, r.off); + } + /// Retrieve the referenced stack variable. + /// \param[out] udm stkvar, may be nullptr + /// \param p_idaoff if specified, will hold IDA stkoff after the call. + /// \return index of stkvar in the frame or -1 + ssize_t hexapi get_stkvar(udm_t *udm=nullptr, uval_t *p_idaoff=nullptr) const; +}; + +//------------------------------------------------------------------------- +/// Scattered operand info. Used for mop_sc +struct scif_t : public vdloc_t +{ + /// Pointer to the parent mba_t object. + /// Some operations may convert a scattered operand into something simpler, + /// (a stack operand, for example). We will need to create stkvar_ref_t at + /// that moment, this is why we need this pointer. + /// See notes for lvar_ref_t::mba. + mba_t *mba; + + /// Usually scattered operands are created from a function prototype, + /// which has the name information. We preserve it and use it to name + /// the corresponding local variable. + qstring name; + + /// Scattered operands always have type info assigned to them + /// because without it we won't be able to manipulte them. + tinfo_t type; + + scif_t(mba_t *_mba, tinfo_t *tif, qstring *n=nullptr) : mba(_mba) + { + if ( n != nullptr ) + n->swap(name); + tif->swap(type); + } + scif_t &operator =(const vdloc_t &loc) + { + *(vdloc_t *)this = loc; + return *this; + } +}; + +//------------------------------------------------------------------------- +/// An integer constant. Used for mop_n +/// We support 64-bit values but 128-bit values can be represented with mop_p +struct mnumber_t : public operand_locator_t +{ + uint64 value; + uint64 org_value; // original value before changing the operand size + mnumber_t(uint64 v, ea_t _ea=BADADDR, int n=0) + : operand_locator_t(_ea, n), value(v), org_value(v) {} + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + DECLARE_COMPARISONS(mnumber_t) + { + if ( value < r.value ) + return -1; + if ( value > r.value ) + return -1; + return 0; + } + // always use this function instead of manually modifying the 'value' field + void update_value(uint64 val64) + { + value = val64; + org_value = val64; + } +}; + +//------------------------------------------------------------------------- +/// Floating point constant. Used for mop_fn +/// For more details, please see the ieee.h file from IDA SDK. +struct fnumber_t +{ + fpvalue_t fnum; ///< Internal representation of the number + int nbytes; ///< Original size of the constant in bytes + operator uint16 *() { return fnum.w; } + operator const uint16 *() const { return fnum.w; } + void hexapi print(qstring *vout) const; + const char *hexapi dstr() const; + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + DECLARE_COMPARISONS(fnumber_t) + { + return ecmp(fnum, r.fnum); + } + int calc_max_exp() const + { + return nbytes <= 4 ? MAXEXP_FLOAT + : nbytes <= 8 ? MAXEXP_DOUBLE + : MAXEXP_LNGDBL; + } + bool is_nan() const + { + return get_fpvalue_kind(fnum, calc_max_exp()) == FPV_NAN; + } +}; + +//------------------------------------------------------------------------- +/// \defgroup SHINS_ Bits to control how we print instructions +///@{ +#define SHINS_NUMADDR 0x01 ///< display definition addresses for numbers +#define SHINS_VALNUM 0x02 ///< display value numbers +#define SHINS_SHORT 0x04 ///< do not display use-def chains and other attrs +#define SHINS_LDXEA 0x08 ///< display address of ldx expressions (not used) +///@} + +//------------------------------------------------------------------------- +/// How to handle side effect of change_size() +/// Sometimes we need to create a temporary operand and change its size in order +/// to check some hypothesis. If we revert our changes, we do not want that the +/// database (global variables, stack frame, etc) changes in any manner. +enum side_effect_t +{ + NO_SIDEFF, ///< change operand size but ignore side effects + ///< if you decide to keep the changed operand, + ///< handle_new_size() must be called + WITH_SIDEFF, ///< change operand size and handle side effects + ONLY_SIDEFF, ///< only handle side effects + ANY_REGSIZE = 0x80, ///< any register size is permitted + ANY_FPSIZE = 0x100, ///< any size of floating operand is permitted +}; + +//------------------------------------------------------------------------- +/// A microinstruction operand. +/// This is the smallest building block of our microcode. +/// Operands will be part of instructions, which are then grouped into basic blocks. +/// The microcode consists of an array of such basic blocks + some additional info. +class mop_t +{ + void hexapi copy(const mop_t &rop); +public: + /// Operand type. + mopt_t t; + + /// Operand properties. + uint8 oprops; +#define OPROP_IMPDONE 0x01 ///< imported operand (a pointer) has been dereferenced +#define OPROP_UDT 0x02 ///< a struct or union +#define OPROP_FLOAT 0x04 ///< possibly floating value +#define OPROP_CCFLAGS 0x08 ///< mop_n: a pc-relative value + ///< mop_a: an address obtained from a relocation + ///< else: value of a condition code register (like mr_cc) +#define OPROP_UDEFVAL 0x10 ///< uses undefined value +#define OPROP_LOWADDR 0x20 ///< a low address offset +#define OPROP_ABI 0x40 ///< is used to organize arg/retval of a call + ///< such operands should be combined more carefully + ///< than others at least on BE platforms + + /// Value number. + /// Zero means unknown. + /// Operands with the same value number are equal. + uint16 valnum; + + /// Operand size. + /// Usually it is 1,2,4,8 or NOSIZE but for UDTs other sizes are permitted + int size; + + /// The following union holds additional details about the operand. + /// Depending on the operand type different kinds of info are stored. + /// You should access these fields only after verifying the operand type. + /// All pointers are owned by the operand and are freed by its destructor. + union + { + mreg_t r; // mop_r register number + mnumber_t *nnn; // mop_n immediate value + minsn_t *d; // mop_d result (destination) of another instruction + stkvar_ref_t *s; // mop_S stack variable + ea_t g; // mop_v global variable (its linear address) + int b; // mop_b block number (used in jmp,call instructions) + mcallinfo_t *f; // mop_f function call information + lvar_ref_t *l; // mop_l local variable + mop_addr_t *a; // mop_a variable whose address is taken + char *helper; // mop_h helper function name + char *cstr; // mop_str utf8 string constant, user representation + mcases_t *c; // mop_c cases + fnumber_t *fpc; // mop_fn floating point constant + mop_pair_t *pair; // mop_p operand pair + scif_t *scif; // mop_sc scattered operand info + }; + // -- End of data fields, member function declarations follow: + + void set_impptr_done() { oprops |= OPROP_IMPDONE; } + void set_udt() { oprops |= OPROP_UDT; } + void set_undef_val() { oprops |= OPROP_UDEFVAL; } + void set_lowaddr() { oprops |= OPROP_LOWADDR; } + void set_for_abi() { oprops |= OPROP_ABI; } + bool is_impptr_done() const { return (oprops & OPROP_IMPDONE) != 0; } + bool is_udt() const { return (oprops & OPROP_UDT) != 0; } + bool probably_floating() const { return (oprops & OPROP_FLOAT) != 0; } + bool is_undef_val() const { return (oprops & OPROP_UDEFVAL) != 0; } + bool is_lowaddr() const { return (oprops & OPROP_LOWADDR) != 0; } + bool is_for_abi() const { return (oprops & OPROP_ABI) != 0; } + bool is_ccflags() const + { + return (oprops & OPROP_CCFLAGS) != 0 + && (t == mop_l || t == mop_v || t == mop_S || t == mop_r); + } + bool is_pcval() const + { + return t == mop_n && (oprops & OPROP_CCFLAGS) != 0; + } + bool is_glbaddr_from_fixup() const + { + return is_glbaddr() && (oprops & OPROP_CCFLAGS) != 0; + } + + mop_t() { zero(); } + mop_t(const mop_t &rop) { copy(rop); } + mop_t(mreg_t _r, int _s) : t(mop_r), oprops(0), valnum(0), size(_s), r(_r) {} + mop_t &operator=(const mop_t &rop) { return assign(rop); } + mop_t &hexapi assign(const mop_t &rop); + ~mop_t() + { + erase(); + } + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + void zero() { t = mop_z; oprops = 0; valnum = 0; size = NOSIZE; nnn = nullptr; } + void hexapi swap(mop_t &rop); + void hexapi erase(); + void erase_but_keep_size() { int s2 = size; erase(); size = s2; } + + void hexapi print(qstring *vout, int shins_flags=SHINS_SHORT|SHINS_VALNUM) const; + const char *hexapi dstr() const; // use this function for debugging + + //----------------------------------------------------------------------- + // Operand creation + //----------------------------------------------------------------------- + /// Create operand from mlist_t. + /// Example: if LST contains 4 bits for R0.4, our operand will be + /// (t=mop_r, r=R0, size=4) + /// \param mba pointer to microcode + /// \param lst list of locations + /// \param fullsize mba->fullsize + /// \return success + bool hexapi create_from_mlist(mba_t *mba, const mlist_t &lst, sval_t fullsize); + + /// Create operand from ivlset_t. + /// Example: if IVS contains [glbvar..glbvar+4), our operand will be + /// (t=mop_v, g=&glbvar, size=4) + /// \param mba pointer to microcode + /// \param ivs set of memory intervals + /// \param fullsize mba->fullsize + /// \return success + bool hexapi create_from_ivlset(mba_t *mba, const ivlset_t &ivs, sval_t fullsize); + + /// Create operand from vdloc_t. + /// Example: if LOC contains (type=ALOC_REG1, r=R0), our operand will be + /// (t=mop_r, r=R0, size=_SIZE) + /// \param mba pointer to microcode + /// \param loc location + /// \param _size operand size + /// Note: this function cannot handle scattered locations. + /// \return success + void hexapi create_from_vdloc(mba_t *mba, const vdloc_t &loc, int _size); + + /// Create operand from scattered vdloc_t. + /// Example: if LOC is (ALOC_DIST, {EAX.4, EDX.4}) and TYPE is _LARGE_INTEGER, + /// our operand will be + /// (t=mop_sc, scif={EAX.4, EDX.4}) + /// \param mba pointer to microcode + /// \param name name of the operand, if available + /// \param type type of the operand, must be present + /// \param loc a scattered location + /// \return success + void hexapi create_from_scattered_vdloc( + mba_t *mba, + const char *name, + tinfo_t type, + const vdloc_t &loc); + + /// Create operand from an instruction. + /// This function creates a nested instruction that can be used as an operand. + /// Example: if m="add x,y,z", our operand will be (t=mop_d,d=m). + /// The destination operand of 'add' (z) is lost. + /// \param m instruction to embed into operand. may not be nullptr. + void hexapi create_from_insn(const minsn_t *m); + + /// Create an integer constant operand. + /// \param _value value to store in the operand + /// \param _size size of the value in bytes (1,2,4,8) + /// \param _ea address of the processor instruction that made the value + /// \param opnum operand number of the processor instruction + void hexapi make_number(uint64 _value, int _size, ea_t _ea=BADADDR, int opnum=0); + + /// Create a floating point constant operand. + /// \param bytes pointer to the floating point value as used by the current + /// processor (e.g. for x86 it must be in IEEE 754) + /// \param _size number of bytes occupied by the constant. + /// \return success + bool hexapi make_fpnum(const void *bytes, size_t _size); + + /// Create a register operand without erasing previous data. + /// \param reg micro register number + /// Note: this function does not erase the previous contents of the operand; + /// call erase() if necessary + void _make_reg(mreg_t reg) + { + t = mop_r; + r = reg; + } + void _make_reg(mreg_t reg, int _size) + { + t = mop_r; + r = reg; + size = _size; + } + /// Create a register operand. + void make_reg(mreg_t reg) { erase(); _make_reg(reg); } + void make_reg(mreg_t reg, int _size) { erase(); _make_reg(reg, _size); } + + /// Create a local variable operand. + /// \param mba pointer to microcode + /// \param idx index into mba->vars + /// \param off offset from the beginning of the variable + /// Note: this function does not erase the previous contents of the operand; + /// call erase() if necessary + void _make_lvar(mba_t *mba, int idx, sval_t off=0) + { + t = mop_l; + l = new lvar_ref_t(mba, idx, off); + } + + /// Create a global variable operand without erasing previous data. + /// \param ea address of the variable + /// Note: this function does not erase the previous contents of the operand; + /// call erase() if necessary + void hexapi _make_gvar(ea_t ea); + /// Create a global variable operand. + void hexapi make_gvar(ea_t ea); + + /// Create a stack variable operand. + /// \param mba pointer to microcode + /// \param off decompiler stkoff + /// Note: this function does not erase the previous contents of the operand; + /// call erase() if necessary + void _make_stkvar(mba_t *mba, sval_t off) + { + t = mop_S; + s = new stkvar_ref_t(mba, off); + } + void make_stkvar(mba_t *mba, sval_t off) { erase(); _make_stkvar(mba, off); } + + /// Create pair of registers. + /// \param loreg register holding the low part of the value + /// \param hireg register holding the high part of the value + /// \param halfsize the size of each of loreg/hireg + void hexapi make_reg_pair(int loreg, int hireg, int halfsize); + + /// Create a nested instruction without erasing previous data. + /// \param ins pointer to the instruction to encapsulate into the operand + /// Note: this function does not erase the previous contents of the operand; + /// call erase() if necessary + /// See also create_from_insn, which is higher level + void _make_insn(minsn_t *ins); + /// Create a nested instruction. + void make_insn(minsn_t *ins) { erase(); _make_insn(ins); } + + /// Create a block reference operand without erasing previous data. + /// \param blknum block number + /// Note: this function does not erase the previous contents of the operand; + /// call erase() if necessary + void _make_blkref(int blknum) + { + t = mop_b; + b = blknum; + } + /// Create a global variable operand. + void make_blkref(int blknum) { erase(); _make_blkref(blknum); } + + /// Create a helper operand. + /// A helper operand usually keeps a built-in function name like "va_start" + /// It is essentially just an arbitrary identifier without any additional info. + void hexapi make_helper(const char *name); + + /// Create a constant string operand. + void _make_strlit(const char *str) + { + t = mop_str; + cstr = ::qstrdup(str); + } + void _make_strlit(qstring *str) // str is consumed + { + t = mop_str; + cstr = str->extract(); + } + + /// Create a call info operand without erasing previous data. + /// \param fi callinfo + /// Note: this function does not erase the previous contents of the operand; + /// call erase() if necessary + void _make_callinfo(mcallinfo_t *fi) + { + t = mop_f; + f = fi; + } + + /// Create a 'switch cases' operand without erasing previous data. + /// Note: this function does not erase the previous contents of the operand; + /// call erase() if necessary + void _make_cases(mcases_t *_cases) + { + t = mop_c; + c = _cases; + } + + /// Create a pair operand without erasing previous data. + /// Note: this function does not erase the previous contents of the operand; + /// call erase() if necessary + void _make_pair(mop_pair_t *_pair) + { + t = mop_p; + pair = _pair; + } + + //----------------------------------------------------------------------- + // Various operand tests + //----------------------------------------------------------------------- + bool empty() const { return t == mop_z; } + /// Is a global variable? + bool is_glbvar() const { return t == mop_v; } + /// Is a stack variable? + bool is_stkvar() const { return t == mop_S; } + /// Is a register operand? + /// See also get_mreg_name() + bool is_reg() const { return t == mop_r; } + /// Is the specified register? + bool is_reg(mreg_t _r) const { return t == mop_r && r == _r; } + /// Is the specified register of the specified size? + bool is_reg(mreg_t _r, int _size) const { return t == mop_r && r == _r && size == _size; } + /// Is a list of arguments? + bool is_arglist() const { return t == mop_f; } + /// Is a condition code? + bool is_cc() const { return is_reg() && r >= mr_cf && r < mr_first; } + /// Is a bit register? + /// This includes condition codes and eventually other bit registers + static bool hexapi is_bit_reg(mreg_t reg); + bool is_bit_reg() const { return is_reg() && is_bit_reg(r); } + /// Is a kernel register? + bool is_kreg() const; + /// Is a block reference? + bool is_mblock() const { return t == mop_b; } + /// Is a block reference to the specified block? + bool is_mblock(int serial) const { return is_mblock() && b == serial; } + /// Is a scattered operand? + bool is_scattered() const { return t == mop_sc; } + /// Is address of a global memory cell? + bool is_glbaddr() const; + /// Is address of the specified global memory cell? + bool is_glbaddr(ea_t ea) const; + /// Is address of a stack variable? + bool is_stkaddr() const; + /// Is a sub-instruction? + bool is_insn() const { return t == mop_d; } + /// Is a sub-instruction with the specified opcode? + bool is_insn(mcode_t code) const; + /// Has any side effects? + /// \param include_ldx_and_divs consider ldx/div/mod as having side effects? + bool has_side_effects(bool include_ldx_and_divs=false) const; + /// Is it possible for the operand to use aliased memory? + bool hexapi may_use_aliased_memory() const; + + /// Are the possible values of the operand only 0 and 1? + /// This function returns true for 0/1 constants, bit registers, + /// the result of 'set' insns, etc. + bool hexapi is01() const; + + /// Does the high part of the operand consist of the sign bytes? + /// \param nbytes number of bytes that were sign extended. + /// the remaining size-nbytes high bytes must be sign bytes + /// Example: is_sign_extended_from(xds.4(op.1), 1) -> true + /// because the high 3 bytes are certainly sign bits + bool hexapi is_sign_extended_from(int nbytes) const; + + /// Does the high part of the operand consist of zero bytes? + /// \param nbytes number of bytes that were zero extended. + /// the remaining size-nbytes high bytes must be zero + /// Example: is_zero_extended_from(xdu.8(op.1), 2) -> true + /// because the high 6 bytes are certainly zero + bool hexapi is_zero_extended_from(int nbytes) const; + + /// Does the high part of the operand consist of zero or sign bytes? + bool is_extended_from(int nbytes, bool is_signed) const + { + if ( is_signed ) + return is_sign_extended_from(nbytes); + else + return is_zero_extended_from(nbytes); + } + + //----------------------------------------------------------------------- + // Comparisons + //----------------------------------------------------------------------- + /// Compare operands. + /// This is the main comparison function for operands. + /// \param rop operand to compare with + /// \param eqflags combination of \ref EQ_ bits + bool hexapi equal_mops(const mop_t &rop, int eqflags) const; + bool operator==(const mop_t &rop) const { return equal_mops(rop, 0); } + bool operator!=(const mop_t &rop) const { return !equal_mops(rop, 0); } + + /// Lexographical operand comparison. + /// It can be used to store mop_t in various containers, like std::set + bool operator <(const mop_t &rop) const { return lexcompare(rop) < 0; } + friend int lexcompare(const mop_t &a, const mop_t &b) { return a.lexcompare(b); } + int hexapi lexcompare(const mop_t &rop) const; + + //----------------------------------------------------------------------- + // Visiting operand parts + //----------------------------------------------------------------------- + /// Visit the operand and all its sub-operands. + /// This function visits the current operand as well. + /// \param mv visitor object + /// \param type operand type + /// \param is_target is a destination operand? + int hexapi for_all_ops( + mop_visitor_t &mv, + const tinfo_t *type=nullptr, + bool is_target=false); + + /// Visit all sub-operands of a scattered operand. + /// This function does not visit the current operand, only its sub-operands. + /// All sub-operands are synthetic and are destroyed after the visitor. + /// This function works only with scattered operands. + /// \param sv visitor object + int hexapi for_all_scattered_submops(scif_visitor_t &sv) const; + + //----------------------------------------------------------------------- + // Working with mop_n operands + //----------------------------------------------------------------------- + /// Retrieve value of a constant integer operand. + /// These functions can be called only for mop_n operands. + /// See is_constant() that can be called on any operand. + uint64 value(bool is_signed) const { return extend_sign(nnn->value, size, is_signed); } + int64 signed_value() const { return value(true); } + uint64 unsigned_value() const { return value(false); } + void update_numop_value(uint64 val) + { + nnn->update_value(extend_sign(val, size, false)); + } + + /// Retrieve value of a constant integer operand. + /// \param out pointer to the output buffer + /// \param is_signed should treat the value as signed + /// \return true if the operand is mop_n + bool hexapi is_constant(uint64 *out=nullptr, bool is_signed=true) const; + + bool is_equal_to(uint64 n, bool is_signed=true) const + { + uint64 v; + return is_constant(&v, is_signed) && v == n; + } + bool is_zero() const { return is_equal_to(0, false); } + bool is_one() const { return is_equal_to(1, false); } + bool is_positive_constant() const + { + uint64 v; + return is_constant(&v, true) && int64(v) > 0; + } + bool is_negative_constant() const + { + uint64 v; + return is_constant(&v, true) && int64(v) < 0; + } + + //----------------------------------------------------------------------- + // Working with mop_S operands + //----------------------------------------------------------------------- + /// Retrieve the referenced stack variable. + /// \param[out] udm stkvar, may be nullptr + /// \param p_idaoff if specified, will hold IDA stkoff after the call. + /// \return index of stkvar in the frame or -1 + ssize_t get_stkvar(udm_t *udm=nullptr, uval_t *p_idaoff=nullptr) const + { + return s->get_stkvar(udm, p_idaoff); + } + + /// Get the referenced stack offset. + /// This function can also handle mop_sc if it is entirely mapped into + /// a continuous stack region. + /// \param p_vdoff the output buffer + /// \return success + bool hexapi get_stkoff(sval_t *p_vdoff) const; + + //----------------------------------------------------------------------- + // Working with mop_d operands + //----------------------------------------------------------------------- + /// Get subinstruction of the operand. + /// If the operand has a subinstruction with the specified opcode, return it. + /// \param code desired opcode + /// \return pointer to the instruction or nullptr + const minsn_t *get_insn(mcode_t code) const; + minsn_t *get_insn(mcode_t code); + + //----------------------------------------------------------------------- + // Transforming operands + //----------------------------------------------------------------------- + /// Make the low part of the operand. + /// This function takes into account the memory endianness (byte sex) + /// \param width the desired size of the operand part in bytes + /// \return success + bool hexapi make_low_half(int width); + + /// Make the high part of the operand. + /// This function takes into account the memory endianness (byte sex) + /// \param width the desired size of the operand part in bytes + /// \return success + bool hexapi make_high_half(int width); + + /// Make the first part of the operand. + /// This function does not care about the memory endianness + /// \param width the desired size of the operand part in bytes + /// \return success + bool hexapi make_first_half(int width); + + /// Make the second part of the operand. + /// This function does not care about the memory endianness + /// \param width the desired size of the operand part in bytes + /// \return success + bool hexapi make_second_half(int width); + + /// Shift the operand. + /// This function shifts only the beginning of the operand. + /// The operand size will be changed. + /// Examples: shift_mop(AH.1, -1) -> AX.2 + /// shift_mop(qword_00000008.8, 4) -> dword_0000000C.4 + /// shift_mop(xdu.8(op.4), 4) -> #0.4 + /// shift_mop(#0x12345678.4, 3) -> #12.1 + /// \param offset shift count (the number of bytes to shift) + /// \return success + bool hexapi shift_mop(int offset); + + /// Change the operand size. + /// Examples: change_size(AL.1, 2) -> AX.2 + /// change_size(qword_00000008.8, 4) -> dword_00000008.4 + /// change_size(xdu.8(op.4), 4) -> op.4 + /// change_size(#0x12345678.4, 1) -> #0x78.1 + /// \param nsize new operand size + /// \param sideff may modify the database because of the size change? + /// \return success + bool hexapi change_size(int nsize, side_effect_t sideff=WITH_SIDEFF); + bool double_size(side_effect_t sideff=WITH_SIDEFF) { return change_size(size*2, sideff); } + + /// Move subinstructions with side effects out of the operand. + /// If we decide to delete an instruction operand, it is a good idea to + /// call this function. Alternatively we should skip such operands + /// by calling mop_t::has_side_effects() + /// For example, if we transform: jnz x, x, @blk => goto @blk + /// then we must call this function before deleting the X operands. + /// \param blk current block + /// \param top top level instruction that contains our operand + /// \param moved_calls pointer to the boolean that will track if all side + /// effects get handled correctly. must be false initially. + /// \return false failed to preserve a side effect, it is not safe to + /// delete the operand + /// true no side effects or successfully preserved them + bool hexapi preserve_side_effects( + mblock_t *blk, + minsn_t *top, + bool *moved_calls=nullptr); + + /// Apply a unary opcode to the operand. + /// \param mcode opcode to apply. it must accept 'l' and 'd' operands + /// but not 'r'. examples: m_low/m_high/m_xds/m_xdu + /// \param ea value of minsn_t::ea for the newly created insruction + /// \param newsize new operand size + /// Example: apply_ld_mcode(m_low) will convert op => low(op) + void hexapi apply_ld_mcode(mcode_t mcode, ea_t ea, int newsize); + void apply_xdu(ea_t ea, int newsize) { apply_ld_mcode(m_xdu, ea, newsize); } + void apply_xds(ea_t ea, int newsize) { apply_ld_mcode(m_xds, ea, newsize); } +}; +DECLARE_TYPE_AS_MOVABLE(mop_t); + +/// Pair of operands +class mop_pair_t +{ +public: + mop_t lop; ///< low operand + mop_t hop; ///< high operand + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; + +/// Address of an operand (mop_l, mop_v, mop_S, mop_r) +class mop_addr_t : public mop_t +{ +public: + int insize = NOSIZE; // how many bytes of the pointed operand can be read + int outsize = NOSIZE; // how many bytes of the pointed operand can be written + + mop_addr_t() {} + mop_addr_t(const mop_addr_t &ra) + : mop_t(ra), insize(ra.insize), outsize(ra.outsize) {} + mop_addr_t(const mop_t &ra, int isz, int osz) + : mop_t(ra), insize(isz), outsize(osz) {} + + mop_addr_t &operator=(const mop_addr_t &rop) + { + *(mop_t *)this = mop_t(rop); + insize = rop.insize; + outsize = rop.outsize; + return *this; + } + int lexcompare(const mop_addr_t &ra) const + { + int code = mop_t::lexcompare(ra); + return code != 0 ? code + : insize != ra.insize ? (insize-ra.insize) + : outsize != ra.outsize ? (outsize-ra.outsize) + : 0; + } +}; + +/// A call argument +class mcallarg_t : public mop_t // #callarg +{ +public: + ea_t ea = BADADDR; ///< address where the argument was initialized. + ///< BADADDR means unknown. + tinfo_t type; ///< formal argument type + qstring name; ///< formal argument name + argloc_t argloc; ///< ida argloc + uint32 flags = 0; ///< FAI_... + + mcallarg_t() {} + mcallarg_t(const mop_t &rarg) : mop_t(rarg) {} + void copy_mop(const mop_t &op) { *(mop_t *)this = op; } + void hexapi print(qstring *vout, int shins_flags=SHINS_SHORT|SHINS_VALNUM) const; + const char *hexapi dstr() const; + void hexapi set_regarg(mreg_t mr, int sz, const tinfo_t &tif); + void set_regarg(mreg_t mr, const tinfo_t &tif) + { + set_regarg(mr, tif.get_size(), tif); + } + void set_regarg(mreg_t mr, char dt, type_sign_t sign = type_unsigned) + { + int sz = get_dtype_size(dt); + set_regarg(mr, sz, get_int_type_by_width_and_sign(sz, sign)); + } + void make_int(int val, ea_t val_ea, int opno = 0) + { + type = tinfo_t(BTF_INT); + make_number(val, inf_get_cc_size_i(), val_ea, opno); + } + void make_uint(int val, ea_t val_ea, int opno = 0) + { + type = tinfo_t(BTF_UINT); + make_number(val, inf_get_cc_size_i(), val_ea, opno); + } +}; +DECLARE_TYPE_AS_MOVABLE(mcallarg_t); +typedef qvector mcallargs_t; + +/// Function roles. +/// They are used to calculate use/def lists and to recognize functions +/// without using string comparisons. +enum funcrole_t +{ + ROLE_UNK, ///< unknown function role + ROLE_EMPTY, ///< empty, does not do anything (maybe spoils regs) + ROLE_MEMSET, ///< memset(void *dst, uchar value, size_t count); + ROLE_MEMSET32, ///< memset32(void *dst, uint32 value, size_t count); + ROLE_MEMSET64, ///< memset64(void *dst, uint64 value, size_t count); + ROLE_MEMCPY, ///< memcpy(void *dst, const void *src, size_t count); + ROLE_STRCPY, ///< strcpy(char *dst, const char *src); + ROLE_STRLEN, ///< strlen(const char *src); + ROLE_STRCAT, ///< strcat(char *dst, const char *src); + ROLE_TAIL, ///< char *tail(const char *str); + ROLE_BUG, ///< BUG() helper macro: never returns, causes exception + ROLE_ALLOCA, ///< alloca() function + ROLE_BSWAP, ///< bswap() function (any size) + ROLE_PRESENT, ///< present() function (used in patterns) + ROLE_CONTAINING_RECORD, ///< CONTAINING_RECORD() macro + ROLE_FASTFAIL, ///< __fastfail() + ROLE_READFLAGS, ///< __readeflags, __readcallersflags + ROLE_IS_MUL_OK, ///< is_mul_ok + ROLE_SATURATED_MUL, ///< saturated_mul + ROLE_BITTEST, ///< [lock] bt + ROLE_BITTESTANDSET, ///< [lock] bts + ROLE_BITTESTANDRESET, ///< [lock] btr + ROLE_BITTESTANDCOMPLEMENT, ///< [lock] btc + ROLE_VA_ARG, ///< va_arg() macro + ROLE_VA_COPY, ///< va_copy() function + ROLE_VA_START, ///< va_start() function + ROLE_VA_END, ///< va_end() function + ROLE_ROL, ///< rotate left + ROLE_ROR, ///< rotate right + ROLE_CFSUB3, ///< carry flag after subtract with carry + ROLE_OFSUB3, ///< overflow flag after subtract with carry + ROLE_ABS, ///< integer absolute value + ROLE_3WAYCMP0, ///< 3-way compare helper, returns -1/0/1 + ROLE_3WAYCMP1, ///< 3-way compare helper, returns 0/1/2 + ROLE_WMEMCPY, ///< wchar_t *wmemcpy(wchar_t *dst, const wchar_t *src, size_t n) + ROLE_WMEMSET, ///< wchar_t *wmemset(wchar_t *dst, wchar_t wc, size_t n) + ROLE_WCSCPY, ///< wchar_t *wcscpy(wchar_t *dst, const wchar_t *src); + ROLE_WCSLEN, ///< size_t wcslen(const wchar_t *s) + ROLE_WCSCAT, ///< wchar_t *wcscat(wchar_t *dst, const wchar_t *src) + ROLE_SSE_CMP4, ///< e.g. _mm_cmpgt_ss + ROLE_SSE_CMP8, ///< e.g. _mm_cmpgt_sd +}; + +/// \defgroup FUNC_NAME_ Well known function names +///@{ +#define FUNC_NAME_MEMCPY "memcpy" +#define FUNC_NAME_WMEMCPY "wmemcpy" +#define FUNC_NAME_MEMSET "memset" +#define FUNC_NAME_WMEMSET "wmemset" +#define FUNC_NAME_MEMSET32 "memset32" +#define FUNC_NAME_MEMSET64 "memset64" +#define FUNC_NAME_STRCPY "strcpy" +#define FUNC_NAME_WCSCPY "wcscpy" +#define FUNC_NAME_STRLEN "strlen" +#define FUNC_NAME_WCSLEN "wcslen" +#define FUNC_NAME_STRCAT "strcat" +#define FUNC_NAME_WCSCAT "wcscat" +#define FUNC_NAME_TAIL "tail" +#define FUNC_NAME_VA_ARG "va_arg" +#define FUNC_NAME_EMPTY "$empty" +#define FUNC_NAME_PRESENT "$present" +#define FUNC_NAME_CONTAINING_RECORD "CONTAINING_RECORD" +#define FUNC_NAME_MORESTACK "runtime_morestack" +///@} + + +// the default 256 function arguments is too big, we use a lower value +#undef MAX_FUNC_ARGS +#define MAX_FUNC_ARGS 64 + +/// Information about a call +class mcallinfo_t // #callinfo +{ +public: + ea_t callee; ///< address of the called function, if known + int solid_args; ///< number of solid args. + ///< there may be variadic args in addtion + int call_spd = 0; ///< sp value at call insn + int stkargs_top = 0; ///< first offset past stack arguments + callcnv_t cc = CM_CC_INVALID; ///< calling convention + mcallargs_t args; ///< call arguments + mopvec_t retregs; ///< return register(s) (e.g., AX, AX:DX, etc.) + ///< this vector is built from return_regs + tinfo_t return_type; ///< type of the returned value + argloc_t return_argloc; ///< location of the returned value + + mlist_t return_regs; ///< list of values returned by the function + mlist_t spoiled; ///< list of spoiled locations (includes return_regs) + mlist_t pass_regs; ///< passthrough registers: registers that depend on input + ///< values (subset of spoiled) + ivlset_t visible_memory; ///< what memory is visible to the call? + mlist_t dead_regs; ///< registers defined by the function but never used. + ///< upon propagation we do the following: + ///< - dead_regs += return_regs + ///< - retregs.clear() since the call is propagated + int flags = 0; ///< combination of \ref FCI_... bits +/// \defgroup FCI_ Call properties +///@{ +#define FCI_PROP 0x001 ///< call has been propagated +#define FCI_DEAD 0x002 ///< some return registers were determined dead +#define FCI_FINAL 0x004 ///< call type is final, should not be changed +#define FCI_NORET 0x008 ///< call does not return +#define FCI_PURE 0x010 ///< pure function +#define FCI_NOSIDE 0x020 ///< call does not have side effects +#define FCI_SPLOK 0x040 ///< spoiled/visible_memory lists have been + ///< optimized. for some functions we can reduce them + ///< as soon as information about the arguments becomes + ///< available. in order not to try optimize them again + ///< we use this bit. +#define FCI_HASCALL 0x080 ///< A function is an synthetic helper combined + ///< from several instructions and at least one + ///< of them was a call to a real functions +#define FCI_HASFMT 0x100 ///< A variadic function with recognized + ///< printf- or scanf-style format string +#define FCI_EXPLOCS 0x400 ///< all arglocs are specified explicitly +///@} + funcrole_t role = ROLE_UNK; ///< function role + type_attrs_t fti_attrs; ///< extended function attributes + + mcallinfo_t(ea_t _callee=BADADDR, int _sargs=0) + : callee(_callee), solid_args(_sargs) + { + } + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + int hexapi lexcompare(const mcallinfo_t &f) const; + bool hexapi set_type(const tinfo_t &type); + tinfo_t hexapi get_type() const; + bool is_vararg() const { return is_vararg_cc(cc); } + void hexapi print(qstring *vout, int size=-1, int shins_flags=SHINS_SHORT|SHINS_VALNUM) const; + const char *hexapi dstr() const; +}; + +/// List of switch cases and targets +class mcases_t // #cases +{ +public: + casevec_t values; ///< expression values for each target + intvec_t targets; ///< target block numbers + + void swap(mcases_t &r) { values.swap(r.values); targets.swap(r.targets); } + DECLARE_COMPARISONS(mcases_t); + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + bool empty() const { return targets.empty(); } + size_t size() const { return targets.size(); } + void resize(int s) { values.resize(s); targets.resize(s); } + void hexapi print(qstring *vout) const; + const char *hexapi dstr() const; +}; + +//------------------------------------------------------------------------- +/// Value offset (microregister number or stack offset) +struct voff_t +{ + sval_t off = -1; ///< register number or stack offset + mopt_t type = mop_z; ///< mop_r - register, mop_S - stack, mop_z - undefined + + voff_t() {} + voff_t(mopt_t _type, sval_t _off) : off(_off), type(_type) {} + voff_t(const mop_t &op) + { + if ( op.is_reg() || op.is_stkvar() ) + set(op.t, op.is_reg() ? op.r : op.s->off); + } + + void set(mopt_t _type, sval_t _off) { type = _type; off = _off; } + void set_stkoff(sval_t stkoff) { set(mop_S, stkoff); } + void set_reg(mreg_t mreg) { set(mop_r, mreg); } + void undef() { set(mop_z, -1); } + + bool defined() const { return type != mop_z; } + bool is_reg() const { return type == mop_r; } + bool is_stkoff() const { return type == mop_S; } + mreg_t get_reg() const { QASSERT(51892, is_reg()); return off; } + sval_t get_stkoff() const { QASSERT(51893, is_stkoff()); return off; } + + void inc(sval_t delta) { off += delta; } + voff_t add(int width) const { return voff_t(type, off+width); } + sval_t diff(const voff_t &r) const { QASSERT(51894, type == r.type); return off - r.off; } + + DECLARE_COMPARISONS(voff_t) + { + int code = ::compare(type, r.type); + return code != 0 ? code : ::compare(off, r.off); + } +}; + +//------------------------------------------------------------------------- +/// Value interval (register or stack range) +struct vivl_t : voff_t +{ + int size; ///< Interval size in bytes + + vivl_t(mopt_t _type = mop_z, sval_t _off = -1, int _size = 0) + : voff_t(_type, _off), size(_size) {} + vivl_t(const class chain_t &ch); + vivl_t(const mop_t &op) : voff_t(op), size(op.size) {} + + // Make a value interval + void set(mopt_t _type, sval_t _off, int _size = 0) + { voff_t::set(_type, _off); size = _size; } + void set(const voff_t &voff, int _size) + { set(voff.type, voff.off, _size); } + void set_stkoff(sval_t stkoff, int sz = 0) { set(mop_S, stkoff, sz); } + void set_reg (mreg_t mreg, int sz = 0) { set(mop_r, mreg, sz); } + + /// Extend a value interval using another value interval of the same type + /// \return success + bool hexapi extend_to_cover(const vivl_t &r); + + /// Intersect value intervals the same type + /// \return size of the resulting intersection + uval_t hexapi intersect(const vivl_t &r); + + /// Do two value intervals overlap? + bool overlap(const vivl_t &r) const + { + return type == r.type + && interval::overlap(off, size, r.off, r.size); + } + /// Does our value interval include another? + bool includes(const vivl_t &r) const + { + return type == r.type + && interval::includes(off, size, r.off, r.size); + } + + /// Does our value interval contain the specified value offset? + bool contains(const voff_t &voff2) const + { + return type == voff2.type + && interval::contains(off, size, voff2.off); + } + + // Comparisons + DECLARE_COMPARISONS(vivl_t) + { + int code = voff_t::compare(r); + return code; //return code != 0 ? code : ::compare(size, r.size); + } + bool operator==(const mop_t &mop) const + { + return type == mop.t && off == (mop.is_reg() ? mop.r : mop.s->off); + } + void hexapi print(qstring *vout) const; + const char *hexapi dstr() const; +}; + +//------------------------------------------------------------------------- +/// ud (use->def) and du (def->use) chain. +/// We store in chains only the block numbers, not individual instructions +/// See https://en.wikipedia.org/wiki/Use-define_chain +class chain_t : public intvec_t // sequence of block numbers +{ + voff_t k; ///< Value offset of the chain. + ///< (what variable is this chain about) + +public: + int width = 0; ///< size of the value in bytes + int varnum = -1; ///< allocated variable index (-1 - not allocated yet) + uchar flags; ///< combination \ref CHF_ bits +/// \defgroup CHF_ Chain properties +///@{ +#define CHF_INITED 0x01 ///< is chain initialized? (valid only after lvar allocation) +#define CHF_REPLACED 0x02 ///< chain operands have been replaced? +#define CHF_OVER 0x04 ///< overlapped chain +#define CHF_FAKE 0x08 ///< fake chain created by widen_chains() +#define CHF_PASSTHRU 0x10 ///< pass-thru chain, must use the input variable to the block +#define CHF_TERM 0x20 ///< terminating chain; the variable does not survive across the block +///@} + chain_t() : flags(CHF_INITED) {} + chain_t(mopt_t t, sval_t off, int w=1, int v=-1) + : k(t, off), width(w), varnum(v), flags(CHF_INITED) {} + chain_t(const voff_t &_k, int w=1) + : k(_k), width(w), varnum(-1), flags(CHF_INITED) {} + void set_value(const chain_t &r) + { width = r.width; varnum = r.varnum; flags = r.flags; *(intvec_t *)this = (intvec_t &)r; } + const voff_t &key() const { return k; } + bool is_inited() const { return (flags & CHF_INITED) != 0; } + bool is_reg() const { return k.is_reg(); } + bool is_stkoff() const { return k.is_stkoff(); } + bool is_replaced() const { return (flags & CHF_REPLACED) != 0; } + bool is_overlapped() const { return (flags & CHF_OVER) != 0; } + bool is_fake() const { return (flags & CHF_FAKE) != 0; } + bool is_passreg() const { return (flags & CHF_PASSTHRU) != 0; } + bool is_term() const { return (flags & CHF_TERM) != 0; } + void set_inited(bool b) { setflag(flags, CHF_INITED, b); } + void set_replaced(bool b) { setflag(flags, CHF_REPLACED, b); } + void set_overlapped(bool b) { setflag(flags, CHF_OVER, b); } + void set_term(bool b) { setflag(flags, CHF_TERM, b); } + mreg_t get_reg() const { return k.get_reg(); } + sval_t get_stkoff() const { return k.get_stkoff(); } + bool overlap(const chain_t &r) const + { return k.type == r.k.type && interval::overlap(k.off, width, r.k.off, r.width); } + bool includes(const chain_t &r) const + { return k.type == r.k.type && interval::includes(k.off, width, r.k.off, r.width); } + const voff_t endoff() const { return k.add(width); } + + bool operator<(const chain_t &r) const { return key() < r.key(); } + + void hexapi print(qstring *vout) const; + const char *hexapi dstr() const; + /// Append the contents of the chain to the specified list of locations. + void hexapi append_list(const mba_t *mba, mlist_t *list) const; + void clear_varnum() { varnum = -1; set_replaced(false); } +}; + +//------------------------------------------------------------------------- +#if defined(__NT__) +# ifdef _DEBUG +# define SIZEOF_BLOCK_CHAINS 32 +#else +# define SIZEOF_BLOCK_CHAINS 24 +# endif +#elif defined(__MAC__) +# define SIZEOF_BLOCK_CHAINS 32 +#else +# define SIZEOF_BLOCK_CHAINS 56 +#endif + +/// Chains of one block. +/// Please note that this class is based on std::set and it must be accessed +/// using the block_chains_begin(), block_chains_find() and similar functions. +/// This is required because different compilers use different implementations +/// of std::set. However, since the size of std::set depends on the compilation +/// options, we replace it with a byte array. +class block_chains_t +{ + size_t body[SIZEOF_BLOCK_CHAINS/sizeof(size_t)]; // opaque std::set, uncopyable +public: + /// Get chain for the specified register + /// \param reg register number + /// \param width size of register in bytes + const chain_t *get_reg_chain(mreg_t reg, int width=1) const + { return get_chain((chain_t(mop_r, reg, width))); } + chain_t *get_reg_chain(mreg_t reg, int width=1) + { return get_chain((chain_t(mop_r, reg, width))); } + + /// Get chain for the specified stack offset + /// \param off stack offset + /// \param width size of stack value in bytes + const chain_t *get_stk_chain(sval_t off, int width=1) const + { return get_chain(chain_t(mop_S, off, width)); } + chain_t *get_stk_chain(sval_t off, int width=1) + { return get_chain(chain_t(mop_S, off, width)); } + + /// Get chain for the specified value offset. + /// \param k value offset (register number or stack offset) + /// \param width size of value in bytes + const chain_t *get_chain(const voff_t &k, int width=1) const + { return get_chain(chain_t(k, width)); } + chain_t *get_chain(const voff_t &k, int width=1) + { return (chain_t*)((const block_chains_t *)this)->get_chain(k, width); } + + /// Get chain similar to the specified chain + /// \param ch chain to search for. only its 'k' and 'width' are used. + const chain_t *hexapi get_chain(const chain_t &ch) const; + chain_t *get_chain(const chain_t &ch) + { return (chain_t*)((const block_chains_t *)this)->get_chain(ch); } + + void hexapi print(qstring *vout) const; + const char *hexapi dstr() const; + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; +//------------------------------------------------------------------------- +/// Chain visitor class +struct chain_visitor_t +{ + block_chains_t *parent = nullptr; ///< parent of the current chain + virtual ~chain_visitor_t() {} + virtual int idaapi visit_chain(int nblock, chain_t &ch) = 0; +}; + +//------------------------------------------------------------------------- +/// Graph chains. +/// This class represents all ud and du chains of the decompiled function +typedef qvector block_chains_vec_t; +class graph_chains_t : public block_chains_vec_t +{ + int lock = 0; ///< are chained locked? (in-use) +public: + ~graph_chains_t() { QASSERT(50444, !lock); } + /// Visit all chains + /// \param cv chain visitor + /// \param gca_flags combination of GCA_ bits + int hexapi for_all_chains(chain_visitor_t &cv, int gca_flags); + /// \defgroup GCA_ chain visitor flags + //@{ +#define GCA_EMPTY 0x01 ///< include empty chains +#define GCA_SPEC 0x02 ///< include chains for special registers +#define GCA_ALLOC 0x04 ///< enumerate only allocated chains +#define GCA_NALLOC 0x08 ///< enumerate only non-allocated chains +#define GCA_OFIRST 0x10 ///< consider only chains of the first block +#define GCA_OLAST 0x20 ///< consider only chains of the last block + //@} + /// Are the chains locked? + /// It is a good idea to lock the chains before using them. This ensures + /// that they won't be recalculated and reallocated during the use. + /// See the \ref chain_keeper_t class for that. + bool is_locked() const { return lock != 0; } + /// Lock the chains + void acquire() { lock++; } + /// Unlock the chains + void hexapi release(); + void swap(graph_chains_t &r) + { + qvector::swap(r); + std::swap(lock, r.lock); + } +}; +//------------------------------------------------------------------------- +/// Microinstruction class #insn +class minsn_t +{ + void hexapi init(ea_t _ea); + void hexapi copy(const minsn_t &m); +public: + mcode_t opcode; ///< instruction opcode + int iprops; ///< combination of \ref IPROP_ bits + minsn_t *next; ///< next insn in doubly linked list. check also nexti() + minsn_t *prev; ///< prev insn in doubly linked list. check also previ() + ea_t ea; ///< instruction address + mop_t l; ///< left operand + mop_t r; ///< right operand + mop_t d; ///< destination operand + + /// \defgroup IPROP_ instruction property bits + //@{ + // bits to be used in patterns: +#define IPROP_OPTIONAL 0x0001 ///< optional instruction +#define IPROP_PERSIST 0x0002 ///< persistent insn; they are not destroyed +#define IPROP_WILDMATCH 0x0004 ///< match multiple insns + + // instruction attributes: +#define IPROP_CLNPOP 0x0008 ///< the purpose of the instruction is to clean stack + ///< (e.g. "pop ecx" is often used for that) +#define IPROP_FPINSN 0x0010 ///< floating point insn +#define IPROP_FARCALL 0x0020 ///< call of a far function using push cs/call sequence +#define IPROP_TAILCALL 0x0040 ///< tail call +#define IPROP_ASSERT 0x0080 ///< assertion: usually mov #val, op. + ///< assertions are used to help the optimizer. + ///< assertions are ignored when generating ctree + + // instruction history: +#define IPROP_SPLIT 0x0700 ///< the instruction has been split: +#define IPROP_SPLIT1 0x0100 ///< into 1 byte +#define IPROP_SPLIT2 0x0200 ///< into 2 bytes +#define IPROP_SPLIT4 0x0300 ///< into 4 bytes +#define IPROP_SPLIT8 0x0400 ///< into 8 bytes +#define IPROP_COMBINED 0x0800 ///< insn has been modified because of a partial reference +#define IPROP_EXTSTX 0x1000 ///< this is m_ext propagated into m_stx +#define IPROP_IGNLOWSRC 0x2000 ///< low part of the instruction source operand + ///< has been created artificially + ///< (this bit is used only for 'and x, 80...') +#define IPROP_INV_JX 0x4000 ///< inverted conditional jump +#define IPROP_WAS_NORET 0x8000 ///< was noret icall +#define IPROP_MULTI_MOV 0x10000 ///< the minsn was generated as part of insn that moves multiple registers + ///< (example: STM on ARM may transfer multiple registers) + + ///< bits that can be set by plugins: +#define IPROP_DONT_PROP 0x20000 ///< may not propagate +#define IPROP_DONT_COMB 0x40000 ///< may not combine this instruction with others +#define IPROP_MBARRIER 0x80000 ///< this instruction acts as a memory barrier + ///< (instructions accessing memory may not be reordered past it) +#define IPROP_UNMERGED 0x100000 ///< 'goto' instruction was transformed info 'call' +#define IPROP_UNPAIRED 0x200000 ///< instruction is a result of del_dest_pairs() transformation + //@} + + bool is_optional() const { return (iprops & IPROP_OPTIONAL) != 0; } + bool is_combined() const { return (iprops & IPROP_COMBINED) != 0; } + bool is_farcall() const { return (iprops & IPROP_FARCALL) != 0; } + bool is_cleaning_pop() const { return (iprops & IPROP_CLNPOP) != 0; } + bool is_extstx() const { return (iprops & IPROP_EXTSTX) != 0; } + bool is_tailcall() const { return (iprops & IPROP_TAILCALL) != 0; } + bool is_fpinsn() const { return (iprops & IPROP_FPINSN) != 0; } + bool is_assert() const { return (iprops & IPROP_ASSERT) != 0; } + bool is_persistent() const { return (iprops & IPROP_PERSIST) != 0; } + bool is_wild_match() const { return (iprops & IPROP_WILDMATCH) != 0; } + bool is_propagatable() const { return (iprops & IPROP_DONT_PROP) == 0; } + bool is_ignlowsrc() const { return (iprops & IPROP_IGNLOWSRC) != 0; } + bool is_inverted_jx() const { return (iprops & IPROP_INV_JX) != 0; } + bool was_noret_icall() const { return (iprops & IPROP_WAS_NORET) != 0; } + bool is_multimov() const { return (iprops & IPROP_MULTI_MOV) != 0; } + bool is_combinable() const { return (iprops & IPROP_DONT_COMB) == 0; } + bool was_split() const { return (iprops & IPROP_SPLIT) != 0; } + bool is_mbarrier() const { return (iprops & IPROP_MBARRIER) != 0; } + bool was_unmerged() const { return (iprops & IPROP_UNMERGED) != 0; } + bool was_unpaired() const { return (iprops & IPROP_UNPAIRED) != 0; } + + void set_optional() { iprops |= IPROP_OPTIONAL; } + void hexapi set_combined(); + void clr_combined() { iprops &= ~IPROP_COMBINED; } + void set_farcall() { iprops |= IPROP_FARCALL; } + void set_cleaning_pop() { iprops |= IPROP_CLNPOP; } + void set_extstx() { iprops |= IPROP_EXTSTX; } + void set_tailcall() { iprops |= IPROP_TAILCALL; } + void clr_tailcall() { iprops &= ~IPROP_TAILCALL; } + void set_fpinsn() { iprops |= IPROP_FPINSN; } + void clr_fpinsn() { iprops &= ~IPROP_FPINSN; } + void set_assert() { iprops |= IPROP_ASSERT; } + void clr_assert() { iprops &= ~IPROP_ASSERT; } + void set_persistent() { iprops |= IPROP_PERSIST; } + void set_wild_match() { iprops |= IPROP_WILDMATCH; } + void clr_propagatable() { iprops |= IPROP_DONT_PROP; } + void set_ignlowsrc() { iprops |= IPROP_IGNLOWSRC; } + void clr_ignlowsrc() { iprops &= ~IPROP_IGNLOWSRC; } + void set_inverted_jx() { iprops |= IPROP_INV_JX; } + void set_noret_icall() { iprops |= IPROP_WAS_NORET; } + void clr_noret_icall() { iprops &= ~IPROP_WAS_NORET; } + void set_multimov() { iprops |= IPROP_MULTI_MOV; } + void clr_multimov() { iprops &= ~IPROP_MULTI_MOV; } + void set_combinable() { iprops &= ~IPROP_DONT_COMB; } + void clr_combinable() { iprops |= IPROP_DONT_COMB; } + void set_mbarrier() { iprops |= IPROP_MBARRIER; } + void set_unmerged() { iprops |= IPROP_UNMERGED; } + void set_split_size(int s) + { // s may be only 1,2,4,8. other values are ignored + iprops &= ~IPROP_SPLIT; + iprops |= (s == 1 ? IPROP_SPLIT1 + : s == 2 ? IPROP_SPLIT2 + : s == 4 ? IPROP_SPLIT4 + : s == 8 ? IPROP_SPLIT8 : 0); + } + int get_split_size() const + { + int cnt = (iprops & IPROP_SPLIT) >> 8; + return cnt == 0 ? 0 : 1 << (cnt-1); + } + + /// Constructor + minsn_t(ea_t _ea) { init(_ea); } + minsn_t(const minsn_t &m) { next = prev = nullptr; copy(m); } + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + + /// Assignment operator. It does not copy prev/next fields. + minsn_t &operator=(const minsn_t &m) { copy(m); return *this; } + + /// Swap two instructions. + /// The prev/next fields are not modified by this function + /// because it would corrupt the doubly linked list. + void hexapi swap(minsn_t &m); + + + /// Generate insn text into the buffer + void hexapi print(qstring *vout, int shins_flags=SHINS_SHORT|SHINS_VALNUM) const; + + /// Get displayable text without tags in a static buffer + const char *hexapi dstr() const; + + /// Change the instruction address. + /// This function modifies subinstructions as well. + void hexapi setaddr(ea_t new_ea); + + /// Optimize one instruction without context. + /// This function does not have access to the instruction context (the + /// previous and next instructions in the list, the block number, etc). + /// It performs only basic optimizations that are available without this info. + /// \param optflags combination of \ref OPTI_ bits + /// \return number of changes, 0-unchanged + /// See also mblock_t::optimize_insn() + int optimize_solo(int optflags=0) { return optimize_subtree(nullptr, nullptr, nullptr, nullptr, optflags); } + /// \defgroup OPTI_ optimization flags + //@{ +#define OPTI_ADDREXPRS 0x0001 ///< optimize all address expressions (&x+N; &x-&y) +#define OPTI_MINSTKREF 0x0002 ///< may update minstkref +#define OPTI_COMBINSNS 0x0004 ///< may combine insns (only for optimize_insn) +#define OPTI_NO_LDXOPT 0x0008 ///< the function is called after the + ///< propagation attempt, we do not optimize + ///< low/high(ldx) in this case +#define OPTI_NO_VALRNG 0x0010 ///< forbid using valranges + //@} + + /// Optimize instruction in its context. + /// Do not use this function, use mblock_t::optimize() + int hexapi optimize_subtree( + mblock_t *blk, + minsn_t *top, + minsn_t *parent, + ea_t *converted_call, + int optflags=OPTI_MINSTKREF); + + /// Visit all instruction operands. + /// This function visits subinstruction operands as well. + /// \param mv operand visitor + /// \return non-zero value returned by mv.visit_mop() or zero + int hexapi for_all_ops(mop_visitor_t &mv); + + /// Visit all instructions. + /// This function visits the instruction itself and all its subinstructions. + /// \param mv instruction visitor + /// \return non-zero value returned by mv.visit_mop() or zero + int hexapi for_all_insns(minsn_visitor_t &mv); + + /// Convert instruction to nop. + /// This function erases all info but the prev/next fields. + /// In most cases it is better to use mblock_t::make_nop(), which also + /// marks the block lists as dirty. + void hexapi _make_nop(); + + /// Compare instructions. + /// This is the main comparison function for instructions. + /// \param m instruction to compare with + /// \param eqflags combination of \ref EQ_ bits + bool hexapi equal_insns(const minsn_t &m, int eqflags) const; // intelligent comparison + /// \defgroup EQ_ comparison bits + //@{ +#define EQ_IGNSIZE 0x0001 ///< ignore source operand sizes +#define EQ_IGNCODE 0x0002 ///< ignore instruction opcodes +#define EQ_CMPDEST 0x0004 ///< compare instruction destinations +#define EQ_OPTINSN 0x0008 ///< optimize mop_d operands + //@} + + /// Lexographical comparison + /// It can be used to store minsn_t in various containers, like std::set + bool operator <(const minsn_t &ri) const { return lexcompare(ri) < 0; } + int hexapi lexcompare(const minsn_t &ri) const; + + //----------------------------------------------------------------------- + // Call instructions + //----------------------------------------------------------------------- + /// Is a non-returing call? + /// \param flags combination of NORET_... bits + bool hexapi is_noret_call(int flags=0); +#define NORET_IGNORE_WAS_NORET_ICALL 0x01 // ignore was_noret_icall() bit +#define NORET_FORBID_ANALYSIS 0x02 // forbid additional analysis + + /// Is an unknown call? + /// Unknown calls are calls without the argument list (mcallinfo_t). + /// Usually the argument lists are determined by mba_t::analyze_calls(). + /// Unknown calls exist until the MMAT_CALLS maturity level. + /// See also \ref mblock_t::is_call_block + bool is_unknown_call() const { return is_mcode_call(opcode) && d.empty(); } + + /// Is a helper call with the specified name? + /// Helper calls usually have well-known function names (see \ref FUNC_NAME_) + /// but they may have any other name. The decompiler does not assume any + /// special meaning for non-well-known names. + bool hexapi is_helper(const char *name) const; + + /// Find a call instruction. + /// Check for the current instruction and its subinstructions. + /// \param with_helpers consider helper calls as well? + minsn_t *hexapi find_call(bool with_helpers=false) const; + + /// Does the instruction contain a call? + bool contains_call(bool with_helpers=false) const { return find_call(with_helpers) != nullptr; } + + /// Does the instruction have a side effect? + /// \param include_ldx_and_divs consider ldx/div/mod as having side effects? + /// stx is always considered as having side effects. + /// Apart from ldx/std only call may have side effects. + bool hexapi has_side_effects(bool include_ldx_and_divs=false) const; + + /// Get the function role of a call + funcrole_t get_role() const { return d.is_arglist() ? d.f->role : ROLE_UNK; } + bool is_memcpy() const { return get_role() == ROLE_MEMCPY; } + bool is_memset() const { return get_role() == ROLE_MEMSET; } + bool is_alloca() const { return get_role() == ROLE_ALLOCA; } + bool is_bswap () const { return get_role() == ROLE_BSWAP; } + bool is_readflags () const { return get_role() == ROLE_READFLAGS; } + + //----------------------------------------------------------------------- + // Misc + //----------------------------------------------------------------------- + /// Does the instruction have the specified opcode? + /// This function searches subinstructions as well. + /// \param mcode opcode to search for. + bool contains_opcode(mcode_t mcode) const { return find_opcode(mcode) != nullptr; } + + /// Find a (sub)insruction with the specified opcode. + /// \param mcode opcode to search for. + const minsn_t *find_opcode(mcode_t mcode) const { return (CONST_CAST(minsn_t*)(this))->find_opcode(mcode); } + minsn_t *hexapi find_opcode(mcode_t mcode); + + /// Find an operand that is a subinsruction with the specified opcode. + /// This function checks only the 'l' and 'r' operands of the current insn. + /// \param[out] other pointer to the other operand + /// (&r if we return &l and vice versa) + /// \param op opcode to search for + /// \return &l or &r or nullptr + const minsn_t *hexapi find_ins_op(const mop_t **other, mcode_t op=m_nop) const; + minsn_t *find_ins_op(mop_t **other, mcode_t op=m_nop) { return CONST_CAST(minsn_t*)((CONST_CAST(const minsn_t*)(this))->find_ins_op((const mop_t**)other, op)); } + + /// Find a numeric operand of the current instruction. + /// This function checks only the 'l' and 'r' operands of the current insn. + /// \param[out] other pointer to the other operand + /// (&r if we return &l and vice versa) + /// \return &l or &r or nullptr + const mop_t *hexapi find_num_op(const mop_t **other) const; + mop_t *find_num_op(mop_t **other) { return CONST_CAST(mop_t*)((CONST_CAST(const minsn_t*)(this))->find_num_op((const mop_t**)other)); } + + bool is_mov() const { return opcode == m_mov || (opcode == m_f2f && l.size == d.size); } + bool is_like_move() const { return is_mov() || is_mcode_xdsu(opcode) || opcode == m_low; } + + /// Does the instruction modify its 'd' operand? + /// Some instructions (e.g. m_stx) do not modify the 'd' operand. + bool hexapi modifies_d() const; + bool modifies_pair_mop() const { return d.t == mop_p && modifies_d(); } + + /// Is the instruction in the specified range of instructions? + /// \param m1 beginning of the range in the doubly linked list + /// \param m2 end of the range in the doubly linked list (excluded, may be nullptr) + /// This function assumes that m1 and m2 belong to the same basic block + /// and they are top level instructions. + bool hexapi is_between(const minsn_t *m1, const minsn_t *m2) const; + + /// Is the instruction after the specified one? + /// \param m the instruction to compare against in the list + bool is_after(const minsn_t *m) const { return m != nullptr && is_between(m->next, nullptr); } + + /// Is it possible for the instruction to use aliased memory? + bool hexapi may_use_aliased_memory() const; + + /// Serialize an instruction + /// \param b the output buffer + /// \return the serialization format that was used to store info + int hexapi serialize(bytevec_t *b) const; + + /// Deserialize an instruction + /// \param bytes pointer to serialized data + /// \param nbytes number of bytes to deserialize + /// \param format_version serialization format version. this value is returned by minsn_t::serialize() + /// \return success + bool hexapi deserialize(const uchar *bytes, size_t nbytes, int format_version); + +}; + +/// Skip assertions forward +const minsn_t *hexapi getf_reginsn(const minsn_t *ins); +/// Skip assertions backward +const minsn_t *hexapi getb_reginsn(const minsn_t *ins); +inline minsn_t *getf_reginsn(minsn_t *ins) { return CONST_CAST(minsn_t*)(getf_reginsn(CONST_CAST(const minsn_t *)(ins))); } +inline minsn_t *getb_reginsn(minsn_t *ins) { return CONST_CAST(minsn_t*)(getb_reginsn(CONST_CAST(const minsn_t *)(ins))); } + +//------------------------------------------------------------------------- +class intval64_t +{ +public: + uint64 val; + int size; // in bytes + + intval64_t(uint64 v=0, int _s=1) : val(trunc(v, _s)), size(_s) {} + int64 sval() const { return extend_sign(val, size, true); } + uint64 uval() const { return val; } + void print(qstring *vout) const { vout->sprnt("0x%" FMT_64 "X.%d", val, size); } + + //------------------------------------------------------------------------ + bool operator==(const intval64_t &o) const + { + return size == o.size && val == o.val; + } + + //------------------------------------------------------------------------ + bool operator!=(const intval64_t &o) const + { + return !(*this == o); + } + + //------------------------------------------------------------------------ + bool operator<(const intval64_t &o) const + { + QASSERT(52898, size == o.size); + return val < o.val; + } + + //------------------------------------------------------------------------ + intval64_t sext(int target_sz) const + { + QASSERT(52899, target_sz >= size); + return intval64_t(sval(), target_sz); + } + + //------------------------------------------------------------------------ + intval64_t zext(int target_sz) const + { + QASSERT(52900, target_sz >= size); + return intval64_t(val, target_sz); + } + + //------------------------------------------------------------------------ + intval64_t low(int target_sz) const + { + QASSERT(52901, target_sz <= size); + return intval64_t(val, target_sz); + } + + //------------------------------------------------------------------------ + intval64_t high(int target_sz) const + { + QASSERT(52902, target_sz <= size); + int bytes_to_remove = size - target_sz; + return intval64_t(right_ushift(val, 8 * bytes_to_remove), target_sz); + } + + //------------------------------------------------------------------------ + intval64_t operator+(const intval64_t &o) const + { + check_size_equal(o); + return intval64_t(val + o.val, size); + } + + //------------------------------------------------------------------------ + intval64_t operator-(const intval64_t &o) const + { + check_size_equal(o); + return intval64_t(val - o.val, size); + } + + //------------------------------------------------------------------------- + intval64_t operator*(const intval64_t &o) const + { + check_size_equal(o); + return intval64_t(val * o.val, size); + } + + //------------------------------------------------------------------------ + intval64_t operator/(const intval64_t &o) const + { + check_size_equal(o); + if ( o.val == 0 ) + throw "division by zero occurred when emulating instruction"; + return intval64_t(val / o.val, size); + } + + //------------------------------------------------------------------------ + intval64_t sdiv(const intval64_t &o) const + { + check_size_equal(o); + if ( o.val == 0 ) + throw "division by zero occurred when emulating instruction"; + int64 res; + uint64 l = val; + uint64 r = o.val; + switch ( size ) + { + case 1: res = int8(l) / int8(r); break; + case 2: res = int16(l) / int16(r); break; + case 4: res = int32(l) / int32(r); break; + case 8: res = int64(l) / int64(r); break; + default: INTERR(30666); + } + + return intval64_t(res, size); + } + + //------------------------------------------------------------------------ + intval64_t operator%(const intval64_t &o) const + { + check_size_equal(o); + if ( o.val == 0 ) + throw "division by zero occurred when emulating instruction"; + return intval64_t(val % o.val, size); + } + //------------------------------------------------------------------------ + intval64_t smod(const intval64_t &o) const + { + check_size_equal(o); + if ( o.val == 0 ) + throw "division by zero occurred when emulating instruction"; + int64 res = -1; + uint64 l = val; + uint64 r = o.val; + switch ( size ) + { + case 1: res = int8(l) % int8(r); break; + case 2: res = int16(l) % int16(r); break; + case 4: res = int32(l) % int32(r); break; + case 8: res = int64(l) % int64(r); break; + default: INTERR(52903); + } + + return intval64_t(res, size); + } + + //------------------------------------------------------------------------ + intval64_t operator<<(const intval64_t &o) const + { + return intval64_t(left_shift(val, o.val), size); + } + + //------------------------------------------------------------------------ + intval64_t operator>>(const intval64_t &o) const + { + return intval64_t(right_ushift(val, o.val), size); + } + + //------------------------------------------------------------------------ + intval64_t sar(const intval64_t &o) const + { + return intval64_t(right_sshift(sval(), o.val), size); + } + + //------------------------------------------------------------------------ + intval64_t operator|(const intval64_t &o) const + { + check_size_equal(o); + return intval64_t(val | o.val, size); + } + + //------------------------------------------------------------------------ + intval64_t operator&(const intval64_t &o) const + { + check_size_equal(o); + return intval64_t(val & o.val, size); + } + + //------------------------------------------------------------------------ + intval64_t operator^(const intval64_t &o) const + { + check_size_equal(o); + return intval64_t(val ^ o.val, size); + } + + //------------------------------------------------------------------------ + intval64_t operator-() const + { + return intval64_t(0-val, size); + } + + //------------------------------------------------------------------------ + intval64_t operator!() const + { + return intval64_t(!val, size); + } + + //------------------------------------------------------------------------ + intval64_t operator~() const + { + return intval64_t(~val, size); + } + +private: + //------------------------------------------------------------------------ + static uint64 trunc(uint64 v, int w) // truncate v to w bytes + { + QASSERT(52904, w == 1 || w == 2 || w == 4 || w == 8); + return v & make_mask(w * 8); + } + + void check_size_equal(const intval64_t &o) const + { + QASSERT(52905, size == o.size); + } +}; + +//------------------------------------------------------------------------- +// A simple 64bit emulator that can handle integer microcode instructions. +// This does not include control transfer instructions (except conditional jumps) +// and nop/ldx/stx/setp/... +// If the value cannot be calculated, an exception will be thrown: +// vd_failure_t(MERR_EMULATOR, ea_if_known, error_message); +class int64_emulator_t +{ +public: + virtual ~int64_emulator_t() {} + + // Retreive the value assigned to an operand. + // This function is called for mop_r, mop_S, mop_v, mop_l + virtual intval64_t get_mop_value(const mop_t &mop) = 0; + + // Calculate the operand value. + // For register/stack/memory/lvar operands get_mop_value() will be called. + intval64_t hexapi mop_value(const mop_t &mop); + + // Calculate the result of applying the instruction opcode to its source + // operands. This function does not store the result to the destination operand. + // For example: "add r0, #2, ..." will return 3 if r0 contains 1. + intval64_t hexapi minsn_value(const minsn_t &insn); +}; + +//------------------------------------------------------------------------- +/// Basic block types +enum mblock_type_t +{ + BLT_NONE = 0, ///< unknown block type + BLT_STOP = 1, ///< stops execution regularly (must be the last block) + BLT_0WAY = 2, ///< does not have successors (tail is a noret function) + BLT_1WAY = 3, ///< passes execution to one block (regular or goto block) + BLT_2WAY = 4, ///< passes execution to two blocks (conditional jump) + BLT_NWAY = 5, ///< passes execution to many blocks (switch idiom) + BLT_XTRN = 6, ///< external block (out of function address) +}; + +// Maximal bit range +#define MAXRANGE bitrange_t(0, USHRT_MAX) + +//------------------------------------------------------------------------- +/// Microcode of one basic block. +/// All blocks are part of a doubly linked list. They can also be addressed +/// by indexing the mba->natural array. A block contains a doubly linked list +/// of instructions, various location lists that are used for data flow +/// analysis, and other attributes. +class mblock_t +{ + friend class codegen_t; + DECLARE_UNCOPYABLE(mblock_t) + void hexapi init(); +public: + mblock_t *nextb; ///< next block in the doubly linked list + mblock_t *prevb; ///< previous block in the doubly linked list + uint32 flags; ///< combination of \ref MBL_ bits + /// \defgroup MBL_ Basic block properties + //@{ +#define MBL_PRIV 0x0001 ///< private block - no instructions except + ///< the specified are accepted (used in patterns) +#define MBL_NONFAKE 0x0000 ///< regular block +#define MBL_FAKE 0x0002 ///< fake block +#define MBL_GOTO 0x0004 ///< this block is a goto target +#define MBL_TCAL 0x0008 ///< aritifical call block for tail calls +#define MBL_PUSH 0x0010 ///< needs "convert push/pop instructions" +#define MBL_DMT64 0x0020 ///< needs "demote 64bits" +#define MBL_COMB 0x0040 ///< needs "combine" pass +#define MBL_PROP 0x0080 ///< needs 'propagation' pass +#define MBL_DEAD 0x0100 ///< needs "eliminate deads" pass +#define MBL_LIST 0x0200 ///< use/def lists are ready (not dirty) +#define MBL_INCONST 0x0400 ///< inconsistent lists: we are building them +#define MBL_CALL 0x0800 ///< call information has been built +#define MBL_BACKPROP 0x1000 ///< performed backprop_cc +#define MBL_NORET 0x2000 ///< dead end block: doesn't return execution control +#define MBL_DSLOT 0x4000 ///< block for delay slot +#define MBL_VALRANGES 0x8000 ///< should optimize using value ranges +#define MBL_KEEP 0x10000 ///< do not remove even if unreachable +#define MBL_INLINED 0x20000 ///< block was inlined, not originally part of mbr +#define MBL_EXTFRAME 0x40000 ///< an inlined block with an external frame + //@} + ea_t start; ///< start address + ea_t end; ///< end address + ///< note: we cannot rely on start/end addresses + ///< very much because instructions are + ///< propagated between blocks + minsn_t *head; ///< pointer to the first instruction of the block + minsn_t *tail; ///< pointer to the last instruction of the block + mba_t *mba; ///< the parent micro block array + int serial; ///< block number + mblock_type_t type; ///< block type (BLT_NONE - not computed yet) + + mlist_t dead_at_start; ///< data that is dead at the block entry + mlist_t mustbuse; ///< data that must be used by the block + mlist_t maybuse; ///< data that may be used by the block + mlist_t mustbdef; ///< data that must be defined by the block + mlist_t maybdef; ///< data that may be defined by the block + mlist_t dnu; ///< data that is defined but not used in the block + + sval_t maxbsp; ///< maximal sp value in the block (0...stacksize) + sval_t minbstkref; ///< lowest stack location accessible with indirect + ///< addressing (offset from the stack bottom) + ///< initially it is 0 (not computed) + sval_t minbargref; ///< the same for arguments + + intvec_t predset; ///< control flow graph: list of our predecessors + ///< use npred() and pred() to access it + intvec_t succset; ///< control flow graph: list of our successors + ///< use nsucc() and succ() to access it + + // the exact size of this class is not documented, there may be more fields + char reserved[]; + + void mark_lists_dirty() { flags &= ~MBL_LIST; request_propagation(); } + void request_propagation() { flags |= MBL_PROP; } + bool needs_propagation() const { return (flags & MBL_PROP) != 0; } + void request_demote64() { flags |= MBL_DMT64; } + bool lists_dirty() const { return (flags & MBL_LIST) == 0; } + bool lists_ready() const { return (flags & (MBL_LIST|MBL_INCONST)) == MBL_LIST; } + int make_lists_ready() // returns number of changes + { + if ( lists_ready() ) + return 0; + return build_lists(false); + } + + /// Get number of block predecessors + int npred() const { return predset.size(); } // number of xrefs to the block + /// Get number of block successors + int nsucc() const { return succset.size(); } // number of xrefs from the block + // Get predecessor number N + int pred(int n) const { return predset[n]; } + // Get successor number N + int succ(int n) const { return succset[n]; } + + mblock_t() = delete; + virtual ~mblock_t(); + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + bool empty() const { return head == nullptr; } + + + /// Print block contents. + /// \param vp print helpers class. it can be used to direct the printed + /// info to any destination + void hexapi print(vd_printer_t &vp) const; + + /// Dump block info. + /// This function is useful for debugging, see mba_t::dump for info + void hexapi dump() const; + AS_PRINTF(2, 0) void hexapi vdump_block(const char *title, va_list va) const; + AS_PRINTF(2, 3) void dump_block(const char *title, ...) const + { + va_list va; + va_start(va, title); + vdump_block(title, va); + va_end(va); + } + + //----------------------------------------------------------------------- + // Functions to insert/remove insns during the microcode optimization phase. + // See codegen_t, microcode_filter_t, udcall_t classes for the initial + // microcode generation. + //----------------------------------------------------------------------- + /// Insert instruction into the doubly linked list + /// \param nm new instruction + /// \param om existing instruction, part of the doubly linked list + /// if nullptr, then the instruction will be inserted at the beginning + /// of the list + /// NM will be inserted immediately after OM + /// \return pointer to NM + minsn_t *hexapi insert_into_block(minsn_t *nm, minsn_t *om); + + /// Remove instruction from the doubly linked list + /// \param m instruction to remove + /// The removed instruction is not deleted, the caller gets its ownership + /// \return pointer to the next instruction + minsn_t *hexapi remove_from_block(minsn_t *m); + + //----------------------------------------------------------------------- + // Iterator over instructions and operands + //----------------------------------------------------------------------- + /// Visit all instructions. + /// This function visits subinstructions too. + /// \param mv instruction visitor + /// \return zero or the value returned by mv.visit_insn() + /// See also mba_t::for_all_topinsns() + int hexapi for_all_insns(minsn_visitor_t &mv); + + /// Visit all operands. + /// This function visit subinstruction operands too. + /// \param mv operand visitor + /// \return zero or the value returned by mv.visit_mop() + int hexapi for_all_ops(mop_visitor_t &mv); + + /// Visit all operands that use LIST. + /// \param list ptr to the list of locations. it may be modified: + /// parts that get redefined by the instructions in [i1,i2) + /// will be deleted. + /// \param i1 starting instruction. must be a top level insn. + /// \param i2 ending instruction (excluded). must be a top level insn. + /// \param mmv operand visitor + /// \return zero or the value returned by mmv.visit_mop() + int hexapi for_all_uses( + mlist_t *list, + minsn_t *i1, + minsn_t *i2, + mlist_mop_visitor_t &mmv); + + //----------------------------------------------------------------------- + // Optimization functions + //----------------------------------------------------------------------- + /// Optimize one instruction in the context of the block. + /// \param m pointer to a top level instruction + /// \param optflags combination of \ref OPTI_ bits + /// \return number of changes made to the block + /// This function may change other instructions in the block too. + /// However, it will not destroy top level instructions (it may convert them + /// to nop's). This function performs only intrablock modifications. + /// See also minsn_t::optimize_solo() + int hexapi optimize_insn(minsn_t *m, int optflags=OPTI_MINSTKREF|OPTI_COMBINSNS); + + /// Optimize a basic block. + /// Usually there is no need to call this function explicitly because the + /// decompiler will call it itself if optinsn_t::func or optblock_t::func + /// return non-zero. + /// \return number of changes made to the block + int hexapi optimize_block(); + + /// Build def-use lists and eliminate deads. + /// \param kill_deads do delete dead instructions? + /// \return the number of eliminated instructions + /// Better mblock_t::call make_lists_ready() rather than this function. + int hexapi build_lists(bool kill_deads); + + /// Remove a jump at the end of the block if it is useless. + /// This function preserves any side effects when removing a useless jump. + /// Both conditional and unconditional jumps are handled (and jtbl too). + /// This function deletes useless jumps, not only replaces them with a nop. + /// (please note that \optimize_insn does not handle useless jumps). + /// \return number of changes made to the block + int hexapi optimize_useless_jump(); + + //----------------------------------------------------------------------- + // Functions that build with use/def lists. These lists are used to + // reprsent list of registers and stack locations that are either modified + // or accessed by microinstructions. + //----------------------------------------------------------------------- + /// Append use-list of an operand. + /// This function calculates list of locations that may or must be used + /// by the operand and appends it to LIST. + /// \param list ptr to the output buffer. we will append to it. + /// \param op operand to calculate the use list of + /// \param maymust should we calculate 'may-use' or 'must-use' list? + /// see \ref maymust_t for more details. + /// \param mask if only part of the operand should be considered, + /// a bitmask can be used to specify which part. + /// example: op=AX,mask=0xFF means that we will consider only AL. + void hexapi append_use_list( + mlist_t *list, + const mop_t &op, + maymust_t maymust, + bitrange_t mask=MAXRANGE) const; + + /// Append def-list of an operand. + /// This function calculates list of locations that may or must be modified + /// by the operand and appends it to LIST. + /// \param list ptr to the output buffer. we will append to it. + /// \param op operand to calculate the def list of + /// \param maymust should we calculate 'may-def' or 'must-def' list? + /// see \ref maymust_t for more details. + void hexapi append_def_list( + mlist_t *list, + const mop_t &op, + maymust_t maymust) const; + + /// Build use-list of an instruction. + /// This function calculates list of locations that may or must be used + /// by the instruction. Examples: + /// "ldx ds.2, eax.4, ebx.4", may-list: all aliasable memory + /// "ldx ds.2, eax.4, ebx.4", must-list: empty + /// Since LDX uses EAX for indirect access, it may access any aliasable + /// memory. On the other hand, we cannot tell for sure which memory cells + /// will be accessed, this is why the must-list is empty. + /// \param ins instruction to calculate the use list of + /// \param maymust should we calculate 'may-use' or 'must-use' list? + /// see \ref maymust_t for more details. + /// \return the calculated use-list + mlist_t hexapi build_use_list(const minsn_t &ins, maymust_t maymust) const; + + /// Build def-list of an instruction. + /// This function calculates list of locations that may or must be modified + /// by the instruction. Examples: + /// "stx ebx.4, ds.2, eax.4", may-list: all aliasable memory + /// "stx ebx.4, ds.2, eax.4", must-list: empty + /// Since STX uses EAX for indirect access, it may modify any aliasable + /// memory. On the other hand, we cannot tell for sure which memory cells + /// will be modified, this is why the must-list is empty. + /// \param ins instruction to calculate the def list of + /// \param maymust should we calculate 'may-def' or 'must-def' list? + /// see \ref maymust_t for more details. + /// \return the calculated def-list + mlist_t hexapi build_def_list(const minsn_t &ins, maymust_t maymust) const; + + //----------------------------------------------------------------------- + // The use/def lists can be used to search for interesting instructions + //----------------------------------------------------------------------- + /// Is the list used by the specified instruction range? + /// \param list list of locations. LIST may be modified by the function: + /// redefined locations will be removed from it. + /// \param i1 starting instruction of the range (must be a top level insn) + /// \param i2 end instruction of the range (must be a top level insn) + /// i2 is excluded from the range. it can be specified as nullptr. + /// i1 and i2 must belong to the same block. + /// \param maymust should we search in 'may-access' or 'must-access' mode? + bool is_used(mlist_t *list, const minsn_t *i1, const minsn_t *i2, maymust_t maymust=MAY_ACCESS) const + { return find_first_use(list, i1, i2, maymust) != nullptr; } + + /// Find the first insn that uses the specified list in the insn range. + /// \param list list of locations. LIST may be modified by the function: + /// redefined locations will be removed from it. + /// \param i1 starting instruction of the range (must be a top level insn) + /// \param i2 end instruction of the range (must be a top level insn) + /// i2 is excluded from the range. it can be specified as nullptr. + /// i1 and i2 must belong to the same block. + /// \param maymust should we search in 'may-access' or 'must-access' mode? + /// \return pointer to such instruction or nullptr. + /// Upon return LIST will contain only locations not redefined + /// by insns [i1..result] + const minsn_t *hexapi find_first_use(mlist_t *list, const minsn_t *i1, const minsn_t *i2, maymust_t maymust=MAY_ACCESS) const; + minsn_t *find_first_use(mlist_t *list, minsn_t *i1, const minsn_t *i2, maymust_t maymust=MAY_ACCESS) const + { + return CONST_CAST(minsn_t*)(find_first_use(list, + CONST_CAST(const minsn_t*)(i1), + i2, + maymust)); + } + + /// Is the list redefined by the specified instructions? + /// \param list list of locations to check. + /// \param i1 starting instruction of the range (must be a top level insn) + /// \param i2 end instruction of the range (must be a top level insn) + /// i2 is excluded from the range. it can be specified as nullptr. + /// i1 and i2 must belong to the same block. + /// \param maymust should we search in 'may-access' or 'must-access' mode? + bool is_redefined( + const mlist_t &list, + const minsn_t *i1, + const minsn_t *i2, + maymust_t maymust=MAY_ACCESS) const + { + return find_redefinition(list, i1, i2, maymust) != nullptr; + } + + /// Find the first insn that redefines any part of the list in the insn range. + /// \param list list of locations to check. + /// \param i1 starting instruction of the range (must be a top level insn) + /// \param i2 end instruction of the range (must be a top level insn) + /// i2 is excluded from the range. it can be specified as nullptr. + /// i1 and i2 must belong to the same block. + /// \param maymust should we search in 'may-access' or 'must-access' mode? + /// \return pointer to such instruction or nullptr. + const minsn_t *hexapi find_redefinition( + const mlist_t &list, + const minsn_t *i1, + const minsn_t *i2, + maymust_t maymust=MAY_ACCESS) const; + minsn_t *find_redefinition( + const mlist_t &list, + minsn_t *i1, + const minsn_t *i2, + maymust_t maymust=MAY_ACCESS) const + { + return CONST_CAST(minsn_t*)(find_redefinition(list, + CONST_CAST(const minsn_t*)(i1), + i2, + maymust)); + } + + /// Is the right hand side of the instruction redefined the insn range? + /// "right hand side" corresponds to the source operands of the instruction. + /// \param ins instruction to consider + /// \param i1 starting instruction of the range (must be a top level insn) + /// \param i2 end instruction of the range (must be a top level insn) + /// i2 is excluded from the range. it can be specified as nullptr. + /// i1 and i2 must belong to the same block. + bool hexapi is_rhs_redefined(const minsn_t *ins, const minsn_t *i1, const minsn_t *i2) const; + + /// Find the instruction that accesses the specified operand. + /// This function search inside one block. + /// \param op operand to search for + /// \param[in,out] parent ptr to ptr to a top level instruction. + /// in: denotes the beginning of the search range. + /// out: denotes the parent of the found instruction. + /// \param mend end instruction of the range (must be a top level insn) + /// mend is excluded from the range. it can be specified as nullptr. + /// parent and mend must belong to the same block. + /// \param fdflags combination of \ref FD_ bits + /// \return the instruction that accesses the operand. this instruction + /// may be a sub-instruction. to find out the top level + /// instruction, check out *parent. + /// nullptr means 'not found'. + minsn_t *hexapi find_access( + const mop_t &op, + minsn_t **parent, + const minsn_t *mend, + int fdflags) const; + /// \defgroup FD_ bits for mblock_t::find_access + //@{ +#define FD_BACKWARD 0x0000 ///< search direction +#define FD_FORWARD 0x0001 ///< search direction +#define FD_USE 0x0000 ///< look for use +#define FD_DEF 0x0002 ///< look for definition +#define FD_DIRTY 0x0004 ///< ignore possible implicit definitions + ///< by function calls and indirect memory access + //@} + + // Convenience functions: + minsn_t *find_def( + const mop_t &op, + minsn_t **p_i1, + const minsn_t *i2, + int fdflags) + { + return find_access(op, p_i1, i2, fdflags|FD_DEF); + } + minsn_t *find_use( + const mop_t &op, + minsn_t **p_i1, + const minsn_t *i2, + int fdflags) + { + return find_access(op, p_i1, i2, fdflags|FD_USE); + } + + /// Find possible values for a block. + /// \param res set of value ranges + /// \param vivl what to search for + /// \param vrflags combination of \ref VR_ bits + bool hexapi get_valranges( + valrng_t *res, + const vivl_t &vivl, + int vrflags) const; + + /// Find possible values for an instruction. + /// \param res set of value ranges + /// \param vivl what to search for + /// \param m insn to search value ranges at. \sa VR_ bits + /// \param vrflags combination of \ref VR_ bits + bool hexapi get_valranges( + valrng_t *res, + const vivl_t &vivl, + const minsn_t *m, + int vrflags) const; + + /// \defgroup VR_ bits for get_valranges + //@{ +#define VR_AT_START 0x0000 ///< get value ranges before the instruction or + ///< at the block start (if M is nullptr) +#define VR_AT_END 0x0001 ///< get value ranges after the instruction or + ///< at the block end, just after the last + ///< instruction (if M is nullptr) +#define VR_EXACT 0x0002 ///< find exact match. if not set, the returned + ///< valrng size will be >= vivl.size + //@} + + /// Erase the instruction (convert it to nop) and mark the lists dirty. + /// This is the recommended function to use because it also marks the block + /// use-def lists dirty. + void make_nop(minsn_t *m) { m->_make_nop(); mark_lists_dirty(); } + + /// Calculate number of regular instructions in the block. + /// Assertions are skipped by this function. + /// \return Number of non-assertion instructions in the block. + size_t hexapi get_reginsn_qty() const; + + bool is_call_block() const { return tail != nullptr && is_mcode_call(tail->opcode); } + bool is_unknown_call() const { return tail != nullptr && tail->is_unknown_call(); } + bool is_nway() const { return type == BLT_NWAY; } + bool is_branch() const { return type == BLT_2WAY && tail->d.is_mblock(); } + bool is_simple_goto_block() const + { + return get_reginsn_qty() == 1 + && tail->opcode == m_goto + && tail->l.is_mblock(); + } + bool is_simple_jcnd_block() const + { + return is_branch() + && npred() == 1 + && get_reginsn_qty() == 1 + && is_mcode_convertible_to_set(tail->opcode); + } +}; +//------------------------------------------------------------------------- +/// Warning ids +enum warnid_t +{ + WARN_VARARG_REGS, ///< 0 cannot handle register arguments in vararg function, discarded them + WARN_ILL_PURGED, ///< 1 odd caller purged bytes %d, correcting + WARN_ILL_FUNCTYPE, ///< 2 invalid function type '%s' has been ignored + WARN_VARARG_TCAL, ///< 3 cannot handle tail call to vararg + WARN_VARARG_NOSTK, ///< 4 call vararg without local stack + WARN_VARARG_MANY, ///< 5 too many varargs, some ignored + WARN_ADDR_OUTARGS, ///< 6 cannot handle address arithmetics in outgoing argument area of stack frame -- unused + WARN_DEP_UNK_CALLS, ///< 7 found interdependent unknown calls + WARN_ILL_ELLIPSIS, ///< 8 erroneously detected ellipsis type has been ignored + WARN_GUESSED_TYPE, ///< 9 using guessed type %s; + WARN_EXP_LINVAR, ///< 10 failed to expand a linear variable + WARN_WIDEN_CHAINS, ///< 11 failed to widen chains + WARN_BAD_PURGED, ///< 12 inconsistent function type and number of purged bytes + WARN_CBUILD_LOOPS, ///< 13 too many cbuild loops + WARN_NO_SAVE_REST, ///< 14 could not find valid save-restore pair for %s + WARN_ODD_INPUT_REG, ///< 15 odd input register %s + WARN_ODD_ADDR_USE, ///< 16 odd use of a variable address + WARN_MUST_RET_FP, ///< 17 function return type is incorrect (must be floating point) + WARN_ILL_FPU_STACK, ///< 18 inconsistent fpu stack + WARN_SELFREF_PROP, ///< 19 self-referencing variable has been detected + WARN_WOULD_OVERLAP, ///< 20 variables would overlap: %s + WARN_ARRAY_INARG, ///< 21 array has been used for an input argument + WARN_MAX_ARGS, ///< 22 too many input arguments, some ignored + WARN_BAD_FIELD_TYPE,///< 23 incorrect structure member type for %s::%s, ignored + WARN_WRITE_CONST, ///< 24 write access to const memory at %a has been detected + WARN_BAD_RETVAR, ///< 25 wrong return variable + WARN_FRAG_LVAR, ///< 26 fragmented variable at %s may be wrong + WARN_HUGE_STKOFF, ///< 27 exceedingly huge offset into the stack frame + WARN_UNINITED_REG, ///< 28 reference to an uninitialized register has been removed: %s + WARN_FIXED_INSN, ///< 29 fixed broken insn + WARN_WRONG_VA_OFF, ///< 30 wrong offset of va_list variable + WARN_CR_NOFIELD, ///< 31 CONTAINING_RECORD: no field '%s' in struct '%s' at %d + WARN_CR_BADOFF, ///< 32 CONTAINING_RECORD: too small offset %d for struct '%s' + WARN_BAD_STROFF, ///< 33 user specified stroff has not been processed: %s + WARN_BAD_VARSIZE, ///< 34 inconsistent variable size for '%s' + WARN_UNSUPP_REG, ///< 35 unsupported processor register '%s' + WARN_UNALIGNED_ARG, ///< 36 unaligned function argument '%s' + WARN_BAD_STD_TYPE, ///< 37 corrupted or unexisting local type '%s' + WARN_BAD_CALL_SP, ///< 38 bad sp value at call + WARN_MISSED_SWITCH, ///< 39 wrong markup of switch jump, skipped it + WARN_BAD_SP, ///< 40 positive sp value %a has been found + WARN_BAD_STKPNT, ///< 41 wrong sp change point + WARN_UNDEF_LVAR, ///< 42 variable '%s' is possibly undefined + WARN_JUMPOUT, ///< 43 control flows out of bounds + WARN_BAD_VALRNG, ///< 44 values range analysis failed + WARN_BAD_SHADOW, ///< 45 ignored the value written to the shadow area of the succeeding call + WARN_OPT_VALRNG, ///< 46 conditional instruction was optimized away because %s + WARN_RET_LOCREF, ///< 47 returning address of temporary local variable '%s' + WARN_BAD_MAPDST, ///< 48 too short map destination '%s' for variable '%s' + WARN_BAD_INSN, ///< 49 bad instruction + WARN_ODD_ABI, ///< 50 encountered odd instruction for the current ABI + WARN_UNBALANCED_STACK, ///< 51 unbalanced stack, ignored a potential tail call + WARN_OPT_VALRNG2, ///< 52 mask 0x%X is shortened because %s <= 0x%X" + WARN_OPT_VALRNG3, ///< 53 masking with 0X%X was optimized away because %s <= 0x%X + WARN_OPT_USELESS_JCND, ///< 54 simplified comparisons for '%s': %s became %s + WARN_SUBFRAME_OVERFLOW, ///< 55 call arguments overflow the function chunk frame + WARN_OPT_VALRNG4, ///< 56 the cases %s were optimized away because %s + WARN_MAX, ///< may be used in notes as a placeholder when the + ///< warning id is not available +}; + +/// Warning instances +struct hexwarn_t +{ + ea_t ea; ///< Address where the warning occurred + warnid_t id; ///< Warning id + qstring text; ///< Fully formatted text of the warning + DECLARE_COMPARISONS(hexwarn_t) + { + if ( ea < r.ea ) + return -1; + if ( ea > r.ea ) + return 1; + if ( id < r.id ) + return -1; + if ( id > r.id ) + return 1; + return strcmp(text.c_str(), r.text.c_str()); + } +}; +DECLARE_TYPE_AS_MOVABLE(hexwarn_t); +typedef qvector hexwarns_t; + +//------------------------------------------------------------------------- +/// Microcode maturity levels +enum mba_maturity_t +{ + MMAT_ZERO, ///< microcode does not exist + MMAT_GENERATED, ///< generated microcode + MMAT_PREOPTIMIZED, ///< preoptimized pass is complete + MMAT_LOCOPT, ///< local optimization of each basic block is complete. + ///< control flow graph is ready too. + MMAT_CALLS, ///< detected call arguments. see also hxe_calls_done + MMAT_GLBOPT1, ///< performed the first pass of global optimization + MMAT_GLBOPT2, ///< most global optimization passes are done + MMAT_GLBOPT3, ///< completed all global optimization. microcode is fixed now. + MMAT_LVARS, ///< allocated local variables +}; + +//------------------------------------------------------------------------- +enum memreg_index_t ///< memory region types +{ + MMIDX_GLBLOW, ///< global memory: low part + MMIDX_LVARS, ///< stack: local variables + MMIDX_RETADDR, ///< stack: return address + MMIDX_SHADOW, ///< stack: shadow arguments + MMIDX_ARGS, ///< stack: regular stack arguments + MMIDX_GLBHIGH, ///< global memory: high part +}; + +//------------------------------------------------------------------------- +/// Ranges to decompile. Either a function or an explicit vector of ranges. +struct mba_ranges_t +{ + func_t *pfn = nullptr; ///< function to decompile. if not null, then function mode. + rangevec_t ranges; ///< snippet mode: ranges to decompile. + ///< function mode: list of outlined ranges + mba_ranges_t(func_t *_pfn=nullptr) : pfn(_pfn) {} + mba_ranges_t(const rangevec_t &r) : ranges(r) {} + ea_t start() const { return (pfn != nullptr ? *pfn : ranges[0]).start_ea; } + bool empty() const { return pfn == nullptr && ranges.empty(); } + void clear() { pfn = nullptr; ranges.clear(); } + bool is_snippet() const { return pfn == nullptr; } + bool hexapi range_contains(ea_t ea) const; + bool is_fragmented() const + { + int n_frags = ranges.size(); + if ( pfn != nullptr ) + n_frags += pfn->tailqty + 1; + return n_frags > 1; + } +}; + +/// Item iterator of arbitrary rangevec items +struct range_item_iterator_t +{ + const rangevec_t *ranges = nullptr; + const range_t *rptr = nullptr; // pointer into ranges + ea_t cur = BADADDR; // current address + bool set(const rangevec_t &r); + bool next_code(); + ea_t current() const { return cur; } +}; + +/// Item iterator for mba_ranges_t +struct mba_item_iterator_t +{ + range_item_iterator_t rii; + func_item_iterator_t fii; + bool func_items_done = true; + bool set(const mba_ranges_t &mbr) + { + bool ok = false; + if ( mbr.pfn != nullptr ) + { + ok = fii.set(mbr.pfn); + if ( ok ) + func_items_done = false; + } + if ( rii.set(mbr.ranges) ) + ok = true; + return ok; + } + bool next_code() + { + bool ok = false; + if ( !func_items_done ) + { + ok = fii.next_code(); + if ( !ok ) + func_items_done = true; + } + if ( !ok ) + ok = rii.next_code(); + return ok; + } + ea_t current() const + { + return func_items_done ? rii.current() : fii.current(); + } +}; + +/// Chunk iterator of arbitrary rangevec items +struct range_chunk_iterator_t +{ + const range_t *rptr = nullptr; // pointer into ranges + const range_t *rend = nullptr; + bool set(const rangevec_t &r) { rptr = r.begin(); rend = r.end(); return rptr != rend; } + bool next() { return ++rptr != rend; } + const range_t &chunk() const { return *rptr; } +}; + +/// Chunk iterator for mba_ranges_t +struct mba_range_iterator_t +{ + range_chunk_iterator_t rii; + func_tail_iterator_t fii; // this is used if rii.rptr==nullptr + bool is_snippet() const { return rii.rptr != nullptr; } + bool set(const mba_ranges_t &mbr) + { + if ( mbr.is_snippet() ) + return rii.set(mbr.ranges); + else + return fii.set(mbr.pfn); + } + bool next() + { + if ( is_snippet() ) + return rii.next(); + else + return fii.next(); + } + const range_t &chunk() const + { + return is_snippet() ? rii.chunk() : fii.chunk(); + } +}; + +//------------------------------------------------------------------------- +/// Array of micro blocks representing microcode for a decompiled function. +/// The first micro block is the entry point, the last one is the exit point. +/// The entry and exit blocks are always empty. The exit block is generated +/// at MMAT_LOCOPT maturity level. +class mba_t +{ + DECLARE_UNCOPYABLE(mba_t) + uint32 flags; + uint32 flags2; + +public: + // bits to describe the microcode, set by the decompiler +#define MBA_PRCDEFS 0x00000001 ///< use precise defeas for chain-allocated lvars +#define MBA_NOFUNC 0x00000002 ///< function is not present, addresses might be wrong +#define MBA_PATTERN 0x00000004 ///< microcode pattern, callinfo is present +#define MBA_LOADED 0x00000008 ///< loaded gdl, no instructions (debugging) +#define MBA_RETFP 0x00000010 ///< function returns floating point value +#define MBA_SPLINFO 0x00000020 ///< (final_type ? idb_spoiled : spoiled_regs) is valid +#define MBA_PASSREGS 0x00000040 ///< has mcallinfo_t::pass_regs +#define MBA_THUNK 0x00000080 ///< thunk function +#define MBA_CMNSTK 0x00000100 ///< stkvars+stkargs should be considered as one area + + // bits to describe analysis stages and requests +#define MBA_PREOPT 0x00000200 ///< preoptimization stage complete +#define MBA_CMBBLK 0x00000400 ///< request to combine blocks +#define MBA_ASRTOK 0x00000800 ///< assertions have been generated +#define MBA_CALLS 0x00001000 ///< callinfo has been built +#define MBA_ASRPROP 0x00002000 ///< assertion have been propagated +#define MBA_SAVRST 0x00004000 ///< save-restore analysis has been performed +#define MBA_RETREF 0x00008000 ///< return type has been refined +#define MBA_GLBOPT 0x00010000 ///< microcode has been optimized globally +#define MBA_LVARS0 0x00040000 ///< lvar pre-allocation has been performed +#define MBA_LVARS1 0x00080000 ///< lvar real allocation has been performed +#define MBA_DELPAIRS 0x00100000 ///< pairs have been deleted once +#define MBA_CHVARS 0x00200000 ///< can verify chain varnums + + // bits that can be set by the caller: +#define MBA_SHORT 0x00400000 ///< use short display +#define MBA_COLGDL 0x00800000 ///< display graph after each reduction +#define MBA_INSGDL 0x01000000 ///< display instruction in graphs +#define MBA_NICE 0x02000000 ///< apply transformations to c code +#define MBA_REFINE 0x04000000 ///< may refine return value size +#define MBA_WINGR32 0x10000000 ///< use wingraph32 +#define MBA_NUMADDR 0x20000000 ///< display definition addresses for numbers +#define MBA_VALNUM 0x40000000 ///< display value numbers + +#define MBA_INITIAL_FLAGS (MBA_INSGDL|MBA_NICE|MBA_CMBBLK|MBA_REFINE\ + |MBA_PRCDEFS|MBA_WINGR32|MBA_VALNUM) + +#define MBA2_LVARNAMES_OK 0x00000001 ///< may verify lvar_names? +#define MBA2_LVARS_RENAMED 0x00000002 ///< accept empty names now? +#define MBA2_OVER_CHAINS 0x00000004 ///< has overlapped chains? +#define MBA2_VALRNG_DONE 0x00000008 ///< calculated valranges? +#define MBA2_IS_CTR 0x00000010 ///< is constructor? +#define MBA2_IS_DTR 0x00000020 ///< is destructor? +#define MBA2_ARGIDX_OK 0x00000040 ///< may verify input argument list? +#define MBA2_NO_DUP_CALLS 0x00000080 ///< forbid multiple calls with the same ea +#define MBA2_NO_DUP_LVARS 0x00000100 ///< forbid multiple lvars with the same ea +#define MBA2_UNDEF_RETVAR 0x00000200 ///< return value is undefined +#define MBA2_ARGIDX_SORTED 0x00000400 ///< args finally sorted according to ABI + ///< (e.g. reverse stkarg order in Borland) +#define MBA2_CODE16_BIT 0x00000800 ///< the code16 bit got removed +#define MBA2_STACK_RETVAL 0x00001000 ///< the return value (or its part) is on the stack +#define MBA2_HAS_OUTLINES 0x00002000 ///< calls to outlined code have been inlined +#define MBA2_NO_FRAME 0x00004000 ///< do not use function frame info (only snippet mode) +#define MBA2_PROP_COMPLEX 0x00008000 ///< allow propagation of more complex variable definitions + +#define MBA2_DONT_VERIFY 0x80000000 ///< Do not verify microcode. This flag + ///< is recomended to be set only when + ///< debugging decompiler plugins + +#define MBA2_INITIAL_FLAGS (MBA2_LVARNAMES_OK|MBA2_LVARS_RENAMED) + +#define MBA2_ALL_FLAGS 0x0001FFFF + + bool precise_defeas() const { return (flags & MBA_PRCDEFS) != 0; } + bool optimized() const { return (flags & MBA_GLBOPT) != 0; } + bool short_display() const { return (flags & MBA_SHORT ) != 0; } + bool show_reduction() const { return (flags & MBA_COLGDL) != 0; } + bool graph_insns() const { return (flags & MBA_INSGDL) != 0; } + bool loaded_gdl() const { return (flags & MBA_LOADED) != 0; } + bool should_beautify()const { return (flags & MBA_NICE ) != 0; } + bool rtype_refined() const { return (flags & MBA_RETREF) != 0; } + bool may_refine_rettype() const { return (flags & MBA_REFINE) != 0; } + bool use_wingraph32() const { return (flags & MBA_WINGR32) != 0; } + bool display_numaddrs() const { return (flags & MBA_NUMADDR) != 0; } + bool display_valnums() const { return (flags & MBA_VALNUM) != 0; } + bool is_pattern() const { return (flags & MBA_PATTERN) != 0; } + bool is_thunk() const { return (flags & MBA_THUNK) != 0; } + bool saverest_done() const { return (flags & MBA_SAVRST) != 0; } + bool callinfo_built() const { return (flags & MBA_CALLS) != 0; } + bool really_alloc() const { return (flags & MBA_LVARS0) != 0; } + bool lvars_allocated()const { return (flags & MBA_LVARS1) != 0; } + bool chain_varnums_ok()const { return (flags & MBA_CHVARS) != 0; } + bool returns_fpval() const { return (flags & MBA_RETFP) != 0; } + bool has_passregs() const { return (flags & MBA_PASSREGS) != 0; } + bool generated_asserts() const { return (flags & MBA_ASRTOK) != 0; } + bool propagated_asserts() const { return (flags & MBA_ASRPROP) != 0; } + bool deleted_pairs() const { return (flags & MBA_DELPAIRS) != 0; } + bool common_stkvars_stkargs() const { return (flags & MBA_CMNSTK) != 0; } + bool lvar_names_ok() const { return (flags2 & MBA2_LVARNAMES_OK) != 0; } + bool lvars_renamed() const { return (flags2 & MBA2_LVARS_RENAMED) != 0; } + bool has_over_chains() const { return (flags2 & MBA2_OVER_CHAINS) != 0; } + bool valranges_done() const { return (flags2 & MBA2_VALRNG_DONE) != 0; } + bool argidx_ok() const { return (flags2 & MBA2_ARGIDX_OK) != 0; } + bool argidx_sorted() const { return (flags2 & MBA2_ARGIDX_SORTED) != 0; } + bool code16_bit_removed() const { return (flags2 & MBA2_CODE16_BIT) != 0; } + bool has_stack_retval() const { return (flags2 & MBA2_STACK_RETVAL) != 0; } + bool has_outlines() const { return (flags2 & MBA2_HAS_OUTLINES) != 0; } + bool is_ctr() const { return (flags2 & MBA2_IS_CTR) != 0; } + bool is_dtr() const { return (flags2 & MBA2_IS_DTR) != 0; } + bool is_cdtr() const { return (flags2 & (MBA2_IS_CTR|MBA2_IS_DTR)) != 0; } + bool prop_complex() const { return (flags2 & MBA2_PROP_COMPLEX) != 0; } + int get_mba_flags() const { return flags; } + int get_mba_flags2() const { return flags2; } + void set_mba_flags(int f) { flags |= f; } + void clr_mba_flags(int f) { flags &= ~f; } + void set_mba_flags2(int f) { flags2 |= f; } + void clr_mba_flags2(int f) { flags2 &= ~f; } + void clr_cdtr() { flags2 &= ~(MBA2_IS_CTR|MBA2_IS_DTR); } + int calc_shins_flags() const + { + int shins_flags = 0; + if ( short_display() ) + shins_flags |= SHINS_SHORT; + if ( display_valnums() ) + shins_flags |= SHINS_VALNUM; + if ( display_numaddrs() ) + shins_flags |= SHINS_NUMADDR; + return shins_flags; + } + +/* + +-----------+ <- inargtop + | prmN | + | ... | <- minargref + | prm0 | + +-----------+ <- inargoff + |shadow_args| + +-----------+ + | retaddr | + frsize+frregs +-----------+ <- initial esp | + | frregs | | + +frsize +-----------+ <- typical ebp | + | | | | + | | | fpd | + | | | | + | frsize | <- current ebp | + | | | + | | | + | | | stacksize + | | | + | | | + | | <- minstkref | + stkvar base off 0 +---.. | | | current + | | | | stack + | | | | pointer + | | | | range + |tmpstk_size| | | (what getspd() returns) + | | | | + | | | | + +-----------+ <- minimal sp | | offset 0 for the decompiler (vd) + + There is a detail that may add confusion when working with stack variables. + The decompiler does not use the same stack offsets as IDA. + The picture above should explain the difference: + - IDA stkoffs are displayed on the left, decompiler stkoffs - on the right + - Decompiler stkoffs are always >= 0 + - IDA stkoff==0 corresponds to stkoff==tmpstk_size in the decompiler + - See stkoff_vd2ida and stkoff_ida2vd below to convert IDA stkoffs to vd stkoff + +*/ + + // convert a stack offset used in vd to a stack offset used in ida stack frame + sval_t hexapi stkoff_vd2ida(sval_t off) const; + // convert a ida stack frame offset to a stack offset used in vd + sval_t hexapi stkoff_ida2vd(sval_t off) const; + sval_t argbase() const + { + return retsize + stacksize; + } + static vdloc_t hexapi idaloc2vd(const argloc_t &loc, int width, sval_t spd); + vdloc_t hexapi idaloc2vd(const argloc_t &loc, int width) const; + + static argloc_t hexapi vd2idaloc(const vdloc_t &loc, int width, sval_t spd); + argloc_t hexapi vd2idaloc(const vdloc_t &loc, int width) const; + + bool is_stkarg(const lvar_t &v) const + { + return v.is_stk_var() && v.get_stkoff() >= inargoff; + } + ssize_t get_stkvar( + udm_t *udm, + sval_t vd_stkoff, + uval_t *p_idaoff=nullptr, + tinfo_t *p_frame=nullptr) const; + // get lvar location + argloc_t get_ida_argloc(const lvar_t &v) const + { + return vd2idaloc(v.location, v.width); + } + mba_ranges_t mbr; + ea_t entry_ea = BADADDR; + ea_t last_prolog_ea = BADADDR; + ea_t first_epilog_ea = BADADDR; + int qty = 0; ///< number of basic blocks + int npurged = -1; ///< -1 - unknown + callcnv_t cc = CM_CC_UNKNOWN; ///< calling convention + sval_t tmpstk_size = 0; ///< size of the temporary stack part + ///< (which dynamically changes with push/pops) + sval_t frsize = 0; ///< size of local stkvars range in the stack frame + sval_t frregs = 0; ///< size of saved registers range in the stack frame + sval_t fpd = 0; ///< frame pointer delta + int pfn_flags = 0; ///< copy of func_t::flags + int retsize = 0; ///< size of return address in the stack frame + int shadow_args = 0; ///< size of shadow argument area + sval_t fullsize = 0; ///< Full stack size including incoming args + sval_t stacksize = 0; ///< The maximal size of the function stack including + ///< bytes allocated for outgoing call arguments + ///< (up to retaddr) + sval_t inargoff = 0; ///< offset of the first stack argument; + ///< after fix_scattered_movs() INARGOFF may + ///< be less than STACKSIZE + sval_t minstkref = 0; ///< The lowest stack location whose address was taken + ea_t minstkref_ea = BADADDR; ///< address with lowest minstkref (for debugging) + sval_t minargref = 0; ///< The lowest stack argument location whose address was taken + ///< This location and locations above it can be aliased + ///< It controls locations >= inargoff-shadow_args + sval_t spd_adjust = 0; ///< If sp>0, the max positive sp value + ivlset_t gotoff_stkvars; ///< stkvars that hold .got offsets. considered to be unaliasable + ivlset_t restricted_memory; + ivlset_t aliased_memory = ALLMEM; ///< aliased_memory+restricted_memory=ALLMEM + mlist_t nodel_memory; ///< global dead elimination may not delete references to this area + rlist_t consumed_argregs; ///< registers converted into stack arguments, should not be used as arguments + + mba_maturity_t maturity = MMAT_ZERO; ///< current maturity level + mba_maturity_t reqmat = MMAT_ZERO; ///< required maturity level + + bool final_type = false; ///< is the function type final? (specified by the user) + tinfo_t idb_type; ///< function type as retrieved from the database + reginfovec_t idb_spoiled; ///< MBA_SPLINFO && final_type: info in ida format + mlist_t spoiled_list; ///< MBA_SPLINFO && !final_type: info in vd format + int fti_flags = 0; ///< FTI_... constants for the current function + +#define NALT_VD 2 ///< this index is not used by ida + + qstring label; ///< name of the function or pattern (colored) + lvars_t vars; ///< local variables + intvec_t argidx; ///< input arguments (indexes into 'vars') + int retvaridx = -1; ///< index of variable holding the return value + ///< -1 means none + + ea_t error_ea = BADADDR; ///< during microcode generation holds ins.ea + qstring error_strarg; + + mblock_t *blocks = nullptr; ///< double linked list of blocks + mblock_t **natural = nullptr; ///< natural order of blocks + + ivl_with_name_t std_ivls[6]; ///< we treat memory as consisting of 6 parts + ///< see \ref memreg_index_t + + mutable hexwarns_t notes; + mutable uchar occurred_warns[32]; // occurred warning messages + // (even disabled warnings are taken into account) + bool write_to_const_detected() const + { + return test_bit(occurred_warns, WARN_WRITE_CONST); + } + bool bad_call_sp_detected() const + { + return test_bit(occurred_warns, WARN_BAD_CALL_SP); + } + bool regargs_is_not_aligned() const + { + return test_bit(occurred_warns, WARN_UNALIGNED_ARG); + } + bool has_bad_sp() const + { + return test_bit(occurred_warns, WARN_BAD_SP); + } + + // the exact size of this class is not documented, there may be more fields + char reserved[]; + mba_t(); // use gen_microcode() or create_empty_mba() to create microcode objects + ~mba_t() { term(); } + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + void hexapi term(); + func_t *hexapi get_curfunc() const; + bool use_frame() const { return get_curfunc() != nullptr; } + bool range_contains(ea_t ea) const { return mbr.range_contains(map_fict_ea(ea)); } + bool is_snippet() const { return mbr.is_snippet(); } + + /// Set maturity level. + /// \param mat new maturity level + /// \return error code + /// Plugins may use this function to skip some parts of the analysis. + /// The maturity level cannot be decreased. + merror_t hexapi set_maturity(mba_maturity_t mat); + + /// Optimize each basic block locally + /// \param locopt_bits combination of \ref LOCOPT_ bits + /// \return number of changes. 0 means nothing changed + /// This function is called by the decompiler, usually there is no need to + /// call it explicitly. + int hexapi optimize_local(int locopt_bits); + /// \defgroup LOCOPT_ Bits for optimize_local() + //@{ +#define LOCOPT_ALL 0x0001 ///< redo optimization for all blocks. if this bit + ///< is not set, only dirty blocks will be optimized +#define LOCOPT_REFINE 0x0002 ///< refine return type, ok to fail +#define LOCOPT_REFINE2 0x0004 ///< refine return type, try harder + //@} + + /// Build control flow graph. + /// This function may be called only once. It calculates the type of each + /// basic block and the adjacency list. optimize_local() calls this function + /// if necessary. You need to call this function only before MMAT_LOCOPT. + /// \return error code + merror_t hexapi build_graph(); + + /// Get control graph. + /// Call build_graph() if you need the graph before MMAT_LOCOPT. + mbl_graph_t *hexapi get_graph(); + + /// Analyze calls and determine calling conventions. + /// \param acflags permitted actions that are necessary for successful detection + /// of calling conventions. See \ref ACFL_ + /// \return number of calls. -1 means error. + int hexapi analyze_calls(int acflags); + /// \defgroup ACFL_ Bits for analyze_calls() + //@{ +#define ACFL_LOCOPT 0x01 ///< perform local propagation (requires ACFL_BLKOPT) +#define ACFL_BLKOPT 0x02 ///< perform interblock transformations +#define ACFL_GLBPROP 0x04 ///< perform global propagation +#define ACFL_GLBDEL 0x08 ///< perform dead code eliminition +#define ACFL_GUESS 0x10 ///< may guess calling conventions + //@} + + /// Optimize microcode globally. + /// This function applies various optimization methods until we reach the + /// fixed point. After that it preallocates lvars unless reqmat forbids it. + /// \return error code + merror_t hexapi optimize_global(); + + /// Allocate local variables. + /// Must be called only immediately after optimize_global(), with no + /// modifications to the microcode. Converts registers, + /// stack variables, and similar operands into mop_l. This call will not fail + /// because all necessary checks were performed in optimize_global(). + /// After this call the microcode reaches its final state. + void hexapi alloc_lvars(); + + /// Dump microcode to a file. + /// The file will be created in the directory pointed by IDA_DUMPDIR envvar. + /// Dump will be created only if IDA is run under debugger. + void hexapi dump() const; + AS_PRINTF(3, 0) void hexapi vdump_mba(bool _verify, const char *title, va_list va) const; + AS_PRINTF(3, 4) void dump_mba(bool _verify, const char *title, ...) const + { + va_list va; + va_start(va, title); + vdump_mba(_verify, title, va); + va_end(va); + } + + /// Print microcode to any destination. + /// \param vp print sink + void hexapi print(vd_printer_t &vp) const; + + /// Verify microcode consistency. + /// \param always if false, the check will be performed only if ida runs + /// under debugger + /// If any inconsistency is discovered, an internal error will be generated. + /// We strongly recommend you to call this function before returing control + /// to the decompiler from your callbacks, in the case if you modified + /// the microcode. If the microcode is inconsistent, this function will + /// generate an internal error. We provide the source code of this function + /// in the plugins/hexrays_sdk/verifier directory for your reference. + void hexapi verify(bool always) const; + + /// Mark the microcode use-def chains dirty. + /// Call this function is any inter-block data dependencies got changed + /// because of your modifications to the microcode. Failing to do so may + /// cause an internal error. + void hexapi mark_chains_dirty(); + + /// Get basic block by its serial number. + const mblock_t *get_mblock(uint n) const { QASSERT(52719, n < qty); return natural[n]; } + mblock_t *get_mblock(uint n) { return CONST_CAST(mblock_t*)((CONST_CAST(const mba_t *)(this))->get_mblock(n)); } + + /// Insert a block in the middle of the mbl array. + /// The very first block of microcode must be empty, it is the entry block. + /// The very last block of microcode must be BLT_STOP, it is the exit block. + /// Therefore inserting a new block before the entry point or after the exit + /// block is not a good idea. + /// \param bblk the new block will be inserted before BBLK + /// \return ptr to the new block + mblock_t *hexapi insert_block(int bblk); + + /// Split a block: insert a new one after the block, move some instructions + /// to new block + /// \param blk block to be split + /// \param start_insn all instructions to be moved to new block: starting with this one up to the end + /// \return ptr to the new block + mblock_t *hexapi split_block(mblock_t *blk, minsn_t *start_insn); + + /// Delete a block. + /// \param blk block to delete + /// \return true if at least one of the other blocks became empty or unreachable + bool hexapi remove_block(mblock_t *blk); + bool hexapi remove_blocks(int start_blk, int end_blk); // end_blk is excluded + + /// Make a copy of a block. + /// This function makes a simple copy of the block. It does not fix the + /// predecessor and successor lists, they must be fixed if necessary. + /// \param blk block to copy + /// \param new_serial position of the copied block + /// \param cpblk_flags combination of \ref CPBLK_... bits + /// \return pointer to the new copy + mblock_t *hexapi copy_block(mblock_t *blk, int new_serial, int cpblk_flags=3); +/// \defgroup CPBLK_ Batch decompilation bits +///@{ +#define CPBLK_FAST 0x0000 ///< do not update minbstkref and minbargref +#define CPBLK_MINREF 0x0001 ///< update minbstkref and minbargref +#define CPBLK_OPTJMP 0x0002 ///< del the jump insn at the end of the block + ///< if it becomes useless +///@} + + /// Delete all empty and unreachable blocks. + /// Blocks marked with MBL_KEEP won't be deleted. + bool hexapi remove_empty_and_unreachable_blocks(); + + /// Merge blocks. + /// This function merges blocks constituting linear flow. + /// It calls remove_empty_and_unreachable_blocks() as well. + /// \return true if changed any blocks + bool hexapi merge_blocks(); + + /// Visit all operands of all instructions. + /// \param mv operand visitor + /// \return non-zero value returned by mv.visit_mop() or zero + int hexapi for_all_ops(mop_visitor_t &mv); + + /// Visit all instructions. + /// This function visits all instruction and subinstructions. + /// \param mv instruction visitor + /// \return non-zero value returned by mv.visit_mop() or zero + int hexapi for_all_insns(minsn_visitor_t &mv); + + /// Visit all top level instructions. + /// \param mv instruction visitor + /// \return non-zero value returned by mv.visit_mop() or zero + int hexapi for_all_topinsns(minsn_visitor_t &mv); + + /// Find an operand in the microcode. + /// This function tries to find the operand that matches LIST. + /// Any operand that overlaps with LIST is considered as a match. + /// \param[out] ctx context information for the result + /// \param ea desired address of the operand. BADADDR means to accept any address. + /// \param is_dest search for destination operand? this argument may be + /// ignored if the exact match could not be found + /// \param list list of locations the correspond to the operand + /// \return pointer to the operand or nullptr. + mop_t *hexapi find_mop(op_parent_info_t *ctx, ea_t ea, bool is_dest, const mlist_t &list); + + /// Create a call of a helper function. + /// \param ea The desired address of the instruction + /// \param helper The helper name + /// \param rettype The return type (nullptr or empty type means 'void') + /// \param callargs The helper arguments (nullptr-no arguments) + /// \param out The operand where the call result should be stored. + /// If this argument is not nullptr, "mov helper_call(), out" + /// will be generated. Otherwise "call helper()" will be + /// generated. Note: the size of this operand must be equal + /// to the RETTYPE size + /// \return pointer to the created instruction or nullptr if error + minsn_t *hexapi create_helper_call( + ea_t ea, + const char *helper, + const tinfo_t *rettype=nullptr, + const mcallargs_t *callargs=nullptr, + const mop_t *out=nullptr); + + /// Prepare the lists of registers & memory that are defined/killed by a + /// function + /// \param[out] return_regs defined regs to return (eax,edx) + /// \param[out] spoiled spoiled regs (flags,ecx,mem) + /// \param type the function type + /// \param call_ea the call insn address (if known) + /// \param tail_call is it the tail call? + void hexapi get_func_output_lists( + mlist_t *return_regs, + mlist_t *spoiled, + const tinfo_t &type, + ea_t call_ea=BADADDR, + bool tail_call=false); + + /// Get input argument of the decompiled function. + /// \param n argument number (0..nargs-1) + lvar_t &hexapi arg(int n); + const lvar_t &arg(int n) const { return CONST_CAST(mba_t*)(this)->arg(n); } + + /// Allocate a fictional address. + /// This function can be used to allocate a new unique address for a new + /// instruction, if re-using any existing address leads to conflicts. + /// For example, if the last instruction of the function modifies R0 + /// and falls through to the next function, it will be a tail call: + /// LDM R0!, {R4,R7} + /// end of the function + /// start of another function + /// In this case R0 generates two different lvars at the same address: + /// - one modified by LDM + /// - another that represents the return value from the tail call + /// + /// Another example: a third-party plugin makes a copy of an instruction. + /// This may lead to the generation of two variables at the same address. + /// Example 3: fictional addresses can be used for new instructions created + /// while modifying the microcode. + /// This function can be used to allocate a new unique address for a new + /// instruction or a variable. + /// The fictional address is selected from an unallocated address range. + /// \param real_ea real instruction address (BADADDR is ok too) + /// \return a unique fictional address + ea_t hexapi alloc_fict_ea(ea_t real_ea); + + /// Resolve a fictional address. + /// This function provides a reverse of the mapping made by alloc_fict_ea(). + /// \param fict_ea fictional definition address + /// \return the real instruction address + ea_t hexapi map_fict_ea(ea_t fict_ea) const; + + /// Get information about various memory regions. + /// We map the stack frame to the global memory, to some unused range. + const ivl_t &get_std_region(memreg_index_t idx) const; + const ivl_t &get_lvars_region() const; + const ivl_t &get_shadow_region() const; + const ivl_t &get_args_region() const; + ivl_t get_stack_region() const; // get entire stack region + + /// Serialize mbl array into a sequence of bytes. + void hexapi serialize(bytevec_t &vout) const; + + /// Deserialize a byte sequence into mbl array. + /// \param bytes pointer to the beginning of the byte sequence. + /// \param nbytes number of bytes in the byte sequence. + /// \return new mbl array + WARN_UNUSED_RESULT static mba_t *hexapi deserialize(const uchar *bytes, size_t nbytes); + + /// Create and save microcode snapshot + void hexapi save_snapshot(const char *description); + + /// Allocate a kernel register. + /// \param size size of the register in bytes + /// \param check_size if true, only the sizes that correspond to a size of + /// a basic type will be accepted. + /// \return allocated register. mr_none means failure. + mreg_t hexapi alloc_kreg(size_t size, bool check_size=true); + + /// Free a kernel register. + /// If wrong arguments are passed, this function will generate an internal error. + /// \param reg a previously allocated kernel register + /// \param size size of the register in bytes + void hexapi free_kreg(mreg_t reg, size_t size); + +/// \defgroup INLINE_ inline_func() flags +///@{ +#define INLINE_EXTFRAME 0x0001 ///< Inlined function has its own (external) frame +#define INLINE_DONTCOPY 0x0002 ///< Do not reuse old inlined copy even if it exists +///@} + /// Inline a range. + /// This function may be called only during the initial microcode generation phase. + /// \param cdg the codegenerator object + /// \param blknum the block contaning the call/jump instruction to inline + /// \param ranges the set of ranges to inline. in the case of multiple calls + /// to inline_func(), ranges will be compared using their start + /// addresses. if two ranges have the same address, they will be + /// considered the same. + /// \param decomp_flags combination of \ref DECOMP_ bits + /// \param inline_flags combination of \ref INLINE_ bits + /// \return error code + merror_t hexapi inline_func( + codegen_t &cdg, + int blknum, + mba_ranges_t &ranges, + int decomp_flags=0, + int inline_flags=0); + + // Find a sp change point. + // returns stkpnt p, where p->ea <= ea + const stkpnt_t *hexapi locate_stkpnt(ea_t ea) const; + + bool hexapi set_lvar_name(lvar_t &v, const char *name, int flagbits); + bool set_nice_lvar_name(lvar_t &v, const char *name) { return set_lvar_name(v, name, CVAR_NAME); } + bool set_user_lvar_name(lvar_t &v, const char *name) { return set_lvar_name(v, name, CVAR_NAME|CVAR_UNAME); } +}; +using mbl_array_t = mba_t; +//------------------------------------------------------------------------- +/// Convenience class to release graph chains automatically. +/// Use this class instead of using graph_chains_t directly. +class chain_keeper_t +{ + graph_chains_t *gc; + chain_keeper_t &operator=(const chain_keeper_t &); // not defined +public: + chain_keeper_t(graph_chains_t *_gc) : gc(_gc) { QASSERT(50446, gc != nullptr); gc->acquire(); } + ~chain_keeper_t() + { + gc->release(); + } + block_chains_t &operator[](size_t idx) { return (*gc)[idx]; } + block_chains_t &front() { return gc->front(); } + block_chains_t &back() { return gc->back(); } + operator graph_chains_t &() { return *gc; } + int for_all_chains(chain_visitor_t &cv, int gca) { return gc->for_all_chains(cv, gca); } + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; + +//------------------------------------------------------------------------- +/// Kind of use-def and def-use chains +enum gctype_t +{ + GC_REGS_AND_STKVARS, ///< registers and stkvars (restricted memory only) + GC_ASR, ///< all the above and assertions + GC_XDSU, ///< only registers calculated with FULL_XDSU + GC_END, ///< number of chain types + GC_DIRTY_ALL = (1 << (2*GC_END))-1, ///< bitmask to represent all chains +}; + +//------------------------------------------------------------------------- +/// Control flow graph of microcode. +class mbl_graph_t : public simple_graph_t +{ + mba_t *mba; ///< pointer to the mbl array + int dirty = GC_DIRTY_ALL; ///< what kinds of use-def chains are dirty? + int chain_stamp = 0; ///< we increment this counter each time chains are recalculated + graph_chains_t gcs[2*GC_END]; ///< cached use-def chains + bool exclude_never_jumping_edges = true; + + /// Is LIST accessed between two instructions? + /// This function can analyze all path between the specified instructions + /// and find if the specified list is used in any of them. The instructions + /// may be located in different basic blocks. This function does not use + /// use-def chains but use the graph for analysis. It may be slow in some + /// cases but its advantage is that is does not require building the use-def + /// chains. + /// \param list list to verify + /// \param b1 starting block + /// \param b2 ending block. may be -1, it means all possible paths from b1 + /// \param m1 starting instruction (in b1) + /// \param m2 ending instruction (in b2). excluded. may be nullptr. + /// \param access_type read or write access? + /// \param maymust may access or must access? + /// \return true if found an access to the list + bool hexapi is_accessed_globally( + const mlist_t &list, // list to verify + int b1, // starting block + int b2, // ending block + const minsn_t *m1, // starting instruction (in b1) + const minsn_t *m2, // ending instruction (in b2) + access_type_t access_type, + maymust_t maymust) const; + int get_ud_gc_idx(gctype_t gctype) const { return (gctype << 1); } + int get_du_gc_idx(gctype_t gctype) const { return (gctype << 1)+1; } + int get_ud_dirty_bit(gctype_t gctype) { return 1 << get_ud_gc_idx(gctype); } + int get_du_dirty_bit(gctype_t gctype) { return 1 << get_du_gc_idx(gctype); } + +public: + /// Is the use-def chain of the specified kind dirty? + bool is_ud_chain_dirty(gctype_t gctype) + { + int bit = get_ud_dirty_bit(gctype); + return (dirty & bit) != 0; + } + + /// Is the def-use chain of the specified kind dirty? + bool is_du_chain_dirty(gctype_t gctype) + { + int bit = get_du_dirty_bit(gctype); + return (dirty & bit) != 0; + } + int get_chain_stamp() const { return chain_stamp; } + + /// Get use-def chains. + graph_chains_t *hexapi get_ud(gctype_t gctype); + + /// Get def-use chains. + graph_chains_t *hexapi get_du(gctype_t gctype); + + /// Is LIST redefined in the graph? + bool is_redefined_globally(const mlist_t &list, int b1, int b2, const minsn_t *m1, const minsn_t *m2, maymust_t maymust=MAY_ACCESS) const + { return is_accessed_globally(list, b1, b2, m1, m2, WRITE_ACCESS, maymust); } + + /// Is LIST used in the graph? + bool is_used_globally(const mlist_t &list, int b1, int b2, const minsn_t *m1, const minsn_t *m2, maymust_t maymust=MAY_ACCESS) const + { return is_accessed_globally(list, b1, b2, m1, m2, READ_ACCESS, maymust); } + + mblock_t *get_mblock(int n) const { return mba->get_mblock(n); } +}; + +//------------------------------------------------------------------------- +// Helper for codegen_t. It takes into account delay slots +struct cdg_insn_iterator_t +{ + const mba_t *mba; // to check range + ea_t ea = BADADDR; // next insn to decode + ea_t end = BADADDR; // end of the block + ea_t dslot = BADADDR; // address of the insn in the delay slot + insn_t dslot_insn; // instruction in the delay slot + ea_t severed_branch = BADADDR; // address of the severed branch insn + // (when this branch insn ends the previous block) + bool is_likely_dslot = false; // execute delay slot only when jumping + + cdg_insn_iterator_t(const mba_t *mba_) : mba(mba_) {} + cdg_insn_iterator_t(const cdg_insn_iterator_t &r) = default; + cdg_insn_iterator_t &operator=(const cdg_insn_iterator_t &r) = default; + + bool ok() const { return ea < end; } + bool has_dslot() const { return dslot != BADADDR; } + bool dslot_with_xrefs() const { return dslot >= end; } + // the current insn is the severed delayed insn (when this starts a block) + bool is_severed_dslot() const { return severed_branch != BADADDR; } + void start(const range_t &rng) + { + ea = rng.start_ea; + end = rng.end_ea; + } + merror_t hexapi next(insn_t *ins); +}; + +//------------------------------------------------------------------------- +/// Helper class to generate the initial microcode +class codegen_t +{ +public: + mba_t *mba; // ptr to mbl array + mblock_t *mb = nullptr; // current basic block + insn_t insn; // instruction to generate microcode for + char ignore_micro = IM_NONE; // value of get_ignore_micro() for the insn + cdg_insn_iterator_t ii; // instruction iterator + size_t reserved[4]; + + codegen_t() = delete; + virtual ~codegen_t() + { + clear(); + } + void hexapi clear(); // does not clear everything yet + + /// Analyze prolog/epilog of the function to decompile. + /// If prolog is found, allocate and fill 'mba->pi' structure. + /// \param fc flow chart + /// \param reachable bitmap of reachable blocks + /// \return error code + virtual merror_t idaapi analyze_prolog( + const class qflow_chart_t &fc, + const class bitset_t &reachable) = 0; + + /// Generate microcode for one instruction. + /// The instruction is in INSN + /// \return MERR_OK - all ok + /// MERR_BLOCK - all ok, need to switch to new block + /// MERR_BADBLK - delete current block and continue + /// other error codes are fatal + virtual merror_t idaapi gen_micro() = 0; + + /// Generate microcode to load one operand. + /// \param opnum number of INSN operand + /// \param flags reserved for future use + /// \return register containing the operand. + virtual mreg_t idaapi load_operand(int opnum, int flags=0) = 0; + + /// This method is called when the microcode generation is done + virtual void idaapi microgen_completed() {} + + /// Setup internal data to handle new instruction. + /// This method should be called before calling gen_micro(). + /// Usually gen_micro() is called by the decompiler. You have to call this + /// function explicitly only if you yourself call gen_micro(). + /// The instruction is in INSN + /// \return MERR_OK - all ok + /// other error codes are fatal + virtual merror_t idaapi prepare_gen_micro() { return MERR_OK; } + + /// Generate microcode to calculate the address of a memory operand. + /// \param n - number of INSN operand + /// \param flags - reserved for future use + /// \return register containing the operand address. + /// mr_none - failed (not a memory operand) + virtual mreg_t idaapi load_effective_address(int n, int flags=0) = 0; + + /// Generate microcode to store an operand. + /// In case of success an arbitrary number of instructions can be + /// generated (and even no instruction if the source and target are the same) + /// \param n - number of target INSN operand + /// \param mop - operand to be stored + /// \param flags - reserved for future use + /// \param outins - (OUT) the last generated instruction + // (nullptr if no instruction was generated) + /// \return success + virtual bool idaapi store_operand(int n, const mop_t &mop, int flags=0, minsn_t **outins=nullptr); + + /// Emit one microinstruction. + /// The L, R, D arguments usually mean the register number. However, they depend + /// on CODE. For example: + /// - for m_goto and m_jcnd L is the target address + /// - for m_ldc L is the constant value to load + /// + /// \param code instruction opcode + /// \param width operand size in bytes + /// \param l left operand + /// \param r right operand + /// \param d destination operand + /// \param offsize for ldx/stx, the size of the offset operand + /// for ldc, operand number of the constant value + /// -1, set the FP instruction (e.g. for m_mov) + /// \return created microinstruction. can be nullptr if the instruction got + /// immediately optimized away. + minsn_t *hexapi emit(mcode_t code, int width, uval_t l, uval_t r, uval_t d, int offsize); + + /// Emit one microinstruction. + /// This variant takes a data type not a size. + minsn_t *idaapi emit_micro_mvm( + mcode_t code, + op_dtype_t dtype, + uval_t l, + uval_t r, + uval_t d, + int offsize) + { + return emit(code, get_dtype_size(dtype), l, r, d, offsize); + } + + /// Emit one microinstruction. + /// This variant accepts pointers to operands. It is more difficult to use + /// but permits to create virtually any instruction. Operands may be nullptr + /// when it makes sense. + minsn_t *hexapi emit(mcode_t code, const mop_t *l, const mop_t *r, const mop_t *d); + +}; + +//------------------------------------------------------------------------- +/// Parse DIRECTIVE and update the current configuration variables. +/// For the syntax see hexrays.cfg +bool hexapi change_hexrays_config(const char *directive); + +//------------------------------------------------------------------------- +inline void mop_t::_make_insn(minsn_t *ins) +{ + t = mop_d; + d = ins; +} + +inline bool mop_t::has_side_effects(bool include_ldx_and_divs) const +{ + return is_insn() && d->has_side_effects(include_ldx_and_divs); +} + +inline bool mop_t::is_kreg() const +{ + return is_reg() && ::is_kreg(r); +} + +inline minsn_t *mop_t::get_insn(mcode_t code) +{ + return is_insn(code) ? d : nullptr; +} +inline const minsn_t *mop_t::get_insn(mcode_t code) const +{ + return is_insn(code) ? d : nullptr; +} + +inline bool mop_t::is_insn(mcode_t code) const +{ + return is_insn() && d->opcode == code; +} + +inline bool mop_t::is_glbaddr() const +{ + return t == mop_a && a->is_glbvar(); +} + +inline bool mop_t::is_glbaddr(ea_t ea) const +{ + return is_glbaddr() && a->g == ea; +} + +inline bool mop_t::is_stkaddr() const +{ + return t == mop_a && a->is_stkvar(); +} + +inline vivl_t::vivl_t(const chain_t &ch) + : voff_t(ch.key().type, ch.is_reg() ? ch.get_reg() : ch.get_stkoff()), + size(ch.width) +{ +} + +// The following memory regions exist +// start length +// ------------------------ --------- +// lvars spbase stacksize +// retaddr spbase+stacksize retsize +// shadow spbase+stacksize+retsize shadow_args +// args inargoff MAX_FUNC_ARGS*sp_width-shadow_args +// globals data_segment sizeof_data_segment +// heap everything else? + +inline const ivl_t &mba_t::get_std_region(memreg_index_t idx) const +{ + return std_ivls[idx].ivl; +} + +inline const ivl_t &mba_t::get_lvars_region() const +{ + return get_std_region(MMIDX_LVARS); +} + +inline const ivl_t &mba_t::get_shadow_region() const +{ + return get_std_region(MMIDX_SHADOW); +} + +inline const ivl_t &mba_t::get_args_region() const +{ + return get_std_region(MMIDX_ARGS); +} + +inline ivl_t mba_t::get_stack_region() const +{ + return ivl_t(std_ivls[MMIDX_LVARS].ivl.off, fullsize); +} + +//------------------------------------------------------------------------- +/// Get decompiler version. +/// The returned string is of the form ... +/// \return pointer to version string. For example: "2.0.0.140605" + +const char *hexapi get_hexrays_version(); + +/// \defgroup OPF_ open_pseudocode flags +/// Used in open_pseudocode +///@{ +#define OPF_REUSE 0x00 ///< reuse existing window +#define OPF_NEW_WINDOW 0x01 ///< open new window +#define OPF_REUSE_ACTIVE 0x02 ///< reuse existing window, only if the + ///< currently active widget is a pseudocode view +#define OPF_NO_WAIT 0x08 ///< do not display waitbox if decompilation happens +///@} + +#define OPF_WINDOW_MGMT_MASK 0x07 + + +/// Open pseudocode window. +/// The specified function is decompiled and the pseudocode window is opened. +/// \param ea function to decompile +/// \param flags: a combination of OPF_ flags +/// \return false if failed + +vdui_t *hexapi open_pseudocode(ea_t ea, int flags); + + +/// Close pseudocode window. +/// \param f pointer to window +/// \return false if failed + +bool hexapi close_pseudocode(TWidget *f); + + +/// Get the vdui_t instance associated to the TWidget +/// \param f pointer to window +/// \return a vdui_t *, or nullptr + +vdui_t *hexapi get_widget_vdui(TWidget *f); + + +/// \defgroup VDRUN_ Batch decompilation bits +///@{ +#define VDRUN_NEWFILE 0x00000000 ///< Create a new file or overwrite existing file +#define VDRUN_APPEND 0x00000001 ///< Create a new file or append to existing file +#define VDRUN_ONLYNEW 0x00000002 ///< Fail if output file already exists +#define VDRUN_SILENT 0x00000004 ///< Silent decompilation +#define VDRUN_SENDIDB 0x00000008 ///< Send problematic databases to hex-rays.com +#define VDRUN_MAYSTOP 0x00000010 ///< The user can cancel decompilation +#define VDRUN_CMDLINE 0x00000020 ///< Called from ida's command line +#define VDRUN_STATS 0x00000040 ///< Print statistics into vd_stats.txt +#define VDRUN_LUMINA 0x00000080 ///< Use lumina server +#define VDRUN_PERF 0x00200000 ///< Print performance stats to ida.log +///@} + +/// Batch decompilation. +/// Decompile all or the specified functions +/// \return true if no internal error occurred and the user has not cancelled decompilation +/// \param outfile name of the output file +/// \param funcaddrs list of functions to decompile. +/// If nullptr or empty, then decompile all nonlib functions +/// \param flags \ref VDRUN_ + +bool hexapi decompile_many(const char *outfile, const eavec_t *funcaddrs, int flags); + + +/// Exception object: decompiler failure information +struct hexrays_failure_t +{ + merror_t code = MERR_OK; ///< \ref MERR_ + ea_t errea = BADADDR; ///< associated address + qstring str; ///< string information + hexrays_failure_t() {} + hexrays_failure_t(merror_t c, ea_t ea, const char *buf=nullptr) : code(c), errea(ea), str(buf) {} + hexrays_failure_t(merror_t c, ea_t ea, const qstring &buf) : code(c), errea(ea), str(buf) {} + qstring hexapi desc() const; + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; + +/// Exception object: decompiler exception +struct vd_failure_t : public std::exception +{ + hexrays_failure_t hf; + vd_failure_t() {} + vd_failure_t(merror_t code, ea_t ea, const char *buf=nullptr) : hf(code, ea, buf) {} + vd_failure_t(merror_t code, ea_t ea, const qstring &buf) : hf(code, ea, buf) {} + vd_failure_t(const hexrays_failure_t &_hf) : hf(_hf) {} + qstring desc() const { return hf.desc(); } +#ifndef SWIG + virtual const char *what() const noexcept override { return "decompilation failure"; } +#endif +#ifdef __GNUC__ + ~vd_failure_t() throw() {} +#endif + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; + +/// Exception object: decompiler internal error +struct vd_interr_t : public vd_failure_t +{ + vd_interr_t(ea_t ea, const qstring &buf) : vd_failure_t(MERR_INTERR, ea, buf) {} + vd_interr_t(ea_t ea, const char *buf) : vd_failure_t(MERR_INTERR, ea, buf) {} +}; + +/// Send the database to Hex-Rays. +/// This function sends the current database to the Hex-Rays server. +/// The database is sent in the compressed form over an encrypted (SSL) connection. +/// \param err failure description object. Empty hexrays_failure_t object can +/// be used if error information is not available. +/// \param silent if false, a dialog box will be displayed before sending the database. + +void hexapi send_database(const hexrays_failure_t &err, bool silent); + +/// Result of get_current_operand() +struct gco_info_t +{ + qstring name; ///< register or stkvar name + union + { + sval_t stkoff; ///< if stkvar, stack offset + int regnum; ///< if register, the register id + }; + int size; ///< operand size + int flags; +#define GCO_STK 0x0000 ///< a stack variable +#define GCO_REG 0x0001 ///< is register? otherwise a stack variable +#define GCO_USE 0x0002 ///< is source operand? +#define GCO_DEF 0x0004 ///< is destination operand? + bool is_reg() const { return (flags & GCO_REG) != 0; } + bool is_use() const { return (flags & GCO_USE) != 0; } + bool is_def() const { return (flags & GCO_DEF) != 0; } + + /// Append operand info to LIST. + /// This function converts IDA register number or stack offset to + /// a decompiler list. + /// \param list list to append to + /// \param mba microcode object + bool hexapi append_to_list(mlist_t *list, const mba_t *mba) const; + + /// Convert operand info to VIVL. + /// The returned VIVL can be used, for example, in a call of + /// get_valranges(). + vivl_t cvt_to_ivl() const + { + vivl_t ret; + if ( is_reg() ) + ret.set_reg(regnum, size); + else + ret.set_stkoff(stkoff, size); + return ret; + } +}; + +/// Get the instruction operand under the cursor. +/// This function determines the operand that is under the cursor in the active +/// disassembly listing. If the operand refers to a register or stack variable, +/// it returns true. +/// \param out[out] output buffer +bool hexapi get_current_operand(gco_info_t *out); + +void hexapi remitem(const citem_t *e); +//------------------------------------------------------------------------- +/// Ctree item code. At the beginning of this list there are expression +/// codes (cot_...), followed by statement codes (cit_...). +enum ctype_t +{ + cot_empty = 0, + cot_comma = 1, ///< x, y + cot_asg = 2, ///< x = y + cot_asgbor = 3, ///< x |= y + cot_asgxor = 4, ///< x ^= y + cot_asgband = 5, ///< x &= y + cot_asgadd = 6, ///< x += y + cot_asgsub = 7, ///< x -= y + cot_asgmul = 8, ///< x *= y + cot_asgsshr = 9, ///< x >>= y signed + cot_asgushr = 10, ///< x >>= y unsigned + cot_asgshl = 11, ///< x <<= y + cot_asgsdiv = 12, ///< x /= y signed + cot_asgudiv = 13, ///< x /= y unsigned + cot_asgsmod = 14, ///< x %= y signed + cot_asgumod = 15, ///< x %= y unsigned + cot_tern = 16, ///< x ? y : z + cot_lor = 17, ///< x || y + cot_land = 18, ///< x && y + cot_bor = 19, ///< x | y + cot_xor = 20, ///< x ^ y + cot_band = 21, ///< x & y + cot_eq = 22, ///< x == y int or fpu (see EXFL_FPOP) + cot_ne = 23, ///< x != y int or fpu (see EXFL_FPOP) + cot_sge = 24, ///< x >= y signed or fpu (see EXFL_FPOP) + cot_uge = 25, ///< x >= y unsigned + cot_sle = 26, ///< x <= y signed or fpu (see EXFL_FPOP) + cot_ule = 27, ///< x <= y unsigned + cot_sgt = 28, ///< x > y signed or fpu (see EXFL_FPOP) + cot_ugt = 29, ///< x > y unsigned + cot_slt = 30, ///< x < y signed or fpu (see EXFL_FPOP) + cot_ult = 31, ///< x < y unsigned + cot_sshr = 32, ///< x >> y signed + cot_ushr = 33, ///< x >> y unsigned + cot_shl = 34, ///< x << y + cot_add = 35, ///< x + y + cot_sub = 36, ///< x - y + cot_mul = 37, ///< x * y + cot_sdiv = 38, ///< x / y signed + cot_udiv = 39, ///< x / y unsigned + cot_smod = 40, ///< x % y signed + cot_umod = 41, ///< x % y unsigned + cot_fadd = 42, ///< x + y fp + cot_fsub = 43, ///< x - y fp + cot_fmul = 44, ///< x * y fp + cot_fdiv = 45, ///< x / y fp + cot_fneg = 46, ///< -x fp + cot_neg = 47, ///< -x + cot_cast = 48, ///< (type)x + cot_lnot = 49, ///< !x + cot_bnot = 50, ///< ~x + cot_ptr = 51, ///< *x, access size in 'ptrsize' + cot_ref = 52, ///< &x + cot_postinc = 53, ///< x++ + cot_postdec = 54, ///< x-- + cot_preinc = 55, ///< ++x + cot_predec = 56, ///< --x + cot_call = 57, ///< x(...) + cot_idx = 58, ///< x[y] + cot_memref = 59, ///< x.m + cot_memptr = 60, ///< x->m, access size in 'ptrsize' + cot_num = 61, ///< n + cot_fnum = 62, ///< fpc + cot_str = 63, ///< string constant (user representation) + cot_obj = 64, ///< obj_ea + cot_var = 65, ///< v + cot_insn = 66, ///< instruction in expression, internal representation only + cot_sizeof = 67, ///< sizeof(x) + cot_helper = 68, ///< arbitrary name + cot_type = 69, ///< arbitrary type + cot_last = cot_type, + cit_empty = 70, ///< instruction types start here + cit_block = 71, ///< block-statement: { ... } + cit_expr = 72, ///< expression-statement: expr; + cit_if = 73, ///< if-statement + cit_for = 74, ///< for-statement + cit_while = 75, ///< while-statement + cit_do = 76, ///< do-statement + cit_switch = 77, ///< switch-statement + cit_break = 78, ///< break-statement + cit_continue = 79, ///< continue-statement + cit_return = 80, ///< return-statement + cit_goto = 81, ///< goto-statement + cit_asm = 82, ///< asm-statement + cit_try = 83, ///< C++ try-statement + cit_throw = 84, ///< C++ throw-statement + cit_end +}; + +/// Negate a comparison operator. For example, \ref cot_sge becomes \ref cot_slt +ctype_t hexapi negated_relation(ctype_t op); +/// Swap a comparison operator. For example, \ref cot_sge becomes \ref cot_sle +ctype_t hexapi swapped_relation(ctype_t op); +/// Get operator sign. Meaningful for sign-dependent operators, like \ref cot_sdiv +type_sign_t hexapi get_op_signness(ctype_t op); +/// Convert plain operator into assignment operator. For example, \ref cot_add returns \ref cot_asgadd +ctype_t hexapi asgop(ctype_t cop); +/// Convert assignment operator into plain operator. For example, \ref cot_asgadd returns \ref cot_add +/// \return cot_empty is the input operator is not an assignment operator. +ctype_t hexapi asgop_revert(ctype_t cop); +/// Does operator use the 'x' field of cexpr_t? +inline bool op_uses_x(ctype_t op) { return (op >= cot_comma && op <= cot_memptr) || op == cot_sizeof; } +/// Does operator use the 'y' field of cexpr_t? +inline bool op_uses_y(ctype_t op) { return (op >= cot_comma && op <= cot_fdiv) || op == cot_idx; } +/// Does operator use the 'z' field of cexpr_t? +inline bool op_uses_z(ctype_t op) { return op == cot_tern; } +/// Is binary operator? +inline bool is_binary(ctype_t op) { return op_uses_y(op) && op != cot_tern; } // x,y +/// Is unary operator? +inline bool is_unary(ctype_t op) { return op >= cot_fneg && op <= cot_predec; } +/// Is comparison operator? +inline bool is_relational(ctype_t op) { return op >= cot_eq && op <= cot_ult; } +/// Is assignment operator? +inline bool is_assignment(ctype_t op) { return op >= cot_asg && op <= cot_asgumod; } +// Can operate on UDTs? +inline bool accepts_udts(ctype_t op) { return op == cot_asg || op == cot_comma || op > cot_last; } +/// Is pre/post increment/decrement operator? +inline bool is_prepost(ctype_t op) { return op >= cot_postinc && op <= cot_predec; } +/// Is commutative operator? +inline bool is_commutative(ctype_t op) +{ + return op == cot_bor + || op == cot_xor + || op == cot_band + || op == cot_add + || op == cot_mul + || op == cot_fadd + || op == cot_fmul + || op == cot_ne + || op == cot_eq; +} +/// Is additive operator? +inline bool is_additive(ctype_t op) +{ + return op == cot_add + || op == cot_sub + || op == cot_fadd + || op == cot_fsub; +} +/// Is multiplicative operator? +inline bool is_multiplicative(ctype_t op) +{ + return op == cot_mul + || op == cot_sdiv + || op == cot_udiv + || op == cot_fmul + || op == cot_fdiv; +} + +/// Is bit related operator? +inline bool is_bitop(ctype_t op) +{ + return op == cot_bor + || op == cot_xor + || op == cot_band + || op == cot_bnot; +} + +/// Is logical operator? +inline bool is_logical(ctype_t op) +{ + return op == cot_lor + || op == cot_land + || op == cot_lnot; +} + +/// Is loop statement code? +inline bool is_loop(ctype_t op) +{ + return op == cit_for + || op == cit_while + || op == cit_do; +} +/// Does a break statement influence the specified statement code? +inline bool is_break_consumer(ctype_t op) +{ + return is_loop(op) || op == cit_switch; +} + +/// Is Lvalue operator? +inline bool is_lvalue(ctype_t op) +{ + return op == cot_ptr // *x + || op == cot_idx // x[y] + || op == cot_memref // x.m + || op == cot_memptr // x->m + || op == cot_obj // v + || op == cot_var; // l +} + +/// Is the operator allowed on small structure or union? +inline bool accepts_small_udts(ctype_t op) +{ + return op == cot_asg + || op == cot_eq + || op == cot_ne + || op == cot_comma + || op == cot_tern + || (op > cot_last && op < cit_end); // any insn +} + +/// An immediate number +struct cnumber_t +{ + uint64 _value = 0; ///< its value + number_format_t nf; ///< how to represent it + cnumber_t(int _opnum=0) : nf(_opnum) {} + + /// Get text representation + /// \param vout output buffer + /// \param type number type + /// \param parent parent expression + /// \param nice_stroff out: printed as stroff expression + void hexapi print( + qstring *vout, + const tinfo_t &type, + const citem_t *parent=nullptr, + bool *nice_stroff=nullptr) const; + + /// Get value. + /// This function will properly extend the number sign to 64bits + /// depending on the type sign. + uint64 hexapi value(const tinfo_t &type) const; + + /// Assign new value + /// \param v new value + /// \param nbytes size of the new value in bytes + /// \param sign sign of the value + void hexapi assign(uint64 v, int nbytes, type_sign_t sign); + + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + DECLARE_COMPARISONS(cnumber_t); +}; + +/// Reference to a local variable +struct var_ref_t +{ + mba_t *mba; ///< pointer to the underlying micro array + int idx; ///< index into lvars_t + lvar_t &getv() const { return mba->vars[idx]; } + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + DECLARE_COMPARISONS(var_ref_t); +}; + +/// Vector of parents +typedef qvector citem_pointers_t; +typedef citem_pointers_t parents_t; + +/// Ctree maturity level. The level will increase +/// as we switch from one phase of ctree generation to the next one +enum ctree_maturity_t +{ + CMAT_ZERO, ///< does not exist + CMAT_BUILT, ///< just generated + CMAT_TRANS1, ///< applied first wave of transformations + CMAT_NICE, ///< nicefied expressions + CMAT_TRANS2, ///< applied second wave of transformations + CMAT_CPA, ///< corrected pointer arithmetic + CMAT_TRANS3, ///< applied third wave of transformations + CMAT_CASTED, ///< added necessary casts + CMAT_FINAL, ///< ready-to-use +}; + +//-------------------------------------------------------------------------- +/// Comment item preciser. +/// Item preciser is used to assign comments to ctree items +/// A ctree item may have several comments attached to it. For example, +/// an if-statement may have the following comments:
+///  if ( ... )    // cmt1
+///  {             // cmt2
+///  }             // cmt3
+///  else          // cmt4
+///  {                     -- usually the else block has a separate ea
+///  } 
+/// The first 4 comments will have the same ea. In order to denote the exact +/// line for the comment, we store the item_preciser along with ea. +enum item_preciser_t +{ + // inner comments (comments within an expression) + ITP_EMPTY, ///< nothing + ITP_ARG1, ///< , (64 entries are reserved for 64 call arguments) + ITP_ARG64 = ITP_ARG1+63, // , + ITP_BRACE1, // ( + ITP_INNER_LAST = ITP_BRACE1, + // outer comments + ITP_ASM, ///< __asm-line + ITP_ELSE, ///< else-line + ITP_DO, ///< do-line + ITP_SEMI, ///< semicolon + ITP_CURLY1, ///< { + ITP_CURLY2, ///< } + ITP_BRACE2, ///< ) + ITP_COLON, ///< : (label) + ITP_BLOCK1, ///< opening block comment. this comment is printed before the item + ///< (other comments are indented and printed after the item) + ITP_BLOCK2, ///< closing block comment. + ITP_TRY, ///< C++ try statement + ITP_CASE = 0x40000000, ///< bit for switch cases + ITP_SIGN = 0x20000000, ///< if this bit is set too, then we have a negative case value + // this is a hack, we better introduce special indexes for case values + // case value >= ITP_CASE will be processed incorrectly +}; +/// Ctree location. Used to denote comment locations. +struct treeloc_t +{ + ea_t ea; + item_preciser_t itp; + bool operator < (const treeloc_t &r) const + { + return ea < r.ea + || (ea == r.ea && itp < r.itp); + } + bool operator == (const treeloc_t &r) const + { + return ea == r.ea && itp == r.itp; + } + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; + +/// Comment retrieval type. +/// Ctree remembers what comments have already been retrieved. +/// This is done because our mechanism of item_precisers is still +/// not perfect and in theory some listing lines cannot be told +/// apart. To avoid comment duplication, we remember if a comment +/// has already been used or not. +enum cmt_retrieval_type_t +{ + RETRIEVE_ONCE, ///< Retrieve comment if it has not been used yet + RETRIEVE_ALWAYS, ///< Retrieve comment even if it has been used +}; + +/// Ctree item comment. +/// For each comment we remember its body and the fact of its retrieval +struct citem_cmt_t : public qstring +{ + mutable bool used = false; ///< the comment has been retrieved? + citem_cmt_t() {} + citem_cmt_t(const char *s) : qstring(s) {} +}; + +// Comments are attached to tree locations: +typedef std::map user_cmts_t; + +/// Generic ctree item locator. It can be used for instructions and some expression +/// types. However, we need more precise locators for other items (e.g. for numbers) +struct citem_locator_t +{ + ea_t ea; ///< citem address + ctype_t op; ///< citem operation + citem_locator_t() = delete; +public: + citem_locator_t(ea_t _ea, ctype_t _op) : ea(_ea), op(_op) {} + citem_locator_t(const citem_t *i); + DECLARE_COMPARISONS(citem_locator_t); + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; + +// citem_t::iflags are attached to (ea,op) pairs +typedef std::map user_iflags_t; + +// union field selections +// they are represented as a vector of integers. each integer represents the +// number of union field (0 means the first union field, etc) +// the size of this vector is equal to the number of nested unions in the selection. +typedef std::map user_unions_t; + +//-------------------------------------------------------------------------- +struct bit_bound_t +{ + int16 nbits; // total number of non-zero bits. we cannot guarantee that + // they are zero. example: a random "int var" has nbits==32 + int16 sbits; // number of sign bits (they can be either 0 or 1, all of them) + // if bits are known to be zeroes, they are not taken into account here + // (in this case nbits should be reduced) + // if bits are unknown and can be anything, they cannot be included + // in sbits. + // sbits==1 is a special case and should not be used + bit_bound_t(int n=0, int s=0) : nbits(n), sbits(s) {} +}; + +//-------------------------------------------------------------------------- +/// Basic ctree item. This is an abstract class (but we don't use virtual +/// functions in ctree, so the compiler will not disallow you to create citem_t +/// instances). However, items of pure citem_t type must never be created. +/// Two classes, cexpr_t and cinsn_t are derived from it. +struct citem_t +{ + ea_t ea = BADADDR; ///< address that corresponds to the item. may be BADADDR + ctype_t op; ///< item type + int label_num = -1; ///< label number. -1 means no label. items of the expression + ///< types (cot_...) should not have labels at the final maturity + ///< level, but at the intermediate levels any ctree item + ///< may have a label. Labels must be unique. Usually + ///< they correspond to the basic block numbers. + mutable int index = -1; ///< an index in cfunc_t::treeitems. + ///< meaningful only after print_func() + citem_t(ctype_t o=cot_empty) : op(o) {} + /// Swap two citem_t + void swap(citem_t &r) + { + std::swap(ea, r.ea); + std::swap(op, r.op); + std::swap(label_num, r.label_num); + } + /// Is an expression? + bool is_expr() const { return op <= cot_last; } + /// Does the item contain an expression? + bool hexapi contains_expr(const cexpr_t *e) const; + /// Does the item contain a label? + bool hexapi contains_label() const; + /// Find parent of the specified item. + /// \param item Item to find the parent of. The search will be performed + /// among the children of the item pointed by \c this. + /// \return nullptr if not found + const citem_t *hexapi find_parent_of(const citem_t *item) const; + citem_t *find_parent_of(const citem_t *item) + { return CONST_CAST(citem_t*)((CONST_CAST(const citem_t*)(this))->find_parent_of(item)); } + citem_t *hexapi find_closest_addr(ea_t _ea); + void print1(qstring *vout, const cfunc_t *func) const; + ~citem_t() + { + remitem(this); + } + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; +DECLARE_TYPE_AS_MOVABLE(citem_t); + +/// Ctree item: expression. +/// Depending on the exact expression item type, various fields of this structure are used. +struct cexpr_t : public citem_t +{ + union + { + cnumber_t *n; ///< used for \ref cot_num + fnumber_t *fpc; ///< used for \ref cot_fnum + struct + { + union + { + var_ref_t v; ///< used for \ref cot_var + ea_t obj_ea; ///< used for \ref cot_obj + }; + int refwidth; ///< how many bytes are accessed? (-1: none) + }; + struct + { + cexpr_t *x; ///< the first operand of the expression + union + { + cexpr_t *y; ///< the second operand of the expression + carglist_t *a;///< argument list (used for \ref cot_call) + uint32 m; ///< member offset (used for \ref cot_memptr, \ref cot_memref) + ///< for unions, the member number + }; + union + { + cexpr_t *z; ///< the third operand of the expression + int ptrsize; ///< memory access size (used for \ref cot_ptr, \ref cot_memptr) + }; + }; + cinsn_t *insn; ///< an embedded statement, they are prohibited + ///< at the final maturity stage (\ref CMAT_FINAL) + char *helper; ///< helper name (used for \ref cot_helper) + char *string; ///< utf8 string constant, user representation (used for \ref cot_str) + }; + tinfo_t type; ///< expression type. must be carefully maintained + uint32 exflags = 0; ///< \ref EXFL_ +/// \defgroup EXFL_ Expression attributes +/// Used in cexpr_t::exflags +///@{ +#define EXFL_CPADONE 0x0001 ///< pointer arithmetic correction done +#define EXFL_LVALUE 0x0002 ///< expression is lvalue even if it doesn't look like it +#define EXFL_FPOP 0x0004 ///< floating point operation +#define EXFL_ALONE 0x0008 ///< standalone helper +#define EXFL_CSTR 0x0010 ///< string literal +#define EXFL_PARTIAL 0x0020 ///< type of the expression is considered partial +#define EXFL_UNDEF 0x0040 ///< expression uses undefined value +#define EXFL_JUMPOUT 0x0080 ///< jump out-of-function +#define EXFL_VFTABLE 0x0100 ///< is ptr to vftable (used for \ref cot_memptr, \ref cot_memref) +#define EXFL_ALL 0x01FF ///< all currently defined bits +///@} + /// Pointer arithmetic correction done for this expression? + bool cpadone() const { return (exflags & EXFL_CPADONE) != 0; } + bool is_odd_lvalue() const { return (exflags & EXFL_LVALUE) != 0; } + bool is_fpop() const { return (exflags & EXFL_FPOP) != 0; } + bool is_cstr() const { return (exflags & EXFL_CSTR) != 0; } + bool is_type_partial() const { return (exflags & EXFL_PARTIAL) != 0; } + bool is_undef_val() const { return (exflags & EXFL_UNDEF) != 0; } + bool is_jumpout() const { return (exflags & EXFL_JUMPOUT) != 0; } + bool is_vftable() const { return (exflags & EXFL_VFTABLE) != 0; } + + + void set_cpadone() { exflags |= EXFL_CPADONE; } + void set_vftable() { exflags |= EXFL_VFTABLE; } + void set_type_partial(bool val = true) + { + if ( val ) + exflags |= EXFL_PARTIAL; + else + exflags &= ~EXFL_PARTIAL; + } + + cexpr_t() : x(nullptr), y(nullptr), z(nullptr) {} + cexpr_t(ctype_t cexpr_op, cexpr_t *_x, cexpr_t *_y=nullptr, cexpr_t *_z=nullptr) + : citem_t(cexpr_op), x(_x), y(_y), z(_z) {} + cexpr_t(mba_t *mba, const lvar_t &v); + cexpr_t(const cexpr_t &r) : citem_t() { *this = r; } + void swap(cexpr_t &r) + { + citem_t &ci = *this; + ci.swap(r); + std::swap(x, r.x); + std::swap(y, r.y); + std::swap(z, r.z); + type.swap(r.type); + std::swap(exflags, r.exflags); + } + cexpr_t &operator=(const cexpr_t &r) { return assign(r); } + cexpr_t &hexapi assign(const cexpr_t &r); + DECLARE_COMPARISONS(cexpr_t); + ~cexpr_t() { cleanup(); } + + /// Replace the expression. + /// The children of the expression are abandoned (not freed). + /// The expression pointed by 'r' is moved to 'this' expression + /// \param r the source expression. It is deleted after being copied + void hexapi replace_by(cexpr_t *r); + + /// Cleanup the expression. + /// This function properly deletes all children and sets the item type to cot_empty. + void hexapi cleanup(); + + /// Assign a number to the expression. + /// \param func current function + /// \param value number value + /// \param nbytes size of the number in bytes + /// \param sign number sign + void hexapi put_number(cfunc_t *func, uint64 value, int nbytes, type_sign_t sign=no_sign); + + /// Print expression into one line. + /// \param vout output buffer + /// \param func parent function. This argument is used to find out the referenced variable names. + void hexapi print1(qstring *vout, const cfunc_t *func) const; + + /// Calculate the type of the expression. + /// Use this function to calculate the expression type when a new expression is built + /// \param recursive if true, types of all children expression will be calculated + /// before calculating our type + void hexapi calc_type(bool recursive); + + /// Compare two expressions. + /// This function tries to compare two expressions in an 'intelligent' manner. + /// For example, it knows about commutitive operators and can ignore useless casts. + /// \param r the expression to compare against the current expression + /// \return true expressions can be considered equal + bool hexapi equal_effect(const cexpr_t &r) const; + + /// Verify if the specified item is our parent. + /// \param parent possible parent item + /// \return true if the specified item is our parent + bool hexapi is_child_of(const citem_t *parent) const; + + /// Check if the expression contains the specified operator. + /// \param needed_op operator code to search for + /// \param times how many times the operator code should be present + /// \return true if the expression has at least TIMES children with NEEDED_OP + bool hexapi contains_operator(ctype_t needed_op, int times=1) const; + + /// Does the expression contain a comma operator? + bool contains_comma(int times=1) const { return contains_operator(cot_comma, times); } + /// Does the expression contain an embedded statement operator? + bool contains_insn(int times=1) const { return contains_operator(cot_insn, times); } + /// Does the expression contain an embedded statement operator or a label? + bool contains_insn_or_label() const { return contains_insn() || contains_label(); } + /// Does the expression contain a comma operator or an embedded statement operator or a label? + bool contains_comma_or_insn_or_label(int maxcommas=1) const { return contains_comma(maxcommas) || contains_insn_or_label(); } + /// Is nice expression? + /// Nice expressions do not contain comma operators, embedded statements, or labels. + bool is_nice_expr() const { return !contains_comma_or_insn_or_label(); } + /// Is nice condition?. + /// Nice condition is a nice expression of the boolean type. + bool is_nice_cond() const { return is_nice_expr() && type.is_bool(); } + /// Is call object? + /// \return true if our expression is the call object of the specified parent expression. + bool is_call_object_of(const citem_t *parent) const { return parent != nullptr && parent->op == cot_call && ((cexpr_t*)parent)->x == this; } + /// Is call argument? + /// \return true if our expression is a call argument of the specified parent expression. + bool is_call_arg_of(const citem_t *parent) const { return parent != nullptr && parent->op == cot_call && ((cexpr_t*)parent)->x != this; } + /// Get expression sign + type_sign_t get_type_sign() const { return type.get_sign(); } + /// Is expression unsigned? + bool is_type_unsigned() const { return type.is_unsigned(); } + /// Is expression signed? + bool is_type_signed() const { return type.is_signed(); } + /// Get max number of bits that can really be used by the expression. + /// For example, x % 16 can yield only 4 non-zero bits, higher bits are zero + bit_bound_t hexapi get_high_nbit_bound() const; + /// Get min number of bits that are certainly required to represent the expression. + /// For example, constant 16 always uses 5 bits: 10000. + int hexapi get_low_nbit_bound() const; + /// Check if the expression requires an lvalue. + /// \param child The function will check if this child of our expression must be an lvalue. + /// \return true if child must be an lvalue. + bool hexapi requires_lvalue(const cexpr_t *child) const; + /// Check if the expression has side effects. + /// Calls, pre/post inc/dec, and assignments have side effects. + bool hexapi has_side_effects() const; + /// Does the expression look like a boolean expression? + /// In other words, its possible values are only 0 and 1. + bool like_boolean() const; + /// Check if the expression if aliasable. + /// Simple registers and non-aliasble stack slots return false. + bool is_aliasable() const; + /// Get numeric value of the expression. + /// This function can be called only on cot_num expressions! + uint64 numval() const + { + QASSERT(50071, op == cot_num); + return n->value(type); + } + /// Check if the expression is a number with the specified value. + bool is_const_value(uint64 _v) const + { + return op == cot_num && numval() == _v; + } + /// Check if the expression is a negative number. + bool is_negative_const() const + { + return op == cot_num && int64(numval()) < 0; + } + /// Check if the expression is a non-negative number. + bool is_non_negative_const() const + { + return op == cot_num && int64(numval()) >= 0; + } + /// Check if the expression is a non-zero number. + bool is_non_zero_const() const + { + return op == cot_num && numval() != 0; + } + /// Check if the expression is a zero. + bool is_zero_const() const { return is_const_value(0); } + /// Does the PARENT need the expression value + bool is_value_used(const citem_t *parent) const; + /// Get expression value. + /// \param out Pointer to the variable where the expression value is returned. + /// \return true if the expression is a number. + bool get_const_value(uint64 *out) const + { + if ( op == cot_num ) + { + if ( out != nullptr ) + *out = numval(); + return true; + } + return false; + } + /// May the expression be a pointer? + bool hexapi maybe_ptr() const; + + /// Find pointer or array child. + cexpr_t *get_ptr_or_array() + { + if ( x->type.is_ptr_or_array() ) + return x; + if ( y->type.is_ptr_or_array() ) + return y; + return nullptr; + } + /// Find the child with the specified operator. + const cexpr_t *find_op(ctype_t _op) const + { + if ( x->op == _op ) + return x; + if ( y->op == _op ) + return y; + return nullptr; + } + cexpr_t *find_op(ctype_t _op) + { + return (cexpr_t *)((const cexpr_t *)this)->find_op(_op); + } + + /// Find the operand with a numeric value + const cexpr_t *find_num_op() const { return find_op(cot_num); } + cexpr_t *find_num_op() { return find_op(cot_num); } + /// Find the pointer operand. + /// This function returns the pointer operand for binary expressions. + const cexpr_t *find_ptr_or_array(bool remove_eqsize_casts) const; + /// Get the other operand. + /// This function returns the other operand (not the specified one) + /// for binary expressions. + const cexpr_t *theother(const cexpr_t *what) const { return what == x ? y : x; } + cexpr_t *theother(const cexpr_t *what) { return what == x ? y : x; } + + // these are inline functions, see below + bool get_1num_op(cexpr_t **o1, cexpr_t **o2); + bool get_1num_op(const cexpr_t **o1, const cexpr_t **o2) const; + + const char *hexapi dstr() const; +}; +DECLARE_TYPE_AS_MOVABLE(cexpr_t); + +/// Statement with an expression. +/// This is a base class for various statements with expressions. +struct ceinsn_t +{ + cexpr_t expr; ///< Expression of the statement + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; +DECLARE_TYPE_AS_MOVABLE(ceinsn_t); + +/// Should curly braces be printed? +enum use_curly_t +{ + CALC_CURLY_BRACES, ///< print curly braces if necessary + NO_CURLY_BRACES, ///< don't print curly braces + USE_CURLY_BRACES, ///< print curly braces without any checks +}; + +/// If statement +struct cif_t : public ceinsn_t +{ + cinsn_t *ithen = nullptr; ///< Then-branch of the if-statement + cinsn_t *ielse = nullptr; ///< Else-branch of the if-statement. May be nullptr. + cif_t() {} + cif_t(const cif_t &r) : ceinsn_t(), ithen(nullptr), ielse(nullptr) { *this = r; } + cif_t &operator=(const cif_t &r) { return assign(r); } + cif_t &hexapi assign(const cif_t &r); + DECLARE_COMPARISONS(cif_t); + ~cif_t() { cleanup(); } + void cleanup(); +}; + +/// Base class for loop statements +struct cloop_t : public ceinsn_t +{ + cinsn_t *body = nullptr; + cloop_t(cinsn_t *b=nullptr) : body(b) {} + cloop_t(const cloop_t &r) : ceinsn_t() { *this = r; } + cloop_t &operator=(const cloop_t &r) { return assign(r); } + cloop_t &hexapi assign(const cloop_t &r); + ~cloop_t() { cleanup(); } + void cleanup(); +}; + +/// For-loop +struct cfor_t : public cloop_t +{ + cexpr_t init; ///< Initialization expression + cexpr_t step; ///< Step expression + DECLARE_COMPARISONS(cfor_t); +}; + +/// While-loop +struct cwhile_t : public cloop_t +{ + DECLARE_COMPARISONS(cwhile_t); +}; + +/// Do-loop +struct cdo_t : public cloop_t +{ + DECLARE_COMPARISONS(cdo_t); +}; + +/// Return statement +struct creturn_t : public ceinsn_t +{ + DECLARE_COMPARISONS(creturn_t); +}; + +/// Goto statement +struct cgoto_t +{ + int label_num; ///< Target label number + void print(const citem_t *parent, int indent, vc_printer_t &vp) const; + DECLARE_COMPARISONS(cgoto_t); + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; + +/// asm statement +struct casm_t : public eavec_t +{ + casm_t(ea_t ea) { push_back(ea); } + casm_t(const casm_t &r) : eavec_t(eavec_t(r)) {} + DECLARE_COMPARISONS(casm_t); + void print(const citem_t *parent, int indent, vc_printer_t &vp) const; + bool one_insn() const { return size() == 1; } + void genasm(qstring *buf, ea_t ea) const; +}; + +/// Vector of pointers to statements. +typedef qvector cinsnptrvec_t; + +/// Ctree item: statement. +/// Depending on the exact statement type, various fields of the union are used. +struct cinsn_t : public citem_t +{ + union + { + cblock_t *cblock; ///< details of block-statement + cexpr_t *cexpr; ///< details of expression-statement + cif_t *cif; ///< details of if-statement + cfor_t *cfor; ///< details of for-statement + cwhile_t *cwhile; ///< details of while-statement + cdo_t *cdo; ///< details of do-statement + cswitch_t *cswitch; ///< details of switch-statement + creturn_t *creturn; ///< details of return-statement + cgoto_t *cgoto; ///< details of goto-statement + casm_t *casm; ///< details of asm-statement + ctry_t *ctry; ///< details of try-statement + cthrow_t *cthrow; ///< details of throw-statement + }; + + cinsn_t() { zero(); } + cinsn_t(const cinsn_t &r) : citem_t(cit_empty) { *this = r; } + void swap(cinsn_t &r) { citem_t::swap(r); std::swap(cblock, r.cblock); } + cinsn_t &operator=(const cinsn_t &r) { return assign(r); } + cinsn_t &hexapi assign(const cinsn_t &r); + DECLARE_COMPARISONS(cinsn_t); + ~cinsn_t() { cleanup(); } + + /// Replace the statement. + /// The children of the statement are abandoned (not freed). + /// The statement pointed by 'r' is moved to 'this' statement + /// \param r the source statement. It is deleted after being copied + void hexapi replace_by(cinsn_t *r); + + /// Cleanup the statement. + /// This function properly deletes all children and sets the item type to cit_empty. + void hexapi cleanup(); + + /// Overwrite with zeroes without cleaning memory or deleting children + void zero() { op = cit_empty; cblock = nullptr; } + + /// Create a new statement. + /// The current statement must be a block. The new statement will be appended to it. + /// \param insn_ea statement address + cinsn_t &hexapi new_insn(ea_t insn_ea); + + /// Create a new if-statement. + /// The current statement must be a block. The new statement will be appended to it. + /// \param cnd if condition. It will be deleted after being copied. + cif_t &hexapi create_if(cexpr_t *cnd); + + /// Print the statement into many lines. + /// \param indent indention (number of spaces) for the statement + /// \param vp printer helper class which will receive the generated text. + /// \param use_curly if the statement is a block, how should curly braces be printed. + void hexapi print(int indent, vc_printer_t &vp, use_curly_t use_curly=CALC_CURLY_BRACES) const; + + /// Print the statement into one line. + /// Currently this function is not available. + /// \param vout output buffer + /// \param func parent function. This argument is used to find out the referenced variable names. + void hexapi print1(qstring *vout, const cfunc_t *func) const; + + /// Check if the statement passes execution to the next statement. + /// \return false if the statement breaks the control flow (like goto, return, etc) + bool hexapi is_ordinary_flow() const; + + /// Check if the statement contains a statement of the specified type. + /// \param type statement opcode to look for + /// \param times how many times TYPE should be present + /// \return true if the statement has at least TIMES children with opcode == TYPE + bool hexapi contains_insn(ctype_t type, int times=1) const; + + /// Collect free \c break statements. + /// This function finds all free \c break statements within the current statement. + /// A \c break statement is free if it does not have a loop or switch parent that + /// that is also within the current statement. + /// \param breaks pointer to the variable where the vector of all found free + /// \c break statements is returned. This argument can be nullptr. + /// \return true if some free \c break statements have been found + bool hexapi collect_free_breaks(cinsnptrvec_t *breaks); + + /// Collect free \c continue statements. + /// This function finds all free \c continue statements within the current statement. + /// A \c continue statement is free if it does not have a loop parent that + /// that is also within the current statement. + /// \param continues pointer to the variable where the vector of all found free + /// \c continue statements is returned. This argument can be nullptr. + /// \return true if some free \c continue statements have been found + bool hexapi collect_free_continues(cinsnptrvec_t *continues); + + /// Check if the statement has free \c break statements. + bool contains_free_break() const { return CONST_CAST(cinsn_t*)(this)->collect_free_breaks(nullptr); } + /// Check if the statement has free \c continue statements. + bool contains_free_continue() const { return CONST_CAST(cinsn_t*)(this)->collect_free_continues(nullptr); } + + const char *hexapi dstr() const; +}; +DECLARE_TYPE_AS_MOVABLE(cinsn_t); + +typedef qlist cinsn_list_t; + +/// Compound statement (curly braces) +struct cblock_t : public cinsn_list_t // we need list to be able to manipulate +{ // its elements freely + DECLARE_COMPARISONS(cblock_t); +}; + +/// Function argument +struct carg_t : public cexpr_t +{ + bool is_vararg = false; ///< is a vararg (matches ...) + tinfo_t formal_type; ///< formal parameter type (if known) + void consume_cexpr(cexpr_t *e) + { + e->swap(*this); + delete e; + } + DECLARE_COMPARISONS(carg_t) + { + return cexpr_t::compare(r); + } +}; +DECLARE_TYPE_AS_MOVABLE(carg_t); + +/// Function argument list +struct carglist_t : public qvector +{ + tinfo_t functype; ///< function object type + int flags = 0; ///< call flags +#define CFL_FINAL 0x0001 ///< call type is final, should not be changed +#define CFL_HELPER 0x0002 ///< created from a decompiler helper function +#define CFL_NORET 0x0004 ///< call does not return + carglist_t() {} + carglist_t(const tinfo_t &ftype, int fl = 0) : functype(ftype), flags(fl) {} + DECLARE_COMPARISONS(carglist_t); + void print(qstring *vout, const cfunc_t *func) const; + int print(int curpos, vc_printer_t &vp) const; +}; + +/// Switch case. Usually cinsn_t is a block +struct ccase_t : public cinsn_t +{ + uint64vec_t values; ///< List of case values. + ///< if empty, then 'default' case + DECLARE_COMPARISONS(ccase_t); + void set_insn(cinsn_t *i); // deletes 'i' + size_t size() const { return values.size(); } + const uint64 &value(int i) const { return values[i]; } +}; +DECLARE_TYPE_AS_MOVABLE(ccase_t); + +/// Vector of switch cases +struct ccases_t : public qvector +{ + DECLARE_COMPARISONS(ccases_t); + int find_value(uint64 v) const; +}; + +/// Switch statement +struct cswitch_t : public ceinsn_t +{ + cnumber_t mvnf; ///< Maximal switch value and number format + ccases_t cases; ///< Switch cases: values and instructions + DECLARE_COMPARISONS(cswitch_t); +}; + +/// Catch expression +struct catchexpr_t +{ + cexpr_t obj; ///< the caught object. if obj.op==cot_empty, no object. + ///< ideally, obj.op==cot_var + qstring fake_type; ///< if not empty, type of the caught object. + ///< ideally, obj.type should be enough. however, in some + ///< cases the detailed type info is not available. + DECLARE_COMPARISONS(catchexpr_t); + void swap(catchexpr_t &r) + { + obj.swap(r.obj); + fake_type.swap(r.fake_type); + } + bool is_catch_all() const { return obj.op == cot_empty && fake_type.empty(); } +}; +DECLARE_TYPE_AS_MOVABLE(catchexpr_t); +typedef qvector catchexprs_t; + +/// Catch clause: "catch ( type obj )" +struct ccatch_t : public cblock_t +{ + catchexprs_t exprs; + DECLARE_COMPARISONS(ccatch_t); + bool is_catch_all() const { return exprs.empty(); } + void swap(ccatch_t &r) + { + exprs.swap(r.exprs); + cblock_t *cb = this; + cb->swap(r); + } +}; +typedef qvector ccatchvec_t; + +/// C++ Try statement. +/// This structure is also used to represent wind statements. +struct ctry_t : public cblock_t +{ + ccatchvec_t catchs; ///< "catch all", if present, must be the last element. + ///< wind-statements must have "catch all" and nothing else. + size_t old_state = 0; ///< old state number (internal, MSVC related) + size_t new_state = 0; ///< new state number (internal, MSVC related) + + /// Is C++ wind statement? (not part of the C++ language) + /// MSVC generates code like the following to keep track of constructed + /// objects and destroy them upon an exception. Example: + /// + /// // an object is constructed at this point + /// __wind + /// { + /// // some other code that may throw an exception + /// } + /// __unwind + /// { + /// // this code is executed only if there was an exception + /// // in the __wind block. normally here we destroy the object + /// // after that the exception is passed to the + /// // exception handler, regular control flow is interrupted here. + /// } + /// // regular logic continues here, if there were no exceptions + /// // also the object's destructor is called + bool is_wind = false; // if false, then try/catch + + DECLARE_COMPARISONS(ctry_t); + void print(const citem_t *parent, int indent, vc_printer_t &vp) const; +}; + +/// Throw statement +struct cthrow_t : public ceinsn_t +{ + DECLARE_COMPARISONS(cthrow_t); +}; + +//--------------------------------------------------------------------------- +/// Additional position information for cblocks +struct cblock_pos_t +{ + cblock_t *blk; // cinsn_t::cblock or cinsn_t::ctry or cinsn_t::ctry->catchs[x] + cblock_t::iterator p; // iterator pointing to the current item + bool is_first_insn() const { return p == blk->begin(); } + cinsn_t *insn() const { return &*p; } + cinsn_t *prev_insn() { --p; return insn(); } +}; +DECLARE_TYPE_AS_MOVABLE(cblock_pos_t); +typedef qvector cblock_posvec_t; + +/// A generic helper class that is used for ctree traversal. +/// When traversing the ctree, the currently visited ctree item and its children +/// can be freely modified without interrupting the traversal. However, if a +/// parent of the visited item is modified, the traversal must be immediately +/// stopped by returning a non-zero value. +struct ctree_visitor_t +{ + int cv_flags; ///< \ref CV_ +/// \defgroup CV_ Ctree visitor property bits +/// Used in ctree_visitor_t::cv_flags +///@{ +#define CV_FAST 0x0000 ///< do not maintain parent information +#define CV_PRUNE 0x0001 ///< this bit is set by visit...() to prune the walk +#define CV_PARENTS 0x0002 ///< maintain parent information +#define CV_POST 0x0004 ///< call the leave...() functions +#define CV_RESTART 0x0008 ///< restart enumeration at the top expr (apply_to_exprs) +#define CV_INSNS 0x0010 ///< visit only statements, prune all expressions + ///< do not use before the final ctree maturity because + ///< expressions may contain statements at intermediate + ///< stages (see cot_insn). Otherwise you risk missing + ///< statements embedded into expressions. +///@} + /// Should the parent information by maintained? + bool maintain_parents() const { return (cv_flags & CV_PARENTS) != 0; } + /// Should the traversal skip the children of the current item? + bool must_prune() const { return (cv_flags & CV_PRUNE) != 0; } + /// Should the traversal restart? + bool must_restart() const { return (cv_flags & CV_RESTART) != 0; } + /// Should the leave...() functions be called? + bool is_postorder() const { return (cv_flags & CV_POST) != 0; } + /// Should all expressions be automatically pruned? + bool only_insns() const { return (cv_flags & CV_INSNS) != 0; } + /// Prune children. + /// This function may be called by a visitor() to skip all children of the current item. + void prune_now() { cv_flags |= CV_PRUNE; } + /// Do not prune children. This is an internal function, no need to call it. + void clr_prune() { cv_flags &= ~CV_PRUNE; } + /// Restart the travesal. Meaningful only in apply_to_exprs() + void set_restart() { cv_flags |= CV_RESTART; } + /// Do not restart. This is an internal function, no need to call it. + void clr_restart() { cv_flags &= ~CV_RESTART; } + + parents_t parents; ///< Vector of parents of the current item + cblock_posvec_t bposvec; ///< Vector of block positions. + ///< Only cit_block and cit_try parents have + ///< the corresponding element in this vector. + + /// Constructor. + /// This constructor can be used with CV_FAST, CV_PARENTS + /// combined with CV_POST, CV_ONLYINS + ctree_visitor_t(int _flags) : cv_flags(_flags) {} + + virtual ~ctree_visitor_t() {} + /// Traverse ctree. + /// The traversal will start at the specified item and continue until + /// of one the visit_...() functions return a non-zero value. + /// \param item root of the ctree to traverse + /// \param parent parent of the specified item. can be specified as nullptr. + /// \return 0 or a non-zero value returned by a visit_...() function + int hexapi apply_to(citem_t *item, citem_t *parent); + + /// Traverse only expressions. + /// The traversal will start at the specified item and continue until + /// of one the visit_...() functions return a non-zero value. + /// \param item root of the ctree to traverse + /// \param parent parent of the specified item. can be specified as nullptr. + /// \return 0 or a non-zero value returned by a visit_...() function + int hexapi apply_to_exprs(citem_t *item, citem_t *parent); + + /// Get parent of the current item as an item (statement or expression) + citem_t *parent_item() + { + return !parents.empty() ? parents.back() : nullptr; + } + + /// Get parent of the current item as an expression + cexpr_t *parent_expr() + { + citem_t *item = parent_item(); + return item != nullptr && item->is_expr() ? (cexpr_t *)item : nullptr; + } + + /// Get parent of the current item as a statement + cinsn_t *parent_insn() + { + citem_t *item = parent_item(); + return item != nullptr && !item->is_expr() ? (cinsn_t *)item : nullptr; + } + + // the following functions are redefined by the derived class + // in order to perform the desired actions during the traversal + + /// Visit a statement. + /// This is a visitor function which should be overridden by a derived + /// class to do some useful work. + /// This visitor performs pre-order traserval, i.e. an item is visited before + /// its children. + /// \return 0 to continue the traversal, nonzero to stop. + virtual int idaapi visit_insn(cinsn_t *) { return 0; } + + /// Visit an expression. + /// This is a visitor function which should be overridden by a derived + /// class to do some useful work. + /// This visitor performs pre-order traserval, i.e. an item is visited before + /// its children. + /// \return 0 to continue the traversal, nonzero to stop. + virtual int idaapi visit_expr(cexpr_t *) { return 0; } + + /// Visit a statement after having visited its children. + /// This is a visitor function which should be overridden by a derived + /// class to do some useful work. + /// This visitor performs post-order traserval, i.e. an item is visited after + /// its children. + /// \return 0 to continue the traversal, nonzero to stop. + virtual int idaapi leave_insn(cinsn_t *) { return 0; } + + /// Visit an expression after having visited its children. + /// This is a visitor function which should be overridden by a derived + /// class to do some useful work. + /// This visitor performs post-order traserval, i.e. an item is visited after + /// its children. + /// \return 0 to continue the traversal, nonzero to stop. + virtual int idaapi leave_expr(cexpr_t *) { return 0; } + + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; + +/// A helper ctree traversal class that maintains parent information +struct ctree_parentee_t : public ctree_visitor_t +{ + ctree_parentee_t(bool post=false) + : ctree_visitor_t((post ? CV_POST : 0)|CV_PARENTS) {} + + /// Recalculate type of parent nodes. + /// If a node type has been changed, the visitor must recalculate + /// all parent types, otherwise the ctree becomes inconsistent. + /// If during this recalculation a parent node is added/deleted, + /// this function returns true. In this case the traversal must be + /// stopped because the information about parent nodes is stale. + /// \return false-ok to continue the traversal, true-must stop. + bool hexapi recalc_parent_types(); +}; + +/// Class to traverse the whole function. +struct cfunc_parentee_t : public ctree_parentee_t +{ + cfunc_t *func; ///< Pointer to current function + cfunc_parentee_t(cfunc_t *f, bool post=false) + : ctree_parentee_t(post), func(f) {} + + /// Calculate rvalue type. + /// This function tries to determine the type of the specified item + /// based on its context. For example, if the current expression is the + /// right side of an assignment operator, the type + /// of its left side will be returned. This function can be used to determine the 'best' + /// type of the specified expression. + /// \param[in] e expression to determine the desired type + /// \param[out] target 'best' type of the expression will be returned here + /// \return false if failed + bool hexapi calc_rvalue_type(tinfo_t *target, const cexpr_t *e); +}; + +//--------------------------------------------------------------------------- +/// Invisible COLOR_ADDR tags in the output text are used to refer to ctree items and variables +struct ctree_anchor_t +{ + uval_t value = BADADDR; +#define ANCHOR_INDEX 0x1FFFFFFF +#define ANCHOR_MASK 0xC0000000 +#define ANCHOR_CITEM 0x00000000 ///< c-tree item +#define ANCHOR_LVAR 0x40000000 ///< declaration of local variable +#define ANCHOR_ITP 0x80000000 ///< item type preciser +#define ANCHOR_BLKCMT 0x20000000 ///< block comment (for ctree items) + int get_index() const { return value & ANCHOR_INDEX; } + item_preciser_t get_itp() const { return item_preciser_t(value & ~ANCHOR_ITP); } + bool is_valid_anchor() const { return value != BADADDR; } + bool is_citem_anchor() const { return (value & ANCHOR_MASK) == ANCHOR_CITEM; } + bool is_lvar_anchor() const { return (value & ANCHOR_MASK) == ANCHOR_LVAR; } + bool is_itp_anchor() const { return (value & ANCHOR_ITP) != 0; } + bool is_blkcmt_anchor() const { return (value & ANCHOR_BLKCMT) != 0; } +}; + +/// Type of the cursor item. +enum cursor_item_type_t +{ + VDI_NONE, ///< undefined + VDI_EXPR, ///< c-tree item + VDI_LVAR, ///< declaration of local variable + VDI_FUNC, ///< the function itself (the very first line with the function prototype) + VDI_TAIL, ///< cursor is at (beyond) the line end (commentable line) +}; + +/// Cursor item. +/// Information about the item under the cursor +struct ctree_item_t +{ + cursor_item_type_t citype = VDI_NONE; ///< Item type + union + { + citem_t *it; + cexpr_t *e; ///< VDI_EXPR: Expression + cinsn_t *i; ///< VDI_EXPR: Statement + lvar_t *l; ///< VDI_LVAR: Local variable + cfunc_t *f; ///< VDI_FUNC: Function + treeloc_t loc; ///< VDI_TAIL: Line tail + }; + + void verify(const mba_t *mba) const; + + /// Get type of a structure field. + /// If the current item is a structure/union field, + /// this function will return information about it. + /// \param[out] udm pointer to buffer for the udt member info. + /// \param[out] parent pointer to buffer for the struct/union type. + /// \param[out] p_offset pointer to the offset in bits inside udt. + /// \return member index or -1 if failed + /// Both output parameters can be nullptr. + + int hexapi get_udm( + udm_t *udm=nullptr, + tinfo_t *parent=nullptr, + uint64 *p_offset=nullptr) const; + + /// Get type of an enum member. + /// If the current item is a symbolic constant, + /// this function will return information about it. + /// \param[out] parent pointer to buffer for the enum type. + /// \return member index or -1 if failed + + int hexapi get_edm(tinfo_t *parent) const; + + /// Get pointer to local variable. + /// If the current item is a local variable, + /// this function will return pointer to its definition. + /// \return nullptr if failed + + lvar_t *hexapi get_lvar() const; + + + /// Get address of the current item. + /// Each ctree item has an address. + /// \return BADADDR if failed + + ea_t hexapi get_ea() const; + + + /// Get label number of the current item. + /// \param[in] gln_flags Combination of \ref GLN_ bits + /// \return -1 if failed or no label + + int hexapi get_label_num(int gln_flags) const; +/// \defgroup GLN_ get_label_num control +///@{ +#define GLN_CURRENT 0x01 ///< get label of the current item +#define GLN_GOTO_TARGET 0x02 ///< get goto target +#define GLN_ALL 0x03 ///< get both +///@} + + /// Is the current item is a ctree item? + bool is_citem() const { return citype == VDI_EXPR; } + + void hexapi print(qstring *vout) const; + const char *hexapi dstr() const; + HEXRAYS_MEMORY_ALLOCATION_FUNCS() +}; + +/// Unused label disposition. +enum allow_unused_labels_t +{ + FORBID_UNUSED_LABELS = 0, ///< Unused labels cause interr + ALLOW_UNUSED_LABELS = 1, ///< Unused labels are permitted +}; + +typedef std::map user_labels_t; + +/// Logically negate the specified expression. +/// The specified expression will be logically negated. +/// For example, "x == y" is converted into "x != y" by this function. +/// \param e expression to negate. After the call, e must not be used anymore +/// because it can be changed by the function. The function return value +/// must be used to refer to the expression. +/// \return logically negated expression. + +cexpr_t *hexapi lnot(cexpr_t *e); + + +/// Create a new block-statement. + +cinsn_t *hexapi new_block(); + + +/// Create a helper object. +/// This function creates a helper object. +/// The named function is not required to exist, the decompiler will only print +/// its name in the output. Helper functions are usually used to represent arbitrary +/// function or macro calls in the output. +/// \param standalone false:helper must be called; true:helper can be used in any expression +/// \param type type of the create function object +/// \param format printf-style format string that will be used to create the function name. +/// \param va additional arguments for printf +/// \return the created expression. + +AS_PRINTF(3, 0) cexpr_t *hexapi vcreate_helper(bool standalone, const tinfo_t &type, const char *format, va_list va); + +/// Create a helper object.. +AS_PRINTF(3, 4) inline cexpr_t *create_helper(bool standalone, const tinfo_t &type, const char *format, ...) +{ + va_list va; + va_start(va, format); + cexpr_t *e = vcreate_helper(standalone, type, format, va); + va_end(va); + return e; +} + + +/// Create a helper call expression. +/// This function creates a new expression: a call of a helper function. +/// \param rettype type of the whole expression. +/// \param args helper arguments. this object will be consumed by the function. +/// if there are no args, this parameter may be specified as nullptr. +/// \param format printf-style format string that will be used to create the function name. +/// \param va additional arguments for printf +/// \return the created expression. + +AS_PRINTF(3, 0) cexpr_t *hexapi vcall_helper(const tinfo_t &rettype, carglist_t *args, const char *format, va_list va); + +/// Create a helper call. +AS_PRINTF(3, 4) inline cexpr_t *call_helper( + const tinfo_t &rettype, + carglist_t *args, + const char *format, ...) +{ + va_list va; + va_start(va, format); + cexpr_t *e = vcall_helper(rettype, args, format, va); + va_end(va); + return e; +} + + +/// Create a number expression +/// \param n value +/// \param func current function +/// \param ea definition address of the number +/// \param opnum operand number of the number (in the disassembly listing) +/// \param sign number sign +/// \param size size of number in bytes +/// Please note that the type of the resulting expression can be anything because +/// it can be inherited from the disassembly listing or taken from the user +/// specified number representation in the pseudocode view. + +cexpr_t *hexapi make_num(uint64 n, cfunc_t *func=nullptr, ea_t ea=BADADDR, int opnum=0, type_sign_t sign=no_sign, int size=0); + + +/// Create a reference. +/// This function performs the following conversion: "obj" => "&obj". +/// It can handle casts, annihilate "&*", and process other special cases. + +cexpr_t *hexapi make_ref(cexpr_t *e); + + +/// Dereference a pointer. +/// This function dereferences a pointer expression. +/// It performs the following conversion: "ptr" => "*ptr" +/// It can handle discrepancies in the pointer type and the access size. +/// \param e expression to deference +/// \param ptrsize access size +/// \param is_flt dereferencing for floating point access? +/// \return dereferenced expression + +cexpr_t *hexapi dereference(cexpr_t *e, int ptrsize, bool is_flt=false); + + +/// Save user defined labels into the database. +/// \param func_ea the entry address of the function, +/// ignored if FUNC != nullptr +/// \param user_labels collection of user defined labels +/// \param func pointer to current function, +/// if FUNC != nullptr, then save labels using a more stable +/// method that preserves them even when the decompiler +/// output drastically changes + +void hexapi save_user_labels(ea_t func_ea, const user_labels_t *user_labels, const cfunc_t *func=nullptr); + + +/// Save user defined comments into the database. +/// \param func_ea the entry address of the function +/// \param user_cmts collection of user defined comments + +void hexapi save_user_cmts(ea_t func_ea, const user_cmts_t *user_cmts); + +/// Save user defined number formats into the database. +/// \param func_ea the entry address of the function +/// \param numforms collection of user defined comments + +void hexapi save_user_numforms(ea_t func_ea, const user_numforms_t *numforms); + + +/// Save user defined citem iflags into the database. +/// \param func_ea the entry address of the function +/// \param iflags collection of user defined citem iflags + +void hexapi save_user_iflags(ea_t func_ea, const user_iflags_t *iflags); + + +/// Save user defined union field selections into the database. +/// \param func_ea the entry address of the function +/// \param unions collection of union field selections + +void hexapi save_user_unions(ea_t func_ea, const user_unions_t *unions); + + +/// Restore user defined labels from the database. +/// \param func_ea the entry address of the function, +/// ignored if FUNC != nullptr +/// \param func pointer to current function +/// \return collection of user defined labels. +/// The returned object must be deleted by the caller using delete_user_labels() + +user_labels_t *hexapi restore_user_labels(ea_t func_ea, const cfunc_t *func=nullptr); + + +/// Restore user defined comments from the database. +/// \param func_ea the entry address of the function +/// \return collection of user defined comments. +/// The returned object must be deleted by the caller using delete_user_cmts() + +user_cmts_t *hexapi restore_user_cmts(ea_t func_ea); + + +/// Restore user defined number formats from the database. +/// \param func_ea the entry address of the function +/// \return collection of user defined number formats. +/// The returned object must be deleted by the caller using delete_user_numforms() + +user_numforms_t *hexapi restore_user_numforms(ea_t func_ea); + + +/// Restore user defined citem iflags from the database. +/// \param func_ea the entry address of the function +/// \return collection of user defined iflags. +/// The returned object must be deleted by the caller using delete_user_iflags() + +user_iflags_t *hexapi restore_user_iflags(ea_t func_ea); + + +/// Restore user defined union field selections from the database. +/// \param func_ea the entry address of the function +/// \return collection of union field selections +/// The returned object must be deleted by the caller using delete_user_unions() + +user_unions_t *hexapi restore_user_unions(ea_t func_ea); + + +typedef std::map eamap_t; +// map of instruction boundaries. may contain INS_EPILOG for the epilog instructions +typedef std::map boundaries_t; +#define INS_EPILOG ((cinsn_t *)1) + +// Tags to find this location quickly: #cfunc_t #func_t +//------------------------------------------------------------------------- +/// Decompiled function. Decompilation result is kept here. +struct cfunc_t +{ + ea_t entry_ea; ///< function entry address + mba_t *mba; ///< underlying microcode + cinsn_t body; ///< function body, must be a block + intvec_t &argidx; ///< list of arguments (indexes into vars) + ctree_maturity_t maturity; ///< maturity level + // The following maps must be accessed using helper functions. + // Example: for user_labels_t, see functions starting with "user_labels_". + user_labels_t *user_labels;///< user-defined labels. + user_cmts_t *user_cmts; ///< user-defined comments. + user_numforms_t *numforms; ///< user-defined number formats. + user_iflags_t *user_iflags;///< user-defined item flags \ref CIT_ + user_unions_t *user_unions;///< user-defined union field selections. +/// \defgroup CIT_ ctree item iflags bits +///@{ +#define CIT_COLLAPSED 0x0001 ///< display ctree item in collapsed form +///@} + int refcnt; ///< reference count to this object. use cfuncptr_t + int statebits; ///< current cfunc_t state. see \ref CFS_ +/// \defgroup CFS_ cfunc state bits +#define CFS_BOUNDS 0x0001 ///< 'eamap' and 'boundaries' are ready +#define CFS_TEXT 0x0002 ///< 'sv' is ready (and hdrlines) +#define CFS_LVARS_HIDDEN 0x0004 ///< local variable definitions are collapsed +#define CFS_LOCKED 0x0008 ///< cfunc is temporarily locked + eamap_t *eamap; ///< ea->insn map. use \ref get_eamap + boundaries_t *boundaries; ///< map of instruction boundaries. use \ref get_boundaries + strvec_t sv; ///< decompilation output: function text. use \ref get_pseudocode + int hdrlines; ///< number of lines in the declaration area + mutable citem_pointers_t treeitems; ///< vector of pointers to citem_t objects (nodes constituting the ctree) + + // the exact size of this class is not documented, there may be more fields + char reserved[]; + +public: + cfunc_t(mba_t *mba); // use create_cfunc() + ~cfunc_t() { cleanup(); } + void release() { delete this; } + HEXRAYS_MEMORY_ALLOCATION_FUNCS() + + /// Generate the function body. + /// This function (re)generates the function body from the underlying microcode. + void hexapi build_c_tree(); + + /// Verify the ctree. + /// This function verifies the ctree. If the ctree is malformed, an internal error + /// is generated. Use it to verify the ctree after your modifications. + /// \param aul Are unused labels acceptable? + /// \param even_without_debugger if false and there is no debugger, the verification will be skipped + void hexapi verify(allow_unused_labels_t aul, bool even_without_debugger) const; + + /// Print function prototype. + /// \param vout output buffer + void hexapi print_dcl(qstring *vout) const; + + /// Print function text. + /// \param vp printer helper class to receive the generated text. + void hexapi print_func(vc_printer_t &vp) const; + + /// Get the function type. + /// \param type variable where the function type is returned + /// \return false if failure + bool hexapi get_func_type(tinfo_t *type) const; + + /// Get vector of local variables. + /// \return pointer to the vector of local variables. If you modify this vector, + /// the ctree must be regenerated in order to have correct cast operators. + /// Use build_c_tree() for that. + /// Removing lvars should be done carefully: all references in ctree + /// and microcode must be corrected after that. + lvars_t *hexapi get_lvars(); + + /// Get stack offset delta. + /// The local variable stack offsets retrieved by v.location.stkoff() + /// should be adjusted before being used as stack frame offsets in IDA. + /// \return the delta to apply. + /// example: ida_stkoff = v.location.stkoff() - f->get_stkoff_delta() + sval_t hexapi get_stkoff_delta(); + + /// Find the label. + /// \return pointer to the ctree item with the specified label number. + citem_t *hexapi find_label(int label); + + /// Remove unused labels. + /// This function checks what labels are really used by the function and + /// removes the unused ones. You must call it after deleting a goto statement. + void hexapi remove_unused_labels(); + + /// Retrieve a user defined comment. + /// \param loc ctree location + /// \param rt should already retrieved comments retrieved again? + /// \return pointer to the comment string or nullptr + const char *hexapi get_user_cmt(const treeloc_t &loc, cmt_retrieval_type_t rt) const; + + /// Set a user defined comment. + /// This function stores the specified comment in the cfunc_t structure. + /// The save_user_cmts() function must be called after it. + /// \param loc ctree location + /// \param cmt new comment. if empty or nullptr, then an existing comment is deleted. + void hexapi set_user_cmt(const treeloc_t &loc, const char *cmt); + + /// Retrieve citem iflags. + /// \param loc citem locator + /// \return \ref CIT_ or 0 + int32 hexapi get_user_iflags(const citem_locator_t &loc) const; + + /// Set citem iflags. + /// \param loc citem locator + /// \param iflags new iflags + void hexapi set_user_iflags(const citem_locator_t &loc, int32 iflags); + + /// Check if there are orphan comments. + bool hexapi has_orphan_cmts() const; + + /// Delete all orphan comments. + /// The save_user_cmts() function must be called after this call. + int hexapi del_orphan_cmts(); + + /// Retrieve a user defined union field selection. + /// \param ea address + /// \param path out: path describing the union selection. + /// \return pointer to the path or nullptr + bool hexapi get_user_union_selection(ea_t ea, intvec_t *path); + + /// Set a union field selection. + /// The save_user_unions() function must be called after calling this function. + /// \param ea address + /// \param path in: path describing the union selection. + void hexapi set_user_union_selection(ea_t ea, const intvec_t &path); + + /// Save user-defined labels into the database + void hexapi save_user_labels() const; + /// Save user-defined comments into the database + void hexapi save_user_cmts() const; + /// Save user-defined number formats into the database + void hexapi save_user_numforms() const; + /// Save user-defined iflags into the database + void hexapi save_user_iflags() const; + /// Save user-defined union field selections into the database + void hexapi save_user_unions() const; + + /// Get ctree item for the specified cursor position. + /// \return false if failed to get the current item + /// \param line line of decompilation text (element of \ref sv) + /// \param x x cursor coordinate in the line + /// \param is_ctree_line does the line belong to statement area? (if not, it is assumed to belong to the declaration area) + /// \param phead ptr to the first item on the line (used to attach block comments). May be nullptr + /// \param pitem ptr to the current item. May be nullptr + /// \param ptail ptr to the last item on the line (used to attach indented comments). May be nullptr + /// \sa vdui_t::get_current_item() + bool hexapi get_line_item( + const char *line, + int x, + bool is_ctree_line, + ctree_item_t *phead, + ctree_item_t *pitem, + ctree_item_t *ptail); + + /// Get information about decompilation warnings. + /// \return reference to the vector of warnings + hexwarns_t &hexapi get_warnings(); + + /// Get pointer to ea->insn map. + /// This function initializes eamap if not done yet. + eamap_t &hexapi get_eamap(); + + /// Get pointer to map of instruction boundaries. + /// This function initializes the boundary map if not done yet. + boundaries_t &hexapi get_boundaries(); + + /// Get pointer to decompilation output: the pseudocode. + /// This function generates pseudocode if not done yet. + const strvec_t &hexapi get_pseudocode(); + + /// Refresh ctext after a ctree modification. + /// This function informs the decompiler that ctree (\ref body) have been + /// modified and ctext (\ref sv) does not correspond to it anymore. + /// It also refreshes the pseudocode windows if there is any. + void hexapi refresh_func_ctext(); + + /// Recalculate item adresses. + /// This function may be required after shuffling ctree items. + /// For example, when adding or removing statements of a block, + /// or changing 'if' statements. + void hexapi recalc_item_addresses(); + + bool hexapi gather_derefs(const ctree_item_t &ci, udt_type_data_t *udm=nullptr) const; + bool hexapi find_item_coords(const citem_t *item, int *px, int *py); + bool locked() const { return (statebits & CFS_LOCKED) != 0; } +private: + /// Cleanup. + /// Properly delete all children and free memory. + void hexapi cleanup(); + DECLARE_UNCOPYABLE(cfunc_t) +}; +typedef qvector cfuncptrs_t; + +/// \defgroup DECOMP_ decompile() flags +///@{ +#define DECOMP_NO_WAIT 0x0001 ///< do not display waitbox +#define DECOMP_NO_CACHE 0x0002 ///< do not use decompilation cache (snippets are never cached) +#define DECOMP_NO_FRAME 0x0004 ///< do not use function frame info (only snippet mode) +#define DECOMP_WARNINGS 0x0008 ///< display warnings in the output window +#define DECOMP_ALL_BLKS 0x0010 ///< generate microcode for unreachable blocks +#define DECOMP_NO_HIDE 0x0020 ///< do not close display waitbox. see close_hexrays_waitbox() +#define DECOMP_GXREFS_DEFLT 0x0000 ///< the default behavior: do not update the + ///< global xrefs cache upon decompile() call, + ///< but when the pseudocode text is generated + ///< (e.g., through cfunc_t.get_pseudocode()) +#define DECOMP_GXREFS_NOUPD 0x0040 ///< do not update the global xrefs cache +#define DECOMP_GXREFS_FORCE 0x0080 ///< update the global xrefs cache immediately +#define DECOMP_VOID_MBA 0x0100 ///< return empty mba object (to be used with gen_microcode) +#define DECOMP_OUTLINE 0x80000000 ///< generate code for an outline +///@} + +/// Close the waitbox displayed by the decompiler. +/// Useful if DECOMP_NO_HIDE was used during decompilation. + +void hexapi close_hexrays_waitbox(); + + +/// Decompile a snippet or a function. +/// \param mbr what to decompile +/// \param hf extended error information (if failed) +/// \param decomp_flags bitwise combination of \ref DECOMP_... bits +/// \return pointer to the decompilation result (a reference counted pointer). +/// nullptr if failed. + +cfuncptr_t hexapi decompile( + const mba_ranges_t &mbr, + hexrays_failure_t *hf=nullptr, + int decomp_flags=0); + + +/// Decompile a function. +/// Multiple decompilations of the same function return the same object. +/// \param pfn pointer to function to decompile +/// \param hf extended error information (if failed) +/// \param decomp_flags bitwise combination of \ref DECOMP_... bits +/// \return pointer to the decompilation result (a reference counted pointer). +/// nullptr if failed. + +inline cfuncptr_t decompile_func( + func_t *pfn, + hexrays_failure_t *hf=nullptr, + int decomp_flags=0) +{ + mba_ranges_t mbr(pfn); + return decompile(mbr, hf, decomp_flags); +} + + +/// Decompile a snippet. +/// \param ranges snippet ranges. ranges[0].start_ea is the entry point +/// \param hf extended error information (if failed) +/// \param decomp_flags bitwise combination of \ref DECOMP_... bits +/// \return pointer to the decompilation result (a reference counted pointer). +/// nullptr if failed. + +inline cfuncptr_t decompile_snippet( + const rangevec_t &ranges, + hexrays_failure_t *hf=nullptr, + int decomp_flags=0) +{ + mba_ranges_t mbr(ranges); + return decompile(mbr, hf, decomp_flags); +} + + +/// Generate microcode of an arbitrary code snippet +/// \param mbr snippet ranges +/// \param hf extended error information (if failed) +/// \param retlist list of registers the snippet returns +/// \param decomp_flags bitwise combination of \ref DECOMP_... bits +/// \param reqmat required microcode maturity +/// \return pointer to the microcode, nullptr if failed. + +mba_t *hexapi gen_microcode( + const mba_ranges_t &mbr, + hexrays_failure_t *hf=nullptr, + const mlist_t *retlist=nullptr, + int decomp_flags=0, + mba_maturity_t reqmat=MMAT_GLBOPT3); + +/// Create an empty microcode object +inline mba_t *create_empty_mba( + const mba_ranges_t &mbr, + hexrays_failure_t *hf=nullptr) +{ + return gen_microcode(mbr, hf, nullptr, DECOMP_VOID_MBA); +} + + +/// Create a new cfunc_t object. +/// \param mba microcode object. +/// After creating the cfunc object it takes the ownership of MBA. + +cfuncptr_t hexapi create_cfunc(mba_t *mba); + + +/// Flush the cached decompilation results. +/// Erases a cache entry for the specified function. +/// \param ea function to erase from the cache +/// \param close_views close pseudocode windows that show the function +/// \return if a cache entry existed. + +bool hexapi mark_cfunc_dirty(ea_t ea, bool close_views=false); + + +/// Flush all cached decompilation results. + +void hexapi clear_cached_cfuncs(); + + +/// Do we have a cached decompilation result for 'ea'? + +bool hexapi has_cached_cfunc(ea_t ea); + +//-------------------------------------------------------------------------- +// Now cinsn_t class is defined, define the cleanup functions: +inline void cif_t::cleanup() { delete ithen; delete ielse; } +inline void cloop_t::cleanup() { delete body; } + +/// Print item into one line. +/// \param vout output buffer +/// \param func parent function. This argument is used to find out the referenced variable names. +/// \return length of the generated text. + +inline void citem_t::print1(qstring *vout, const cfunc_t *func) const +{ + if ( is_expr() ) + ((cexpr_t*)this)->print1(vout, func); + else + ((cinsn_t*)this)->print1(vout, func); +} + +/// Get pointers to operands. at last one operand should be a number +/// o1 will be pointer to the number + +inline bool cexpr_t::get_1num_op(cexpr_t **o1, cexpr_t **o2) +{ + if ( x->op == cot_num ) + { + *o1 = x; + *o2 = y; + } + else + { + if ( y->op != cot_num ) + return false; + *o1 = y; + *o2 = x; + } + return true; +} + +inline bool cexpr_t::get_1num_op(const cexpr_t **o1, const cexpr_t **o2) const +{ + return CONST_CAST(cexpr_t*)(this)->get_1num_op( + CONST_CAST(cexpr_t**)(o1), + CONST_CAST(cexpr_t**)(o2)); +} + +inline citem_locator_t::citem_locator_t(const citem_t *i) + : ea(i != nullptr ? i->ea : BADADDR), + op(i != nullptr ? i->op : cot_empty) +{ +} + +const char *hexapi get_ctype_name(ctype_t op); +qstring hexapi create_field_name(const tinfo_t &type, uval_t offset=BADADDR); +typedef void *hexdsp_t(int code, ...); +const int64 HEXRAYS_API_MAGIC = 0x00DEC0DE00000004LL; + +/// Decompiler events. +/// Use install_hexrays_callback() to install a handler for decompiler events. +/// When the possible return value is not specified, your callback +/// must return zero. +enum hexrays_event_t ENUM_SIZE(int) +{ + // When a function is decompiled, the following events occur: + + hxe_flowchart, ///< Flowchart has been generated. + ///< \param fc (qflow_chart_t *) + ///< \param mba (mba_t *) + ///< \param reachable_blocks (bitset_t *) + ///< \param decomp_flags (int) + ///< \return \ref MERR_ + + hxe_stkpnts, ///< SP change points have been calculated. + ///< \param mba (mba_t *) + ///< \param stkpnts (stkpnts_t *) + ///< \return \ref MERR_ + ///< This event is generated for each inlined range as well. + + hxe_prolog, ///< Prolog analysis has been finished. + ///< \param mba (mba_t *) + ///< \param fc (qflow_chart_t *) + ///< \param reachable_blocks (const bitset_t *) + ///< \param decomp_flags (int) + ///< \return \ref MERR_ + ///< This event is generated for each inlined range as well. + + hxe_microcode, ///< Microcode has been generated. + ///< \param mba (mba_t *) + ///< \return \ref MERR_ + + hxe_preoptimized, ///< Microcode has been preoptimized. + ///< \param mba (mba_t *) + ///< \return \ref MERR_ + + hxe_locopt, ///< Basic block level optimization has been finished. + ///< \param mba (mba_t *) + ///< \return \ref MERR_ + + hxe_prealloc, ///< Local variables: preallocation step begins. + ///< \param mba (mba_t *) + ///< This event may occur several times. + ///< Should return: 1 if modified microcode + ///< Negative values are \ref MERR_ + + hxe_glbopt, ///< Global optimization has been finished. + ///< If microcode is modified, MERR_LOOP must be returned. + ///< It will cause a complete restart of the optimization. + ///< \param mba (mba_t *) + ///< \return \ref MERR_ + + hxe_pre_structural, ///< Structure analysis is starting. + ///< \param ct (control_graph_t *) in/out: control graph + ///< \param cfunc (cfunc_t *) in: the current function + ///< \param g (const simple_graph_t *) in: control flow graph + ///< \return \ref MERR_; MERR_BLOCK means that the analysis has been performed by a plugin + + hxe_structural, ///< Structural analysis has been finished. + ///< \param ct (control_graph_t *) + + hxe_maturity, ///< Ctree maturity level is being changed. + ///< \param cfunc (cfunc_t *) + ///< \param new_maturity (ctree_maturity_t) + + hxe_interr, ///< Internal error has occurred. + ///< \param errcode (int ) + + hxe_combine, ///< Trying to combine instructions of basic block. + ///< \param blk (mblock_t *) + ///< \param insn (minsn_t *) + ///< Should return: 1 if combined the current instruction with a preceding one + /// -1 if the instruction should not be combined + /// 0 else + + hxe_print_func, ///< Printing ctree and generating text. + ///< \param cfunc (cfunc_t *) + ///< \param vp (vc_printer_t *) + ///< Returns: 1 if text has been generated by the plugin + ///< It is forbidden to modify ctree at this event. + + hxe_func_printed, ///< Function text has been generated. Plugins may + ///< modify the text in \ref cfunc_t::sv. However, + ///< it is too late to modify the ctree or microcode. + ///< The text uses regular color codes (see lines.hpp) + ///< COLOR_ADDR is used to store pointers to ctree items. + ///< \param cfunc (cfunc_t *) + + hxe_resolve_stkaddrs, ///< The optimizer is about to resolve stack addresses. + ///< \param mba (mba_t *) + + hxe_build_callinfo, ///< Analyzing a call instruction. + ///< \param blk (mblock_t *) blk->tail is the call. + ///< \param type (tinfo_t *) buffer for the output type. + ///< \param callinfo (mcallinfo_t **) prepared callinfo. + ///< The plugin should either specify the function type, + ///< either allocate and return a new mcallinfo_t object. + + hxe_callinfo_built, ///< A call instruction has been anallyzed. + ///< \param blk (mblock_t *) blk->tail is the call. + + hxe_calls_done, ///< All calls have been analyzed. + ///< \param mba (mba_t *) + ///< This event is generated immediately after analyzing + ///< all calls, before any optimizitions, call unmerging + ///< and block merging. + + hxe_begin_inlining, ///< Starting to inline outlined functions. + ///< \param cdg (codegen_t *) + ///< \param decomp_flags (int) + ///< \return \ref MERR_ + ///< This is an opportunity to inline other ranges. + + hxe_inlining_func, ///< A set of ranges is going to be inlined. + ///< \param cdg (codegen_t *) + ///< \param blk (int) the block containing call/jump to inline + ///< \param mbr (mba_ranges_t *) the range to inline + + hxe_inlined_func, ///< A set of ranges got inlined. + ///< \param cdg (codegen_t *) + ///< \param blk (int) the block containing call/jump to inline + ///< \param mbr (mba_ranges_t *) the range to inline + ///< \param i1 (int) blknum of the first inlined block + ///< \param i2 (int) blknum of the last inlined block (excluded) + + hxe_collect_warnings, ///< Collect warning messages from plugins. + ///< These warnings will be displayed at the function header, + ///< after the user-defined comments. + ///< \param warnings (qstrvec_t *) + ///< \param cfunc (cfunc_t *) + + // User interface related events: + + hxe_open_pseudocode=100, + ///< New pseudocode view has been opened. + ///< \param vu (vdui_t *) + + hxe_switch_pseudocode,///< Existing pseudocode view has been reloaded + ///< with a new function. Its text has not been + ///< refreshed yet, only cfunc and mba pointers are ready. + ///< \param vu (vdui_t *) + + hxe_refresh_pseudocode,///< Existing pseudocode text has been refreshed. + ///< Adding/removing pseudocode lines is forbidden in this event. + ///< \param vu (vdui_t *) + ///< See also hxe_text_ready, which happens earlier + + hxe_close_pseudocode, ///< Pseudocode view is being closed. + ///< \param vu (vdui_t *) + + hxe_keyboard, ///< Keyboard has been hit. + ///< \param vu (vdui_t *) + ///< \param key_code (int) VK_... + ///< \param shift_state (int) + ///< Should return: 1 if the event has been handled + + hxe_right_click, ///< Mouse right click. + ///< Use hxe_populating_popup instead, in case you + ///< want to add items in the popup menu. + ///< \param vu (vdui_t *) + + hxe_double_click, ///< Mouse double click. + ///< \param vu (vdui_t *) + ///< \param shift_state (int) + ///< Should return: 1 if the event has been handled + + hxe_curpos, ///< Current cursor position has been changed. + ///< (for example, by left-clicking or using keyboard)\n + ///< \param vu (vdui_t *) + + hxe_create_hint, ///< Create a hint for the current item. + ///< \see ui_get_custom_viewer_hint + ///< \param vu (vdui_t *) + ///< \param hint (qstring *) + ///< \param important_lines (int *) + ///< Possible return values: + ///< \retval 0 continue collecting hints with other subscribers + ///< \retval 1 stop collecting hints + + hxe_text_ready, ///< Decompiled text is ready. + ///< \param vu (vdui_t *) + ///< This event can be used to modify the output text (sv). + ///< Obsolete. Please use hxe_func_printed instead. + + hxe_populating_popup, ///< Populating popup menu. We can add menu items now. + ///< \param widget (TWidget *) + ///< \param popup_handle (TPopupMenu *) + ///< \param vu (vdui_t *) + + lxe_lvar_name_changed,///< Local variable got renamed. + ///< \param vu (vdui_t *) + ///< \param v (lvar_t *) + ///< \param name (const char *) + ///< \param is_user_name (bool) + ///< Please note that it is possible to read/write + ///< user settings for lvars directly from the idb. + + lxe_lvar_type_changed,///< Local variable type got changed. + ///< \param vu (vdui_t *) + ///< \param v (lvar_t *) + ///< \param tinfo (const tinfo_t *) + ///< Please note that it is possible to read/write + ///< user settings for lvars directly from the idb. + + lxe_lvar_cmt_changed, ///< Local variable comment got changed. + ///< \param vu (vdui_t *) + ///< \param v (lvar_t *) + ///< \param cmt (const char *) + ///< Please note that it is possible to read/write + ///< user settings for lvars directly from the idb. + + lxe_lvar_mapping_changed, ///< Local variable mapping got changed. + ///< \param vu (vdui_t *) + ///< \param from (lvar_t *) + ///< \param to (lvar_t *) + ///< Please note that it is possible to read/write + ///< user settings for lvars directly from the idb. + + hxe_cmt_changed, ///< Comment got changed. + ///< \param cfunc (cfunc_t *) + ///< \param loc (const treeloc_t *) + ///< \param cmt (const char *) + + hxe_mba_maturity, ///< Maturity level of an MBA was changed. + ///< \param mba (mba_t *) + ///< \param reqmat (mba_maturity_t) requested maturity level + ///< \return \ref MERR_ +}; + +/// Handler of decompiler events. +/// \param ud user data. the value specified at the handler installation time +/// is passed here. +/// \param event decompiler event code +/// \param va additional arguments +/// \return as a rule the callback must return 0 unless specified otherwise +/// in the event description. + +typedef ssize_t idaapi hexrays_cb_t(void *ud, hexrays_event_t event, va_list va); + + +/// Install handler for decompiler events. +/// \param callback handler to install +/// \param ud user data. this pointer will be passed to your handler by the decompiler. +/// \return false if failed + +bool hexapi install_hexrays_callback(hexrays_cb_t *callback, void *ud); + +/// Uninstall handler for decompiler events. +/// \param callback handler to uninstall +/// \param ud user data. if nullptr, all handler corresponding to \c callback is uninstalled. +/// if not nullptr, only the callback instance with the specified \c ud value is uninstalled. +/// \return number of uninstalled handlers. + +int hexapi remove_hexrays_callback(hexrays_cb_t *callback, void *ud); + + +//--------------------------------------------------------------------------- +/// \defgroup vdui User interface definitions +///@{ + +/// Type of the input device. +/// How the user command has been invoked +enum input_device_t +{ + USE_KEYBOARD = 0, ///< Keyboard + USE_MOUSE = 1, ///< Mouse +}; +///@} + +//--------------------------------------------------------------------------- +/// Cursor position in the output text (pseudocode). +struct ctext_position_t +{ + int lnnum; ///< Line number + int x; ///< x coordinate of the cursor within the window + int y; ///< y coordinate of the cursor within the window + /// Is the cursor in the variable/type declaration area? + /// \param hdrlines Number of lines of the declaration area + bool in_ctree(int hdrlines) const { return lnnum >= hdrlines; } + /// Comparison operators + DECLARE_COMPARISONS(ctext_position_t) + { + if ( lnnum < r.lnnum ) return -1; + if ( lnnum > r.lnnum ) return 1; + if ( x < r.x ) return -1; + if ( x > r.x ) return 1; + return 0; + } + ctext_position_t(int _lnnum=-1, int _x=0, int _y=0) + : lnnum(_lnnum), x(_x), y(_y) {} +}; + +/// Navigation history item. +/// Holds information about interactive decompilation history. +/// Currently this is not saved in the database. +struct history_item_t : public ctext_position_t +{ + ea_t func_ea; ///< The entry address of the decompiled function + ea_t curr_ea; ///< Current address + ea_t end = BADADDR; ///< BADADDR-decompile a function; otherwise end of the range + history_item_t(ea_t fea=BADADDR, ea_t cea=BADADDR, int _lnnum=-1, int _x=0, int _y=0) + : ctext_position_t(_lnnum, _x, _y), func_ea(fea), curr_ea(cea) {} + history_item_t(ea_t fea, ea_t cea, const ctext_position_t &p) + : ctext_position_t(p), func_ea(fea), curr_ea(cea) {} +}; + +/// Navigation history. +typedef qstack history_t; + +/// Comment types +typedef int cmt_type_t; +const cmt_type_t + CMT_NONE = 0x0000, ///< No comment is possible + CMT_TAIL = 0x0001, ///< Indented comment + CMT_BLOCK1 = 0x0002, ///< Anterioir block comment + CMT_BLOCK2 = 0x0004, ///< Posterior block comment + CMT_LVAR = 0x0008, ///< Local variable comment + CMT_FUNC = 0x0010, ///< Function comment + CMT_ALL = 0x001F; ///< All comments + +//--------------------------------------------------------------------------- +/// Information about the pseudocode window +struct vdui_t +{ + int flags = 0; ///< \ref VDUI_ +/// \defgroup VDUI_ Properties of pseudocode window +/// Used in vdui_t::flags +///@{ +#define VDUI_VISIBLE 0x0001 ///< is visible? +#define VDUI_VALID 0x0002 ///< is valid? +///@} + + /// Is the pseudocode window visible? + /// if not, it might be invisible or destroyed + bool visible() const { return (flags & VDUI_VISIBLE) != 0; } + /// Does the pseudocode window contain valid code? + /// It can become invalid if the function type gets changed in IDA. + bool valid() const { return (flags & VDUI_VALID) != 0; } + /// Does the pseudocode window contain valid code? + /// We lock windows before modifying them, to avoid recursion due to + /// the events generated by the IDA kernel. + /// \retval true The window is locked and may have stale info + bool locked() const { return cfunc != nullptr && cfunc->locked(); } + void set_visible(bool v) { setflag(flags, VDUI_VISIBLE, v); } + void set_valid(bool v) { setflag(flags, VDUI_VALID, v); } + bool hexapi set_locked(bool v); // returns true-redecompiled + + int view_idx; ///< pseudocode window index (0..) + TWidget *ct = nullptr;///< pseudocode view + TWidget *toplevel = nullptr; + + mba_t *mba; ///< pointer to underlying microcode + cfuncptr_t cfunc; ///< pointer to function object + merror_t last_code; ///< result of the last user action. See \ref MERR_ + + // The following fields are valid after get_current_item(): + ctext_position_t cpos; ///< Current ctext position + ctree_item_t head; ///< First ctree item on the current line (for block comments) + ctree_item_t item; ///< Current ctree item + ctree_item_t tail; ///< Tail ctree item on the current line (for indented comments) + + vdui_t(); // do not create your own vdui_t objects + + /// Refresh pseudocode window. + /// This is the highest level refresh function. + /// It causes the most profound refresh possible and can lead to redecompilation + /// of the current function. Please consider using refresh_ctext() + /// if you need a more superficial refresh. + /// \param redo_mba true means to redecompile the current function\n + /// false means to rebuild ctree without regenerating microcode + /// \sa refresh_ctext() + void hexapi refresh_view(bool redo_mba); + + /// Refresh pseudocode window. + /// This function refreshes the pseudocode window by regenerating its text + /// from cfunc_t. Instead of this function use refresh_func_ctext(), which + /// refreshes all pseudocode windows for the function. + /// \sa refresh_view(), refresh_func_ctext() + void hexapi refresh_ctext(bool activate=true); // deprecated + + /// Display the specified pseudocode. + /// This function replaces the pseudocode window contents with the + /// specified cfunc_t. + /// \param f pointer to the function to display. + /// \param activate should the pseudocode window get focus? + void hexapi switch_to(cfuncptr_t f, bool activate); + + /// Is the current item a statement? + //// \return false if the cursor is in the local variable/type declaration area\n + /// true if the cursor is in the statement area + bool in_ctree() const { return cpos.in_ctree(cfunc->hdrlines); } + + /// Get current number. + /// If the current item is a number, return pointer to it. + /// \return nullptr if the current item is not a number + /// This function returns non-null for the cases of a 'switch' statement + /// Also, if the current item is a casted number, then this function will succeed. + cnumber_t *hexapi get_number(); + + /// Get current label. + /// If there is a label under the cursor, return its number. + /// \return -1 if there is no label under the cursor. + /// prereq: get_current_item() has been called + int hexapi get_current_label(); + + /// Clear the pseudocode window. + /// It deletes the current function and microcode. + void hexapi clear(); + + /// Refresh the current position. + /// This function refreshes the \ref cpos field. + /// \return false if failed + /// \param idv keyboard or mouse + bool hexapi refresh_cpos(input_device_t idv); + + /// Get current item. + /// This function refreshes the \ref cpos, \ref item, \ref tail fields. + /// \return false if failed + /// \param idv keyboard or mouse + /// \sa cfunc_t::get_line_item() + bool hexapi get_current_item(input_device_t idv); + + /// Rename local variable. + /// This function displays a dialog box and allows the user to rename a local variable. + /// \return false if failed or cancelled + /// \param v pointer to local variable + bool hexapi ui_rename_lvar(lvar_t *v); + + /// Rename local variable. + /// This function permanently renames a local variable. + /// \return false if failed + /// \param v pointer to local variable + /// \param name new variable name + /// \param is_user_name use true to save the new name into the database. + /// use false to delete the saved name. + /// \sa ::rename_lvar() + bool hexapi rename_lvar(lvar_t *v, const char *name, bool is_user_name); + + /// Set type of a function call + /// This function displays a dialog box and allows the user to change + /// the type of a function call + /// \return false if failed or cancelled + /// \param e pointer to call expression + bool hexapi ui_set_call_type(const cexpr_t *e); + + /// Set local variable type. + /// This function displays a dialog box and allows the user to change + /// the type of a local variable. + /// \return false if failed or cancelled + /// \param v pointer to local variable + bool hexapi ui_set_lvar_type(lvar_t *v); + + /// Set local variable type. + /// This function permanently sets a local variable type and clears + /// NOPTR flag if it was set before by function 'set_noptr_lvar' + /// \return false if failed + /// \param v pointer to local variable + /// \param type new variable type + bool hexapi set_lvar_type(lvar_t *v, const tinfo_t &type); + + /// Inform that local variable should have a non-pointer type + /// This function permanently sets a corresponding variable flag (NOPTR) + /// and removes type if it was set before by function 'set_lvar_type' + /// \return false if failed + /// \param v pointer to local variable + bool hexapi set_noptr_lvar(lvar_t *v); + + /// Set local variable comment. + /// This function displays a dialog box and allows the user to edit + /// the comment of a local variable. + /// \return false if failed or cancelled + /// \param v pointer to local variable + bool hexapi ui_edit_lvar_cmt(lvar_t *v); + + /// Set local variable comment. + /// This function permanently sets a variable comment. + /// \return false if failed + /// \param v pointer to local variable + /// \param cmt new comment + bool hexapi set_lvar_cmt(lvar_t *v, const char *cmt); + + /// Map a local variable to another. + /// This function displays a variable list and allows the user to select mapping. + /// \return false if failed or cancelled + /// \param v pointer to local variable + bool hexapi ui_map_lvar(lvar_t *v); + + /// Unmap a local variable. + /// This function displays list of variables mapped to the specified variable + /// and allows the user to select a variable to unmap. + /// \return false if failed or cancelled + /// \param v pointer to local variable + bool hexapi ui_unmap_lvar(lvar_t *v); + + /// Map a local variable to another. + /// This function permanently maps one lvar to another. + /// All occurrences of the mapped variable are replaced by the new variable + /// \return false if failed + /// \param from the variable being mapped + /// \param to the variable to map to. if nullptr, unmaps the variable + bool hexapi map_lvar(lvar_t *from, lvar_t *to); + + /// Set structure field type. + /// This function displays a dialog box and allows the user to change + /// the type of a structure field. + /// \return false if failed or cancelled + /// \param udt_type structure/union type + /// \param udm_idx index of the structure/union member + bool hexapi set_udm_type(tinfo_t &udt_type, int udm_idx); + + /// Rename structure field. + /// This function displays a dialog box and allows the user to rename + /// a structure field. + /// \return false if failed or cancelled + /// \param udt_type structure/union type + /// \param udm_idx index of the structure/union member + bool hexapi rename_udm(tinfo_t &udt_type, int udm_idx); + + /// Set global item type. + /// This function displays a dialog box and allows the user to change + /// the type of a global item (data or function). + /// \return false if failed or cancelled + /// \param ea address of the global item + bool hexapi set_global_type(ea_t ea); + + /// Rename global item. + /// This function displays a dialog box and allows the user to rename + /// a global item (data or function). + /// \return false if failed or cancelled + /// \param ea address of the global item + bool hexapi rename_global(ea_t ea); + + /// Rename a label. + /// This function displays a dialog box and allows the user to rename + /// a statement label. + /// \return false if failed or cancelled + /// \param label label number + bool hexapi rename_label(int label); + + /// Process the Enter key. + /// This function jumps to the definition of the item under the cursor. + /// If the current item is a function, it will be decompiled. + /// If the current item is a global data, its disassemly text will be displayed. + /// \return false if failed + /// \param idv what cursor must be used, the keyboard or the mouse + /// \param omflags OM_NEWWIN: new pseudocode window will open, 0: reuse the existing window + bool hexapi jump_enter(input_device_t idv, int omflags); + + /// Jump to disassembly. + /// This function jumps to the address in the disassembly window + /// which corresponds to the current item. The current item is determined + /// based on the current keyboard cursor position. + /// \return false if failed + bool hexapi ctree_to_disasm(); + + /// Check if the specified line can have a comment. + /// Due to the coordinate system for comments: + /// (https://hex-rays.com/blog/coordinate-system-for-hex-rays) + /// some function lines cannot have comments. This function checks if a + /// comment can be attached to the specified line. + /// \return possible comment types + /// \param lnnum line number (0 based) + /// \param cmttype comment types to check + cmt_type_t hexapi calc_cmt_type(size_t lnnum, cmt_type_t cmttype) const; + + /// Edit an indented comment. + /// This function displays a dialog box and allows the user to edit + /// the comment for the specified ctree location. + /// \return false if failed or cancelled + /// \param loc comment location + bool hexapi edit_cmt(const treeloc_t &loc); + + /// Edit a function comment. + /// This function displays a dialog box and allows the user to edit + /// the function comment. + /// \return false if failed or cancelled + bool hexapi edit_func_cmt(); + + /// Delete all orphan comments. + /// Delete all orphan comments and refresh the screen. + /// \return true + bool hexapi del_orphan_cmts(); + + /// Change number base. + /// This function changes the current number representation. + /// \return false if failed + /// \param base number radix (10 or 16)\n + /// 0 means a character constant + bool hexapi set_num_radix(int base); + + /// Convert number to symbolic constant. + /// This function displays a dialog box and allows the user to select + /// a symbolic constant to represent the number. + /// \return false if failed or cancelled + bool hexapi set_num_enum(); + + /// Convert number to structure field offset. + /// Currently not implemented. + /// \return false if failed or cancelled + bool hexapi set_num_stroff(); + + /// Negate a number. + /// This function negates the current number. + /// \return false if failed. + bool hexapi invert_sign(); + + /// Bitwise negate a number. + /// This function inverts all bits of the current number. + /// \return false if failed. + bool hexapi invert_bits(); + + /// Collapse/uncollapse item. + /// This function collapses the current item. + /// \return false if failed. + bool hexapi collapse_item(bool hide); + + /// Collapse/uncollapse local variable declarations. + /// \return false if failed. + bool hexapi collapse_lvars(bool hide); + + /// Split/unsplit item. + /// This function splits the current assignment expression. + /// \return false if failed. + bool hexapi split_item(bool split); + +}; + +//--------------------------------------------------------------------------- +/// Select UDT for the operands using "Select offsets" widget + +/// Operand represention +struct ui_stroff_op_t +{ + qstring text; ///< any text for the column "Operand" of widget + uval_t offset; ///< operand offset, will be used when calculating the UDT path + bool operator==(const ui_stroff_op_t &r) const + { + return text == r.text && offset == r.offset; + } + bool operator!=(const ui_stroff_op_t &r) const { return !(*this == r); } +}; +DECLARE_TYPE_AS_MOVABLE(ui_stroff_op_t); +typedef qvector ui_stroff_ops_t; + +/// Callback to apply the selection +/// \return success +struct ui_stroff_applicator_t +{ + virtual ~ui_stroff_applicator_t() {} + /// \param opnum operand ordinal number, see below + /// \param path path describing the union selection, maybe empty + /// \param top_tif tinfo_t of the selected toplevel UDT + /// \param spath selected path + virtual bool idaapi apply(size_t opnum, const intvec_t &path, const tinfo_t &top_tif, const char *spath) = 0; +}; + +/// Select UDT +/// \param udts list of UDT tinfo_t for the selection, +/// if nullptr or empty then UDTs from the "Local types" will be used +/// \param ops operands +/// \param applicator callback will be called to apply the selection for every operand +int hexapi select_udt_by_offset( + const qvector *udts, + const ui_stroff_ops_t &ops, + ui_stroff_applicator_t &applicator); + + + + +//-------------------------------------------------------------------------- +// PUBLIC HEX-RAYS API +//-------------------------------------------------------------------------- + +/// Hex-Rays decompiler dispatcher. +/// All interaction with the decompiler is carried out by the intermediary of this dispatcher. +typedef void *hexdsp_t(int code, ...); + +/// API call numbers +enum hexcall_t +{ + hx_user_numforms_begin, + hx_user_numforms_end, + hx_user_numforms_next, + hx_user_numforms_prev, + hx_user_numforms_first, + hx_user_numforms_second, + hx_user_numforms_find, + hx_user_numforms_insert, + hx_user_numforms_erase, + hx_user_numforms_clear, + hx_user_numforms_size, + hx_user_numforms_free, + hx_user_numforms_new, + hx_lvar_mapping_begin, + hx_lvar_mapping_end, + hx_lvar_mapping_next, + hx_lvar_mapping_prev, + hx_lvar_mapping_first, + hx_lvar_mapping_second, + hx_lvar_mapping_find, + hx_lvar_mapping_insert, + hx_lvar_mapping_erase, + hx_lvar_mapping_clear, + hx_lvar_mapping_size, + hx_lvar_mapping_free, + hx_lvar_mapping_new, + hx_udcall_map_begin, + hx_udcall_map_end, + hx_udcall_map_next, + hx_udcall_map_prev, + hx_udcall_map_first, + hx_udcall_map_second, + hx_udcall_map_find, + hx_udcall_map_insert, + hx_udcall_map_erase, + hx_udcall_map_clear, + hx_udcall_map_size, + hx_udcall_map_free, + hx_udcall_map_new, + hx_user_cmts_begin, + hx_user_cmts_end, + hx_user_cmts_next, + hx_user_cmts_prev, + hx_user_cmts_first, + hx_user_cmts_second, + hx_user_cmts_find, + hx_user_cmts_insert, + hx_user_cmts_erase, + hx_user_cmts_clear, + hx_user_cmts_size, + hx_user_cmts_free, + hx_user_cmts_new, + hx_user_iflags_begin, + hx_user_iflags_end, + hx_user_iflags_next, + hx_user_iflags_prev, + hx_user_iflags_first, + hx_user_iflags_second, + hx_user_iflags_find, + hx_user_iflags_insert, + hx_user_iflags_erase, + hx_user_iflags_clear, + hx_user_iflags_size, + hx_user_iflags_free, + hx_user_iflags_new, + hx_user_unions_begin, + hx_user_unions_end, + hx_user_unions_next, + hx_user_unions_prev, + hx_user_unions_first, + hx_user_unions_second, + hx_user_unions_find, + hx_user_unions_insert, + hx_user_unions_erase, + hx_user_unions_clear, + hx_user_unions_size, + hx_user_unions_free, + hx_user_unions_new, + hx_user_labels_begin, + hx_user_labels_end, + hx_user_labels_next, + hx_user_labels_prev, + hx_user_labels_first, + hx_user_labels_second, + hx_user_labels_find, + hx_user_labels_insert, + hx_user_labels_erase, + hx_user_labels_clear, + hx_user_labels_size, + hx_user_labels_free, + hx_user_labels_new, + hx_eamap_begin, + hx_eamap_end, + hx_eamap_next, + hx_eamap_prev, + hx_eamap_first, + hx_eamap_second, + hx_eamap_find, + hx_eamap_insert, + hx_eamap_erase, + hx_eamap_clear, + hx_eamap_size, + hx_eamap_free, + hx_eamap_new, + hx_boundaries_begin, + hx_boundaries_end, + hx_boundaries_next, + hx_boundaries_prev, + hx_boundaries_first, + hx_boundaries_second, + hx_boundaries_find, + hx_boundaries_insert, + hx_boundaries_erase, + hx_boundaries_clear, + hx_boundaries_size, + hx_boundaries_free, + hx_boundaries_new, + hx_block_chains_begin, + hx_block_chains_end, + hx_block_chains_next, + hx_block_chains_prev, + hx_block_chains_get, + hx_block_chains_find, + hx_block_chains_insert, + hx_block_chains_erase, + hx_block_chains_clear, + hx_block_chains_size, + hx_block_chains_free, + hx_block_chains_new, + hx_hexrays_alloc, + hx_hexrays_free, + hx_valrng_t_clear, + hx_valrng_t_copy, + hx_valrng_t_assign, + hx_valrng_t_compare, + hx_valrng_t_set_eq, + hx_valrng_t_set_cmp, + hx_valrng_t_reduce_size, + hx_valrng_t_intersect_with, + hx_valrng_t_unite_with, + hx_valrng_t_inverse, + hx_valrng_t_has, + hx_valrng_t_print, + hx_valrng_t_dstr, + hx_valrng_t_cvt_to_single_value, + hx_valrng_t_cvt_to_cmp, + hx_get_merror_desc, + hx_must_mcode_close_block, + hx_is_mcode_propagatable, + hx_negate_mcode_relation, + hx_swap_mcode_relation, + hx_get_signed_mcode, + hx_get_unsigned_mcode, + hx_mcode_modifies_d, + hx_operand_locator_t_compare, + hx_vd_printer_t_print, + hx_file_printer_t_print, + hx_qstring_printer_t_print, + hx_dstr, + hx_is_type_correct, + hx_is_small_udt, + hx_is_nonbool_type, + hx_is_bool_type, + hx_partial_type_num, + hx_get_float_type, + hx_get_int_type_by_width_and_sign, + hx_get_unk_type, + hx_dummy_ptrtype, + hx_get_member_type, + hx_make_pointer, + hx_create_typedef, + hx_get_type, + hx_set_type, + hx_vdloc_t_dstr, + hx_vdloc_t_compare, + hx_vdloc_t_is_aliasable, + hx_print_vdloc, + hx_arglocs_overlap, + hx_lvar_locator_t_compare, + hx_lvar_locator_t_dstr, + hx_lvar_t_dstr, + hx_lvar_t_is_promoted_arg, + hx_lvar_t_accepts_type, + hx_lvar_t_set_lvar_type, + hx_lvar_t_set_width, + hx_lvar_t_append_list, + hx_lvar_t_append_list_, + hx_lvars_t_find_stkvar, + hx_lvars_t_find, + hx_lvars_t_find_lvar, + hx_restore_user_lvar_settings, + hx_save_user_lvar_settings, + hx_modify_user_lvars, + hx_modify_user_lvar_info, + hx_locate_lvar, + hx_restore_user_defined_calls, + hx_save_user_defined_calls, + hx_parse_user_call, + hx_convert_to_user_call, + hx_install_microcode_filter, + hx_udc_filter_t_cleanup, + hx_udc_filter_t_init, + hx_udc_filter_t_apply, + hx_bitset_t_bitset_t, + hx_bitset_t_copy, + hx_bitset_t_add, + hx_bitset_t_add_, + hx_bitset_t_add__, + hx_bitset_t_sub, + hx_bitset_t_sub_, + hx_bitset_t_sub__, + hx_bitset_t_cut_at, + hx_bitset_t_shift_down, + hx_bitset_t_has, + hx_bitset_t_has_all, + hx_bitset_t_has_any, + hx_bitset_t_dstr, + hx_bitset_t_empty, + hx_bitset_t_count, + hx_bitset_t_count_, + hx_bitset_t_last, + hx_bitset_t_fill_with_ones, + hx_bitset_t_fill_gaps, + hx_bitset_t_has_common, + hx_bitset_t_intersect, + hx_bitset_t_is_subset_of, + hx_bitset_t_compare, + hx_bitset_t_goup, + hx_ivl_t_dstr, + hx_ivl_t_compare, + hx_ivlset_t_add, + hx_ivlset_t_add_, + hx_ivlset_t_addmasked, + hx_ivlset_t_sub, + hx_ivlset_t_sub_, + hx_ivlset_t_has_common, + hx_ivlset_t_print, + hx_ivlset_t_dstr, + hx_ivlset_t_count, + hx_ivlset_t_has_common_, + hx_ivlset_t_contains, + hx_ivlset_t_includes, + hx_ivlset_t_intersect, + hx_ivlset_t_compare, + hx_rlist_t_print, + hx_rlist_t_dstr, + hx_mlist_t_addmem, + hx_mlist_t_print, + hx_mlist_t_dstr, + hx_mlist_t_compare, + hx_get_temp_regs, + hx_is_kreg, + hx_reg2mreg, + hx_mreg2reg, + hx_get_mreg_name, + hx_install_optinsn_handler, + hx_remove_optinsn_handler, + hx_install_optblock_handler, + hx_remove_optblock_handler, + hx_simple_graph_t_compute_dominators, + hx_simple_graph_t_compute_immediate_dominators, + hx_simple_graph_t_depth_first_preorder, + hx_simple_graph_t_depth_first_postorder, + hx_simple_graph_t_goup, + hx_mutable_graph_t_resize, + hx_mutable_graph_t_goup, + hx_mutable_graph_t_del_edge, + hx_lvar_ref_t_compare, + hx_lvar_ref_t_var, + hx_stkvar_ref_t_compare, + hx_stkvar_ref_t_get_stkvar, + hx_fnumber_t_print, + hx_fnumber_t_dstr, + hx_mop_t_copy, + hx_mop_t_assign, + hx_mop_t_swap, + hx_mop_t_erase, + hx_mop_t_print, + hx_mop_t_dstr, + hx_mop_t_create_from_mlist, + hx_mop_t_create_from_ivlset, + hx_mop_t_create_from_vdloc, + hx_mop_t_create_from_scattered_vdloc, + hx_mop_t_create_from_insn, + hx_mop_t_make_number, + hx_mop_t_make_fpnum, + hx_mop_t__make_gvar, + hx_mop_t_make_gvar, + hx_mop_t_make_reg_pair, + hx_mop_t_make_helper, + hx_mop_t_is_bit_reg, + hx_mop_t_may_use_aliased_memory, + hx_mop_t_is01, + hx_mop_t_is_sign_extended_from, + hx_mop_t_is_zero_extended_from, + hx_mop_t_equal_mops, + hx_mop_t_lexcompare, + hx_mop_t_for_all_ops, + hx_mop_t_for_all_scattered_submops, + hx_mop_t_is_constant, + hx_mop_t_get_stkoff, + hx_mop_t_make_low_half, + hx_mop_t_make_high_half, + hx_mop_t_make_first_half, + hx_mop_t_make_second_half, + hx_mop_t_shift_mop, + hx_mop_t_change_size, + hx_mop_t_preserve_side_effects, + hx_mop_t_apply_ld_mcode, + hx_mcallarg_t_print, + hx_mcallarg_t_dstr, + hx_mcallarg_t_set_regarg, + hx_mcallinfo_t_lexcompare, + hx_mcallinfo_t_set_type, + hx_mcallinfo_t_get_type, + hx_mcallinfo_t_print, + hx_mcallinfo_t_dstr, + hx_mcases_t_compare, + hx_mcases_t_print, + hx_mcases_t_dstr, + hx_vivl_t_extend_to_cover, + hx_vivl_t_intersect, + hx_vivl_t_print, + hx_vivl_t_dstr, + hx_chain_t_print, + hx_chain_t_dstr, + hx_chain_t_append_list, + hx_chain_t_append_list_, + hx_block_chains_t_get_chain, + hx_block_chains_t_print, + hx_block_chains_t_dstr, + hx_graph_chains_t_for_all_chains, + hx_graph_chains_t_release, + hx_minsn_t_init, + hx_minsn_t_copy, + hx_minsn_t_set_combined, + hx_minsn_t_swap, + hx_minsn_t_print, + hx_minsn_t_dstr, + hx_minsn_t_setaddr, + hx_minsn_t_optimize_subtree, + hx_minsn_t_for_all_ops, + hx_minsn_t_for_all_insns, + hx_minsn_t__make_nop, + hx_minsn_t_equal_insns, + hx_minsn_t_lexcompare, + hx_minsn_t_is_noret_call, + hx_minsn_t_is_helper, + hx_minsn_t_find_call, + hx_minsn_t_has_side_effects, + hx_minsn_t_find_opcode, + hx_minsn_t_find_ins_op, + hx_minsn_t_find_num_op, + hx_minsn_t_modifies_d, + hx_minsn_t_is_between, + hx_minsn_t_may_use_aliased_memory, + hx_minsn_t_serialize, + hx_minsn_t_deserialize, + hx_getf_reginsn, + hx_getb_reginsn, + hx_mblock_t_init, + hx_mblock_t_print, + hx_mblock_t_dump, + hx_mblock_t_vdump_block, + hx_mblock_t_insert_into_block, + hx_mblock_t_remove_from_block, + hx_mblock_t_for_all_insns, + hx_mblock_t_for_all_ops, + hx_mblock_t_for_all_uses, + hx_mblock_t_optimize_insn, + hx_mblock_t_optimize_block, + hx_mblock_t_build_lists, + hx_mblock_t_optimize_useless_jump, + hx_mblock_t_append_use_list, + hx_mblock_t_append_def_list, + hx_mblock_t_build_use_list, + hx_mblock_t_build_def_list, + hx_mblock_t_find_first_use, + hx_mblock_t_find_redefinition, + hx_mblock_t_is_rhs_redefined, + hx_mblock_t_find_access, + hx_mblock_t_get_valranges, + hx_mblock_t_get_valranges_, + hx_mblock_t_get_reginsn_qty, + hx_mba_ranges_t_range_contains, + hx_mba_t_stkoff_vd2ida, + hx_mba_t_stkoff_ida2vd, + hx_mba_t_idaloc2vd, + hx_mba_t_idaloc2vd_, + hx_mba_t_vd2idaloc, + hx_mba_t_vd2idaloc_, + hx_mba_t_term, + hx_mba_t_get_curfunc, + hx_mba_t_set_maturity, + hx_mba_t_optimize_local, + hx_mba_t_build_graph, + hx_mba_t_get_graph, + hx_mba_t_analyze_calls, + hx_mba_t_optimize_global, + hx_mba_t_alloc_lvars, + hx_mba_t_dump, + hx_mba_t_vdump_mba, + hx_mba_t_print, + hx_mba_t_verify, + hx_mba_t_mark_chains_dirty, + hx_mba_t_insert_block, + hx_mba_t_remove_block, + hx_mba_t_copy_block, + hx_mba_t_remove_empty_and_unreachable_blocks, + hx_mba_t_merge_blocks, + hx_mba_t_for_all_ops, + hx_mba_t_for_all_insns, + hx_mba_t_for_all_topinsns, + hx_mba_t_find_mop, + hx_mba_t_create_helper_call, + hx_mba_t_get_func_output_lists, + hx_mba_t_arg, + hx_mba_t_alloc_fict_ea, + hx_mba_t_map_fict_ea, + hx_mba_t_serialize, + hx_mba_t_deserialize, + hx_mba_t_save_snapshot, + hx_mba_t_alloc_kreg, + hx_mba_t_free_kreg, + hx_mba_t_inline_func, + hx_mba_t_locate_stkpnt, + hx_mba_t_set_lvar_name, + hx_mbl_graph_t_is_accessed_globally, + hx_mbl_graph_t_get_ud, + hx_mbl_graph_t_get_du, + hx_cdg_insn_iterator_t_next, + hx_codegen_t_clear, + hx_codegen_t_emit, + hx_codegen_t_emit_, + hx_change_hexrays_config, + hx_get_hexrays_version, + hx_open_pseudocode, + hx_close_pseudocode, + hx_get_widget_vdui, + hx_decompile_many, + hx_hexrays_failure_t_desc, + hx_send_database, + hx_gco_info_t_append_to_list, + hx_get_current_operand, + hx_remitem, + hx_negated_relation, + hx_swapped_relation, + hx_get_op_signness, + hx_asgop, + hx_asgop_revert, + hx_cnumber_t_print, + hx_cnumber_t_value, + hx_cnumber_t_assign, + hx_cnumber_t_compare, + hx_var_ref_t_compare, + hx_ctree_visitor_t_apply_to, + hx_ctree_visitor_t_apply_to_exprs, + hx_ctree_parentee_t_recalc_parent_types, + hx_cfunc_parentee_t_calc_rvalue_type, + hx_citem_locator_t_compare, + hx_citem_t_contains_expr, + hx_citem_t_contains_label, + hx_citem_t_find_parent_of, + hx_citem_t_find_closest_addr, + hx_cexpr_t_assign, + hx_cexpr_t_compare, + hx_cexpr_t_replace_by, + hx_cexpr_t_cleanup, + hx_cexpr_t_put_number, + hx_cexpr_t_print1, + hx_cexpr_t_calc_type, + hx_cexpr_t_equal_effect, + hx_cexpr_t_is_child_of, + hx_cexpr_t_contains_operator, + hx_cexpr_t_get_high_nbit_bound, + hx_cexpr_t_get_low_nbit_bound, + hx_cexpr_t_requires_lvalue, + hx_cexpr_t_has_side_effects, + hx_cexpr_t_maybe_ptr, + hx_cexpr_t_dstr, + hx_cif_t_assign, + hx_cif_t_compare, + hx_cloop_t_assign, + hx_cfor_t_compare, + hx_cwhile_t_compare, + hx_cdo_t_compare, + hx_creturn_t_compare, + hx_cthrow_t_compare, + hx_cgoto_t_compare, + hx_casm_t_compare, + hx_cinsn_t_assign, + hx_cinsn_t_compare, + hx_cinsn_t_replace_by, + hx_cinsn_t_cleanup, + hx_cinsn_t_new_insn, + hx_cinsn_t_create_if, + hx_cinsn_t_print, + hx_cinsn_t_print1, + hx_cinsn_t_is_ordinary_flow, + hx_cinsn_t_contains_insn, + hx_cinsn_t_collect_free_breaks, + hx_cinsn_t_collect_free_continues, + hx_cinsn_t_dstr, + hx_cblock_t_compare, + hx_carglist_t_compare, + hx_ccase_t_compare, + hx_ccases_t_compare, + hx_cswitch_t_compare, + hx_ccatch_t_compare, + hx_ctry_t_compare, + hx_ctree_item_t_get_udm, + hx_ctree_item_t_get_edm, + hx_ctree_item_t_get_lvar, + hx_ctree_item_t_get_ea, + hx_ctree_item_t_get_label_num, + hx_ctree_item_t_print, + hx_ctree_item_t_dstr, + hx_lnot, + hx_new_block, + hx_vcreate_helper, + hx_vcall_helper, + hx_make_num, + hx_make_ref, + hx_dereference, + hx_save_user_labels, + hx_save_user_cmts, + hx_save_user_numforms, + hx_save_user_iflags, + hx_save_user_unions, + hx_restore_user_labels, + hx_restore_user_cmts, + hx_restore_user_numforms, + hx_restore_user_iflags, + hx_restore_user_unions, + hx_cfunc_t_build_c_tree, + hx_cfunc_t_verify, + hx_cfunc_t_print_dcl, + hx_cfunc_t_print_func, + hx_cfunc_t_get_func_type, + hx_cfunc_t_get_lvars, + hx_cfunc_t_get_stkoff_delta, + hx_cfunc_t_find_label, + hx_cfunc_t_remove_unused_labels, + hx_cfunc_t_get_user_cmt, + hx_cfunc_t_set_user_cmt, + hx_cfunc_t_get_user_iflags, + hx_cfunc_t_set_user_iflags, + hx_cfunc_t_has_orphan_cmts, + hx_cfunc_t_del_orphan_cmts, + hx_cfunc_t_get_user_union_selection, + hx_cfunc_t_set_user_union_selection, + hx_cfunc_t_save_user_labels, + hx_cfunc_t_save_user_cmts, + hx_cfunc_t_save_user_numforms, + hx_cfunc_t_save_user_iflags, + hx_cfunc_t_save_user_unions, + hx_cfunc_t_get_line_item, + hx_cfunc_t_get_warnings, + hx_cfunc_t_get_eamap, + hx_cfunc_t_get_boundaries, + hx_cfunc_t_get_pseudocode, + hx_cfunc_t_refresh_func_ctext, + hx_cfunc_t_gather_derefs, + hx_cfunc_t_find_item_coords, + hx_cfunc_t_cleanup, + hx_close_hexrays_waitbox, + hx_decompile, + hx_gen_microcode, + hx_create_cfunc, + hx_mark_cfunc_dirty, + hx_clear_cached_cfuncs, + hx_has_cached_cfunc, + hx_get_ctype_name, + hx_create_field_name, + hx_install_hexrays_callback, + hx_remove_hexrays_callback, + hx_vdui_t_set_locked, + hx_vdui_t_refresh_view, + hx_vdui_t_refresh_ctext, + hx_vdui_t_switch_to, + hx_vdui_t_get_number, + hx_vdui_t_get_current_label, + hx_vdui_t_clear, + hx_vdui_t_refresh_cpos, + hx_vdui_t_get_current_item, + hx_vdui_t_ui_rename_lvar, + hx_vdui_t_rename_lvar, + hx_vdui_t_ui_set_call_type, + hx_vdui_t_ui_set_lvar_type, + hx_vdui_t_set_lvar_type, + hx_vdui_t_set_noptr_lvar, + hx_vdui_t_ui_edit_lvar_cmt, + hx_vdui_t_set_lvar_cmt, + hx_vdui_t_ui_map_lvar, + hx_vdui_t_ui_unmap_lvar, + hx_vdui_t_map_lvar, + hx_vdui_t_set_udm_type, + hx_vdui_t_rename_udm, + hx_vdui_t_set_global_type, + hx_vdui_t_rename_global, + hx_vdui_t_rename_label, + hx_vdui_t_jump_enter, + hx_vdui_t_ctree_to_disasm, + hx_vdui_t_calc_cmt_type, + hx_vdui_t_edit_cmt, + hx_vdui_t_edit_func_cmt, + hx_vdui_t_del_orphan_cmts, + hx_vdui_t_set_num_radix, + hx_vdui_t_set_num_enum, + hx_vdui_t_set_num_stroff, + hx_vdui_t_invert_sign, + hx_vdui_t_invert_bits, + hx_vdui_t_collapse_item, + hx_vdui_t_collapse_lvars, + hx_vdui_t_split_item, + hx_select_udt_by_offset, + hx_catchexpr_t_compare, + hx_mba_t_split_block, + hx_mba_t_remove_blocks, + hx_cfunc_t_recalc_item_addresses, + hx_int64_emulator_t_mop_value, + hx_int64_emulator_t_minsn_value, +}; + +typedef size_t iterator_word; + +//-------------------------------------------------------------------------- +/// Check that your plugin is compatible with hex-rays decompiler. +/// This function must be called before calling any other decompiler function. +/// \param flags reserved, must be 0 +/// \return true if the decompiler exists and is compatible with your plugin +inline bool init_hexrays_plugin(int flags=0) +{ + hexdsp_t *dummy; + return callui(ui_broadcast, HEXRAYS_API_MAGIC, &dummy, flags).i == (HEXRAYS_API_MAGIC >> 32); +} + +//-------------------------------------------------------------------------- +/// Stop working with hex-rays decompiler. +inline void term_hexrays_plugin() +{ +} + + +//------------------------------------------------------------------------- +struct user_numforms_iterator_t +{ + iterator_word x; + bool operator==(const user_numforms_iterator_t &p) const { return x == p.x; } + bool operator!=(const user_numforms_iterator_t &p) const { return x != p.x; } +}; + +//------------------------------------------------------------------------- +/// Get reference to the current map key +inline operand_locator_t const &user_numforms_first(user_numforms_iterator_t p) +{ + return *(operand_locator_t *)HEXDSP(hx_user_numforms_first, &p); +} + +//------------------------------------------------------------------------- +/// Get reference to the current map value +inline number_format_t &user_numforms_second(user_numforms_iterator_t p) +{ + return *(number_format_t *)HEXDSP(hx_user_numforms_second, &p); +} + +//------------------------------------------------------------------------- +/// Find the specified key in user_numforms_t +inline user_numforms_iterator_t user_numforms_find(const user_numforms_t *map, const operand_locator_t &key) +{ + user_numforms_iterator_t p; + HEXDSP(hx_user_numforms_find, &p, map, &key); + return p; +} + +//------------------------------------------------------------------------- +/// Insert new (operand_locator_t, number_format_t) pair into user_numforms_t +inline user_numforms_iterator_t user_numforms_insert(user_numforms_t *map, const operand_locator_t &key, const number_format_t &val) +{ + user_numforms_iterator_t p; + HEXDSP(hx_user_numforms_insert, &p, map, &key, &val); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the beginning of user_numforms_t +inline user_numforms_iterator_t user_numforms_begin(const user_numforms_t *map) +{ + user_numforms_iterator_t p; + HEXDSP(hx_user_numforms_begin, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the end of user_numforms_t +inline user_numforms_iterator_t user_numforms_end(const user_numforms_t *map) +{ + user_numforms_iterator_t p; + HEXDSP(hx_user_numforms_end, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the next element +inline user_numforms_iterator_t user_numforms_next(user_numforms_iterator_t p) +{ + HEXDSP(hx_user_numforms_next, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the previous element +inline user_numforms_iterator_t user_numforms_prev(user_numforms_iterator_t p) +{ + HEXDSP(hx_user_numforms_prev, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Erase current element from user_numforms_t +inline void user_numforms_erase(user_numforms_t *map, user_numforms_iterator_t p) +{ + HEXDSP(hx_user_numforms_erase, map, &p); +} + +//------------------------------------------------------------------------- +/// Clear user_numforms_t +inline void user_numforms_clear(user_numforms_t *map) +{ + HEXDSP(hx_user_numforms_clear, map); +} + +//------------------------------------------------------------------------- +/// Get size of user_numforms_t +inline size_t user_numforms_size(user_numforms_t *map) +{ + return (size_t)HEXDSP(hx_user_numforms_size, map); +} + +//------------------------------------------------------------------------- +/// Delete user_numforms_t instance +inline void user_numforms_free(user_numforms_t *map) +{ + HEXDSP(hx_user_numforms_free, map); +} + +//------------------------------------------------------------------------- +/// Create a new user_numforms_t instance +inline user_numforms_t *user_numforms_new() +{ + return (user_numforms_t *)HEXDSP(hx_user_numforms_new); +} + +//------------------------------------------------------------------------- +struct lvar_mapping_iterator_t +{ + iterator_word x; + bool operator==(const lvar_mapping_iterator_t &p) const { return x == p.x; } + bool operator!=(const lvar_mapping_iterator_t &p) const { return x != p.x; } +}; + +//------------------------------------------------------------------------- +/// Get reference to the current map key +inline lvar_locator_t const &lvar_mapping_first(lvar_mapping_iterator_t p) +{ + return *(lvar_locator_t *)HEXDSP(hx_lvar_mapping_first, &p); +} + +//------------------------------------------------------------------------- +/// Get reference to the current map value +inline lvar_locator_t &lvar_mapping_second(lvar_mapping_iterator_t p) +{ + return *(lvar_locator_t *)HEXDSP(hx_lvar_mapping_second, &p); +} + +//------------------------------------------------------------------------- +/// Find the specified key in lvar_mapping_t +inline lvar_mapping_iterator_t lvar_mapping_find(const lvar_mapping_t *map, const lvar_locator_t &key) +{ + lvar_mapping_iterator_t p; + HEXDSP(hx_lvar_mapping_find, &p, map, &key); + return p; +} + +//------------------------------------------------------------------------- +/// Insert new (lvar_locator_t, lvar_locator_t) pair into lvar_mapping_t +inline lvar_mapping_iterator_t lvar_mapping_insert(lvar_mapping_t *map, const lvar_locator_t &key, const lvar_locator_t &val) +{ + lvar_mapping_iterator_t p; + HEXDSP(hx_lvar_mapping_insert, &p, map, &key, &val); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the beginning of lvar_mapping_t +inline lvar_mapping_iterator_t lvar_mapping_begin(const lvar_mapping_t *map) +{ + lvar_mapping_iterator_t p; + HEXDSP(hx_lvar_mapping_begin, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the end of lvar_mapping_t +inline lvar_mapping_iterator_t lvar_mapping_end(const lvar_mapping_t *map) +{ + lvar_mapping_iterator_t p; + HEXDSP(hx_lvar_mapping_end, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the next element +inline lvar_mapping_iterator_t lvar_mapping_next(lvar_mapping_iterator_t p) +{ + HEXDSP(hx_lvar_mapping_next, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the previous element +inline lvar_mapping_iterator_t lvar_mapping_prev(lvar_mapping_iterator_t p) +{ + HEXDSP(hx_lvar_mapping_prev, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Erase current element from lvar_mapping_t +inline void lvar_mapping_erase(lvar_mapping_t *map, lvar_mapping_iterator_t p) +{ + HEXDSP(hx_lvar_mapping_erase, map, &p); +} + +//------------------------------------------------------------------------- +/// Clear lvar_mapping_t +inline void lvar_mapping_clear(lvar_mapping_t *map) +{ + HEXDSP(hx_lvar_mapping_clear, map); +} + +//------------------------------------------------------------------------- +/// Get size of lvar_mapping_t +inline size_t lvar_mapping_size(lvar_mapping_t *map) +{ + return (size_t)HEXDSP(hx_lvar_mapping_size, map); +} + +//------------------------------------------------------------------------- +/// Delete lvar_mapping_t instance +inline void lvar_mapping_free(lvar_mapping_t *map) +{ + HEXDSP(hx_lvar_mapping_free, map); +} + +//------------------------------------------------------------------------- +/// Create a new lvar_mapping_t instance +inline lvar_mapping_t *lvar_mapping_new() +{ + return (lvar_mapping_t *)HEXDSP(hx_lvar_mapping_new); +} + +//------------------------------------------------------------------------- +struct udcall_map_iterator_t +{ + iterator_word x; + bool operator==(const udcall_map_iterator_t &p) const { return x == p.x; } + bool operator!=(const udcall_map_iterator_t &p) const { return x != p.x; } +}; + +//------------------------------------------------------------------------- +/// Get reference to the current map key +inline ea_t const &udcall_map_first(udcall_map_iterator_t p) +{ + return *(ea_t *)HEXDSP(hx_udcall_map_first, &p); +} + +//------------------------------------------------------------------------- +/// Get reference to the current map value +inline udcall_t &udcall_map_second(udcall_map_iterator_t p) +{ + return *(udcall_t *)HEXDSP(hx_udcall_map_second, &p); +} + +//------------------------------------------------------------------------- +/// Find the specified key in udcall_map_t +inline udcall_map_iterator_t udcall_map_find(const udcall_map_t *map, const ea_t &key) +{ + udcall_map_iterator_t p; + HEXDSP(hx_udcall_map_find, &p, map, &key); + return p; +} + +//------------------------------------------------------------------------- +/// Insert new (ea_t, udcall_t) pair into udcall_map_t +inline udcall_map_iterator_t udcall_map_insert(udcall_map_t *map, const ea_t &key, const udcall_t &val) +{ + udcall_map_iterator_t p; + HEXDSP(hx_udcall_map_insert, &p, map, &key, &val); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the beginning of udcall_map_t +inline udcall_map_iterator_t udcall_map_begin(const udcall_map_t *map) +{ + udcall_map_iterator_t p; + HEXDSP(hx_udcall_map_begin, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the end of udcall_map_t +inline udcall_map_iterator_t udcall_map_end(const udcall_map_t *map) +{ + udcall_map_iterator_t p; + HEXDSP(hx_udcall_map_end, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the next element +inline udcall_map_iterator_t udcall_map_next(udcall_map_iterator_t p) +{ + HEXDSP(hx_udcall_map_next, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the previous element +inline udcall_map_iterator_t udcall_map_prev(udcall_map_iterator_t p) +{ + HEXDSP(hx_udcall_map_prev, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Erase current element from udcall_map_t +inline void udcall_map_erase(udcall_map_t *map, udcall_map_iterator_t p) +{ + HEXDSP(hx_udcall_map_erase, map, &p); +} + +//------------------------------------------------------------------------- +/// Clear udcall_map_t +inline void udcall_map_clear(udcall_map_t *map) +{ + HEXDSP(hx_udcall_map_clear, map); +} + +//------------------------------------------------------------------------- +/// Get size of udcall_map_t +inline size_t udcall_map_size(udcall_map_t *map) +{ + return (size_t)HEXDSP(hx_udcall_map_size, map); +} + +//------------------------------------------------------------------------- +/// Delete udcall_map_t instance +inline void udcall_map_free(udcall_map_t *map) +{ + HEXDSP(hx_udcall_map_free, map); +} + +//------------------------------------------------------------------------- +/// Create a new udcall_map_t instance +inline udcall_map_t *udcall_map_new() +{ + return (udcall_map_t *)HEXDSP(hx_udcall_map_new); +} + +//------------------------------------------------------------------------- +struct user_cmts_iterator_t +{ + iterator_word x; + bool operator==(const user_cmts_iterator_t &p) const { return x == p.x; } + bool operator!=(const user_cmts_iterator_t &p) const { return x != p.x; } +}; + +//------------------------------------------------------------------------- +/// Get reference to the current map key +inline treeloc_t const &user_cmts_first(user_cmts_iterator_t p) +{ + return *(treeloc_t *)HEXDSP(hx_user_cmts_first, &p); +} + +//------------------------------------------------------------------------- +/// Get reference to the current map value +inline citem_cmt_t &user_cmts_second(user_cmts_iterator_t p) +{ + return *(citem_cmt_t *)HEXDSP(hx_user_cmts_second, &p); +} + +//------------------------------------------------------------------------- +/// Find the specified key in user_cmts_t +inline user_cmts_iterator_t user_cmts_find(const user_cmts_t *map, const treeloc_t &key) +{ + user_cmts_iterator_t p; + HEXDSP(hx_user_cmts_find, &p, map, &key); + return p; +} + +//------------------------------------------------------------------------- +/// Insert new (treeloc_t, citem_cmt_t) pair into user_cmts_t +inline user_cmts_iterator_t user_cmts_insert(user_cmts_t *map, const treeloc_t &key, const citem_cmt_t &val) +{ + user_cmts_iterator_t p; + HEXDSP(hx_user_cmts_insert, &p, map, &key, &val); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the beginning of user_cmts_t +inline user_cmts_iterator_t user_cmts_begin(const user_cmts_t *map) +{ + user_cmts_iterator_t p; + HEXDSP(hx_user_cmts_begin, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the end of user_cmts_t +inline user_cmts_iterator_t user_cmts_end(const user_cmts_t *map) +{ + user_cmts_iterator_t p; + HEXDSP(hx_user_cmts_end, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the next element +inline user_cmts_iterator_t user_cmts_next(user_cmts_iterator_t p) +{ + HEXDSP(hx_user_cmts_next, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the previous element +inline user_cmts_iterator_t user_cmts_prev(user_cmts_iterator_t p) +{ + HEXDSP(hx_user_cmts_prev, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Erase current element from user_cmts_t +inline void user_cmts_erase(user_cmts_t *map, user_cmts_iterator_t p) +{ + HEXDSP(hx_user_cmts_erase, map, &p); +} + +//------------------------------------------------------------------------- +/// Clear user_cmts_t +inline void user_cmts_clear(user_cmts_t *map) +{ + HEXDSP(hx_user_cmts_clear, map); +} + +//------------------------------------------------------------------------- +/// Get size of user_cmts_t +inline size_t user_cmts_size(user_cmts_t *map) +{ + return (size_t)HEXDSP(hx_user_cmts_size, map); +} + +//------------------------------------------------------------------------- +/// Delete user_cmts_t instance +inline void user_cmts_free(user_cmts_t *map) +{ + HEXDSP(hx_user_cmts_free, map); +} + +//------------------------------------------------------------------------- +/// Create a new user_cmts_t instance +inline user_cmts_t *user_cmts_new() +{ + return (user_cmts_t *)HEXDSP(hx_user_cmts_new); +} + +//------------------------------------------------------------------------- +struct user_iflags_iterator_t +{ + iterator_word x; + bool operator==(const user_iflags_iterator_t &p) const { return x == p.x; } + bool operator!=(const user_iflags_iterator_t &p) const { return x != p.x; } +}; + +//------------------------------------------------------------------------- +/// Get reference to the current map key +inline citem_locator_t const &user_iflags_first(user_iflags_iterator_t p) +{ + return *(citem_locator_t *)HEXDSP(hx_user_iflags_first, &p); +} + +//------------------------------------------------------------------------- +/// Get reference to the current map value +inline int32 &user_iflags_second(user_iflags_iterator_t p) +{ + return *(int32 *)HEXDSP(hx_user_iflags_second, &p); +} + +//------------------------------------------------------------------------- +/// Find the specified key in user_iflags_t +inline user_iflags_iterator_t user_iflags_find(const user_iflags_t *map, const citem_locator_t &key) +{ + user_iflags_iterator_t p; + HEXDSP(hx_user_iflags_find, &p, map, &key); + return p; +} + +//------------------------------------------------------------------------- +/// Insert new (citem_locator_t, int32) pair into user_iflags_t +inline user_iflags_iterator_t user_iflags_insert(user_iflags_t *map, const citem_locator_t &key, const int32 &val) +{ + user_iflags_iterator_t p; + HEXDSP(hx_user_iflags_insert, &p, map, &key, &val); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the beginning of user_iflags_t +inline user_iflags_iterator_t user_iflags_begin(const user_iflags_t *map) +{ + user_iflags_iterator_t p; + HEXDSP(hx_user_iflags_begin, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the end of user_iflags_t +inline user_iflags_iterator_t user_iflags_end(const user_iflags_t *map) +{ + user_iflags_iterator_t p; + HEXDSP(hx_user_iflags_end, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the next element +inline user_iflags_iterator_t user_iflags_next(user_iflags_iterator_t p) +{ + HEXDSP(hx_user_iflags_next, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the previous element +inline user_iflags_iterator_t user_iflags_prev(user_iflags_iterator_t p) +{ + HEXDSP(hx_user_iflags_prev, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Erase current element from user_iflags_t +inline void user_iflags_erase(user_iflags_t *map, user_iflags_iterator_t p) +{ + HEXDSP(hx_user_iflags_erase, map, &p); +} + +//------------------------------------------------------------------------- +/// Clear user_iflags_t +inline void user_iflags_clear(user_iflags_t *map) +{ + HEXDSP(hx_user_iflags_clear, map); +} + +//------------------------------------------------------------------------- +/// Get size of user_iflags_t +inline size_t user_iflags_size(user_iflags_t *map) +{ + return (size_t)HEXDSP(hx_user_iflags_size, map); +} + +//------------------------------------------------------------------------- +/// Delete user_iflags_t instance +inline void user_iflags_free(user_iflags_t *map) +{ + HEXDSP(hx_user_iflags_free, map); +} + +//------------------------------------------------------------------------- +/// Create a new user_iflags_t instance +inline user_iflags_t *user_iflags_new() +{ + return (user_iflags_t *)HEXDSP(hx_user_iflags_new); +} + +//------------------------------------------------------------------------- +struct user_unions_iterator_t +{ + iterator_word x; + bool operator==(const user_unions_iterator_t &p) const { return x == p.x; } + bool operator!=(const user_unions_iterator_t &p) const { return x != p.x; } +}; + +//------------------------------------------------------------------------- +/// Get reference to the current map key +inline ea_t const &user_unions_first(user_unions_iterator_t p) +{ + return *(ea_t *)HEXDSP(hx_user_unions_first, &p); +} + +//------------------------------------------------------------------------- +/// Get reference to the current map value +inline intvec_t &user_unions_second(user_unions_iterator_t p) +{ + return *(intvec_t *)HEXDSP(hx_user_unions_second, &p); +} + +//------------------------------------------------------------------------- +/// Find the specified key in user_unions_t +inline user_unions_iterator_t user_unions_find(const user_unions_t *map, const ea_t &key) +{ + user_unions_iterator_t p; + HEXDSP(hx_user_unions_find, &p, map, &key); + return p; +} + +//------------------------------------------------------------------------- +/// Insert new (ea_t, intvec_t) pair into user_unions_t +inline user_unions_iterator_t user_unions_insert(user_unions_t *map, const ea_t &key, const intvec_t &val) +{ + user_unions_iterator_t p; + HEXDSP(hx_user_unions_insert, &p, map, &key, &val); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the beginning of user_unions_t +inline user_unions_iterator_t user_unions_begin(const user_unions_t *map) +{ + user_unions_iterator_t p; + HEXDSP(hx_user_unions_begin, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the end of user_unions_t +inline user_unions_iterator_t user_unions_end(const user_unions_t *map) +{ + user_unions_iterator_t p; + HEXDSP(hx_user_unions_end, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the next element +inline user_unions_iterator_t user_unions_next(user_unions_iterator_t p) +{ + HEXDSP(hx_user_unions_next, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the previous element +inline user_unions_iterator_t user_unions_prev(user_unions_iterator_t p) +{ + HEXDSP(hx_user_unions_prev, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Erase current element from user_unions_t +inline void user_unions_erase(user_unions_t *map, user_unions_iterator_t p) +{ + HEXDSP(hx_user_unions_erase, map, &p); +} + +//------------------------------------------------------------------------- +/// Clear user_unions_t +inline void user_unions_clear(user_unions_t *map) +{ + HEXDSP(hx_user_unions_clear, map); +} + +//------------------------------------------------------------------------- +/// Get size of user_unions_t +inline size_t user_unions_size(user_unions_t *map) +{ + return (size_t)HEXDSP(hx_user_unions_size, map); +} + +//------------------------------------------------------------------------- +/// Delete user_unions_t instance +inline void user_unions_free(user_unions_t *map) +{ + HEXDSP(hx_user_unions_free, map); +} + +//------------------------------------------------------------------------- +/// Create a new user_unions_t instance +inline user_unions_t *user_unions_new() +{ + return (user_unions_t *)HEXDSP(hx_user_unions_new); +} + +//------------------------------------------------------------------------- +struct user_labels_iterator_t +{ + iterator_word x; + bool operator==(const user_labels_iterator_t &p) const { return x == p.x; } + bool operator!=(const user_labels_iterator_t &p) const { return x != p.x; } +}; + +//------------------------------------------------------------------------- +/// Get reference to the current map key +inline int const &user_labels_first(user_labels_iterator_t p) +{ + return *(int *)HEXDSP(hx_user_labels_first, &p); +} + +//------------------------------------------------------------------------- +/// Get reference to the current map value +inline qstring &user_labels_second(user_labels_iterator_t p) +{ + return *(qstring *)HEXDSP(hx_user_labels_second, &p); +} + +//------------------------------------------------------------------------- +/// Find the specified key in user_labels_t +inline user_labels_iterator_t user_labels_find(const user_labels_t *map, const int &key) +{ + user_labels_iterator_t p; + HEXDSP(hx_user_labels_find, &p, map, &key); + return p; +} + +//------------------------------------------------------------------------- +/// Insert new (int, qstring) pair into user_labels_t +inline user_labels_iterator_t user_labels_insert(user_labels_t *map, const int &key, const qstring &val) +{ + user_labels_iterator_t p; + HEXDSP(hx_user_labels_insert, &p, map, &key, &val); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the beginning of user_labels_t +inline user_labels_iterator_t user_labels_begin(const user_labels_t *map) +{ + user_labels_iterator_t p; + HEXDSP(hx_user_labels_begin, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the end of user_labels_t +inline user_labels_iterator_t user_labels_end(const user_labels_t *map) +{ + user_labels_iterator_t p; + HEXDSP(hx_user_labels_end, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the next element +inline user_labels_iterator_t user_labels_next(user_labels_iterator_t p) +{ + HEXDSP(hx_user_labels_next, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the previous element +inline user_labels_iterator_t user_labels_prev(user_labels_iterator_t p) +{ + HEXDSP(hx_user_labels_prev, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Erase current element from user_labels_t +inline void user_labels_erase(user_labels_t *map, user_labels_iterator_t p) +{ + HEXDSP(hx_user_labels_erase, map, &p); +} + +//------------------------------------------------------------------------- +/// Clear user_labels_t +inline void user_labels_clear(user_labels_t *map) +{ + HEXDSP(hx_user_labels_clear, map); +} + +//------------------------------------------------------------------------- +/// Get size of user_labels_t +inline size_t user_labels_size(user_labels_t *map) +{ + return (size_t)HEXDSP(hx_user_labels_size, map); +} + +//------------------------------------------------------------------------- +/// Delete user_labels_t instance +inline void user_labels_free(user_labels_t *map) +{ + HEXDSP(hx_user_labels_free, map); +} + +//------------------------------------------------------------------------- +/// Create a new user_labels_t instance +inline user_labels_t *user_labels_new() +{ + return (user_labels_t *)HEXDSP(hx_user_labels_new); +} + +//------------------------------------------------------------------------- +struct eamap_iterator_t +{ + iterator_word x; + bool operator==(const eamap_iterator_t &p) const { return x == p.x; } + bool operator!=(const eamap_iterator_t &p) const { return x != p.x; } +}; + +//------------------------------------------------------------------------- +/// Get reference to the current map key +inline ea_t const &eamap_first(eamap_iterator_t p) +{ + return *(ea_t *)HEXDSP(hx_eamap_first, &p); +} + +//------------------------------------------------------------------------- +/// Get reference to the current map value +inline cinsnptrvec_t &eamap_second(eamap_iterator_t p) +{ + return *(cinsnptrvec_t *)HEXDSP(hx_eamap_second, &p); +} + +//------------------------------------------------------------------------- +/// Find the specified key in eamap_t +inline eamap_iterator_t eamap_find(const eamap_t *map, const ea_t &key) +{ + eamap_iterator_t p; + HEXDSP(hx_eamap_find, &p, map, &key); + return p; +} + +//------------------------------------------------------------------------- +/// Insert new (ea_t, cinsnptrvec_t) pair into eamap_t +inline eamap_iterator_t eamap_insert(eamap_t *map, const ea_t &key, const cinsnptrvec_t &val) +{ + eamap_iterator_t p; + HEXDSP(hx_eamap_insert, &p, map, &key, &val); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the beginning of eamap_t +inline eamap_iterator_t eamap_begin(const eamap_t *map) +{ + eamap_iterator_t p; + HEXDSP(hx_eamap_begin, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the end of eamap_t +inline eamap_iterator_t eamap_end(const eamap_t *map) +{ + eamap_iterator_t p; + HEXDSP(hx_eamap_end, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the next element +inline eamap_iterator_t eamap_next(eamap_iterator_t p) +{ + HEXDSP(hx_eamap_next, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the previous element +inline eamap_iterator_t eamap_prev(eamap_iterator_t p) +{ + HEXDSP(hx_eamap_prev, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Erase current element from eamap_t +inline void eamap_erase(eamap_t *map, eamap_iterator_t p) +{ + HEXDSP(hx_eamap_erase, map, &p); +} + +//------------------------------------------------------------------------- +/// Clear eamap_t +inline void eamap_clear(eamap_t *map) +{ + HEXDSP(hx_eamap_clear, map); +} + +//------------------------------------------------------------------------- +/// Get size of eamap_t +inline size_t eamap_size(eamap_t *map) +{ + return (size_t)HEXDSP(hx_eamap_size, map); +} + +//------------------------------------------------------------------------- +/// Delete eamap_t instance +inline void eamap_free(eamap_t *map) +{ + HEXDSP(hx_eamap_free, map); +} + +//------------------------------------------------------------------------- +/// Create a new eamap_t instance +inline eamap_t *eamap_new() +{ + return (eamap_t *)HEXDSP(hx_eamap_new); +} + +//------------------------------------------------------------------------- +struct boundaries_iterator_t +{ + iterator_word x; + bool operator==(const boundaries_iterator_t &p) const { return x == p.x; } + bool operator!=(const boundaries_iterator_t &p) const { return x != p.x; } +}; + +//------------------------------------------------------------------------- +/// Get reference to the current map key +inline cinsn_t *const &boundaries_first(boundaries_iterator_t p) +{ + return *(cinsn_t * *)HEXDSP(hx_boundaries_first, &p); +} + +//------------------------------------------------------------------------- +/// Get reference to the current map value +inline rangeset_t &boundaries_second(boundaries_iterator_t p) +{ + return *(rangeset_t *)HEXDSP(hx_boundaries_second, &p); +} + +//------------------------------------------------------------------------- +/// Find the specified key in boundaries_t +inline boundaries_iterator_t boundaries_find(const boundaries_t *map, const cinsn_t * &key) +{ + boundaries_iterator_t p; + HEXDSP(hx_boundaries_find, &p, map, &key); + return p; +} + +//------------------------------------------------------------------------- +/// Insert new (cinsn_t *, rangeset_t) pair into boundaries_t +inline boundaries_iterator_t boundaries_insert(boundaries_t *map, const cinsn_t * &key, const rangeset_t &val) +{ + boundaries_iterator_t p; + HEXDSP(hx_boundaries_insert, &p, map, &key, &val); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the beginning of boundaries_t +inline boundaries_iterator_t boundaries_begin(const boundaries_t *map) +{ + boundaries_iterator_t p; + HEXDSP(hx_boundaries_begin, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the end of boundaries_t +inline boundaries_iterator_t boundaries_end(const boundaries_t *map) +{ + boundaries_iterator_t p; + HEXDSP(hx_boundaries_end, &p, map); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the next element +inline boundaries_iterator_t boundaries_next(boundaries_iterator_t p) +{ + HEXDSP(hx_boundaries_next, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the previous element +inline boundaries_iterator_t boundaries_prev(boundaries_iterator_t p) +{ + HEXDSP(hx_boundaries_prev, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Erase current element from boundaries_t +inline void boundaries_erase(boundaries_t *map, boundaries_iterator_t p) +{ + HEXDSP(hx_boundaries_erase, map, &p); +} + +//------------------------------------------------------------------------- +/// Clear boundaries_t +inline void boundaries_clear(boundaries_t *map) +{ + HEXDSP(hx_boundaries_clear, map); +} + +//------------------------------------------------------------------------- +/// Get size of boundaries_t +inline size_t boundaries_size(boundaries_t *map) +{ + return (size_t)HEXDSP(hx_boundaries_size, map); +} + +//------------------------------------------------------------------------- +/// Delete boundaries_t instance +inline void boundaries_free(boundaries_t *map) +{ + HEXDSP(hx_boundaries_free, map); +} + +//------------------------------------------------------------------------- +/// Create a new boundaries_t instance +inline boundaries_t *boundaries_new() +{ + return (boundaries_t *)HEXDSP(hx_boundaries_new); +} + +//------------------------------------------------------------------------- +struct block_chains_iterator_t +{ + iterator_word x; + bool operator==(const block_chains_iterator_t &p) const { return x == p.x; } + bool operator!=(const block_chains_iterator_t &p) const { return x != p.x; } +}; + +//------------------------------------------------------------------------- +/// Get reference to the current set value +inline chain_t &block_chains_get(block_chains_iterator_t p) +{ + return *(chain_t *)HEXDSP(hx_block_chains_get, &p); +} + +//------------------------------------------------------------------------- +/// Find the specified key in set block_chains_t +inline block_chains_iterator_t block_chains_find(const block_chains_t *set, const chain_t &val) +{ + block_chains_iterator_t p; + HEXDSP(hx_block_chains_find, &p, set, &val); + return p; +} + +//------------------------------------------------------------------------- +/// Insert new (chain_t) into set block_chains_t +inline block_chains_iterator_t block_chains_insert(block_chains_t *set, const chain_t &val) +{ + block_chains_iterator_t p; + HEXDSP(hx_block_chains_insert, &p, set, &val); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the beginning of block_chains_t +inline block_chains_iterator_t block_chains_begin(const block_chains_t *set) +{ + block_chains_iterator_t p; + HEXDSP(hx_block_chains_begin, &p, set); + return p; +} + +//------------------------------------------------------------------------- +/// Get iterator pointing to the end of block_chains_t +inline block_chains_iterator_t block_chains_end(const block_chains_t *set) +{ + block_chains_iterator_t p; + HEXDSP(hx_block_chains_end, &p, set); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the next element +inline block_chains_iterator_t block_chains_next(block_chains_iterator_t p) +{ + HEXDSP(hx_block_chains_next, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Move to the previous element +inline block_chains_iterator_t block_chains_prev(block_chains_iterator_t p) +{ + HEXDSP(hx_block_chains_prev, &p); + return p; +} + +//------------------------------------------------------------------------- +/// Erase current element from block_chains_t +inline void block_chains_erase(block_chains_t *set, block_chains_iterator_t p) +{ + HEXDSP(hx_block_chains_erase, set, &p); +} + +//------------------------------------------------------------------------- +/// Clear block_chains_t +inline void block_chains_clear(block_chains_t *set) +{ + HEXDSP(hx_block_chains_clear, set); +} + +//------------------------------------------------------------------------- +/// Get size of block_chains_t +inline size_t block_chains_size(block_chains_t *set) +{ + return (size_t)HEXDSP(hx_block_chains_size, set); +} + +//------------------------------------------------------------------------- +/// Delete block_chains_t instance +inline void block_chains_free(block_chains_t *set) +{ + HEXDSP(hx_block_chains_free, set); +} + +//------------------------------------------------------------------------- +/// Create a new block_chains_t instance +inline block_chains_t *block_chains_new() +{ + return (block_chains_t *)HEXDSP(hx_block_chains_new); +} + +//-------------------------------------------------------------------------- +inline void *hexrays_alloc(size_t size) +{ + return HEXDSP(hx_hexrays_alloc, size); +} + +//-------------------------------------------------------------------------- +inline void hexrays_free(void *ptr) +{ + HEXDSP(hx_hexrays_free, ptr); +} + +//-------------------------------------------------------------------------- +inline void valrng_t::clear() +{ + HEXDSP(hx_valrng_t_clear, this); +} + +//-------------------------------------------------------------------------- +inline void valrng_t::copy(const valrng_t &r) +{ + HEXDSP(hx_valrng_t_copy, this, &r); +} + +//-------------------------------------------------------------------------- +inline valrng_t &valrng_t::assign(const valrng_t &r) +{ + return *(valrng_t *)HEXDSP(hx_valrng_t_assign, this, &r); +} + +//-------------------------------------------------------------------------- +inline int valrng_t::compare(const valrng_t &r) const +{ + return (int)(size_t)HEXDSP(hx_valrng_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline void valrng_t::set_eq(uvlr_t v) +{ + HEXDSP(hx_valrng_t_set_eq, this, v); +} + +//-------------------------------------------------------------------------- +inline void valrng_t::set_cmp(cmpop_t cmp, uvlr_t _value) +{ + HEXDSP(hx_valrng_t_set_cmp, this, cmp, _value); +} + +//-------------------------------------------------------------------------- +inline bool valrng_t::reduce_size(int new_size) +{ + return (uchar)(size_t)HEXDSP(hx_valrng_t_reduce_size, this, new_size) != 0; +} + +//-------------------------------------------------------------------------- +inline bool valrng_t::intersect_with(const valrng_t &r) +{ + return (uchar)(size_t)HEXDSP(hx_valrng_t_intersect_with, this, &r) != 0; +} + +//-------------------------------------------------------------------------- +inline bool valrng_t::unite_with(const valrng_t &r) +{ + return (uchar)(size_t)HEXDSP(hx_valrng_t_unite_with, this, &r) != 0; +} + +//-------------------------------------------------------------------------- +inline void valrng_t::inverse() +{ + HEXDSP(hx_valrng_t_inverse, this); +} + +//-------------------------------------------------------------------------- +inline bool valrng_t::has(uvlr_t v) const +{ + return (uchar)(size_t)HEXDSP(hx_valrng_t_has, this, v) != 0; +} + +//-------------------------------------------------------------------------- +inline void valrng_t::print(qstring *vout) const +{ + HEXDSP(hx_valrng_t_print, this, vout); +} + +//-------------------------------------------------------------------------- +inline const char *valrng_t::dstr() const +{ + return (const char *)HEXDSP(hx_valrng_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline bool valrng_t::cvt_to_single_value(uvlr_t *v) const +{ + return (uchar)(size_t)HEXDSP(hx_valrng_t_cvt_to_single_value, this, v) != 0; +} + +//-------------------------------------------------------------------------- +inline bool valrng_t::cvt_to_cmp(cmpop_t *cmp, uvlr_t *val) const +{ + return (uchar)(size_t)HEXDSP(hx_valrng_t_cvt_to_cmp, this, cmp, val) != 0; +} + +//-------------------------------------------------------------------------- +inline ea_t get_merror_desc(qstring *out, merror_t code, mba_t *mba) +{ + ea_t retval; + HEXDSP(hx_get_merror_desc, &retval, out, code, mba); + return retval; +} + +//-------------------------------------------------------------------------- +inline THREAD_SAFE bool must_mcode_close_block(mcode_t mcode, bool including_calls) +{ + return (uchar)(size_t)HEXDSP(hx_must_mcode_close_block, mcode, including_calls) != 0; +} + +//-------------------------------------------------------------------------- +inline THREAD_SAFE bool is_mcode_propagatable(mcode_t mcode) +{ + return (uchar)(size_t)HEXDSP(hx_is_mcode_propagatable, mcode) != 0; +} + +//-------------------------------------------------------------------------- +inline THREAD_SAFE mcode_t negate_mcode_relation(mcode_t code) +{ + return (mcode_t)(size_t)HEXDSP(hx_negate_mcode_relation, code); +} + +//-------------------------------------------------------------------------- +inline THREAD_SAFE mcode_t swap_mcode_relation(mcode_t code) +{ + return (mcode_t)(size_t)HEXDSP(hx_swap_mcode_relation, code); +} + +//-------------------------------------------------------------------------- +inline THREAD_SAFE mcode_t get_signed_mcode(mcode_t code) +{ + return (mcode_t)(size_t)HEXDSP(hx_get_signed_mcode, code); +} + +//-------------------------------------------------------------------------- +inline THREAD_SAFE mcode_t get_unsigned_mcode(mcode_t code) +{ + return (mcode_t)(size_t)HEXDSP(hx_get_unsigned_mcode, code); +} + +//-------------------------------------------------------------------------- +inline THREAD_SAFE bool mcode_modifies_d(mcode_t mcode) +{ + return (uchar)(size_t)HEXDSP(hx_mcode_modifies_d, mcode) != 0; +} + +//-------------------------------------------------------------------------- +inline int operand_locator_t::compare(const operand_locator_t &r) const +{ + return (int)(size_t)HEXDSP(hx_operand_locator_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline AS_PRINTF(3, 4) int vd_printer_t::print(int indent, const char *format, ...) +{ + va_list va; + va_start(va, format); + int retval = (int)(size_t)HEXDSP(hx_vd_printer_t_print, this, indent, format, va); + va_end(va); + return retval; +} + +//-------------------------------------------------------------------------- +inline AS_PRINTF(3, 4) int file_printer_t::print(int indent, const char *format, ...) +{ + va_list va; + va_start(va, format); + int retval = (int)(size_t)HEXDSP(hx_file_printer_t_print, this, indent, format, va); + va_end(va); + return retval; +} + +//-------------------------------------------------------------------------- +inline AS_PRINTF(3, 4) int qstring_printer_t::print(int indent, const char *format, ...) +{ + va_list va; + va_start(va, format); + int retval = (int)(size_t)HEXDSP(hx_qstring_printer_t_print, this, indent, format, va); + va_end(va); + return retval; +} + +//-------------------------------------------------------------------------- +inline const char *dstr(const tinfo_t *tif) +{ + return (const char *)HEXDSP(hx_dstr, tif); +} + +//-------------------------------------------------------------------------- +inline bool is_type_correct(const type_t *ptr) +{ + return (uchar)(size_t)HEXDSP(hx_is_type_correct, ptr) != 0; +} + +//-------------------------------------------------------------------------- +inline bool is_small_udt(const tinfo_t &tif) +{ + return (uchar)(size_t)HEXDSP(hx_is_small_udt, &tif) != 0; +} + +//-------------------------------------------------------------------------- +inline bool is_nonbool_type(const tinfo_t &type) +{ + return (uchar)(size_t)HEXDSP(hx_is_nonbool_type, &type) != 0; +} + +//-------------------------------------------------------------------------- +inline bool is_bool_type(const tinfo_t &type) +{ + return (uchar)(size_t)HEXDSP(hx_is_bool_type, &type) != 0; +} + +//-------------------------------------------------------------------------- +inline int partial_type_num(const tinfo_t &type) +{ + return (int)(size_t)HEXDSP(hx_partial_type_num, &type); +} + +//-------------------------------------------------------------------------- +inline tinfo_t get_float_type(int width) +{ + tinfo_t retval; + HEXDSP(hx_get_float_type, &retval, width); + return retval; +} + +//-------------------------------------------------------------------------- +inline tinfo_t get_int_type_by_width_and_sign(int srcwidth, type_sign_t sign) +{ + tinfo_t retval; + HEXDSP(hx_get_int_type_by_width_and_sign, &retval, srcwidth, sign); + return retval; +} + +//-------------------------------------------------------------------------- +inline tinfo_t get_unk_type(int size) +{ + tinfo_t retval; + HEXDSP(hx_get_unk_type, &retval, size); + return retval; +} + +//-------------------------------------------------------------------------- +inline tinfo_t dummy_ptrtype(int ptrsize, bool isfp) +{ + tinfo_t retval; + HEXDSP(hx_dummy_ptrtype, &retval, ptrsize, isfp); + return retval; +} + +//-------------------------------------------------------------------------- +inline tinfo_t make_pointer(const tinfo_t &type) +{ + tinfo_t retval; + HEXDSP(hx_make_pointer, &retval, &type); + return retval; +} + +//-------------------------------------------------------------------------- +inline tinfo_t create_typedef(const char *name) +{ + tinfo_t retval; + HEXDSP(hx_create_typedef, &retval, name); + return retval; +} + +//-------------------------------------------------------------------------- +inline bool get_type(uval_t id, tinfo_t *tif, type_source_t guess) +{ + return (uchar)(size_t)HEXDSP(hx_get_type, id, tif, guess) != 0; +} + +//-------------------------------------------------------------------------- +inline bool set_type(uval_t id, const tinfo_t &tif, type_source_t source, bool force) +{ + return (uchar)(size_t)HEXDSP(hx_set_type, id, &tif, source, force) != 0; +} + +//-------------------------------------------------------------------------- +inline const char *vdloc_t::dstr(int width) const +{ + return (const char *)HEXDSP(hx_vdloc_t_dstr, this, width); +} + +//-------------------------------------------------------------------------- +inline int vdloc_t::compare(const vdloc_t &r) const +{ + return (int)(size_t)HEXDSP(hx_vdloc_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline bool vdloc_t::is_aliasable(const mba_t *mb, int size) const +{ + return (uchar)(size_t)HEXDSP(hx_vdloc_t_is_aliasable, this, mb, size) != 0; +} + +//-------------------------------------------------------------------------- +inline void print_vdloc(qstring *vout, const vdloc_t &loc, int nbytes) +{ + HEXDSP(hx_print_vdloc, vout, &loc, nbytes); +} + +//-------------------------------------------------------------------------- +inline bool arglocs_overlap(const vdloc_t &loc1, size_t w1, const vdloc_t &loc2, size_t w2) +{ + return (uchar)(size_t)HEXDSP(hx_arglocs_overlap, &loc1, w1, &loc2, w2) != 0; +} + +//-------------------------------------------------------------------------- +inline int lvar_locator_t::compare(const lvar_locator_t &r) const +{ + return (int)(size_t)HEXDSP(hx_lvar_locator_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline const char *lvar_locator_t::dstr() const +{ + return (const char *)HEXDSP(hx_lvar_locator_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline const char *lvar_t::dstr() const +{ + return (const char *)HEXDSP(hx_lvar_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline bool lvar_t::is_promoted_arg() const +{ + return (uchar)(size_t)HEXDSP(hx_lvar_t_is_promoted_arg, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool lvar_t::accepts_type(const tinfo_t &t, bool may_change_thisarg) +{ + return (uchar)(size_t)HEXDSP(hx_lvar_t_accepts_type, this, &t, may_change_thisarg) != 0; +} + +//-------------------------------------------------------------------------- +inline bool lvar_t::set_lvar_type(const tinfo_t &t, bool may_fail) +{ + return (uchar)(size_t)HEXDSP(hx_lvar_t_set_lvar_type, this, &t, may_fail) != 0; +} + +//-------------------------------------------------------------------------- +inline bool lvar_t::set_width(int w, int svw_flags) +{ + return (uchar)(size_t)HEXDSP(hx_lvar_t_set_width, this, w, svw_flags) != 0; +} + +//-------------------------------------------------------------------------- +inline void lvar_t::append_list(const mba_t *mba, mlist_t *lst, bool pad_if_scattered) const +{ + HEXDSP(hx_lvar_t_append_list, this, mba, lst, pad_if_scattered); +} + +//-------------------------------------------------------------------------- +inline int lvars_t::find_stkvar(sval_t spoff, int width) +{ + return (int)(size_t)HEXDSP(hx_lvars_t_find_stkvar, this, spoff, width); +} + +//-------------------------------------------------------------------------- +inline lvar_t *lvars_t::find(const lvar_locator_t &ll) +{ + return (lvar_t *)HEXDSP(hx_lvars_t_find, this, &ll); +} + +//-------------------------------------------------------------------------- +inline int lvars_t::find_lvar(const vdloc_t &location, int width, int defblk) const +{ + return (int)(size_t)HEXDSP(hx_lvars_t_find_lvar, this, &location, width, defblk); +} + +//-------------------------------------------------------------------------- +inline bool restore_user_lvar_settings(lvar_uservec_t *lvinf, ea_t func_ea) +{ + return (uchar)(size_t)HEXDSP(hx_restore_user_lvar_settings, lvinf, func_ea) != 0; +} + +//-------------------------------------------------------------------------- +inline void save_user_lvar_settings(ea_t func_ea, const lvar_uservec_t &lvinf) +{ + HEXDSP(hx_save_user_lvar_settings, func_ea, &lvinf); +} + +//-------------------------------------------------------------------------- +inline bool modify_user_lvars(ea_t entry_ea, user_lvar_modifier_t &mlv) +{ + return (uchar)(size_t)HEXDSP(hx_modify_user_lvars, entry_ea, &mlv) != 0; +} + +//-------------------------------------------------------------------------- +inline bool modify_user_lvar_info(ea_t func_ea, uint mli_flags, const lvar_saved_info_t &info) +{ + return (uchar)(size_t)HEXDSP(hx_modify_user_lvar_info, func_ea, mli_flags, &info) != 0; +} + +//-------------------------------------------------------------------------- +inline bool locate_lvar(lvar_locator_t *out, ea_t func_ea, const char *varname) +{ + return (uchar)(size_t)HEXDSP(hx_locate_lvar, out, func_ea, varname) != 0; +} + +//-------------------------------------------------------------------------- +inline bool restore_user_defined_calls(udcall_map_t *udcalls, ea_t func_ea) +{ + return (uchar)(size_t)HEXDSP(hx_restore_user_defined_calls, udcalls, func_ea) != 0; +} + +//-------------------------------------------------------------------------- +inline void save_user_defined_calls(ea_t func_ea, const udcall_map_t &udcalls) +{ + HEXDSP(hx_save_user_defined_calls, func_ea, &udcalls); +} + +//-------------------------------------------------------------------------- +inline bool parse_user_call(udcall_t *udc, const char *decl, bool silent) +{ + return (uchar)(size_t)HEXDSP(hx_parse_user_call, udc, decl, silent) != 0; +} + +//-------------------------------------------------------------------------- +inline merror_t convert_to_user_call(const udcall_t &udc, codegen_t &cdg) +{ + return (merror_t)(size_t)HEXDSP(hx_convert_to_user_call, &udc, &cdg); +} + +//-------------------------------------------------------------------------- +inline bool install_microcode_filter(microcode_filter_t *filter, bool install) +{ + auto hrdsp = HEXDSP; + return hrdsp != nullptr && (uchar)(size_t)hrdsp(hx_install_microcode_filter, filter, install) != 0; +} + +//-------------------------------------------------------------------------- +inline void udc_filter_t::cleanup() +{ + HEXDSP(hx_udc_filter_t_cleanup, this); +} + +//-------------------------------------------------------------------------- +inline bool udc_filter_t::init(const char *decl) +{ + return (uchar)(size_t)HEXDSP(hx_udc_filter_t_init, this, decl) != 0; +} + +//-------------------------------------------------------------------------- +inline merror_t udc_filter_t::apply(codegen_t &cdg) +{ + return (merror_t)(size_t)HEXDSP(hx_udc_filter_t_apply, this, &cdg); +} + +//-------------------------------------------------------------------------- +inline bitset_t::bitset_t(const bitset_t &m) +{ + HEXDSP(hx_bitset_t_bitset_t, this, &m); +} + +//-------------------------------------------------------------------------- +inline bitset_t &bitset_t::copy(const bitset_t &m) +{ + return *(bitset_t *)HEXDSP(hx_bitset_t_copy, this, &m); +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::add(int bit) +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_add, this, bit) != 0; +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::add(int bit, int width) +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_add_, this, bit, width) != 0; +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::add(const bitset_t &ml) +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_add__, this, &ml) != 0; +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::sub(int bit) +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_sub, this, bit) != 0; +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::sub(int bit, int width) +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_sub_, this, bit, width) != 0; +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::sub(const bitset_t &ml) +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_sub__, this, &ml) != 0; +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::cut_at(int maxbit) +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_cut_at, this, maxbit) != 0; +} + +//-------------------------------------------------------------------------- +inline void bitset_t::shift_down(int shift) +{ + HEXDSP(hx_bitset_t_shift_down, this, shift); +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::has(int bit) const +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_has, this, bit) != 0; +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::has_all(int bit, int width) const +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_has_all, this, bit, width) != 0; +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::has_any(int bit, int width) const +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_has_any, this, bit, width) != 0; +} + +//-------------------------------------------------------------------------- +inline const char *bitset_t::dstr() const +{ + return (const char *)HEXDSP(hx_bitset_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::empty() const +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_empty, this) != 0; +} + +//-------------------------------------------------------------------------- +inline int bitset_t::count() const +{ + return (int)(size_t)HEXDSP(hx_bitset_t_count, this); +} + +//-------------------------------------------------------------------------- +inline int bitset_t::count(int bit) const +{ + return (int)(size_t)HEXDSP(hx_bitset_t_count_, this, bit); +} + +//-------------------------------------------------------------------------- +inline int bitset_t::last() const +{ + return (int)(size_t)HEXDSP(hx_bitset_t_last, this); +} + +//-------------------------------------------------------------------------- +inline void bitset_t::fill_with_ones(int maxbit) +{ + HEXDSP(hx_bitset_t_fill_with_ones, this, maxbit); +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::fill_gaps(int total_nbits) +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_fill_gaps, this, total_nbits) != 0; +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::has_common(const bitset_t &ml) const +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_has_common, this, &ml) != 0; +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::intersect(const bitset_t &ml) +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_intersect, this, &ml) != 0; +} + +//-------------------------------------------------------------------------- +inline bool bitset_t::is_subset_of(const bitset_t &ml) const +{ + return (uchar)(size_t)HEXDSP(hx_bitset_t_is_subset_of, this, &ml) != 0; +} + +//-------------------------------------------------------------------------- +inline int bitset_t::compare(const bitset_t &r) const +{ + return (int)(size_t)HEXDSP(hx_bitset_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int bitset_t::goup(int reg) const +{ + return (int)(size_t)HEXDSP(hx_bitset_t_goup, this, reg); +} + +//-------------------------------------------------------------------------- +inline const char *ivl_t::dstr() const +{ + return (const char *)HEXDSP(hx_ivl_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline int ivl_t::compare(const ivl_t &r) const +{ + return (int)(size_t)HEXDSP(hx_ivl_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline bool ivlset_t::add(const ivl_t &ivl) +{ + return (uchar)(size_t)HEXDSP(hx_ivlset_t_add, this, &ivl) != 0; +} + +//-------------------------------------------------------------------------- +inline bool ivlset_t::add(const ivlset_t &ivs) +{ + return (uchar)(size_t)HEXDSP(hx_ivlset_t_add_, this, &ivs) != 0; +} + +//-------------------------------------------------------------------------- +inline bool ivlset_t::addmasked(const ivlset_t &ivs, const ivl_t &mask) +{ + return (uchar)(size_t)HEXDSP(hx_ivlset_t_addmasked, this, &ivs, &mask) != 0; +} + +//-------------------------------------------------------------------------- +inline bool ivlset_t::sub(const ivl_t &ivl) +{ + return (uchar)(size_t)HEXDSP(hx_ivlset_t_sub, this, &ivl) != 0; +} + +//-------------------------------------------------------------------------- +inline bool ivlset_t::sub(const ivlset_t &ivs) +{ + return (uchar)(size_t)HEXDSP(hx_ivlset_t_sub_, this, &ivs) != 0; +} + +//-------------------------------------------------------------------------- +inline bool ivlset_t::has_common(const ivl_t &ivl, bool strict) const +{ + return (uchar)(size_t)HEXDSP(hx_ivlset_t_has_common, this, &ivl, strict) != 0; +} + +//-------------------------------------------------------------------------- +inline void ivlset_t::print(qstring *vout) const +{ + HEXDSP(hx_ivlset_t_print, this, vout); +} + +//-------------------------------------------------------------------------- +inline const char *ivlset_t::dstr() const +{ + return (const char *)HEXDSP(hx_ivlset_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline asize_t ivlset_t::count() const +{ + asize_t retval; + HEXDSP(hx_ivlset_t_count, &retval, this); + return retval; +} + +//-------------------------------------------------------------------------- +inline bool ivlset_t::has_common(const ivlset_t &ivs) const +{ + return (uchar)(size_t)HEXDSP(hx_ivlset_t_has_common_, this, &ivs) != 0; +} + +//-------------------------------------------------------------------------- +inline bool ivlset_t::contains(uval_t off) const +{ + return (uchar)(size_t)HEXDSP(hx_ivlset_t_contains, this, off) != 0; +} + +//-------------------------------------------------------------------------- +inline bool ivlset_t::includes(const ivlset_t &ivs) const +{ + return (uchar)(size_t)HEXDSP(hx_ivlset_t_includes, this, &ivs) != 0; +} + +//-------------------------------------------------------------------------- +inline bool ivlset_t::intersect(const ivlset_t &ivs) +{ + return (uchar)(size_t)HEXDSP(hx_ivlset_t_intersect, this, &ivs) != 0; +} + +//-------------------------------------------------------------------------- +inline int ivlset_t::compare(const ivlset_t &r) const +{ + return (int)(size_t)HEXDSP(hx_ivlset_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline void rlist_t::print(qstring *vout) const +{ + HEXDSP(hx_rlist_t_print, this, vout); +} + +//-------------------------------------------------------------------------- +inline const char *rlist_t::dstr() const +{ + return (const char *)HEXDSP(hx_rlist_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline bool mlist_t::addmem(ea_t ea, asize_t size) +{ + return (uchar)(size_t)HEXDSP(hx_mlist_t_addmem, this, ea, size) != 0; +} + +//-------------------------------------------------------------------------- +inline void mlist_t::print(qstring *vout) const +{ + HEXDSP(hx_mlist_t_print, this, vout); +} + +//-------------------------------------------------------------------------- +inline const char *mlist_t::dstr() const +{ + return (const char *)HEXDSP(hx_mlist_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline int mlist_t::compare(const mlist_t &r) const +{ + return (int)(size_t)HEXDSP(hx_mlist_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline const mlist_t &get_temp_regs() +{ + return *(const mlist_t *)HEXDSP(hx_get_temp_regs); +} + +//-------------------------------------------------------------------------- +inline bool is_kreg(mreg_t r) +{ + return (uchar)(size_t)HEXDSP(hx_is_kreg, r) != 0; +} + +//-------------------------------------------------------------------------- +inline mreg_t reg2mreg(int reg) +{ + return (mreg_t)(size_t)HEXDSP(hx_reg2mreg, reg); +} + +//-------------------------------------------------------------------------- +inline int mreg2reg(mreg_t reg, int width) +{ + return (int)(size_t)HEXDSP(hx_mreg2reg, reg, width); +} + +//-------------------------------------------------------------------------- +inline int get_mreg_name(qstring *out, mreg_t reg, int width, void *ud) +{ + return (int)(size_t)HEXDSP(hx_get_mreg_name, out, reg, width, ud); +} + +//-------------------------------------------------------------------------- +inline void install_optinsn_handler(optinsn_t *opt) +{ + HEXDSP(hx_install_optinsn_handler, opt); +} + +//-------------------------------------------------------------------------- +inline bool remove_optinsn_handler(optinsn_t *opt) +{ + auto hrdsp = HEXDSP; + return hrdsp != nullptr && (uchar)(size_t)hrdsp(hx_remove_optinsn_handler, opt) != 0; +} + +//-------------------------------------------------------------------------- +inline void install_optblock_handler(optblock_t *opt) +{ + HEXDSP(hx_install_optblock_handler, opt); +} + +//-------------------------------------------------------------------------- +inline bool remove_optblock_handler(optblock_t *opt) +{ + auto hrdsp = HEXDSP; + return hrdsp != nullptr && (uchar)(size_t)hrdsp(hx_remove_optblock_handler, opt) != 0; +} + +//-------------------------------------------------------------------------- +inline void simple_graph_t::compute_dominators(array_of_node_bitset_t &domin, bool post) const +{ + HEXDSP(hx_simple_graph_t_compute_dominators, this, &domin, post); +} + +//-------------------------------------------------------------------------- +inline void simple_graph_t::compute_immediate_dominators(const array_of_node_bitset_t &domin, intvec_t &idomin, bool post) const +{ + HEXDSP(hx_simple_graph_t_compute_immediate_dominators, this, &domin, &idomin, post); +} + +//-------------------------------------------------------------------------- +inline int simple_graph_t::depth_first_preorder(node_ordering_t *pre) const +{ + return (int)(size_t)HEXDSP(hx_simple_graph_t_depth_first_preorder, this, pre); +} + +//-------------------------------------------------------------------------- +inline int simple_graph_t::depth_first_postorder(node_ordering_t *post) const +{ + return (int)(size_t)HEXDSP(hx_simple_graph_t_depth_first_postorder, this, post); +} + +//-------------------------------------------------------------------------- +inline int simple_graph_t::goup(int node) const +{ + return (int)(size_t)HEXDSP(hx_simple_graph_t_goup, this, node); +} + +//-------------------------------------------------------------------------- +inline int lvar_ref_t::compare(const lvar_ref_t &r) const +{ + return (int)(size_t)HEXDSP(hx_lvar_ref_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline lvar_t &lvar_ref_t::var() const +{ + return *(lvar_t *)HEXDSP(hx_lvar_ref_t_var, this); +} + +//-------------------------------------------------------------------------- +inline int stkvar_ref_t::compare(const stkvar_ref_t &r) const +{ + return (int)(size_t)HEXDSP(hx_stkvar_ref_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline ssize_t stkvar_ref_t::get_stkvar(udm_t *udm, uval_t *p_idaoff) const +{ + return (ssize_t)HEXDSP(hx_stkvar_ref_t_get_stkvar, this, udm, p_idaoff); +} + +//-------------------------------------------------------------------------- +inline void fnumber_t::print(qstring *vout) const +{ + HEXDSP(hx_fnumber_t_print, this, vout); +} + +//-------------------------------------------------------------------------- +inline const char *fnumber_t::dstr() const +{ + return (const char *)HEXDSP(hx_fnumber_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline void mop_t::copy(const mop_t &rop) +{ + HEXDSP(hx_mop_t_copy, this, &rop); +} + +//-------------------------------------------------------------------------- +inline mop_t &mop_t::assign(const mop_t &rop) +{ + return *(mop_t *)HEXDSP(hx_mop_t_assign, this, &rop); +} + +//-------------------------------------------------------------------------- +inline void mop_t::swap(mop_t &rop) +{ + HEXDSP(hx_mop_t_swap, this, &rop); +} + +//-------------------------------------------------------------------------- +inline void mop_t::erase() +{ + HEXDSP(hx_mop_t_erase, this); +} + +//-------------------------------------------------------------------------- +inline void mop_t::print(qstring *vout, int shins_flags) const +{ + HEXDSP(hx_mop_t_print, this, vout, shins_flags); +} + +//-------------------------------------------------------------------------- +inline const char *mop_t::dstr() const +{ + return (const char *)HEXDSP(hx_mop_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline bool mop_t::create_from_mlist(mba_t *mba, const mlist_t &lst, sval_t fullsize) +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_create_from_mlist, this, mba, &lst, fullsize) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::create_from_ivlset(mba_t *mba, const ivlset_t &ivs, sval_t fullsize) +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_create_from_ivlset, this, mba, &ivs, fullsize) != 0; +} + +//-------------------------------------------------------------------------- +inline void mop_t::create_from_vdloc(mba_t *mba, const vdloc_t &loc, int _size) +{ + HEXDSP(hx_mop_t_create_from_vdloc, this, mba, &loc, _size); +} + +//-------------------------------------------------------------------------- +inline void mop_t::create_from_scattered_vdloc(mba_t *mba, const char *name, tinfo_t type, const vdloc_t &loc) +{ + HEXDSP(hx_mop_t_create_from_scattered_vdloc, this, mba, name, &type, &loc); +} + +//-------------------------------------------------------------------------- +inline void mop_t::create_from_insn(const minsn_t *m) +{ + HEXDSP(hx_mop_t_create_from_insn, this, m); +} + +//-------------------------------------------------------------------------- +inline void mop_t::make_number(uint64 _value, int _size, ea_t _ea, int opnum) +{ + HEXDSP(hx_mop_t_make_number, this, _value, _size, _ea, opnum); +} + +//-------------------------------------------------------------------------- +inline bool mop_t::make_fpnum(const void *bytes, size_t _size) +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_make_fpnum, this, bytes, _size) != 0; +} + +//-------------------------------------------------------------------------- +inline void mop_t::_make_gvar(ea_t ea) +{ + HEXDSP(hx_mop_t__make_gvar, this, ea); +} + +//-------------------------------------------------------------------------- +inline void mop_t::make_gvar(ea_t ea) +{ + HEXDSP(hx_mop_t_make_gvar, this, ea); +} + +//-------------------------------------------------------------------------- +inline void mop_t::make_reg_pair(int loreg, int hireg, int halfsize) +{ + HEXDSP(hx_mop_t_make_reg_pair, this, loreg, hireg, halfsize); +} + +//-------------------------------------------------------------------------- +inline void mop_t::make_helper(const char *name) +{ + HEXDSP(hx_mop_t_make_helper, this, name); +} + +//-------------------------------------------------------------------------- +inline bool mop_t::is_bit_reg(mreg_t reg) +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_is_bit_reg, reg) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::may_use_aliased_memory() const +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_may_use_aliased_memory, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::is01() const +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_is01, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::is_sign_extended_from(int nbytes) const +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_is_sign_extended_from, this, nbytes) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::is_zero_extended_from(int nbytes) const +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_is_zero_extended_from, this, nbytes) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::equal_mops(const mop_t &rop, int eqflags) const +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_equal_mops, this, &rop, eqflags) != 0; +} + +//-------------------------------------------------------------------------- +inline int mop_t::lexcompare(const mop_t &rop) const +{ + return (int)(size_t)HEXDSP(hx_mop_t_lexcompare, this, &rop); +} + +//-------------------------------------------------------------------------- +inline int mop_t::for_all_ops(mop_visitor_t &mv, const tinfo_t *type, bool is_target) +{ + return (int)(size_t)HEXDSP(hx_mop_t_for_all_ops, this, &mv, type, is_target); +} + +//-------------------------------------------------------------------------- +inline int mop_t::for_all_scattered_submops(scif_visitor_t &sv) const +{ + return (int)(size_t)HEXDSP(hx_mop_t_for_all_scattered_submops, this, &sv); +} + +//-------------------------------------------------------------------------- +inline bool mop_t::is_constant(uint64 *out, bool is_signed) const +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_is_constant, this, out, is_signed) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::get_stkoff(sval_t *p_vdoff) const +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_get_stkoff, this, p_vdoff) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::make_low_half(int width) +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_make_low_half, this, width) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::make_high_half(int width) +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_make_high_half, this, width) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::make_first_half(int width) +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_make_first_half, this, width) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::make_second_half(int width) +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_make_second_half, this, width) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::shift_mop(int offset) +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_shift_mop, this, offset) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::change_size(int nsize, side_effect_t sideff) +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_change_size, this, nsize, sideff) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mop_t::preserve_side_effects(mblock_t *blk, minsn_t *top, bool *moved_calls) +{ + return (uchar)(size_t)HEXDSP(hx_mop_t_preserve_side_effects, this, blk, top, moved_calls) != 0; +} + +//-------------------------------------------------------------------------- +inline void mop_t::apply_ld_mcode(mcode_t mcode, ea_t ea, int newsize) +{ + HEXDSP(hx_mop_t_apply_ld_mcode, this, mcode, ea, newsize); +} + +//-------------------------------------------------------------------------- +inline void mcallarg_t::print(qstring *vout, int shins_flags) const +{ + HEXDSP(hx_mcallarg_t_print, this, vout, shins_flags); +} + +//-------------------------------------------------------------------------- +inline const char *mcallarg_t::dstr() const +{ + return (const char *)HEXDSP(hx_mcallarg_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline void mcallarg_t::set_regarg(mreg_t mr, int sz, const tinfo_t &tif) +{ + HEXDSP(hx_mcallarg_t_set_regarg, this, mr, sz, &tif); +} + +//-------------------------------------------------------------------------- +inline int mcallinfo_t::lexcompare(const mcallinfo_t &f) const +{ + return (int)(size_t)HEXDSP(hx_mcallinfo_t_lexcompare, this, &f); +} + +//-------------------------------------------------------------------------- +inline bool mcallinfo_t::set_type(const tinfo_t &type) +{ + return (uchar)(size_t)HEXDSP(hx_mcallinfo_t_set_type, this, &type) != 0; +} + +//-------------------------------------------------------------------------- +inline tinfo_t mcallinfo_t::get_type() const +{ + tinfo_t retval; + HEXDSP(hx_mcallinfo_t_get_type, &retval, this); + return retval; +} + +//-------------------------------------------------------------------------- +inline void mcallinfo_t::print(qstring *vout, int size, int shins_flags) const +{ + HEXDSP(hx_mcallinfo_t_print, this, vout, size, shins_flags); +} + +//-------------------------------------------------------------------------- +inline const char *mcallinfo_t::dstr() const +{ + return (const char *)HEXDSP(hx_mcallinfo_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline int mcases_t::compare(const mcases_t &r) const +{ + return (int)(size_t)HEXDSP(hx_mcases_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline void mcases_t::print(qstring *vout) const +{ + HEXDSP(hx_mcases_t_print, this, vout); +} + +//-------------------------------------------------------------------------- +inline const char *mcases_t::dstr() const +{ + return (const char *)HEXDSP(hx_mcases_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline bool vivl_t::extend_to_cover(const vivl_t &r) +{ + return (uchar)(size_t)HEXDSP(hx_vivl_t_extend_to_cover, this, &r) != 0; +} + +//-------------------------------------------------------------------------- +inline uval_t vivl_t::intersect(const vivl_t &r) +{ + uval_t retval; + HEXDSP(hx_vivl_t_intersect, &retval, this, &r); + return retval; +} + +//-------------------------------------------------------------------------- +inline void vivl_t::print(qstring *vout) const +{ + HEXDSP(hx_vivl_t_print, this, vout); +} + +//-------------------------------------------------------------------------- +inline const char *vivl_t::dstr() const +{ + return (const char *)HEXDSP(hx_vivl_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline void chain_t::print(qstring *vout) const +{ + HEXDSP(hx_chain_t_print, this, vout); +} + +//-------------------------------------------------------------------------- +inline const char *chain_t::dstr() const +{ + return (const char *)HEXDSP(hx_chain_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline void chain_t::append_list(const mba_t *mba, mlist_t *list) const +{ + HEXDSP(hx_chain_t_append_list, this, mba, list); +} + +//-------------------------------------------------------------------------- +inline const chain_t *block_chains_t::get_chain(const chain_t &ch) const +{ + return (const chain_t *)HEXDSP(hx_block_chains_t_get_chain, this, &ch); +} + +//-------------------------------------------------------------------------- +inline void block_chains_t::print(qstring *vout) const +{ + HEXDSP(hx_block_chains_t_print, this, vout); +} + +//-------------------------------------------------------------------------- +inline const char *block_chains_t::dstr() const +{ + return (const char *)HEXDSP(hx_block_chains_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline int graph_chains_t::for_all_chains(chain_visitor_t &cv, int gca_flags) +{ + return (int)(size_t)HEXDSP(hx_graph_chains_t_for_all_chains, this, &cv, gca_flags); +} + +//-------------------------------------------------------------------------- +inline void graph_chains_t::release() +{ + HEXDSP(hx_graph_chains_t_release, this); +} + +//-------------------------------------------------------------------------- +inline void minsn_t::init(ea_t _ea) +{ + HEXDSP(hx_minsn_t_init, this, _ea); +} + +//-------------------------------------------------------------------------- +inline void minsn_t::copy(const minsn_t &m) +{ + HEXDSP(hx_minsn_t_copy, this, &m); +} + +//-------------------------------------------------------------------------- +inline void minsn_t::set_combined() +{ + HEXDSP(hx_minsn_t_set_combined, this); +} + +//-------------------------------------------------------------------------- +inline void minsn_t::swap(minsn_t &m) +{ + HEXDSP(hx_minsn_t_swap, this, &m); +} + +//-------------------------------------------------------------------------- +inline void minsn_t::print(qstring *vout, int shins_flags) const +{ + HEXDSP(hx_minsn_t_print, this, vout, shins_flags); +} + +//-------------------------------------------------------------------------- +inline const char *minsn_t::dstr() const +{ + return (const char *)HEXDSP(hx_minsn_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline void minsn_t::setaddr(ea_t new_ea) +{ + HEXDSP(hx_minsn_t_setaddr, this, new_ea); +} + +//-------------------------------------------------------------------------- +inline int minsn_t::optimize_subtree(mblock_t *blk, minsn_t *top, minsn_t *parent, ea_t *converted_call, int optflags) +{ + return (int)(size_t)HEXDSP(hx_minsn_t_optimize_subtree, this, blk, top, parent, converted_call, optflags); +} + +//-------------------------------------------------------------------------- +inline int minsn_t::for_all_ops(mop_visitor_t &mv) +{ + return (int)(size_t)HEXDSP(hx_minsn_t_for_all_ops, this, &mv); +} + +//-------------------------------------------------------------------------- +inline int minsn_t::for_all_insns(minsn_visitor_t &mv) +{ + return (int)(size_t)HEXDSP(hx_minsn_t_for_all_insns, this, &mv); +} + +//-------------------------------------------------------------------------- +inline void minsn_t::_make_nop() +{ + HEXDSP(hx_minsn_t__make_nop, this); +} + +//-------------------------------------------------------------------------- +inline bool minsn_t::equal_insns(const minsn_t &m, int eqflags) const +{ + return (uchar)(size_t)HEXDSP(hx_minsn_t_equal_insns, this, &m, eqflags) != 0; +} + +//-------------------------------------------------------------------------- +inline int minsn_t::lexcompare(const minsn_t &ri) const +{ + return (int)(size_t)HEXDSP(hx_minsn_t_lexcompare, this, &ri); +} + +//-------------------------------------------------------------------------- +inline bool minsn_t::is_noret_call(int flags) +{ + return (uchar)(size_t)HEXDSP(hx_minsn_t_is_noret_call, this, flags) != 0; +} + +//-------------------------------------------------------------------------- +inline bool minsn_t::is_helper(const char *name) const +{ + return (uchar)(size_t)HEXDSP(hx_minsn_t_is_helper, this, name) != 0; +} + +//-------------------------------------------------------------------------- +inline minsn_t *minsn_t::find_call(bool with_helpers) const +{ + return (minsn_t *)HEXDSP(hx_minsn_t_find_call, this, with_helpers); +} + +//-------------------------------------------------------------------------- +inline bool minsn_t::has_side_effects(bool include_ldx_and_divs) const +{ + return (uchar)(size_t)HEXDSP(hx_minsn_t_has_side_effects, this, include_ldx_and_divs) != 0; +} + +//-------------------------------------------------------------------------- +inline minsn_t *minsn_t::find_opcode(mcode_t mcode) +{ + return (minsn_t *)HEXDSP(hx_minsn_t_find_opcode, this, mcode); +} + +//-------------------------------------------------------------------------- +inline const minsn_t *minsn_t::find_ins_op(const mop_t **other, mcode_t op) const +{ + return (const minsn_t *)HEXDSP(hx_minsn_t_find_ins_op, this, other, op); +} + +//-------------------------------------------------------------------------- +inline const mop_t *minsn_t::find_num_op(const mop_t **other) const +{ + return (const mop_t *)HEXDSP(hx_minsn_t_find_num_op, this, other); +} + +//-------------------------------------------------------------------------- +inline bool minsn_t::modifies_d() const +{ + return (uchar)(size_t)HEXDSP(hx_minsn_t_modifies_d, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool minsn_t::is_between(const minsn_t *m1, const minsn_t *m2) const +{ + return (uchar)(size_t)HEXDSP(hx_minsn_t_is_between, this, m1, m2) != 0; +} + +//-------------------------------------------------------------------------- +inline bool minsn_t::may_use_aliased_memory() const +{ + return (uchar)(size_t)HEXDSP(hx_minsn_t_may_use_aliased_memory, this) != 0; +} + +//-------------------------------------------------------------------------- +inline int minsn_t::serialize(bytevec_t *b) const +{ + return (int)(size_t)HEXDSP(hx_minsn_t_serialize, this, b); +} + +//-------------------------------------------------------------------------- +inline bool minsn_t::deserialize(const uchar *bytes, size_t nbytes, int format_version) +{ + return (uchar)(size_t)HEXDSP(hx_minsn_t_deserialize, this, bytes, nbytes, format_version) != 0; +} + +//-------------------------------------------------------------------------- +inline const minsn_t *getf_reginsn(const minsn_t *ins) +{ + return (const minsn_t *)HEXDSP(hx_getf_reginsn, ins); +} + +//-------------------------------------------------------------------------- +inline const minsn_t *getb_reginsn(const minsn_t *ins) +{ + return (const minsn_t *)HEXDSP(hx_getb_reginsn, ins); +} + +//-------------------------------------------------------------------------- +inline intval64_t int64_emulator_t::mop_value(const mop_t &mop) +{ + intval64_t retval; + HEXDSP(hx_int64_emulator_t_mop_value, &retval, this, &mop); + return retval; +} + +//-------------------------------------------------------------------------- +inline intval64_t int64_emulator_t::minsn_value(const minsn_t &insn) +{ + intval64_t retval; + HEXDSP(hx_int64_emulator_t_minsn_value, &retval, this, &insn); + return retval; +} + +//-------------------------------------------------------------------------- +inline void mblock_t::init() +{ + HEXDSP(hx_mblock_t_init, this); +} + +//-------------------------------------------------------------------------- +inline void mblock_t::print(vd_printer_t &vp) const +{ + HEXDSP(hx_mblock_t_print, this, &vp); +} + +//-------------------------------------------------------------------------- +inline void mblock_t::dump() const +{ + HEXDSP(hx_mblock_t_dump, this); +} + +//-------------------------------------------------------------------------- +inline AS_PRINTF(2, 0) void mblock_t::vdump_block(const char *title, va_list va) const +{ + HEXDSP(hx_mblock_t_vdump_block, this, title, va); +} + +//-------------------------------------------------------------------------- +inline minsn_t *mblock_t::insert_into_block(minsn_t *nm, minsn_t *om) +{ + return (minsn_t *)HEXDSP(hx_mblock_t_insert_into_block, this, nm, om); +} + +//-------------------------------------------------------------------------- +inline minsn_t *mblock_t::remove_from_block(minsn_t *m) +{ + return (minsn_t *)HEXDSP(hx_mblock_t_remove_from_block, this, m); +} + +//-------------------------------------------------------------------------- +inline int mblock_t::for_all_insns(minsn_visitor_t &mv) +{ + return (int)(size_t)HEXDSP(hx_mblock_t_for_all_insns, this, &mv); +} + +//-------------------------------------------------------------------------- +inline int mblock_t::for_all_ops(mop_visitor_t &mv) +{ + return (int)(size_t)HEXDSP(hx_mblock_t_for_all_ops, this, &mv); +} + +//-------------------------------------------------------------------------- +inline int mblock_t::for_all_uses(mlist_t *list, minsn_t *i1, minsn_t *i2, mlist_mop_visitor_t &mmv) +{ + return (int)(size_t)HEXDSP(hx_mblock_t_for_all_uses, this, list, i1, i2, &mmv); +} + +//-------------------------------------------------------------------------- +inline int mblock_t::optimize_insn(minsn_t *m, int optflags) +{ + return (int)(size_t)HEXDSP(hx_mblock_t_optimize_insn, this, m, optflags); +} + +//-------------------------------------------------------------------------- +inline int mblock_t::optimize_block() +{ + return (int)(size_t)HEXDSP(hx_mblock_t_optimize_block, this); +} + +//-------------------------------------------------------------------------- +inline int mblock_t::build_lists(bool kill_deads) +{ + return (int)(size_t)HEXDSP(hx_mblock_t_build_lists, this, kill_deads); +} + +//-------------------------------------------------------------------------- +inline int mblock_t::optimize_useless_jump() +{ + return (int)(size_t)HEXDSP(hx_mblock_t_optimize_useless_jump, this); +} + +//-------------------------------------------------------------------------- +inline void mblock_t::append_use_list(mlist_t *list, const mop_t &op, maymust_t maymust, bitrange_t mask) const +{ + HEXDSP(hx_mblock_t_append_use_list, this, list, &op, maymust, &mask); +} + +//-------------------------------------------------------------------------- +inline void mblock_t::append_def_list(mlist_t *list, const mop_t &op, maymust_t maymust) const +{ + HEXDSP(hx_mblock_t_append_def_list, this, list, &op, maymust); +} + +//-------------------------------------------------------------------------- +inline mlist_t mblock_t::build_use_list(const minsn_t &ins, maymust_t maymust) const +{ + mlist_t retval; + HEXDSP(hx_mblock_t_build_use_list, &retval, this, &ins, maymust); + return retval; +} + +//-------------------------------------------------------------------------- +inline mlist_t mblock_t::build_def_list(const minsn_t &ins, maymust_t maymust) const +{ + mlist_t retval; + HEXDSP(hx_mblock_t_build_def_list, &retval, this, &ins, maymust); + return retval; +} + +//-------------------------------------------------------------------------- +inline const minsn_t *mblock_t::find_first_use(mlist_t *list, const minsn_t *i1, const minsn_t *i2, maymust_t maymust) const +{ + return (const minsn_t *)HEXDSP(hx_mblock_t_find_first_use, this, list, i1, i2, maymust); +} + +//-------------------------------------------------------------------------- +inline const minsn_t *mblock_t::find_redefinition(const mlist_t &list, const minsn_t *i1, const minsn_t *i2, maymust_t maymust) const +{ + return (const minsn_t *)HEXDSP(hx_mblock_t_find_redefinition, this, &list, i1, i2, maymust); +} + +//-------------------------------------------------------------------------- +inline bool mblock_t::is_rhs_redefined(const minsn_t *ins, const minsn_t *i1, const minsn_t *i2) const +{ + return (uchar)(size_t)HEXDSP(hx_mblock_t_is_rhs_redefined, this, ins, i1, i2) != 0; +} + +//-------------------------------------------------------------------------- +inline minsn_t *mblock_t::find_access(const mop_t &op, minsn_t **parent, const minsn_t *mend, int fdflags) const +{ + return (minsn_t *)HEXDSP(hx_mblock_t_find_access, this, &op, parent, mend, fdflags); +} + +//-------------------------------------------------------------------------- +inline bool mblock_t::get_valranges(valrng_t *res, const vivl_t &vivl, int vrflags) const +{ + return (uchar)(size_t)HEXDSP(hx_mblock_t_get_valranges, this, res, &vivl, vrflags) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mblock_t::get_valranges(valrng_t *res, const vivl_t &vivl, const minsn_t *m, int vrflags) const +{ + return (uchar)(size_t)HEXDSP(hx_mblock_t_get_valranges_, this, res, &vivl, m, vrflags) != 0; +} + +//-------------------------------------------------------------------------- +inline size_t mblock_t::get_reginsn_qty() const +{ + return (size_t)HEXDSP(hx_mblock_t_get_reginsn_qty, this); +} + +//-------------------------------------------------------------------------- +inline bool mba_ranges_t::range_contains(ea_t ea) const +{ + return (uchar)(size_t)HEXDSP(hx_mba_ranges_t_range_contains, this, ea) != 0; +} + +//-------------------------------------------------------------------------- +inline sval_t mba_t::stkoff_vd2ida(sval_t off) const +{ + sval_t retval; + HEXDSP(hx_mba_t_stkoff_vd2ida, &retval, this, off); + return retval; +} + +//-------------------------------------------------------------------------- +inline sval_t mba_t::stkoff_ida2vd(sval_t off) const +{ + sval_t retval; + HEXDSP(hx_mba_t_stkoff_ida2vd, &retval, this, off); + return retval; +} + +//-------------------------------------------------------------------------- +inline vdloc_t mba_t::idaloc2vd(const argloc_t &loc, int width, sval_t spd) +{ + vdloc_t retval; + HEXDSP(hx_mba_t_idaloc2vd, &retval, &loc, width, spd); + return retval; +} + +//-------------------------------------------------------------------------- +inline vdloc_t mba_t::idaloc2vd(const argloc_t &loc, int width) const +{ + vdloc_t retval; + HEXDSP(hx_mba_t_idaloc2vd_, &retval, this, &loc, width); + return retval; +} + +//-------------------------------------------------------------------------- +inline argloc_t mba_t::vd2idaloc(const vdloc_t &loc, int width, sval_t spd) +{ + argloc_t retval; + HEXDSP(hx_mba_t_vd2idaloc, &retval, &loc, width, spd); + return retval; +} + +//-------------------------------------------------------------------------- +inline argloc_t mba_t::vd2idaloc(const vdloc_t &loc, int width) const +{ + argloc_t retval; + HEXDSP(hx_mba_t_vd2idaloc_, &retval, this, &loc, width); + return retval; +} + +//-------------------------------------------------------------------------- +inline void mba_t::term() +{ + HEXDSP(hx_mba_t_term, this); +} + +//-------------------------------------------------------------------------- +inline func_t *mba_t::get_curfunc() const +{ + return (func_t *)HEXDSP(hx_mba_t_get_curfunc, this); +} + +//-------------------------------------------------------------------------- +inline merror_t mba_t::set_maturity(mba_maturity_t mat) +{ + return (merror_t)(size_t)HEXDSP(hx_mba_t_set_maturity, this, mat); +} + +//-------------------------------------------------------------------------- +inline int mba_t::optimize_local(int locopt_bits) +{ + return (int)(size_t)HEXDSP(hx_mba_t_optimize_local, this, locopt_bits); +} + +//-------------------------------------------------------------------------- +inline merror_t mba_t::build_graph() +{ + return (merror_t)(size_t)HEXDSP(hx_mba_t_build_graph, this); +} + +//-------------------------------------------------------------------------- +inline mbl_graph_t *mba_t::get_graph() +{ + return (mbl_graph_t *)HEXDSP(hx_mba_t_get_graph, this); +} + +//-------------------------------------------------------------------------- +inline int mba_t::analyze_calls(int acflags) +{ + return (int)(size_t)HEXDSP(hx_mba_t_analyze_calls, this, acflags); +} + +//-------------------------------------------------------------------------- +inline merror_t mba_t::optimize_global() +{ + return (merror_t)(size_t)HEXDSP(hx_mba_t_optimize_global, this); +} + +//-------------------------------------------------------------------------- +inline void mba_t::alloc_lvars() +{ + HEXDSP(hx_mba_t_alloc_lvars, this); +} + +//-------------------------------------------------------------------------- +inline void mba_t::dump() const +{ + HEXDSP(hx_mba_t_dump, this); +} + +//-------------------------------------------------------------------------- +inline AS_PRINTF(3, 0) void mba_t::vdump_mba(bool _verify, const char *title, va_list va) const +{ + HEXDSP(hx_mba_t_vdump_mba, this, _verify, title, va); +} + +//-------------------------------------------------------------------------- +inline void mba_t::print(vd_printer_t &vp) const +{ + HEXDSP(hx_mba_t_print, this, &vp); +} + +//-------------------------------------------------------------------------- +inline void mba_t::verify(bool always) const +{ + HEXDSP(hx_mba_t_verify, this, always); +} + +//-------------------------------------------------------------------------- +inline void mba_t::mark_chains_dirty() +{ + HEXDSP(hx_mba_t_mark_chains_dirty, this); +} + +//-------------------------------------------------------------------------- +inline mblock_t *mba_t::insert_block(int bblk) +{ + return (mblock_t *)HEXDSP(hx_mba_t_insert_block, this, bblk); +} + +//-------------------------------------------------------------------------- +inline mblock_t *mba_t::split_block(mblock_t *blk, minsn_t *start_insn) +{ + return (mblock_t *)HEXDSP(hx_mba_t_split_block, this, blk, start_insn); +} + +//-------------------------------------------------------------------------- +inline bool mba_t::remove_block(mblock_t *blk) +{ + return (uchar)(size_t)HEXDSP(hx_mba_t_remove_block, this, blk) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mba_t::remove_blocks(int start_blk, int end_blk) +{ + return (uchar)(size_t)HEXDSP(hx_mba_t_remove_blocks, this, start_blk, end_blk) != 0; +} + +//-------------------------------------------------------------------------- +inline mblock_t *mba_t::copy_block(mblock_t *blk, int new_serial, int cpblk_flags) +{ + return (mblock_t *)HEXDSP(hx_mba_t_copy_block, this, blk, new_serial, cpblk_flags); +} + +//-------------------------------------------------------------------------- +inline bool mba_t::remove_empty_and_unreachable_blocks() +{ + return (uchar)(size_t)HEXDSP(hx_mba_t_remove_empty_and_unreachable_blocks, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mba_t::merge_blocks() +{ + return (uchar)(size_t)HEXDSP(hx_mba_t_merge_blocks, this) != 0; +} + +//-------------------------------------------------------------------------- +inline int mba_t::for_all_ops(mop_visitor_t &mv) +{ + return (int)(size_t)HEXDSP(hx_mba_t_for_all_ops, this, &mv); +} + +//-------------------------------------------------------------------------- +inline int mba_t::for_all_insns(minsn_visitor_t &mv) +{ + return (int)(size_t)HEXDSP(hx_mba_t_for_all_insns, this, &mv); +} + +//-------------------------------------------------------------------------- +inline int mba_t::for_all_topinsns(minsn_visitor_t &mv) +{ + return (int)(size_t)HEXDSP(hx_mba_t_for_all_topinsns, this, &mv); +} + +//-------------------------------------------------------------------------- +inline mop_t *mba_t::find_mop(op_parent_info_t *ctx, ea_t ea, bool is_dest, const mlist_t &list) +{ + return (mop_t *)HEXDSP(hx_mba_t_find_mop, this, ctx, ea, is_dest, &list); +} + +//-------------------------------------------------------------------------- +inline minsn_t *mba_t::create_helper_call(ea_t ea, const char *helper, const tinfo_t *rettype, const mcallargs_t *callargs, const mop_t *out) +{ + return (minsn_t *)HEXDSP(hx_mba_t_create_helper_call, this, ea, helper, rettype, callargs, out); +} + +//-------------------------------------------------------------------------- +inline void mba_t::get_func_output_lists(mlist_t *return_regs, mlist_t *spoiled, const tinfo_t &type, ea_t call_ea, bool tail_call) +{ + HEXDSP(hx_mba_t_get_func_output_lists, this, return_regs, spoiled, &type, call_ea, tail_call); +} + +//-------------------------------------------------------------------------- +inline lvar_t &mba_t::arg(int n) +{ + return *(lvar_t *)HEXDSP(hx_mba_t_arg, this, n); +} + +//-------------------------------------------------------------------------- +inline ea_t mba_t::alloc_fict_ea(ea_t real_ea) +{ + ea_t retval; + HEXDSP(hx_mba_t_alloc_fict_ea, &retval, this, real_ea); + return retval; +} + +//-------------------------------------------------------------------------- +inline ea_t mba_t::map_fict_ea(ea_t fict_ea) const +{ + ea_t retval; + HEXDSP(hx_mba_t_map_fict_ea, &retval, this, fict_ea); + return retval; +} + +//-------------------------------------------------------------------------- +inline void mba_t::serialize(bytevec_t &vout) const +{ + HEXDSP(hx_mba_t_serialize, this, &vout); +} + +//-------------------------------------------------------------------------- +inline WARN_UNUSED_RESULT mba_t *mba_t::deserialize(const uchar *bytes, size_t nbytes) +{ + return (mba_t *)HEXDSP(hx_mba_t_deserialize, bytes, nbytes); +} + +//-------------------------------------------------------------------------- +inline void mba_t::save_snapshot(const char *description) +{ + HEXDSP(hx_mba_t_save_snapshot, this, description); +} + +//-------------------------------------------------------------------------- +inline mreg_t mba_t::alloc_kreg(size_t size, bool check_size) +{ + return (mreg_t)(size_t)HEXDSP(hx_mba_t_alloc_kreg, this, size, check_size); +} + +//-------------------------------------------------------------------------- +inline void mba_t::free_kreg(mreg_t reg, size_t size) +{ + HEXDSP(hx_mba_t_free_kreg, this, reg, size); +} + +//-------------------------------------------------------------------------- +inline merror_t mba_t::inline_func(codegen_t &cdg, int blknum, mba_ranges_t &ranges, int decomp_flags, int inline_flags) +{ + return (merror_t)(size_t)HEXDSP(hx_mba_t_inline_func, this, &cdg, blknum, &ranges, decomp_flags, inline_flags); +} + +//-------------------------------------------------------------------------- +inline const stkpnt_t *mba_t::locate_stkpnt(ea_t ea) const +{ + return (const stkpnt_t *)HEXDSP(hx_mba_t_locate_stkpnt, this, ea); +} + +//-------------------------------------------------------------------------- +inline bool mba_t::set_lvar_name(lvar_t &v, const char *name, int flagbits) +{ + return (uchar)(size_t)HEXDSP(hx_mba_t_set_lvar_name, this, &v, name, flagbits) != 0; +} + +//-------------------------------------------------------------------------- +inline bool mbl_graph_t::is_accessed_globally(const mlist_t &list, int b1, int b2, const minsn_t *m1, const minsn_t *m2, access_type_t access_type, maymust_t maymust) const +{ + return (uchar)(size_t)HEXDSP(hx_mbl_graph_t_is_accessed_globally, this, &list, b1, b2, m1, m2, access_type, maymust) != 0; +} + +//-------------------------------------------------------------------------- +inline graph_chains_t *mbl_graph_t::get_ud(gctype_t gctype) +{ + return (graph_chains_t *)HEXDSP(hx_mbl_graph_t_get_ud, this, gctype); +} + +//-------------------------------------------------------------------------- +inline graph_chains_t *mbl_graph_t::get_du(gctype_t gctype) +{ + return (graph_chains_t *)HEXDSP(hx_mbl_graph_t_get_du, this, gctype); +} + +//-------------------------------------------------------------------------- +inline merror_t cdg_insn_iterator_t::next(insn_t *ins) +{ + return (merror_t)(size_t)HEXDSP(hx_cdg_insn_iterator_t_next, this, ins); +} + +//-------------------------------------------------------------------------- +inline void codegen_t::clear() +{ + HEXDSP(hx_codegen_t_clear, this); +} + +//-------------------------------------------------------------------------- +inline minsn_t *codegen_t::emit(mcode_t code, int width, uval_t l, uval_t r, uval_t d, int offsize) +{ + return (minsn_t *)HEXDSP(hx_codegen_t_emit, this, code, width, l, r, d, offsize); +} + +//-------------------------------------------------------------------------- +inline minsn_t *codegen_t::emit(mcode_t code, const mop_t *l, const mop_t *r, const mop_t *d) +{ + return (minsn_t *)HEXDSP(hx_codegen_t_emit_, this, code, l, r, d); +} + +//-------------------------------------------------------------------------- +inline bool change_hexrays_config(const char *directive) +{ + return (uchar)(size_t)HEXDSP(hx_change_hexrays_config, directive) != 0; +} + +//-------------------------------------------------------------------------- +inline const char *get_hexrays_version() +{ + return (const char *)HEXDSP(hx_get_hexrays_version); +} + +//-------------------------------------------------------------------------- +inline vdui_t *open_pseudocode(ea_t ea, int flags) +{ + return (vdui_t *)HEXDSP(hx_open_pseudocode, ea, flags); +} + +//-------------------------------------------------------------------------- +inline bool close_pseudocode(TWidget *f) +{ + return (uchar)(size_t)HEXDSP(hx_close_pseudocode, f) != 0; +} + +//-------------------------------------------------------------------------- +inline vdui_t *get_widget_vdui(TWidget *f) +{ + return (vdui_t *)HEXDSP(hx_get_widget_vdui, f); +} + +//-------------------------------------------------------------------------- +inline bool decompile_many(const char *outfile, const eavec_t *funcaddrs, int flags) +{ + return (uchar)(size_t)HEXDSP(hx_decompile_many, outfile, funcaddrs, flags) != 0; +} + +//-------------------------------------------------------------------------- +inline qstring hexrays_failure_t::desc() const +{ + qstring retval; + HEXDSP(hx_hexrays_failure_t_desc, &retval, this); + return retval; +} + +//-------------------------------------------------------------------------- +inline void send_database(const hexrays_failure_t &err, bool silent) +{ + HEXDSP(hx_send_database, &err, silent); +} + +//-------------------------------------------------------------------------- +inline bool gco_info_t::append_to_list(mlist_t *list, const mba_t *mba) const +{ + return (uchar)(size_t)HEXDSP(hx_gco_info_t_append_to_list, this, list, mba) != 0; +} + +//-------------------------------------------------------------------------- +inline bool get_current_operand(gco_info_t *out) +{ + return (uchar)(size_t)HEXDSP(hx_get_current_operand, out) != 0; +} + +//-------------------------------------------------------------------------- +inline void remitem(const citem_t *e) +{ + HEXDSP(hx_remitem, e); +} + +//-------------------------------------------------------------------------- +inline ctype_t negated_relation(ctype_t op) +{ + return (ctype_t)(size_t)HEXDSP(hx_negated_relation, op); +} + +//-------------------------------------------------------------------------- +inline ctype_t swapped_relation(ctype_t op) +{ + return (ctype_t)(size_t)HEXDSP(hx_swapped_relation, op); +} + +//-------------------------------------------------------------------------- +inline type_sign_t get_op_signness(ctype_t op) +{ + return (type_sign_t)(size_t)HEXDSP(hx_get_op_signness, op); +} + +//-------------------------------------------------------------------------- +inline ctype_t asgop(ctype_t cop) +{ + return (ctype_t)(size_t)HEXDSP(hx_asgop, cop); +} + +//-------------------------------------------------------------------------- +inline ctype_t asgop_revert(ctype_t cop) +{ + return (ctype_t)(size_t)HEXDSP(hx_asgop_revert, cop); +} + +//-------------------------------------------------------------------------- +inline void cnumber_t::print(qstring *vout, const tinfo_t &type, const citem_t *parent, bool *nice_stroff) const +{ + HEXDSP(hx_cnumber_t_print, this, vout, &type, parent, nice_stroff); +} + +//-------------------------------------------------------------------------- +inline uint64 cnumber_t::value(const tinfo_t &type) const +{ + uint64 retval; + HEXDSP(hx_cnumber_t_value, &retval, this, &type); + return retval; +} + +//-------------------------------------------------------------------------- +inline void cnumber_t::assign(uint64 v, int nbytes, type_sign_t sign) +{ + HEXDSP(hx_cnumber_t_assign, this, v, nbytes, sign); +} + +//-------------------------------------------------------------------------- +inline int cnumber_t::compare(const cnumber_t &r) const +{ + return (int)(size_t)HEXDSP(hx_cnumber_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int var_ref_t::compare(const var_ref_t &r) const +{ + return (int)(size_t)HEXDSP(hx_var_ref_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int citem_locator_t::compare(const citem_locator_t &r) const +{ + return (int)(size_t)HEXDSP(hx_citem_locator_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline bool citem_t::contains_expr(const cexpr_t *e) const +{ + return (uchar)(size_t)HEXDSP(hx_citem_t_contains_expr, this, e) != 0; +} + +//-------------------------------------------------------------------------- +inline bool citem_t::contains_label() const +{ + return (uchar)(size_t)HEXDSP(hx_citem_t_contains_label, this) != 0; +} + +//-------------------------------------------------------------------------- +inline const citem_t *citem_t::find_parent_of(const citem_t *item) const +{ + return (const citem_t *)HEXDSP(hx_citem_t_find_parent_of, this, item); +} + +//-------------------------------------------------------------------------- +inline citem_t *citem_t::find_closest_addr(ea_t _ea) +{ + return (citem_t *)HEXDSP(hx_citem_t_find_closest_addr, this, _ea); +} + +//-------------------------------------------------------------------------- +inline cexpr_t &cexpr_t::assign(const cexpr_t &r) +{ + return *(cexpr_t *)HEXDSP(hx_cexpr_t_assign, this, &r); +} + +//-------------------------------------------------------------------------- +inline int cexpr_t::compare(const cexpr_t &r) const +{ + return (int)(size_t)HEXDSP(hx_cexpr_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline void cexpr_t::replace_by(cexpr_t *r) +{ + HEXDSP(hx_cexpr_t_replace_by, this, r); +} + +//-------------------------------------------------------------------------- +inline void cexpr_t::cleanup() +{ + HEXDSP(hx_cexpr_t_cleanup, this); +} + +//-------------------------------------------------------------------------- +inline void cexpr_t::put_number(cfunc_t *func, uint64 value, int nbytes, type_sign_t sign) +{ + HEXDSP(hx_cexpr_t_put_number, this, func, value, nbytes, sign); +} + +//-------------------------------------------------------------------------- +inline void cexpr_t::print1(qstring *vout, const cfunc_t *func) const +{ + HEXDSP(hx_cexpr_t_print1, this, vout, func); +} + +//-------------------------------------------------------------------------- +inline void cexpr_t::calc_type(bool recursive) +{ + HEXDSP(hx_cexpr_t_calc_type, this, recursive); +} + +//-------------------------------------------------------------------------- +inline bool cexpr_t::equal_effect(const cexpr_t &r) const +{ + return (uchar)(size_t)HEXDSP(hx_cexpr_t_equal_effect, this, &r) != 0; +} + +//-------------------------------------------------------------------------- +inline bool cexpr_t::is_child_of(const citem_t *parent) const +{ + return (uchar)(size_t)HEXDSP(hx_cexpr_t_is_child_of, this, parent) != 0; +} + +//-------------------------------------------------------------------------- +inline bool cexpr_t::contains_operator(ctype_t needed_op, int times) const +{ + return (uchar)(size_t)HEXDSP(hx_cexpr_t_contains_operator, this, needed_op, times) != 0; +} + +//-------------------------------------------------------------------------- +inline bit_bound_t cexpr_t::get_high_nbit_bound() const +{ + bit_bound_t retval; + HEXDSP(hx_cexpr_t_get_high_nbit_bound, &retval, this); + return retval; +} + +//-------------------------------------------------------------------------- +inline int cexpr_t::get_low_nbit_bound() const +{ + return (int)(size_t)HEXDSP(hx_cexpr_t_get_low_nbit_bound, this); +} + +//-------------------------------------------------------------------------- +inline bool cexpr_t::requires_lvalue(const cexpr_t *child) const +{ + return (uchar)(size_t)HEXDSP(hx_cexpr_t_requires_lvalue, this, child) != 0; +} + +//-------------------------------------------------------------------------- +inline bool cexpr_t::has_side_effects() const +{ + return (uchar)(size_t)HEXDSP(hx_cexpr_t_has_side_effects, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool cexpr_t::maybe_ptr() const +{ + return (uchar)(size_t)HEXDSP(hx_cexpr_t_maybe_ptr, this) != 0; +} + +//-------------------------------------------------------------------------- +inline const char *cexpr_t::dstr() const +{ + return (const char *)HEXDSP(hx_cexpr_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline cif_t &cif_t::assign(const cif_t &r) +{ + return *(cif_t *)HEXDSP(hx_cif_t_assign, this, &r); +} + +//-------------------------------------------------------------------------- +inline int cif_t::compare(const cif_t &r) const +{ + return (int)(size_t)HEXDSP(hx_cif_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline cloop_t &cloop_t::assign(const cloop_t &r) +{ + return *(cloop_t *)HEXDSP(hx_cloop_t_assign, this, &r); +} + +//-------------------------------------------------------------------------- +inline int cfor_t::compare(const cfor_t &r) const +{ + return (int)(size_t)HEXDSP(hx_cfor_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int cwhile_t::compare(const cwhile_t &r) const +{ + return (int)(size_t)HEXDSP(hx_cwhile_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int cdo_t::compare(const cdo_t &r) const +{ + return (int)(size_t)HEXDSP(hx_cdo_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int creturn_t::compare(const creturn_t &r) const +{ + return (int)(size_t)HEXDSP(hx_creturn_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int cgoto_t::compare(const cgoto_t &r) const +{ + return (int)(size_t)HEXDSP(hx_cgoto_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int casm_t::compare(const casm_t &r) const +{ + return (int)(size_t)HEXDSP(hx_casm_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline cinsn_t &cinsn_t::assign(const cinsn_t &r) +{ + return *(cinsn_t *)HEXDSP(hx_cinsn_t_assign, this, &r); +} + +//-------------------------------------------------------------------------- +inline int cinsn_t::compare(const cinsn_t &r) const +{ + return (int)(size_t)HEXDSP(hx_cinsn_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline void cinsn_t::replace_by(cinsn_t *r) +{ + HEXDSP(hx_cinsn_t_replace_by, this, r); +} + +//-------------------------------------------------------------------------- +inline void cinsn_t::cleanup() +{ + HEXDSP(hx_cinsn_t_cleanup, this); +} + +//-------------------------------------------------------------------------- +inline cinsn_t &cinsn_t::new_insn(ea_t insn_ea) +{ + return *(cinsn_t *)HEXDSP(hx_cinsn_t_new_insn, this, insn_ea); +} + +//-------------------------------------------------------------------------- +inline cif_t &cinsn_t::create_if(cexpr_t *cnd) +{ + return *(cif_t *)HEXDSP(hx_cinsn_t_create_if, this, cnd); +} + +//-------------------------------------------------------------------------- +inline void cinsn_t::print(int indent, vc_printer_t &vp, use_curly_t use_curly) const +{ + HEXDSP(hx_cinsn_t_print, this, indent, &vp, use_curly); +} + +//-------------------------------------------------------------------------- +inline void cinsn_t::print1(qstring *vout, const cfunc_t *func) const +{ + HEXDSP(hx_cinsn_t_print1, this, vout, func); +} + +//-------------------------------------------------------------------------- +inline bool cinsn_t::is_ordinary_flow() const +{ + return (uchar)(size_t)HEXDSP(hx_cinsn_t_is_ordinary_flow, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool cinsn_t::contains_insn(ctype_t type, int times) const +{ + return (uchar)(size_t)HEXDSP(hx_cinsn_t_contains_insn, this, type, times) != 0; +} + +//-------------------------------------------------------------------------- +inline bool cinsn_t::collect_free_breaks(cinsnptrvec_t *breaks) +{ + return (uchar)(size_t)HEXDSP(hx_cinsn_t_collect_free_breaks, this, breaks) != 0; +} + +//-------------------------------------------------------------------------- +inline bool cinsn_t::collect_free_continues(cinsnptrvec_t *continues) +{ + return (uchar)(size_t)HEXDSP(hx_cinsn_t_collect_free_continues, this, continues) != 0; +} + +//-------------------------------------------------------------------------- +inline const char *cinsn_t::dstr() const +{ + return (const char *)HEXDSP(hx_cinsn_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline int cblock_t::compare(const cblock_t &r) const +{ + return (int)(size_t)HEXDSP(hx_cblock_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int carglist_t::compare(const carglist_t &r) const +{ + return (int)(size_t)HEXDSP(hx_carglist_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int ccase_t::compare(const ccase_t &r) const +{ + return (int)(size_t)HEXDSP(hx_ccase_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int ccases_t::compare(const ccases_t &r) const +{ + return (int)(size_t)HEXDSP(hx_ccases_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int cswitch_t::compare(const cswitch_t &r) const +{ + return (int)(size_t)HEXDSP(hx_cswitch_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int catchexpr_t::compare(const catchexpr_t &r) const +{ + return (int)(size_t)HEXDSP(hx_catchexpr_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int ccatch_t::compare(const ccatch_t &r) const +{ + return (int)(size_t)HEXDSP(hx_ccatch_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int ctry_t::compare(const ctry_t &r) const +{ + return (int)(size_t)HEXDSP(hx_ctry_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int cthrow_t::compare(const cthrow_t &r) const +{ + return (int)(size_t)HEXDSP(hx_cthrow_t_compare, this, &r); +} + +//-------------------------------------------------------------------------- +inline int ctree_visitor_t::apply_to(citem_t *item, citem_t *parent) +{ + return (int)(size_t)HEXDSP(hx_ctree_visitor_t_apply_to, this, item, parent); +} + +//-------------------------------------------------------------------------- +inline int ctree_visitor_t::apply_to_exprs(citem_t *item, citem_t *parent) +{ + return (int)(size_t)HEXDSP(hx_ctree_visitor_t_apply_to_exprs, this, item, parent); +} + +//-------------------------------------------------------------------------- +inline bool ctree_parentee_t::recalc_parent_types() +{ + return (uchar)(size_t)HEXDSP(hx_ctree_parentee_t_recalc_parent_types, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool cfunc_parentee_t::calc_rvalue_type(tinfo_t *target, const cexpr_t *e) +{ + return (uchar)(size_t)HEXDSP(hx_cfunc_parentee_t_calc_rvalue_type, this, target, e) != 0; +} + +//-------------------------------------------------------------------------- +inline int ctree_item_t::get_udm(udm_t *udm, tinfo_t *parent, uint64 *p_offset) const +{ + return (int)(size_t)HEXDSP(hx_ctree_item_t_get_udm, this, udm, parent, p_offset); +} + +//-------------------------------------------------------------------------- +inline int ctree_item_t::get_edm(tinfo_t *parent) const +{ + return (int)(size_t)HEXDSP(hx_ctree_item_t_get_edm, this, parent); +} + +//-------------------------------------------------------------------------- +inline lvar_t *ctree_item_t::get_lvar() const +{ + return (lvar_t *)HEXDSP(hx_ctree_item_t_get_lvar, this); +} + +//-------------------------------------------------------------------------- +inline ea_t ctree_item_t::get_ea() const +{ + ea_t retval; + HEXDSP(hx_ctree_item_t_get_ea, &retval, this); + return retval; +} + +//-------------------------------------------------------------------------- +inline int ctree_item_t::get_label_num(int gln_flags) const +{ + return (int)(size_t)HEXDSP(hx_ctree_item_t_get_label_num, this, gln_flags); +} + +//-------------------------------------------------------------------------- +inline void ctree_item_t::print(qstring *vout) const +{ + HEXDSP(hx_ctree_item_t_print, this, vout); +} + +//-------------------------------------------------------------------------- +inline const char *ctree_item_t::dstr() const +{ + return (const char *)HEXDSP(hx_ctree_item_t_dstr, this); +} + +//-------------------------------------------------------------------------- +inline cexpr_t *lnot(cexpr_t *e) +{ + return (cexpr_t *)HEXDSP(hx_lnot, e); +} + +//-------------------------------------------------------------------------- +inline cinsn_t *new_block() +{ + return (cinsn_t *)HEXDSP(hx_new_block); +} + +//-------------------------------------------------------------------------- +inline AS_PRINTF(3, 0) cexpr_t *vcreate_helper(bool standalone, const tinfo_t &type, const char *format, va_list va) +{ + return (cexpr_t *)HEXDSP(hx_vcreate_helper, standalone, &type, format, va); +} + +//-------------------------------------------------------------------------- +inline AS_PRINTF(3, 0) cexpr_t *vcall_helper(const tinfo_t &rettype, carglist_t *args, const char *format, va_list va) +{ + return (cexpr_t *)HEXDSP(hx_vcall_helper, &rettype, args, format, va); +} + +//-------------------------------------------------------------------------- +inline cexpr_t *make_num(uint64 n, cfunc_t *func, ea_t ea, int opnum, type_sign_t sign, int size) +{ + return (cexpr_t *)HEXDSP(hx_make_num, n, func, ea, opnum, sign, size); +} + +//-------------------------------------------------------------------------- +inline cexpr_t *make_ref(cexpr_t *e) +{ + return (cexpr_t *)HEXDSP(hx_make_ref, e); +} + +//-------------------------------------------------------------------------- +inline cexpr_t *dereference(cexpr_t *e, int ptrsize, bool is_flt) +{ + return (cexpr_t *)HEXDSP(hx_dereference, e, ptrsize, is_flt); +} + +//-------------------------------------------------------------------------- +inline void save_user_labels(ea_t func_ea, const user_labels_t *user_labels, const cfunc_t *func) +{ + HEXDSP(hx_save_user_labels, func_ea, user_labels, func); +} + +//-------------------------------------------------------------------------- +inline void save_user_cmts(ea_t func_ea, const user_cmts_t *user_cmts) +{ + HEXDSP(hx_save_user_cmts, func_ea, user_cmts); +} + +//-------------------------------------------------------------------------- +inline void save_user_numforms(ea_t func_ea, const user_numforms_t *numforms) +{ + HEXDSP(hx_save_user_numforms, func_ea, numforms); +} + +//-------------------------------------------------------------------------- +inline void save_user_iflags(ea_t func_ea, const user_iflags_t *iflags) +{ + HEXDSP(hx_save_user_iflags, func_ea, iflags); +} + +//-------------------------------------------------------------------------- +inline void save_user_unions(ea_t func_ea, const user_unions_t *unions) +{ + HEXDSP(hx_save_user_unions, func_ea, unions); +} + +//-------------------------------------------------------------------------- +inline user_labels_t *restore_user_labels(ea_t func_ea, const cfunc_t *func) +{ + return (user_labels_t *)HEXDSP(hx_restore_user_labels, func_ea, func); +} + +//-------------------------------------------------------------------------- +inline user_cmts_t *restore_user_cmts(ea_t func_ea) +{ + return (user_cmts_t *)HEXDSP(hx_restore_user_cmts, func_ea); +} + +//-------------------------------------------------------------------------- +inline user_numforms_t *restore_user_numforms(ea_t func_ea) +{ + return (user_numforms_t *)HEXDSP(hx_restore_user_numforms, func_ea); +} + +//-------------------------------------------------------------------------- +inline user_iflags_t *restore_user_iflags(ea_t func_ea) +{ + return (user_iflags_t *)HEXDSP(hx_restore_user_iflags, func_ea); +} + +//-------------------------------------------------------------------------- +inline user_unions_t *restore_user_unions(ea_t func_ea) +{ + return (user_unions_t *)HEXDSP(hx_restore_user_unions, func_ea); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::build_c_tree() +{ + HEXDSP(hx_cfunc_t_build_c_tree, this); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::verify(allow_unused_labels_t aul, bool even_without_debugger) const +{ + HEXDSP(hx_cfunc_t_verify, this, aul, even_without_debugger); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::print_dcl(qstring *vout) const +{ + HEXDSP(hx_cfunc_t_print_dcl, this, vout); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::print_func(vc_printer_t &vp) const +{ + HEXDSP(hx_cfunc_t_print_func, this, &vp); +} + +//-------------------------------------------------------------------------- +inline bool cfunc_t::get_func_type(tinfo_t *type) const +{ + return (uchar)(size_t)HEXDSP(hx_cfunc_t_get_func_type, this, type) != 0; +} + +//-------------------------------------------------------------------------- +inline lvars_t *cfunc_t::get_lvars() +{ + return (lvars_t *)HEXDSP(hx_cfunc_t_get_lvars, this); +} + +//-------------------------------------------------------------------------- +inline sval_t cfunc_t::get_stkoff_delta() +{ + sval_t retval; + HEXDSP(hx_cfunc_t_get_stkoff_delta, &retval, this); + return retval; +} + +//-------------------------------------------------------------------------- +inline citem_t *cfunc_t::find_label(int label) +{ + return (citem_t *)HEXDSP(hx_cfunc_t_find_label, this, label); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::remove_unused_labels() +{ + HEXDSP(hx_cfunc_t_remove_unused_labels, this); +} + +//-------------------------------------------------------------------------- +inline const char *cfunc_t::get_user_cmt(const treeloc_t &loc, cmt_retrieval_type_t rt) const +{ + return (const char *)HEXDSP(hx_cfunc_t_get_user_cmt, this, &loc, rt); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::set_user_cmt(const treeloc_t &loc, const char *cmt) +{ + HEXDSP(hx_cfunc_t_set_user_cmt, this, &loc, cmt); +} + +//-------------------------------------------------------------------------- +inline int32 cfunc_t::get_user_iflags(const citem_locator_t &loc) const +{ + return (int32)(size_t)HEXDSP(hx_cfunc_t_get_user_iflags, this, &loc); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::set_user_iflags(const citem_locator_t &loc, int32 iflags) +{ + HEXDSP(hx_cfunc_t_set_user_iflags, this, &loc, iflags); +} + +//-------------------------------------------------------------------------- +inline bool cfunc_t::has_orphan_cmts() const +{ + return (uchar)(size_t)HEXDSP(hx_cfunc_t_has_orphan_cmts, this) != 0; +} + +//-------------------------------------------------------------------------- +inline int cfunc_t::del_orphan_cmts() +{ + return (int)(size_t)HEXDSP(hx_cfunc_t_del_orphan_cmts, this); +} + +//-------------------------------------------------------------------------- +inline bool cfunc_t::get_user_union_selection(ea_t ea, intvec_t *path) +{ + return (uchar)(size_t)HEXDSP(hx_cfunc_t_get_user_union_selection, this, ea, path) != 0; +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::set_user_union_selection(ea_t ea, const intvec_t &path) +{ + HEXDSP(hx_cfunc_t_set_user_union_selection, this, ea, &path); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::save_user_labels() const +{ + HEXDSP(hx_cfunc_t_save_user_labels, this); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::save_user_cmts() const +{ + HEXDSP(hx_cfunc_t_save_user_cmts, this); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::save_user_numforms() const +{ + HEXDSP(hx_cfunc_t_save_user_numforms, this); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::save_user_iflags() const +{ + HEXDSP(hx_cfunc_t_save_user_iflags, this); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::save_user_unions() const +{ + HEXDSP(hx_cfunc_t_save_user_unions, this); +} + +//-------------------------------------------------------------------------- +inline bool cfunc_t::get_line_item(const char *line, int x, bool is_ctree_line, ctree_item_t *phead, ctree_item_t *pitem, ctree_item_t *ptail) +{ + return (uchar)(size_t)HEXDSP(hx_cfunc_t_get_line_item, this, line, x, is_ctree_line, phead, pitem, ptail) != 0; +} + +//-------------------------------------------------------------------------- +inline hexwarns_t &cfunc_t::get_warnings() +{ + return *(hexwarns_t *)HEXDSP(hx_cfunc_t_get_warnings, this); +} + +//-------------------------------------------------------------------------- +inline eamap_t &cfunc_t::get_eamap() +{ + return *(eamap_t *)HEXDSP(hx_cfunc_t_get_eamap, this); +} + +//-------------------------------------------------------------------------- +inline boundaries_t &cfunc_t::get_boundaries() +{ + return *(boundaries_t *)HEXDSP(hx_cfunc_t_get_boundaries, this); +} + +//-------------------------------------------------------------------------- +inline const strvec_t &cfunc_t::get_pseudocode() +{ + return *(const strvec_t *)HEXDSP(hx_cfunc_t_get_pseudocode, this); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::refresh_func_ctext() +{ + HEXDSP(hx_cfunc_t_refresh_func_ctext, this); +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::recalc_item_addresses() +{ + HEXDSP(hx_cfunc_t_recalc_item_addresses, this); +} + +//-------------------------------------------------------------------------- +inline bool cfunc_t::gather_derefs(const ctree_item_t &ci, udt_type_data_t *udm) const +{ + return (uchar)(size_t)HEXDSP(hx_cfunc_t_gather_derefs, this, &ci, udm) != 0; +} + +//-------------------------------------------------------------------------- +inline bool cfunc_t::find_item_coords(const citem_t *item, int *px, int *py) +{ + return (uchar)(size_t)HEXDSP(hx_cfunc_t_find_item_coords, this, item, px, py) != 0; +} + +//-------------------------------------------------------------------------- +inline void cfunc_t::cleanup() +{ + HEXDSP(hx_cfunc_t_cleanup, this); +} + +//-------------------------------------------------------------------------- +inline void close_hexrays_waitbox() +{ + HEXDSP(hx_close_hexrays_waitbox); +} + +//-------------------------------------------------------------------------- +inline cfuncptr_t decompile(const mba_ranges_t &mbr, hexrays_failure_t *hf, int decomp_flags) +{ + return cfuncptr_t((cfunc_t *)HEXDSP(hx_decompile, &mbr, hf, decomp_flags)); +} + +//-------------------------------------------------------------------------- +inline mba_t *gen_microcode(const mba_ranges_t &mbr, hexrays_failure_t *hf, const mlist_t *retlist, int decomp_flags, mba_maturity_t reqmat) +{ + return (mba_t *)HEXDSP(hx_gen_microcode, &mbr, hf, retlist, decomp_flags, reqmat); +} + +//-------------------------------------------------------------------------- +inline cfuncptr_t create_cfunc(mba_t *mba) +{ + return cfuncptr_t((cfunc_t *)HEXDSP(hx_create_cfunc, mba)); +} + +//-------------------------------------------------------------------------- +inline bool mark_cfunc_dirty(ea_t ea, bool close_views) +{ + return (uchar)(size_t)HEXDSP(hx_mark_cfunc_dirty, ea, close_views) != 0; +} + +//-------------------------------------------------------------------------- +inline void clear_cached_cfuncs() +{ + HEXDSP(hx_clear_cached_cfuncs); +} + +//-------------------------------------------------------------------------- +inline bool has_cached_cfunc(ea_t ea) +{ + return (uchar)(size_t)HEXDSP(hx_has_cached_cfunc, ea) != 0; +} + +//-------------------------------------------------------------------------- +inline const char *get_ctype_name(ctype_t op) +{ + return (const char *)HEXDSP(hx_get_ctype_name, op); +} + +//-------------------------------------------------------------------------- +inline qstring create_field_name(const tinfo_t &type, uval_t offset) +{ + qstring retval; + HEXDSP(hx_create_field_name, &retval, &type, offset); + return retval; +} + +//-------------------------------------------------------------------------- +inline bool install_hexrays_callback(hexrays_cb_t *callback, void *ud) +{ + return (uchar)(size_t)HEXDSP(hx_install_hexrays_callback, callback, ud) != 0; +} + +//-------------------------------------------------------------------------- +inline int remove_hexrays_callback(hexrays_cb_t *callback, void *ud) +{ + auto hrdsp = HEXDSP; + return hrdsp == nullptr ? 0 : (int)(size_t)hrdsp(hx_remove_hexrays_callback, callback, ud); +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::set_locked(bool v) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_set_locked, this, v) != 0; +} + +//-------------------------------------------------------------------------- +inline void vdui_t::refresh_view(bool redo_mba) +{ + HEXDSP(hx_vdui_t_refresh_view, this, redo_mba); +} + +//-------------------------------------------------------------------------- +inline void vdui_t::refresh_ctext(bool activate) +{ + HEXDSP(hx_vdui_t_refresh_ctext, this, activate); +} + +//-------------------------------------------------------------------------- +inline void vdui_t::switch_to(cfuncptr_t f, bool activate) +{ + HEXDSP(hx_vdui_t_switch_to, this, &f, activate); +} + +//-------------------------------------------------------------------------- +inline cnumber_t *vdui_t::get_number() +{ + return (cnumber_t *)HEXDSP(hx_vdui_t_get_number, this); +} + +//-------------------------------------------------------------------------- +inline int vdui_t::get_current_label() +{ + return (int)(size_t)HEXDSP(hx_vdui_t_get_current_label, this); +} + +//-------------------------------------------------------------------------- +inline void vdui_t::clear() +{ + HEXDSP(hx_vdui_t_clear, this); +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::refresh_cpos(input_device_t idv) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_refresh_cpos, this, idv) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::get_current_item(input_device_t idv) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_get_current_item, this, idv) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::ui_rename_lvar(lvar_t *v) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_ui_rename_lvar, this, v) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::rename_lvar(lvar_t *v, const char *name, bool is_user_name) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_rename_lvar, this, v, name, is_user_name) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::ui_set_call_type(const cexpr_t *e) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_ui_set_call_type, this, e) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::ui_set_lvar_type(lvar_t *v) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_ui_set_lvar_type, this, v) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::set_lvar_type(lvar_t *v, const tinfo_t &type) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_set_lvar_type, this, v, &type) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::set_noptr_lvar(lvar_t *v) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_set_noptr_lvar, this, v) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::ui_edit_lvar_cmt(lvar_t *v) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_ui_edit_lvar_cmt, this, v) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::set_lvar_cmt(lvar_t *v, const char *cmt) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_set_lvar_cmt, this, v, cmt) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::ui_map_lvar(lvar_t *v) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_ui_map_lvar, this, v) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::ui_unmap_lvar(lvar_t *v) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_ui_unmap_lvar, this, v) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::map_lvar(lvar_t *from, lvar_t *to) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_map_lvar, this, from, to) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::set_udm_type(tinfo_t &udt_type, int udm_idx) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_set_udm_type, this, &udt_type, udm_idx) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::rename_udm(tinfo_t &udt_type, int udm_idx) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_rename_udm, this, &udt_type, udm_idx) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::set_global_type(ea_t ea) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_set_global_type, this, ea) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::rename_global(ea_t ea) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_rename_global, this, ea) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::rename_label(int label) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_rename_label, this, label) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::jump_enter(input_device_t idv, int omflags) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_jump_enter, this, idv, omflags) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::ctree_to_disasm() +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_ctree_to_disasm, this) != 0; +} + +//-------------------------------------------------------------------------- +inline cmt_type_t vdui_t::calc_cmt_type(size_t lnnum, cmt_type_t cmttype) const +{ + return (cmt_type_t)(size_t)HEXDSP(hx_vdui_t_calc_cmt_type, this, lnnum, cmttype); +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::edit_cmt(const treeloc_t &loc) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_edit_cmt, this, &loc) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::edit_func_cmt() +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_edit_func_cmt, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::del_orphan_cmts() +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_del_orphan_cmts, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::set_num_radix(int base) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_set_num_radix, this, base) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::set_num_enum() +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_set_num_enum, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::set_num_stroff() +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_set_num_stroff, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::invert_sign() +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_invert_sign, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::invert_bits() +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_invert_bits, this) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::collapse_item(bool hide) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_collapse_item, this, hide) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::collapse_lvars(bool hide) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_collapse_lvars, this, hide) != 0; +} + +//-------------------------------------------------------------------------- +inline bool vdui_t::split_item(bool split) +{ + return (uchar)(size_t)HEXDSP(hx_vdui_t_split_item, this, split) != 0; +} + +//-------------------------------------------------------------------------- +inline int select_udt_by_offset(const qvector *udts, const ui_stroff_ops_t &ops, ui_stroff_applicator_t &applicator) +{ + return (int)(size_t)HEXDSP(hx_select_udt_by_offset, udts, &ops, &applicator); +} + +#ifdef __NT__ +#pragma warning(pop) +#endif +#endif + +``` + +`IdaSDK/ida.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _IDA_HPP +#define _IDA_HPP + +#include +#include + +class plugin_t; +struct plugmod_t; + +/*! \file ida.hpp + + \brief Contains the ::inf structure definition and some + functions common to the whole IDA project. + + The ::inf structure is saved in the database and contains information + specific to the current program being disassembled. + Initially it is filled with values from ida.cfg. + + Although it is not a good idea to change values in ::inf + structure (because you will overwrite values taken from ida.cfg), + you are allowed to do it if you feel it necessary. + +*/ + +//-------------------------------------------------------------------------- +/// Known input file formats (kept in \inf{filetype}): +enum filetype_t +{ + f_EXE_old, ///< MS DOS EXE File + f_COM_old, ///< MS DOS COM File + f_BIN, ///< Binary File + f_DRV, ///< MS DOS Driver + f_WIN, ///< New Executable (NE) + f_HEX, ///< Intel Hex Object File + f_MEX, ///< MOS Technology Hex Object File + f_LX, ///< Linear Executable (LX) + f_LE, ///< Linear Executable (LE) + f_NLM, ///< Netware Loadable Module (NLM) + f_COFF, ///< Common Object File Format (COFF) + f_PE, ///< Portable Executable (PE) + f_OMF, ///< Object Module Format + f_SREC, ///< Motorola SREC (S-record) + f_ZIP, ///< ZIP file (this file is never loaded to IDA database) + f_OMFLIB, ///< Library of OMF Modules + f_AR, ///< ar library + f_LOADER, ///< file is loaded using LOADER DLL + f_ELF, ///< Executable and Linkable Format (ELF) + f_W32RUN, ///< Watcom DOS32 Extender (W32RUN) + f_AOUT, ///< Linux a.out (AOUT) + f_PRC, ///< PalmPilot program file + f_EXE, ///< MS DOS EXE File + f_COM, ///< MS DOS COM File + f_AIXAR, ///< AIX ar library + f_MACHO, ///< Mac OS X Mach-O + f_PSXOBJ, ///< Sony Playstation PSX object file + f_MD1IMG, ///< Mediatek Firmware Image +}; + +/// Is unstructured input file? +inline bool is_filetype_like_binary(filetype_t ft) +{ return ft == f_BIN || ft == f_HEX || ft == f_MEX || ft == f_SREC; } + +//-------------------------------------------------------------------------- + +typedef uchar comp_t; ///< target compiler id +typedef uchar cm_t; ///< calling convention and memory model +typedef uint32 callcnv_t; +typedef qvector callcnvs_t; + +/// Information about the target compiler +struct compiler_info_t +{ + comp_t id = 0; ///< compiler id (see \ref COMP_) + cm_t cm = 0; ///< memory model and calling convention (see \ref CM_) + ///< see also get_cc/set_cc + uchar size_i = 0; ///< sizeof(int) + uchar size_b = 0; ///< sizeof(bool) + uchar size_e = 0; ///< sizeof(enum) + uchar defalign = 0; ///< default alignment for structures + uchar size_s = 0; ///< short + uchar size_l = 0; ///< long + uchar size_ll = 0; ///< longlong + uchar size_ldbl = 0; ///< longdouble (if different from \ph{tbyte_size}) + callcnv_t _new_callcnv = 0; // do not access directly; use get_cc/set_cc + inline callcnv_t get_cc() const; + inline void set_cc(callcnv_t cc); +}; + +//-------------------------------------------------------------------------- +/// Storage types for flag bits +enum storage_type_t +{ + STT_CUR = -1, ///< use current storage type (may be used only as a function argument) + STT_VA = 0, ///< regular storage: virtual arrays, an explicit flag for each byte + STT_MM = 1, ///< memory map: sparse storage. useful for huge objects + STT_DBG = 2, ///< memory map: temporary debugger storage. used internally +}; + +/// \def{EA64_ALIGN, Maintain 64-bit alignments in 64-bit mode} +#ifdef __EA64__ +#define EA64_ALIGN(n) uint32 n; +#else +#define EA64_ALIGN(n) +#endif + +//-------------------------------------------------------------------------- +/// The database parameters. +/// This structure is kept in the ida database. +/// It contains the essential parameters for the current program +#define IDAINFO_TAG_SIZE 3 +#define IDAINFO_PROCNAME_SIZE 16 +#define IDAINFO_STRLIT_PREF_SIZE 16 +struct idainfo +{ + char tag[IDAINFO_TAG_SIZE]; ///< 'IDA' + char zero; ///< this field is not present in the database + ushort version; ///< Version of database + char procname[IDAINFO_PROCNAME_SIZE]; ///< Name of the current processor (with \0) + + ushort s_genflags; ///< \ref INFFL_ +/// \defgroup INFFL_ General idainfo flags +/// Used by idainfo::s_genflags +///@{ +#define INFFL_AUTO 0x01 ///< Autoanalysis is enabled? +#define INFFL_ALLASM 0x02 ///< may use constructs not supported by + ///< the target assembler +#define INFFL_LOADIDC 0x04 ///< loading an idc file that contains database info +#define INFFL_NOUSER 0x08 ///< do not store user info in the database +#define INFFL_READONLY 0x10 ///< (internal) temporary interdiction to modify the database +#define INFFL_CHKOPS 0x20 ///< check manual operands? (unused) +#define INFFL_NMOPS 0x40 ///< allow non-matched operands? (unused) +#define INFFL_GRAPH_VIEW 0x80 ///< currently using graph options (\dto{graph}) +///@} + + uint32 lflags; ///< \ref LFLG_ +/// \defgroup LFLG_ Misc. database flags +/// used by idainfo::lflags +///@{ +#define LFLG_PC_FPP 0x00000001 ///< decode floating point processor instructions? +#define LFLG_PC_FLAT 0x00000002 ///< 32-bit program (or higher)? +#define LFLG_64BIT 0x00000004 ///< 64-bit program? +#define LFLG_IS_DLL 0x00000008 ///< Is dynamic library? +#define LFLG_FLAT_OFF32 0x00000010 ///< treat ::REF_OFF32 as 32-bit offset for 16bit segments (otherwise try SEG16:OFF16) +#define LFLG_MSF 0x00000020 ///< Byte order: is MSB first? +#define LFLG_WIDE_HBF 0x00000040 ///< Bit order of wide bytes: high byte first? + ///< (wide bytes: \ph{dnbits} > 8) +#define LFLG_DBG_NOPATH 0x00000080 ///< do not store input full path in debugger process options +#define LFLG_SNAPSHOT 0x00000100 ///< memory snapshot was taken? +#define LFLG_PACK 0x00000200 ///< pack the database? +#define LFLG_COMPRESS 0x00000400 ///< compress the database? +#define LFLG_KERNMODE 0x00000800 ///< is kernel mode binary? +#define LFLG_ILP32 0x00001000 ///< 64-bit instructions with 64-bit registers, + ///< but 32-bit pointers and address space. + ///< this bit is mutually exclusive with LFLG_64BIT +///@} + +/// \defgroup IDB_ IDB pack modes +/// Used by idainfo::get_pack_mode +///@{ +#define IDB_UNPACKED 0 ///< leave database components unpacked +#define IDB_PACKED 1 ///< pack database components into .idb +#define IDB_COMPRESSED 2 ///< compress & pack database components +///@} + + uint32 database_change_count; ///< incremented after each byte and regular + ///< segment modifications + + ushort filetype; ///< The input file type + + ushort ostype; ///< OS type the program is for + ///< bit definitions in libfuncs.hpp + + ushort apptype; ///< Application type + ///< bit definitions in libfuncs.hpp + + uchar asmtype; ///< target assembler number + + uchar specsegs; ///< What format do special segments use? 0-unspecified, 4-entries are 4 bytes, 8- entries are 8 bytes. + + uint32 af; ///< \ref AF_ +/// \defgroup AF_ Analysis flags +/// used by idainfo::af +///@{ +#define AF_CODE 0x00000001 ///< Trace execution flow +#define AF_MARKCODE 0x00000002 ///< Mark typical code sequences as code +#define AF_JUMPTBL 0x00000004 ///< Locate and create jump tables +#define AF_PURDAT 0x00000008 ///< Control flow to data segment is ignored +#define AF_USED 0x00000010 ///< Analyze and create all xrefs +#define AF_UNK 0x00000020 ///< Delete instructions with no xrefs + +#define AF_PROCPTR 0x00000040 ///< Create function if data xref data->code32 exists +#define AF_PROC 0x00000080 ///< Create functions if call is present +#define AF_FTAIL 0x00000100 ///< Create function tails +#define AF_LVAR 0x00000200 ///< Create stack variables +#define AF_STKARG 0x00000400 ///< Propagate stack argument information +#define AF_REGARG 0x00000800 ///< Propagate register argument information +#define AF_TRACE 0x00001000 ///< Trace stack pointer +#define AF_VERSP 0x00002000 ///< Perform full SP-analysis. (\ph{verify_sp}) +#define AF_ANORET 0x00004000 ///< Perform 'no-return' analysis +#define AF_MEMFUNC 0x00008000 ///< Try to guess member function types +#define AF_TRFUNC 0x00010000 ///< Truncate functions upon code deletion + +#define AF_STRLIT 0x00020000 ///< Create string literal if data xref exists +#define AF_CHKUNI 0x00040000 ///< Check for unicode strings +#define AF_FIXUP 0x00080000 ///< Create offsets and segments using fixup info +#define AF_DREFOFF 0x00100000 ///< Create offset if data xref to seg32 exists +#define AF_IMMOFF 0x00200000 ///< Convert 32bit instruction operand to offset +#define AF_DATOFF 0x00400000 ///< Automatically convert data to offsets + +#define AF_FLIRT 0x00800000 ///< Use flirt signatures +#define AF_SIGCMT 0x01000000 ///< Append a signature name comment for recognized anonymous library functions +#define AF_SIGMLT 0x02000000 ///< Allow recognition of several copies of the same function +#define AF_HFLIRT 0x04000000 ///< Automatically hide library functions + +#define AF_JFUNC 0x08000000 ///< Rename jump functions as j_... +#define AF_NULLSUB 0x10000000 ///< Rename empty functions as nullsub_... + +#define AF_DODATA 0x20000000 ///< Coagulate data segs at the final pass +#define AF_DOCODE 0x40000000 ///< Coagulate code segs at the final pass +#define AF_FINAL 0x80000000 ///< Final pass of analysis +///@} + uint32 af2; ///< \ref AF2_ +/// \defgroup AF2_ Analysis flags 2 +/// Used by idainfo::af2 +///@{ +#define AF2_DOEH 0x00000001 ///< Handle EH information +#define AF2_DORTTI 0x00000002 ///< Handle RTTI information +#define AF2_MACRO 0x00000004 ///< Try to combine several instructions + ///< into a macro instruction +#define AF2_MERGESTR 0x00000008 ///< Merge string literals created using data xrefs +/// remaining 28 bits are reserved +///@} + uval_t baseaddr; ///< base address of the program (paragraphs) + sel_t start_ss; ///< selector of the initial stack segment + sel_t start_cs; ///< selector of the segment with the main entry point + ea_t start_ip; ///< IP register value at the start of + ///< program execution + ea_t start_ea; ///< Linear address of program entry point + ea_t start_sp; ///< SP register value at the start of + ///< program execution + ea_t main; ///< address of main() + ea_t min_ea; ///< current limits of program + ea_t max_ea; ///< maxEA is excluded + ea_t omin_ea; ///< original minEA (is set after loading the input file) + ea_t omax_ea; ///< original maxEA (is set after loading the input file) + + ea_t lowoff; ///< Low limit for offsets + ///< (used in calculation of 'void' operands) + ea_t highoff; ///< High limit for offsets + ///< (used in calculation of 'void' operands) + + uval_t maxref; ///< Max tail for references + + range_t privrange; ///< Range of addresses reserved for internal use. + ///< Initially specified by cfgvar PRIVRANGE + sval_t netdelta; ///< Delta value to be added to all addresses for mapping to netnodes. + ///< Initially 0 + + /// CROSS REFERENCES + uchar xrefnum; ///< Number of references to generate + ///< in the disassembly listing + ///< 0 - xrefs won't be generated at all + uchar type_xrefnum; ///< Number of references to generate + ///< in the struct & enum windows + ///< 0 - xrefs won't be generated at all + uchar refcmtnum; ///< Number of comment lines to + ///< generate for refs to string literals + ///< or demangled names + ///< 0 - such comments won't be + ///< generated at all + uchar s_xrefflag; ///< \ref SW_X +/// \defgroup SW_X Xref options +/// Used by idainfo::s_xrefflag +///@{ +#define SW_SEGXRF 0x01 ///< show segments in xrefs? +#define SW_XRFMRK 0x02 ///< show xref type marks? +#define SW_XRFFNC 0x04 ///< show function offsets? +#define SW_XRFVAL 0x08 ///< show xref values? (otherwise-"...") +///@} + + /// NAMES + ushort max_autoname_len; ///< max autogenerated name length (without zero byte) + char nametype; ///< \ref NM_ +/// \defgroup NM_ Dummy names representation types +/// Used by idainfo::nametype +///@{ +#define NM_REL_OFF 0 +#define NM_PTR_OFF 1 +#define NM_NAM_OFF 2 +#define NM_REL_EA 3 +#define NM_PTR_EA 4 +#define NM_NAM_EA 5 +#define NM_EA 6 +#define NM_EA4 7 +#define NM_EA8 8 +#define NM_SHORT 9 +#define NM_SERIAL 10 +///@} + + uint32 short_demnames; ///< short form of demangled names + uint32 long_demnames; ///< long form of demangled names + ///< see demangle.h for definitions + uchar demnames; ///< \ref DEMNAM_ +/// \defgroup DEMNAM_ Demangled name flags +/// used by idainfo::demnames +///@{ +#define DEMNAM_MASK 3 ///< mask for name form +#define DEMNAM_CMNT 0 ///< display demangled names as comments +#define DEMNAM_NAME 1 ///< display demangled names as regular names +#define DEMNAM_NONE 2 ///< don't display demangled names +#define DEMNAM_GCC3 4 ///< assume gcc3 names (valid for gnu compiler) +#define DEMNAM_FIRST 8 ///< override type info +///@} + + uchar listnames; ///< \ref LN_ +/// \defgroup LN_ Name list options +/// Used by idainfo::listnames +///@{ +#define LN_NORMAL 0x01 ///< include normal names +#define LN_PUBLIC 0x02 ///< include public names +#define LN_AUTO 0x04 ///< include autogenerated names +#define LN_WEAK 0x08 ///< include weak names +///@} + + /// DISASSEMBLY LISTING DETAILS + uchar indent; ///< Indentation for instructions + uchar cmt_indent; ///< Indentation for comments + ushort margin; ///< max length of data lines + ushort lenxref; ///< max length of line with xrefs + uint32 outflags; ///< \ref OFLG_ +/// \defgroup OFLG_ output flags +/// used by idainfo::outflags +///@{ +#define OFLG_SHOW_VOID 0x002 ///< Display void marks? +#define OFLG_SHOW_AUTO 0x004 ///< Display autoanalysis indicator? +#define OFLG_GEN_NULL 0x010 ///< Generate empty lines? +#define OFLG_SHOW_PREF 0x020 ///< Show line prefixes? +#define OFLG_PREF_SEG 0x040 ///< line prefixes with segment name? +#define OFLG_LZERO 0x080 ///< generate leading zeroes in numbers +#define OFLG_GEN_ORG 0x100 ///< Generate 'org' directives? +#define OFLG_GEN_ASSUME 0x200 ///< Generate 'assume' directives? +#define OFLG_GEN_TRYBLKS 0x400 ///< Generate try/catch directives? +///@} + + uchar s_cmtflg; ///< \ref SCF_ +/// \defgroup SCF_ Comment options +/// Used by idainfo::s_cmtflg +///@{ +#define SCF_RPTCMT 0x01 ///< show repeatable comments? +#define SCF_ALLCMT 0x02 ///< comment all lines? +#define SCF_NOCMT 0x04 ///< no comments at all +#define SCF_LINNUM 0x08 ///< show source line numbers +#define SCF_TESTMODE 0x10 ///< testida.idc is running +#define SCF_SHHID_ITEM 0x20 ///< show hidden instructions +#define SCF_SHHID_FUNC 0x40 ///< show hidden functions +#define SCF_SHHID_SEGM 0x80 ///< show hidden segments +///@} + + uchar s_limiter; ///< \ref LMT_ +/// \defgroup LMT_ Delimiter options +/// Used by idainfo::s_limiter +///@{ +#define LMT_THIN 0x01 ///< thin borders +#define LMT_THICK 0x02 ///< thick borders +#define LMT_EMPTY 0x04 ///< empty lines at the end of basic blocks +///@} + + short bin_prefix_size; ///< Number of instruction bytes (opcodes) to show in line prefix + uchar s_prefflag; ///< \ref PREF_ +/// \defgroup PREF_ Line prefix options +/// Used by idainfo::s_prefflag +///@{ +#define PREF_SEGADR 0x01 ///< show segment addresses? +#define PREF_FNCOFF 0x02 ///< show function offsets? +#define PREF_STACK 0x04 ///< show stack pointer? +#define PREF_PFXTRUNC 0x08 ///< truncate instruction bytes if they would need more than 1 line +///@} + + /// STRING LITERALS + uchar strlit_flags; ///< \ref STRF_ +/// \defgroup STRF_ string literal flags +/// Used by idainfo::strlit_flags +///@{ +#define STRF_GEN 0x01 ///< generate names? +#define STRF_AUTO 0x02 ///< names have 'autogenerated' bit? +#define STRF_SERIAL 0x04 ///< generate serial names? +#define STRF_UNICODE 0x08 ///< unicode strings are present? +#define STRF_COMMENT 0x10 ///< generate auto comment for string references? +#define STRF_SAVECASE 0x20 ///< preserve case of strings for identifiers +///@} + uchar strlit_break; ///< string literal line break symbol + char strlit_zeroes; ///< leading zeroes + int32 strtype; ///< current ascii string type + ///< see nalt.hpp for string types + char strlit_pref[IDAINFO_STRLIT_PREF_SIZE]; ///< prefix for string literal names + uval_t strlit_sernum; ///< serial number + + // DATA ITEMS + uval_t datatypes; ///< data types allowed in data carousel + + /// COMPILER + compiler_info_t cc; ///< Target compiler + uint32 abibits; ///< ABI features. Depends on info returned by get_abi_name() + ///< Processor modules may modify them in set_compiler + +/// \defgroup ABI_ abi options +/// Used by idainfo::abibits +///@{ +#define ABI_8ALIGN4 0x00000001 ///< 4 byte alignment for 8byte scalars (__int64/double) inside structures? +#define ABI_PACK_STKARGS 0x00000002 ///< do not align stack arguments to stack slots +#define ABI_BIGARG_ALIGN 0x00000004 ///< use natural type alignment for argument if the alignment exceeds native word size. + ///< (e.g. __int64 argument should be 8byte aligned on some 32bit platforms) +#define ABI_STACK_LDBL 0x00000008 ///< long double arguments are passed on stack +#define ABI_STACK_VARARGS 0x00000010 ///< varargs are always passed on stack (even when there are free registers) +#define ABI_HARD_FLOAT 0x00000020 ///< use the floating-point register set +#define ABI_SET_BY_USER 0x00000040 ///< compiler/abi were set by user flag and require SETCOMP_BY_USER flag to be changed +#define ABI_GCC_LAYOUT 0x00000080 ///< use gcc layout for udts (used for mingw) +#define ABI_MAP_STKARGS 0x00000100 ///< register arguments are mapped to stack area (and consume stack slots) +#define ABI_HUGEARG_ALIGN 0x00000200 ///< use natural type alignment for an argument + ///< even if its alignment exceeds double native word size + ///< (the default is to use double word max). + ///< e.g. if this bit is set, __int128 has 16-byte alignment. + ///< this bit is not used by ida yet +///@} + + uint32 appcall_options; ///< appcall options, see idd.hpp + EA64_ALIGN(padding); + +}; + +enum inftag_t +{ + INF_VERSION = 0, + INF_PROCNAME = 1, + INF_GENFLAGS = 2, + INF_LFLAGS = 3, + INF_DATABASE_CHANGE_COUNT = 4, + INF_FILETYPE = 5, + INF_OSTYPE = 6, + INF_APPTYPE = 7, + INF_ASMTYPE = 8, + INF_SPECSEGS = 9, + INF_AF = 10, + INF_AF2 = 11, + INF_BASEADDR = 12, + INF_START_SS = 13, + INF_START_CS = 14, + INF_START_IP = 15, + INF_START_EA = 16, + INF_START_SP = 17, + INF_MAIN = 18, + INF_MIN_EA = 19, + INF_MAX_EA = 20, + INF_OMIN_EA = 21, + INF_OMAX_EA = 22, + INF_LOWOFF = 23, + INF_HIGHOFF = 24, + INF_MAXREF = 25, + INF_PRIVRANGE = 26, + INF_PRIVRANGE_START_EA = 27, + INF_PRIVRANGE_END_EA = 28, + INF_NETDELTA = 29, + INF_XREFNUM = 30, + INF_TYPE_XREFNUM = 31, + INF_REFCMTNUM = 32, + INF_XREFFLAG = 33, + INF_MAX_AUTONAME_LEN = 34, + INF_NAMETYPE = 35, + INF_SHORT_DEMNAMES = 36, + INF_LONG_DEMNAMES = 37, + INF_DEMNAMES = 38, + INF_LISTNAMES = 39, + INF_INDENT = 40, + INF_CMT_INDENT = 41, + INF_MARGIN = 42, + INF_LENXREF = 43, + INF_OUTFLAGS = 44, + INF_CMTFLG = 45, + INF_LIMITER = 46, + INF_BIN_PREFIX_SIZE = 47, + INF_PREFFLAG = 48, + INF_STRLIT_FLAGS = 49, + INF_STRLIT_BREAK = 50, + INF_STRLIT_ZEROES = 51, + INF_STRTYPE = 52, + INF_STRLIT_PREF = 53, + INF_STRLIT_SERNUM = 54, + INF_DATATYPES = 55, + INF_OBSOLETE_CC = 56, + INF_CC_ID = 57, + INF_CC_CM = 58, + INF_CC_SIZE_I = 59, + INF_CC_SIZE_B = 60, + INF_CC_SIZE_E = 61, + INF_CC_DEFALIGN = 62, + INF_CC_SIZE_S = 63, + INF_CC_SIZE_L = 64, + INF_CC_SIZE_LL = 65, + INF_CC_SIZE_LDBL = 66, + INF_ABIBITS = 67, + INF_APPCALL_OPTIONS = 68, + + // root node fields + INF_FILE_FORMAT_NAME = 69, ///< file format name for loader modules + INF_GROUPS = 70, ///< segment group information (see init_groups()) + INF_H_PATH = 71, ///< C header path + INF_C_MACROS = 72, ///< C predefined macros + INF_INCLUDE = 73, ///< assembler include file name + INF_DUALOP_GRAPH = 74, ///< Graph text representation options + INF_DUALOP_TEXT = 75, ///< Text text representation options + INF_MD5 = 76, ///< MD5 of the input file + INF_IDA_VERSION = 77, ///< version of ida which created the database + INF_STR_ENCODINGS = 78, ///< a list of encodings for the program strings + INF_DBG_BINPATHS = 79, ///< unused (20 indexes) + INF_SHA256 = 80, ///< SHA256 of the input file + INF_ABINAME = 81, ///< ABI name (processor specific) + INF_ARCHIVE_PATH = 82, ///< archive file path + INF_PROBLEMS = 83, ///< problem lists + + INF_SELECTORS = 84, ///< 2..63 are for selector_t blob (see init_selectors()) + INF_NOTEPAD = 85, ///< notepad blob, occupies 1000 indexes (1MB of text) + INF_SRCDBG_PATHS = 86, ///< source debug paths, occupies 20 indexes + INF_SRCDBG_UNDESIRED = 87, ///< user-closed source files, occupies 20 indexes + INF_INITIAL_VERSION = 88, ///< initial version of database + INF_CTIME = 89, ///< database creation timestamp + INF_ELAPSED = 90, ///< seconds database stayed open + INF_NOPENS = 91, ///< how many times the database is opened + INF_CRC32 = 92, ///< input file crc32 + INF_IMAGEBASE = 93, ///< image base + INF_IDSNODE = 94, ///< ids modnode id (for import_module) + INF_FSIZE = 95, ///< input file size + INF_OUTFILEENC = 96, ///< output file encoding index + INF_INPUT_FILE_PATH = 97, + + // more inf fields + INF_COMPILER_INFO = 98, + INF_CALLCNV = 99, + INF_LAST = 100, +}; + +/// Get program specific information (a scalar value) +/// \param tag one of inftag_t constants +/// \return the requested info. if wrong tag is specified, return 0 +idaman size_t ida_export getinf(inftag_t tag); + +/// Get program specific information (a non-scalar value) +/// \param tag one of inftag_t constants +/// \param buf output buffer +/// \param bufsize size of the output buffer +/// \return number of bytes stored in the buffer (<0 - not defined) +idaman ssize_t ida_export getinf_buf(inftag_t tag, void *buf, size_t bufsize); + +/// Get program specific information (a non-scalar value) +/// \param tag one of inftag_t constants +/// \param buf output buffer +/// \return number of bytes stored in the buffer (<0 - not defined) +idaman ssize_t ida_export getinf_str(qstring *buf, inftag_t tag); + +/// Get a flag value +/// \param tag one of inftag_t constants +/// \param flag a flag in the field +/// \return whether the flag is set +idaman bool ida_export getinf_flag(inftag_t tag, uint32 flag); + +/// Set program specific information +/// \param tag one of inftag_t constants +/// \param value the new value +/// \return success +idaman bool ida_export setinf(inftag_t tag, ssize_t value); + +/// Set program specific information +/// \param tag one of inftag_t constants +/// \param buf ptr to new value +/// \param bufsize size of the new value, calculated as strlen(buf)+1 if not specified +/// \return success +idaman bool ida_export setinf_buf(inftag_t tag, const void *buf, size_t bufsize=0); + +/// Set a flag value +/// \param tag one of inftag_t constants +/// \param flag a flag in the field +/// \param value whether to set, or clear the flag +/// \return the previous value +idaman bool ida_export setinf_flag(inftag_t tag, uint32 flag, bool value=true); + +/// Undefine a program specific information +/// \param tag one of inftag_t constants +/// \return success +idaman bool ida_export delinf(inftag_t tag); + + +// +inline ushort inf_get_version() { return ushort(getinf(INF_VERSION)); } +inline bool inf_set_version(ushort _v) { return setinf(INF_VERSION, ssize_t(_v)); } + +inline ushort inf_get_genflags() { return ushort(getinf(INF_GENFLAGS)); } +inline bool inf_set_genflags(ushort _v) { return setinf(INF_GENFLAGS, ssize_t(_v)); } +inline bool inf_is_auto_enabled(void) { return getinf_flag(INF_GENFLAGS, INFFL_AUTO); } +inline bool inf_set_auto_enabled(bool _v=true) { return setinf_flag(INF_GENFLAGS, INFFL_AUTO, _v); } +inline bool inf_use_allasm(void) { return getinf_flag(INF_GENFLAGS, INFFL_ALLASM); } +inline bool inf_set_use_allasm(bool _v=true) { return setinf_flag(INF_GENFLAGS, INFFL_ALLASM, _v); } +inline bool inf_loading_idc(void) { return getinf_flag(INF_GENFLAGS, INFFL_LOADIDC); } +inline bool inf_set_loading_idc(bool _v=true) { return setinf_flag(INF_GENFLAGS, INFFL_LOADIDC, _v); } +inline bool inf_no_store_user_info(void) { return getinf_flag(INF_GENFLAGS, INFFL_NOUSER); } +inline bool inf_set_no_store_user_info(bool _v=true) { return setinf_flag(INF_GENFLAGS, INFFL_NOUSER, _v); } +inline bool inf_readonly_idb(void) { return getinf_flag(INF_GENFLAGS, INFFL_READONLY); } +inline bool inf_set_readonly_idb(bool _v=true) { return setinf_flag(INF_GENFLAGS, INFFL_READONLY, _v); } +inline bool inf_check_manual_ops(void) { return getinf_flag(INF_GENFLAGS, INFFL_CHKOPS); } +inline bool inf_set_check_manual_ops(bool _v=true) { return setinf_flag(INF_GENFLAGS, INFFL_CHKOPS, _v); } +inline bool inf_allow_non_matched_ops(void) { return getinf_flag(INF_GENFLAGS, INFFL_NMOPS); } +inline bool inf_set_allow_non_matched_ops(bool _v=true) { return setinf_flag(INF_GENFLAGS, INFFL_NMOPS, _v); } +inline bool inf_is_graph_view(void) { return getinf_flag(INF_GENFLAGS, INFFL_GRAPH_VIEW); } +inline bool inf_set_graph_view(bool _v=true) { return setinf_flag(INF_GENFLAGS, INFFL_GRAPH_VIEW, _v); } + +inline uint32 inf_get_lflags() { return uint32(getinf(INF_LFLAGS)); } +inline bool inf_set_lflags(uint32 _v) { return setinf(INF_LFLAGS, ssize_t(_v)); } +inline bool inf_decode_fpp(void) { return getinf_flag(INF_LFLAGS, LFLG_PC_FPP); } +inline bool inf_set_decode_fpp(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_PC_FPP, _v); } +// unfortunately the name inf_is_32bit was used in the past to mean inf_is_32bit_or_higher. +// it was misleading, this is why we have more explicit names now. +inline bool inf_is_32bit_or_higher(void) { return getinf_flag(INF_LFLAGS, LFLG_PC_FLAT); } +inline bool inf_is_32bit_exactly(void) { return (getinf(INF_LFLAGS) & (LFLG_PC_FLAT|LFLG_64BIT)) == LFLG_PC_FLAT; } +inline bool inf_set_32bit(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_PC_FLAT, _v); } +inline bool inf_is_16bit(void) { return !inf_is_32bit_or_higher(); } +inline bool inf_is_64bit(void) { return getinf_flag(INF_LFLAGS, LFLG_64BIT); } +inline bool inf_set_64bit(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_64BIT, _v); } +inline bool inf_is_ilp32() { return (getinf(INF_LFLAGS) & (LFLG_PC_FLAT|LFLG_64BIT|LFLG_ILP32)) == (LFLG_PC_FLAT|LFLG_ILP32); } +inline bool inf_set_ilp32(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_ILP32, _v); } +inline bool inf_is_dll(void) { return getinf_flag(INF_LFLAGS, LFLG_IS_DLL); } +inline bool inf_set_dll(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_IS_DLL, _v); } +inline bool inf_is_flat_off32(void) { return getinf_flag(INF_LFLAGS, LFLG_FLAT_OFF32); } +inline bool inf_set_flat_off32(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_FLAT_OFF32, _v); } +inline bool inf_is_be(void) { return getinf_flag(INF_LFLAGS, LFLG_MSF); } +inline bool inf_set_be(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_MSF, _v); } +inline bool inf_is_wide_high_byte_first(void) { return getinf_flag(INF_LFLAGS, LFLG_WIDE_HBF); } +inline bool inf_set_wide_high_byte_first(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_WIDE_HBF, _v); } +inline bool inf_dbg_no_store_path(void) { return getinf_flag(INF_LFLAGS, LFLG_DBG_NOPATH); } +inline bool inf_set_dbg_no_store_path(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_DBG_NOPATH, _v); } +inline bool inf_is_snapshot(void) { return getinf_flag(INF_LFLAGS, LFLG_SNAPSHOT); } +inline bool inf_set_snapshot(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_SNAPSHOT, _v); } +inline bool inf_pack_idb(void) { return getinf_flag(INF_LFLAGS, LFLG_PACK); } +inline bool inf_set_pack_idb(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_PACK, _v); } +inline bool inf_compress_idb(void) { return getinf_flag(INF_LFLAGS, LFLG_COMPRESS); } +inline bool inf_set_compress_idb(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_COMPRESS, _v); } +inline bool inf_is_kernel_mode(void) { return getinf_flag(INF_LFLAGS, LFLG_KERNMODE); } +inline bool inf_set_kernel_mode(bool _v=true) { return setinf_flag(INF_LFLAGS, LFLG_KERNMODE, _v); } + +inline uint inf_get_app_bitness(void) // return 16, 32, or 64 +{ +#ifdef __EA64__ + uint32 f = getinf(INF_LFLAGS) & (LFLG_PC_FLAT|LFLG_64BIT); + return f == 0 ? 16 : f == LFLG_PC_FLAT ? 32 : 64; +#else + return inf_is_32bit_or_higher() ? 32 : 16; +#endif +} + +inline void inf_set_app_bitness(uint bitness) // bitness: 16, 32, or 64 +{ + switch ( bitness ) + { +#ifdef __EA64__ + case 64: inf_set_64bit(); break; +#endif + case 32: inf_set_64bit(false); inf_set_32bit(); break; + case 16: inf_set_32bit(false); break; + default: INTERR(2781); + } +} + +inline uint32 inf_get_database_change_count() { return uint32(getinf(INF_DATABASE_CHANGE_COUNT)); } +inline bool inf_set_database_change_count(uint32 _v) { return setinf(INF_DATABASE_CHANGE_COUNT, ssize_t(_v)); } + +inline filetype_t inf_get_filetype() { return filetype_t(getinf(INF_FILETYPE)); } +inline bool inf_set_filetype(filetype_t _v) { return setinf(INF_FILETYPE, ssize_t(_v)); } + +inline ushort inf_get_ostype() { return ushort(getinf(INF_OSTYPE)); } +inline bool inf_set_ostype(ushort _v) { return setinf(INF_OSTYPE, ssize_t(_v)); } + +inline ushort inf_get_apptype() { return ushort(getinf(INF_APPTYPE)); } +inline bool inf_set_apptype(ushort _v) { return setinf(INF_APPTYPE, ssize_t(_v)); } + +inline uchar inf_get_asmtype() { return uchar(getinf(INF_ASMTYPE)); } +inline bool inf_set_asmtype(uchar _v) { return setinf(INF_ASMTYPE, ssize_t(_v)); } + +inline uchar inf_get_specsegs() { return uchar(getinf(INF_SPECSEGS)); } +inline bool inf_set_specsegs(uchar _v) { return setinf(INF_SPECSEGS, ssize_t(_v)); } + +inline uint32 inf_get_af() { return uint32(getinf(INF_AF)); } +inline bool inf_set_af(uint32 _v) { return setinf(INF_AF, ssize_t(_v)); } +inline bool inf_trace_flow(void) { return getinf_flag(INF_AF, AF_CODE); } +inline bool inf_set_trace_flow(bool _v=true) { return setinf_flag(INF_AF, AF_CODE, _v); } +inline bool inf_mark_code(void) { return getinf_flag(INF_AF, AF_MARKCODE); } +inline bool inf_set_mark_code(bool _v=true) { return setinf_flag(INF_AF, AF_MARKCODE, _v); } +inline bool inf_create_jump_tables(void) { return getinf_flag(INF_AF, AF_JUMPTBL); } +inline bool inf_set_create_jump_tables(bool _v=true) { return setinf_flag(INF_AF, AF_JUMPTBL, _v); } +inline bool inf_noflow_to_data(void) { return getinf_flag(INF_AF, AF_PURDAT); } +inline bool inf_set_noflow_to_data(bool _v=true) { return setinf_flag(INF_AF, AF_PURDAT, _v); } +inline bool inf_create_all_xrefs(void) { return getinf_flag(INF_AF, AF_USED); } +inline bool inf_set_create_all_xrefs(bool _v=true) { return setinf_flag(INF_AF, AF_USED, _v); } +inline bool inf_del_no_xref_insns(void) { return getinf_flag(INF_AF, AF_UNK); } +inline bool inf_set_del_no_xref_insns(bool _v=true) { return setinf_flag(INF_AF, AF_UNK, _v); } +inline bool inf_create_func_from_ptr(void) { return getinf_flag(INF_AF, AF_PROCPTR); } +inline bool inf_set_create_func_from_ptr(bool _v=true) { return setinf_flag(INF_AF, AF_PROCPTR, _v); } +inline bool inf_create_func_from_call(void) { return getinf_flag(INF_AF, AF_PROC); } +inline bool inf_set_create_func_from_call(bool _v=true) { return setinf_flag(INF_AF, AF_PROC, _v); } +inline bool inf_create_func_tails(void) { return getinf_flag(INF_AF, AF_FTAIL); } +inline bool inf_set_create_func_tails(bool _v=true) { return setinf_flag(INF_AF, AF_FTAIL, _v); } +inline bool inf_should_create_stkvars(void) { return getinf_flag(INF_AF, AF_LVAR); } +inline bool inf_set_should_create_stkvars(bool _v=true) { return setinf_flag(INF_AF, AF_LVAR, _v); } +inline bool inf_propagate_stkargs(void) { return getinf_flag(INF_AF, AF_STKARG); } +inline bool inf_set_propagate_stkargs(bool _v=true) { return setinf_flag(INF_AF, AF_STKARG, _v); } +inline bool inf_propagate_regargs(void) { return getinf_flag(INF_AF, AF_REGARG); } +inline bool inf_set_propagate_regargs(bool _v=true) { return setinf_flag(INF_AF, AF_REGARG, _v); } +inline bool inf_should_trace_sp(void) { return getinf_flag(INF_AF, AF_TRACE); } +inline bool inf_set_should_trace_sp(bool _v=true) { return setinf_flag(INF_AF, AF_TRACE, _v); } +inline bool inf_full_sp_ana(void) { return getinf_flag(INF_AF, AF_VERSP); } +inline bool inf_set_full_sp_ana(bool _v=true) { return setinf_flag(INF_AF, AF_VERSP, _v); } +inline bool inf_noret_ana(void) { return getinf_flag(INF_AF, AF_ANORET); } +inline bool inf_set_noret_ana(bool _v=true) { return setinf_flag(INF_AF, AF_ANORET, _v); } +inline bool inf_guess_func_type(void) { return getinf_flag(INF_AF, AF_MEMFUNC); } +inline bool inf_set_guess_func_type(bool _v=true) { return setinf_flag(INF_AF, AF_MEMFUNC, _v); } +inline bool inf_truncate_on_del(void) { return getinf_flag(INF_AF, AF_TRFUNC); } +inline bool inf_set_truncate_on_del(bool _v=true) { return setinf_flag(INF_AF, AF_TRFUNC, _v); } +inline bool inf_create_strlit_on_xref(void) { return getinf_flag(INF_AF, AF_STRLIT); } +inline bool inf_set_create_strlit_on_xref(bool _v=true) { return setinf_flag(INF_AF, AF_STRLIT, _v); } +inline bool inf_check_unicode_strlits(void) { return getinf_flag(INF_AF, AF_CHKUNI); } +inline bool inf_set_check_unicode_strlits(bool _v=true) { return setinf_flag(INF_AF, AF_CHKUNI, _v); } +inline bool inf_create_off_using_fixup(void) { return getinf_flag(INF_AF, AF_FIXUP); } +inline bool inf_set_create_off_using_fixup(bool _v=true) { return setinf_flag(INF_AF, AF_FIXUP, _v); } +inline bool inf_create_off_on_dref(void) { return getinf_flag(INF_AF, AF_DREFOFF); } +inline bool inf_set_create_off_on_dref(bool _v=true) { return setinf_flag(INF_AF, AF_DREFOFF, _v); } +inline bool inf_op_offset(void) { return getinf_flag(INF_AF, AF_IMMOFF); } +inline bool inf_set_op_offset(bool _v=true) { return setinf_flag(INF_AF, AF_IMMOFF, _v); } +inline bool inf_data_offset(void) { return getinf_flag(INF_AF, AF_DATOFF); } +inline bool inf_set_data_offset(bool _v=true) { return setinf_flag(INF_AF, AF_DATOFF, _v); } +inline bool inf_use_flirt(void) { return getinf_flag(INF_AF, AF_FLIRT); } +inline bool inf_set_use_flirt(bool _v=true) { return setinf_flag(INF_AF, AF_FLIRT, _v); } +inline bool inf_append_sigcmt(void) { return getinf_flag(INF_AF, AF_SIGCMT); } +inline bool inf_set_append_sigcmt(bool _v=true) { return setinf_flag(INF_AF, AF_SIGCMT, _v); } +inline bool inf_allow_sigmulti(void) { return getinf_flag(INF_AF, AF_SIGMLT); } +inline bool inf_set_allow_sigmulti(bool _v=true) { return setinf_flag(INF_AF, AF_SIGMLT, _v); } +inline bool inf_hide_libfuncs(void) { return getinf_flag(INF_AF, AF_HFLIRT); } +inline bool inf_set_hide_libfuncs(bool _v=true) { return setinf_flag(INF_AF, AF_HFLIRT, _v); } +inline bool inf_rename_jumpfunc(void) { return getinf_flag(INF_AF, AF_JFUNC); } +inline bool inf_set_rename_jumpfunc(bool _v=true) { return setinf_flag(INF_AF, AF_JFUNC, _v); } +inline bool inf_rename_nullsub(void) { return getinf_flag(INF_AF, AF_NULLSUB); } +inline bool inf_set_rename_nullsub(bool _v=true) { return setinf_flag(INF_AF, AF_NULLSUB, _v); } +inline bool inf_coagulate_data(void) { return getinf_flag(INF_AF, AF_DODATA); } +inline bool inf_set_coagulate_data(bool _v=true) { return setinf_flag(INF_AF, AF_DODATA, _v); } +inline bool inf_coagulate_code(void) { return getinf_flag(INF_AF, AF_DOCODE); } +inline bool inf_set_coagulate_code(bool _v=true) { return setinf_flag(INF_AF, AF_DOCODE, _v); } +inline bool inf_final_pass(void) { return getinf_flag(INF_AF, AF_FINAL); } +inline bool inf_set_final_pass(bool _v=true) { return setinf_flag(INF_AF, AF_FINAL, _v); } + +inline uint32 inf_get_af2() { return uint32(getinf(INF_AF2)); } +inline bool inf_set_af2(uint32 _v) { return setinf(INF_AF2, ssize_t(_v)); } +inline bool inf_handle_eh(void) { return getinf_flag(INF_AF2, AF2_DOEH); } +inline bool inf_set_handle_eh(bool _v=true) { return setinf_flag(INF_AF2, AF2_DOEH, _v); } +inline bool inf_handle_rtti(void) { return getinf_flag(INF_AF2, AF2_DORTTI); } +inline bool inf_set_handle_rtti(bool _v=true) { return setinf_flag(INF_AF2, AF2_DORTTI, _v); } +inline bool inf_macros_enabled(void) { return getinf_flag(INF_AF2, AF2_MACRO); } +inline bool inf_set_macros_enabled(bool _v=true) { return setinf_flag(INF_AF2, AF2_MACRO, _v); } +inline bool inf_merge_strlits(void) { return getinf_flag(INF_AF2, AF2_MERGESTR); } +inline bool inf_set_merge_strlits(bool _v=true) { return setinf_flag(INF_AF2, AF2_MERGESTR, _v); } + +inline uval_t inf_get_baseaddr() { return uval_t(getinf(INF_BASEADDR)); } +inline bool inf_set_baseaddr(uval_t _v) { return setinf(INF_BASEADDR, ssize_t(_v)); } + +inline sel_t inf_get_start_ss() { return sel_t(getinf(INF_START_SS)); } +inline bool inf_set_start_ss(sel_t _v) { return setinf(INF_START_SS, ssize_t(_v)); } + +inline sel_t inf_get_start_cs() { return sel_t(getinf(INF_START_CS)); } +inline bool inf_set_start_cs(sel_t _v) { return setinf(INF_START_CS, ssize_t(_v)); } + +inline ea_t inf_get_start_ip() { return ea_t(getinf(INF_START_IP)); } +inline bool inf_set_start_ip(ea_t _v) { return setinf(INF_START_IP, ssize_t(_v)); } + +inline ea_t inf_get_start_ea() { return ea_t(getinf(INF_START_EA)); } +inline bool inf_set_start_ea(ea_t _v) { return setinf(INF_START_EA, ssize_t(_v)); } + +inline ea_t inf_get_start_sp() { return ea_t(getinf(INF_START_SP)); } +inline bool inf_set_start_sp(ea_t _v) { return setinf(INF_START_SP, ssize_t(_v)); } + +inline ea_t inf_get_main() { return ea_t(getinf(INF_MAIN)); } +inline bool inf_set_main(ea_t _v) { return setinf(INF_MAIN, ssize_t(_v)); } + +inline ea_t inf_get_min_ea() { return ea_t(getinf(INF_MIN_EA)); } +inline bool inf_set_min_ea(ea_t _v) { return setinf(INF_MIN_EA, ssize_t(_v)); } + +inline ea_t inf_get_max_ea() { return ea_t(getinf(INF_MAX_EA)); } +inline bool inf_set_max_ea(ea_t _v) { return setinf(INF_MAX_EA, ssize_t(_v)); } + +inline ea_t inf_get_omin_ea() { return ea_t(getinf(INF_OMIN_EA)); } +inline bool inf_set_omin_ea(ea_t _v) { return setinf(INF_OMIN_EA, ssize_t(_v)); } + +inline ea_t inf_get_omax_ea() { return ea_t(getinf(INF_OMAX_EA)); } +inline bool inf_set_omax_ea(ea_t _v) { return setinf(INF_OMAX_EA, ssize_t(_v)); } + +inline ea_t inf_get_lowoff() { return ea_t(getinf(INF_LOWOFF)); } +inline bool inf_set_lowoff(ea_t _v) { return setinf(INF_LOWOFF, ssize_t(_v)); } + +inline ea_t inf_get_highoff() { return ea_t(getinf(INF_HIGHOFF)); } +inline bool inf_set_highoff(ea_t _v) { return setinf(INF_HIGHOFF, ssize_t(_v)); } + +inline uval_t inf_get_maxref() { return uval_t(getinf(INF_MAXREF)); } +inline bool inf_set_maxref(uval_t _v) { return setinf(INF_MAXREF, ssize_t(_v)); } + +inline sval_t inf_get_netdelta() { return sval_t(getinf(INF_NETDELTA)); } +inline bool inf_set_netdelta(sval_t _v) { return setinf(INF_NETDELTA, ssize_t(_v)); } + +inline uchar inf_get_xrefnum() { return uchar(getinf(INF_XREFNUM)); } +inline bool inf_set_xrefnum(uchar _v) { return setinf(INF_XREFNUM, ssize_t(_v)); } + +inline uchar inf_get_type_xrefnum() { return uchar(getinf(INF_TYPE_XREFNUM)); } +inline bool inf_set_type_xrefnum(uchar _v) { return setinf(INF_TYPE_XREFNUM, ssize_t(_v)); } + +inline uchar inf_get_refcmtnum() { return uchar(getinf(INF_REFCMTNUM)); } +inline bool inf_set_refcmtnum(uchar _v) { return setinf(INF_REFCMTNUM, ssize_t(_v)); } + +inline uchar inf_get_xrefflag() { return uchar(getinf(INF_XREFFLAG)); } +inline bool inf_set_xrefflag(uchar _v) { return setinf(INF_XREFFLAG, ssize_t(_v)); } +inline bool inf_show_xref_seg(void) { return getinf_flag(INF_XREFFLAG, SW_SEGXRF); } +inline bool inf_set_show_xref_seg(bool _v=true) { return setinf_flag(INF_XREFFLAG, SW_SEGXRF, _v); } +inline bool inf_show_xref_tmarks(void) { return getinf_flag(INF_XREFFLAG, SW_XRFMRK); } +inline bool inf_set_show_xref_tmarks(bool _v=true) { return setinf_flag(INF_XREFFLAG, SW_XRFMRK, _v); } +inline bool inf_show_xref_fncoff(void) { return getinf_flag(INF_XREFFLAG, SW_XRFFNC); } +inline bool inf_set_show_xref_fncoff(bool _v=true) { return setinf_flag(INF_XREFFLAG, SW_XRFFNC, _v); } +inline bool inf_show_xref_val(void) { return getinf_flag(INF_XREFFLAG, SW_XRFVAL); } +inline bool inf_set_show_xref_val(bool _v=true) { return setinf_flag(INF_XREFFLAG, SW_XRFVAL, _v); } + +inline ushort inf_get_max_autoname_len() { return ushort(getinf(INF_MAX_AUTONAME_LEN)); } +inline bool inf_set_max_autoname_len(ushort _v) { return setinf(INF_MAX_AUTONAME_LEN, ssize_t(_v)); } + +inline char inf_get_nametype() { return char(getinf(INF_NAMETYPE)); } +inline bool inf_set_nametype(char _v) { return setinf(INF_NAMETYPE, ssize_t(_v)); } + +inline uint32 inf_get_short_demnames() { return uint32(getinf(INF_SHORT_DEMNAMES)); } +inline bool inf_set_short_demnames(uint32 _v) { return setinf(INF_SHORT_DEMNAMES, ssize_t(_v)); } + +inline uint32 inf_get_long_demnames() { return uint32(getinf(INF_LONG_DEMNAMES)); } +inline bool inf_set_long_demnames(uint32 _v) { return setinf(INF_LONG_DEMNAMES, ssize_t(_v)); } + +inline uchar inf_get_demnames() { return uchar(getinf(INF_DEMNAMES)); } +inline bool inf_set_demnames(uchar _v) { return setinf(INF_DEMNAMES, ssize_t(_v)); } + +inline uchar inf_get_listnames() { return uchar(getinf(INF_LISTNAMES)); } +inline bool inf_set_listnames(uchar _v) { return setinf(INF_LISTNAMES, ssize_t(_v)); } + +inline uchar inf_get_indent() { return uchar(getinf(INF_INDENT)); } +inline bool inf_set_indent(uchar _v) { return setinf(INF_INDENT, ssize_t(_v)); } + +inline uchar inf_get_cmt_indent() { return uchar(getinf(INF_CMT_INDENT)); } +inline bool inf_set_cmt_indent(uchar _v) { return setinf(INF_CMT_INDENT, ssize_t(_v)); } + +inline ushort inf_get_margin() { return ushort(getinf(INF_MARGIN)); } +inline bool inf_set_margin(ushort _v) { return setinf(INF_MARGIN, ssize_t(_v)); } + +inline ushort inf_get_lenxref() { return ushort(getinf(INF_LENXREF)); } +inline bool inf_set_lenxref(ushort _v) { return setinf(INF_LENXREF, ssize_t(_v)); } + +inline uint32 inf_get_outflags() { return uint32(getinf(INF_OUTFLAGS)); } +inline bool inf_set_outflags(uint32 _v) { return setinf(INF_OUTFLAGS, ssize_t(_v)); } +inline bool inf_show_void(void) { return getinf_flag(INF_OUTFLAGS, OFLG_SHOW_VOID); } +inline bool inf_set_show_void(bool _v=true) { return setinf_flag(INF_OUTFLAGS, OFLG_SHOW_VOID, _v); } +inline bool inf_show_auto(void) { return getinf_flag(INF_OUTFLAGS, OFLG_SHOW_AUTO); } +inline bool inf_set_show_auto(bool _v=true) { return setinf_flag(INF_OUTFLAGS, OFLG_SHOW_AUTO, _v); } +inline bool inf_gen_null(void) { return getinf_flag(INF_OUTFLAGS, OFLG_GEN_NULL); } +inline bool inf_set_gen_null(bool _v=true) { return setinf_flag(INF_OUTFLAGS, OFLG_GEN_NULL, _v); } +inline bool inf_show_line_pref(void) { return getinf_flag(INF_OUTFLAGS, OFLG_SHOW_PREF); } +inline bool inf_set_show_line_pref(bool _v=true) { return setinf_flag(INF_OUTFLAGS, OFLG_SHOW_PREF, _v); } +inline bool inf_line_pref_with_seg(void) { return getinf_flag(INF_OUTFLAGS, OFLG_PREF_SEG); } +inline bool inf_set_line_pref_with_seg(bool _v=true) { return setinf_flag(INF_OUTFLAGS, OFLG_PREF_SEG, _v); } +inline bool inf_gen_lzero(void) { return getinf_flag(INF_OUTFLAGS, OFLG_LZERO); } +inline bool inf_set_gen_lzero(bool _v=true) { return setinf_flag(INF_OUTFLAGS, OFLG_LZERO, _v); } +inline bool inf_gen_org(void) { return getinf_flag(INF_OUTFLAGS, OFLG_GEN_ORG); } +inline bool inf_set_gen_org(bool _v=true) { return setinf_flag(INF_OUTFLAGS, OFLG_GEN_ORG, _v); } +inline bool inf_gen_assume(void) { return getinf_flag(INF_OUTFLAGS, OFLG_GEN_ASSUME); } +inline bool inf_set_gen_assume(bool _v=true) { return setinf_flag(INF_OUTFLAGS, OFLG_GEN_ASSUME, _v); } +inline bool inf_gen_tryblks(void) { return getinf_flag(INF_OUTFLAGS, OFLG_GEN_TRYBLKS); } +inline bool inf_set_gen_tryblks(bool _v=true) { return setinf_flag(INF_OUTFLAGS, OFLG_GEN_TRYBLKS, _v); } + +inline uchar inf_get_cmtflg() { return uchar(getinf(INF_CMTFLG)); } +inline bool inf_set_cmtflg(uchar _v) { return setinf(INF_CMTFLG, ssize_t(_v)); } +inline bool inf_show_repeatables(void) { return getinf_flag(INF_CMTFLG, SCF_RPTCMT); } +inline bool inf_set_show_repeatables(bool _v=true) { return setinf_flag(INF_CMTFLG, SCF_RPTCMT, _v); } +inline bool inf_show_all_comments(void) { return getinf_flag(INF_CMTFLG, SCF_ALLCMT); } +inline bool inf_set_show_all_comments(bool _v=true) { return setinf_flag(INF_CMTFLG, SCF_ALLCMT, _v); } +inline bool inf_hide_comments(void) { return getinf_flag(INF_CMTFLG, SCF_NOCMT); } +inline bool inf_set_hide_comments(bool _v=true) { return setinf_flag(INF_CMTFLG, SCF_NOCMT, _v); } +inline bool inf_show_src_linnum(void) { return getinf_flag(INF_CMTFLG, SCF_LINNUM); } +inline bool inf_set_show_src_linnum(bool _v=true) { return setinf_flag(INF_CMTFLG, SCF_LINNUM, _v); } +inline bool inf_test_mode(void) { return getinf_flag(INF_CMTFLG, SCF_TESTMODE); } +inline bool inf_show_hidden_insns(void) { return getinf_flag(INF_CMTFLG, SCF_SHHID_ITEM); } +inline bool inf_set_show_hidden_insns(bool _v=true) { return setinf_flag(INF_CMTFLG, SCF_SHHID_ITEM, _v); } +inline bool inf_show_hidden_funcs(void) { return getinf_flag(INF_CMTFLG, SCF_SHHID_FUNC); } +inline bool inf_set_show_hidden_funcs(bool _v=true) { return setinf_flag(INF_CMTFLG, SCF_SHHID_FUNC, _v); } +inline bool inf_show_hidden_segms(void) { return getinf_flag(INF_CMTFLG, SCF_SHHID_SEGM); } +inline bool inf_set_show_hidden_segms(bool _v=true) { return setinf_flag(INF_CMTFLG, SCF_SHHID_SEGM, _v); } + +inline uchar inf_get_limiter() { return uchar(getinf(INF_LIMITER)); } +inline bool inf_set_limiter(uchar _v) { return setinf(INF_LIMITER, ssize_t(_v)); } +inline bool inf_is_limiter_thin(void) { return getinf_flag(INF_LIMITER, LMT_THIN); } +inline bool inf_set_limiter_thin(bool _v=true) { return setinf_flag(INF_LIMITER, LMT_THIN, _v); } +inline bool inf_is_limiter_thick(void) { return getinf_flag(INF_LIMITER, LMT_THICK); } +inline bool inf_set_limiter_thick(bool _v=true) { return setinf_flag(INF_LIMITER, LMT_THICK, _v); } +inline bool inf_is_limiter_empty(void) { return getinf_flag(INF_LIMITER, LMT_EMPTY); } +inline bool inf_set_limiter_empty(bool _v=true) { return setinf_flag(INF_LIMITER, LMT_EMPTY, _v); } + +inline short inf_get_bin_prefix_size() { return short(getinf(INF_BIN_PREFIX_SIZE)); } +inline bool inf_set_bin_prefix_size(short _v) { return setinf(INF_BIN_PREFIX_SIZE, ssize_t(_v)); } + +inline uchar inf_get_prefflag() { return uchar(getinf(INF_PREFFLAG)); } +inline bool inf_set_prefflag(uchar _v) { return setinf(INF_PREFFLAG, ssize_t(_v)); } +inline bool inf_prefix_show_segaddr(void) { return getinf_flag(INF_PREFFLAG, PREF_SEGADR); } +inline bool inf_set_prefix_show_segaddr(bool _v=true) { return setinf_flag(INF_PREFFLAG, PREF_SEGADR, _v); } +inline bool inf_prefix_show_funcoff(void) { return getinf_flag(INF_PREFFLAG, PREF_FNCOFF); } +inline bool inf_set_prefix_show_funcoff(bool _v=true) { return setinf_flag(INF_PREFFLAG, PREF_FNCOFF, _v); } +inline bool inf_prefix_show_stack(void) { return getinf_flag(INF_PREFFLAG, PREF_STACK); } +inline bool inf_set_prefix_show_stack(bool _v=true) { return setinf_flag(INF_PREFFLAG, PREF_STACK, _v); } +inline bool inf_prefix_truncate_opcode_bytes(void) { return getinf_flag(INF_PREFFLAG, PREF_PFXTRUNC); } +inline bool inf_set_prefix_truncate_opcode_bytes(bool _v=true) { return setinf_flag(INF_PREFFLAG, PREF_PFXTRUNC, _v); } + +inline uchar inf_get_strlit_flags() { return uchar(getinf(INF_STRLIT_FLAGS)); } +inline bool inf_set_strlit_flags(uchar _v) { return setinf(INF_STRLIT_FLAGS, ssize_t(_v)); } +inline bool inf_strlit_names(void) { return getinf_flag(INF_STRLIT_FLAGS, STRF_GEN); } +inline bool inf_set_strlit_names(bool _v=true) { return setinf_flag(INF_STRLIT_FLAGS, STRF_GEN, _v); } +inline bool inf_strlit_name_bit(void) { return getinf_flag(INF_STRLIT_FLAGS, STRF_AUTO); } +inline bool inf_set_strlit_name_bit(bool _v=true) { return setinf_flag(INF_STRLIT_FLAGS, STRF_AUTO, _v); } +inline bool inf_strlit_serial_names(void) { return getinf_flag(INF_STRLIT_FLAGS, STRF_SERIAL); } +inline bool inf_set_strlit_serial_names(bool _v=true) { return setinf_flag(INF_STRLIT_FLAGS, STRF_SERIAL, _v); } +inline bool inf_unicode_strlits(void) { return getinf_flag(INF_STRLIT_FLAGS, STRF_UNICODE); } +inline bool inf_set_unicode_strlits(bool _v=true) { return setinf_flag(INF_STRLIT_FLAGS, STRF_UNICODE, _v); } +inline bool inf_strlit_autocmt(void) { return getinf_flag(INF_STRLIT_FLAGS, STRF_COMMENT); } +inline bool inf_set_strlit_autocmt(bool _v=true) { return setinf_flag(INF_STRLIT_FLAGS, STRF_COMMENT, _v); } +inline bool inf_strlit_savecase(void) { return getinf_flag(INF_STRLIT_FLAGS, STRF_SAVECASE); } +inline bool inf_set_strlit_savecase(bool _v=true) { return setinf_flag(INF_STRLIT_FLAGS, STRF_SAVECASE, _v); } + +inline uchar inf_get_strlit_break() { return uchar(getinf(INF_STRLIT_BREAK)); } +inline bool inf_set_strlit_break(uchar _v) { return setinf(INF_STRLIT_BREAK, ssize_t(_v)); } + +inline char inf_get_strlit_zeroes() { return char(getinf(INF_STRLIT_ZEROES)); } +inline bool inf_set_strlit_zeroes(char _v) { return setinf(INF_STRLIT_ZEROES, ssize_t(_v)); } + +inline int32 inf_get_strtype() { return int32(getinf(INF_STRTYPE)); } +inline bool inf_set_strtype(int32 _v) { return setinf(INF_STRTYPE, ssize_t(_v)); } + +inline uval_t inf_get_strlit_sernum() { return uval_t(getinf(INF_STRLIT_SERNUM)); } +inline bool inf_set_strlit_sernum(uval_t _v) { return setinf(INF_STRLIT_SERNUM, ssize_t(_v)); } + +inline uval_t inf_get_datatypes() { return uval_t(getinf(INF_DATATYPES)); } +inline bool inf_set_datatypes(uval_t _v) { return setinf(INF_DATATYPES, ssize_t(_v)); } + +inline uint32 inf_get_abibits() { return uint32(getinf(INF_ABIBITS)); } +inline bool inf_set_abibits(uint32 _v) { return setinf(INF_ABIBITS, ssize_t(_v)); } +inline bool inf_is_mem_aligned4(void) { return getinf_flag(INF_ABIBITS, ABI_8ALIGN4); } +inline bool inf_set_mem_aligned4(bool _v=true) { return setinf_flag(INF_ABIBITS, ABI_8ALIGN4, _v); } +inline bool inf_pack_stkargs(void) { return getinf_flag(INF_ABIBITS, ABI_PACK_STKARGS); } +inline bool inf_set_pack_stkargs(bool _v=true) { return setinf_flag(INF_ABIBITS, ABI_PACK_STKARGS, _v); } +inline bool inf_big_arg_align(void) { return getinf_flag(INF_ABIBITS, ABI_BIGARG_ALIGN); } +inline bool inf_set_big_arg_align(bool _v=true) { return setinf_flag(INF_ABIBITS, ABI_BIGARG_ALIGN, _v); } +inline bool inf_stack_ldbl(void) { return getinf_flag(INF_ABIBITS, ABI_STACK_LDBL); } +inline bool inf_set_stack_ldbl(bool _v=true) { return setinf_flag(INF_ABIBITS, ABI_STACK_LDBL, _v); } +inline bool inf_stack_varargs(void) { return getinf_flag(INF_ABIBITS, ABI_STACK_VARARGS); } +inline bool inf_set_stack_varargs(bool _v=true) { return setinf_flag(INF_ABIBITS, ABI_STACK_VARARGS, _v); } +inline bool inf_is_hard_float(void) { return getinf_flag(INF_ABIBITS, ABI_HARD_FLOAT); } +inline bool inf_set_hard_float(bool _v=true) { return setinf_flag(INF_ABIBITS, ABI_HARD_FLOAT, _v); } +inline bool inf_abi_set_by_user(void) { return getinf_flag(INF_ABIBITS, ABI_SET_BY_USER); } +inline bool inf_set_abi_set_by_user(bool _v=true) { return setinf_flag(INF_ABIBITS, ABI_SET_BY_USER, _v); } +inline bool inf_use_gcc_layout(void) { return getinf_flag(INF_ABIBITS, ABI_GCC_LAYOUT); } +inline bool inf_set_use_gcc_layout(bool _v=true) { return setinf_flag(INF_ABIBITS, ABI_GCC_LAYOUT, _v); } +inline bool inf_map_stkargs(void) { return getinf_flag(INF_ABIBITS, ABI_MAP_STKARGS); } +inline bool inf_set_map_stkargs(bool _v=true) { return setinf_flag(INF_ABIBITS, ABI_MAP_STKARGS, _v); } +inline bool inf_huge_arg_align(void) { return getinf_flag(INF_ABIBITS, ABI_HUGEARG_ALIGN); } +inline bool inf_set_huge_arg_align(bool _v=true) { return setinf_flag(INF_ABIBITS, ABI_HUGEARG_ALIGN, _v); } + +inline uint32 inf_get_appcall_options() { return uint32(getinf(INF_APPCALL_OPTIONS)); } +inline bool inf_set_appcall_options(uint32 _v) { return setinf(INF_APPCALL_OPTIONS, ssize_t(_v)); } + +inline ea_t inf_get_privrange_start_ea() { return ea_t(getinf(INF_PRIVRANGE_START_EA)); } +inline bool inf_set_privrange_start_ea(ea_t _v) { return setinf(INF_PRIVRANGE_START_EA, ssize_t(_v)); } + +inline ea_t inf_get_privrange_end_ea() { return ea_t(getinf(INF_PRIVRANGE_END_EA)); } +inline bool inf_set_privrange_end_ea(ea_t _v) { return setinf(INF_PRIVRANGE_END_EA, ssize_t(_v)); } + +inline comp_t inf_get_cc_id() { return comp_t(getinf(INF_CC_ID)); } +inline bool inf_set_cc_id(comp_t _v) { return setinf(INF_CC_ID, ssize_t(_v)); } + +inline cm_t inf_get_cc_cm() { return cm_t(getinf(INF_CC_CM)); } +inline bool inf_set_cc_cm(cm_t _v) { return setinf(INF_CC_CM, ssize_t(_v)); } + +inline callcnv_t inf_get_callcnv() { return callcnv_t(getinf(INF_CALLCNV)); } +inline bool inf_set_callcnv(callcnv_t _v) { return setinf(INF_CALLCNV, ssize_t(_v)); } + +inline uchar inf_get_cc_size_i() { return uchar(getinf(INF_CC_SIZE_I)); } +inline bool inf_set_cc_size_i(uchar _v) { return setinf(INF_CC_SIZE_I, ssize_t(_v)); } + +inline uchar inf_get_cc_size_b() { return uchar(getinf(INF_CC_SIZE_B)); } +inline bool inf_set_cc_size_b(uchar _v) { return setinf(INF_CC_SIZE_B, ssize_t(_v)); } + +inline uchar inf_get_cc_size_e() { return uchar(getinf(INF_CC_SIZE_E)); } +inline bool inf_set_cc_size_e(uchar _v) { return setinf(INF_CC_SIZE_E, ssize_t(_v)); } + +inline uchar inf_get_cc_defalign() { return uchar(getinf(INF_CC_DEFALIGN)); } +inline bool inf_set_cc_defalign(uchar _v) { return setinf(INF_CC_DEFALIGN, ssize_t(_v)); } + +inline uchar inf_get_cc_size_s() { return uchar(getinf(INF_CC_SIZE_S)); } +inline bool inf_set_cc_size_s(uchar _v) { return setinf(INF_CC_SIZE_S, ssize_t(_v)); } + +inline uchar inf_get_cc_size_l() { return uchar(getinf(INF_CC_SIZE_L)); } +inline bool inf_set_cc_size_l(uchar _v) { return setinf(INF_CC_SIZE_L, ssize_t(_v)); } + +inline uchar inf_get_cc_size_ll() { return uchar(getinf(INF_CC_SIZE_LL)); } +inline bool inf_set_cc_size_ll(uchar _v) { return setinf(INF_CC_SIZE_LL, ssize_t(_v)); } + +inline uchar inf_get_cc_size_ldbl() { return uchar(getinf(INF_CC_SIZE_LDBL)); } +inline bool inf_set_cc_size_ldbl(uchar _v) { return setinf(INF_CC_SIZE_LDBL, ssize_t(_v)); } + +inline bool inf_get_procname(char *buf, size_t bufsize=IDAINFO_PROCNAME_SIZE) +{ + return getinf_buf(INF_PROCNAME, buf, bufsize) > 0; +} +inline qstring inf_get_procname() +{ + char buf[IDAINFO_PROCNAME_SIZE]; + if ( !getinf_buf(INF_PROCNAME, buf, sizeof(buf)) ) + buf[0] = '\0'; + return qstring(buf); +} +inline bool inf_set_procname(const char *_v, size_t len=size_t(-1)) +{ + if ( _v == nullptr ) + return false; + if ( len == size_t(-1) ) + len = ::qstrlen(_v); + return setinf_buf(INF_PROCNAME, _v, qmin(len, IDAINFO_PROCNAME_SIZE)); +} + +inline bool inf_get_strlit_pref(char *buf, size_t bufsize=IDAINFO_STRLIT_PREF_SIZE) +{ + return getinf_buf(INF_STRLIT_PREF, buf, bufsize) > 0; +} +inline qstring inf_get_strlit_pref() +{ + char buf[IDAINFO_STRLIT_PREF_SIZE]; + if ( !getinf_buf(INF_STRLIT_PREF, buf, sizeof(buf)) ) + buf[0] = '\0'; + return qstring(buf); +} +inline bool inf_set_strlit_pref(const char *_v, size_t len=size_t(-1)) +{ + if ( _v == nullptr ) + return false; + if ( len == size_t(-1) ) + len = ::qstrlen(_v); + return setinf_buf(INF_STRLIT_PREF, _v, qmin(len, IDAINFO_STRLIT_PREF_SIZE)); +} + +inline bool inf_get_cc(compiler_info_t *out) { return getinf_buf(INF_COMPILER_INFO, out, sizeof(*out)) > 0; } +inline bool inf_set_cc(const compiler_info_t &_v) { return setinf_buf(INF_COMPILER_INFO, &_v, sizeof(_v)); } + +inline bool inf_get_privrange(range_t *out) { return getinf_buf(INF_PRIVRANGE, out, sizeof(*out)) > 0; } +inline bool inf_set_privrange(const range_t &_v) { return setinf_buf(INF_PRIVRANGE, &_v, sizeof(_v)); } + +// + +inline range_t inf_get_privrange() { range_t r; inf_get_privrange(&r); return r; } + +/// Get/set low/high 16bit halves of inf.af +inline ushort inf_get_af_low() { return inf_get_af() & 0xffff; } +inline void inf_set_af_low(ushort saf) { uint32 af = (inf_get_af() & 0xffff0000) | saf; inf_set_af(af); } +inline ushort inf_get_af_high() { return (inf_get_af() >> 16) & 0xffff; } +inline void inf_set_af_high(ushort saf2) { uint32 af = (inf_get_af() & 0xffff) | (saf2 << 16); inf_set_af(af); } +/// Get/set low 16bit half of inf.af2 +inline ushort inf_get_af2_low() { return inf_get_af2() & 0xffff; } +inline void inf_set_af2_low(ushort saf) { uint32 af2 = (inf_get_af2() & 0xffff0000) | saf; inf_set_af2(af2); } + +inline int inf_get_pack_mode() +{ + uint32 lflags = inf_get_lflags(); + return (lflags & LFLG_COMPRESS) != 0 ? IDB_COMPRESSED + : (lflags & LFLG_PACK) != 0 ? IDB_PACKED + : IDB_UNPACKED; +} + +inline int inf_set_pack_mode(int pack_mode) +{ + int old = inf_get_pack_mode(); + uint32 lflags = inf_get_lflags(); + setflag(lflags, LFLG_COMPRESS, pack_mode == IDB_COMPRESSED); + setflag(lflags, LFLG_PACK, pack_mode == IDB_PACKED); + inf_set_lflags(lflags); + return old; +} + +inline void inf_inc_database_change_count(int cnt=1) +{ inf_set_database_change_count(inf_get_database_change_count()+cnt); } + +/// Get #DEMNAM_MASK bits of #demnames +inline uchar inf_get_demname_form(void) { return uchar(inf_get_demnames() & DEMNAM_MASK); } + +inline uval_t inf_postinc_strlit_sernum(uval_t cnt=1) +{ + uval_t was = inf_get_strlit_sernum(); + inf_set_strlit_sernum(was + cnt); + return was; +} + +inline bool inf_like_binary(void) +{ return is_filetype_like_binary(inf_get_filetype()); } + + + +//------------------------------------------------------------------------- +/// max number of operands allowed for an instruction + +#define UA_MAXOP 8 + + +/// Get default disassembly line options + +inline int calc_default_idaplace_flags(void) +{ + return 0; +} + +//------------------------------------------------------------------------- +/// Convert (sel,off) value to a linear address + +inline ea_t idaapi to_ea(sel_t reg_cs, uval_t reg_ip) +{ + return (reg_cs << 4) + reg_ip; +} + +//------------------------------------------------------------------------- +/// helper class to support 32bit addresses in ida64 + +struct ea_helper_t +{ + const uval_t mask32 = 0xFFFFFFFF; + +#ifdef __EA64__ + const uval_t mask64 = 0xFFFFFFFFFFFFFFFFULL; + + uval_t mask = mask64; + int ea_size = 8; + bool enabled = true; // can be disabled for old databases + + void setup(bool is_64bit) + { + if ( enabled ) + { + mask = is_64bit ? mask64 : mask32; + ea_size = is_64bit ? 8 : 4; + } + } + void disable() + { + enabled = false; + mask = mask64; + ea_size = 8; + } + + bool is_enabled() const { return enabled; } + sval_t ea2sval(uval_t v) const { return ea_size == 8 ? v : int32(v); } + uval_t trunc_uval(uval_t v) const { return v & mask; } + ea_t uval2ea(uval_t v) const { return v == mask ? BADADDR : v; } + bool fits_ea_space(ea_t ea) const { return ea <= mask; } + bool fits_ea_space(const range_t &r) const + { + return fits_ea_space(r.start_ea) && fits_ea_space(r.end_ea); + } + ea_t trunc_ea(ea_t ea) const + { +#ifdef TESTABLE_BUILD + QASSERT(2762, ea != BADADDR); // do not permit to truncate BADADDR +#endif + return ea & mask; + } + ea_t ea_space_end() const + { + // 0x1'0000'0000 is the end of the 32-bit address space in ida64 + return ea_size == 8 ? BADADDR : ea_t(BADADDR32) + 1; + } + ea_t to_ea(sel_t cs, uval_t ip) const + { + ea_t ea = ::to_ea(cs, ip); + if ( !like_badaddr(ea) ) + ea = trunc_ea(ea); + return ea; + } + ea_t fix_ea_tid(ea_t ea) const + { + return ea_size == 8 || inf_get_privrange().contains(ea) + ? ea + : uval2ea(trunc_uval(ea)); + } + bool is_ea_add_ok(uval_t x, uval_t y) const + { + if ( x == BADADDR ) + return y == 0; + if ( y == BADADDR ) + return x == 0; +#ifdef TESTABLE_BUILD + QASSERT(2763, fits_ea_space(x) && fits_ea_space(y)); +#endif + return y <= mask - x; + } + sval_t sval_max() const { return ea_size == 8 ? SVAL_MAX : INT_MAX; } + sval_t sval_min() const { return ea_size == 8 ? SVAL_MIN : INT_MIN; } +#else // __EA64__ + const uval_t mask = mask32; + const int ea_size = sizeof(ea_t); + + void setup(bool /*is_64bit*/) {} + bool is_enabled() const { return false; } + sval_t ea2sval(uval_t v) const { return v; } + ea_t uval2ea(uval_t v) const { return v; } + bool fits_ea_space(ea_t /*ea*/) const { return true; } + bool fits_ea_space(const range_t &) const { return true; } + ea_t trunc_ea(ea_t ea) const { return ea; } + ea_t ea_space_end() const { return BADADDR; } + uval_t trunc_uval(uval_t v) const { return v; } + ea_t to_ea(sel_t cs, uval_t ip) const { return ::to_ea(cs, ip); } + ea_t fix_ea_tid(ea_t ea) const { return ea; } + + bool is_ea_add_ok(uval_t x, uval_t y) const { return is_add_ok(x, y); } + sval_t sval_max() const { return SVAL_MAX; } + sval_t sval_min() const { return SVAL_MIN; } +#endif + + bool like_badaddr(ea_t ea) const { return uval2ea(ea) == BADADDR; } + ea_t align_ea(ea_t ea, int size) const { return trunc_ea(align_up(ea, size)); } + bool advance_ea(ea_t *ea, asize_t n) const + { + if ( !is_ea_add_ok(*ea, n) ) + return false; + *ea += n; + return true; + } +}; + +#ifndef SWIG +#define DEFINE_EA_HELPER_FUNCS(eh) \ + const ea_helper_t &eah() const { return (eh); }\ + bool fits_ea_space(ea_t _ea) const { return (eh).fits_ea_space(_ea); }\ + bool fits_ea_space(const range_t &r) const { return (eh).fits_ea_space(r); }\ + ea_t trunc_ea(ea_t _ea) const { return (eh).trunc_ea(_ea); }\ + uval_t trunc_uval(uval_t v) const { return (eh).trunc_uval(v); }\ + ea_t ea_space_end() const { return (eh).ea_space_end(); }\ + ea_t to_ea(sel_t cs, uval_t ip) const { return (eh).to_ea(cs, ip); }\ + sval_t ea2sval(ea_t _ea) const { return (eh).ea2sval(_ea); }\ + ea_t uval2ea(uval_t v) const { return (eh).uval2ea(v); }\ + bool like_badaddr(ea_t _ea) const { return (eh).like_badaddr(_ea); }\ + bool is_ea_add_ok(uval_t x, uval_t y) const { return (eh).is_ea_add_ok(x, y); }\ + sval_t sval_max() const { return (eh).sval_max(); }\ + ea_t align_ea(ea_t _ea, int size) const { return (eh).align_ea(_ea, size); }\ + bool advance_ea(ea_t *_ea, asize_t _n) const { return (eh).advance_ea(_ea, _n); }\ + ea_t fix_ea_tid(ea_t _ea) const { return (eh).fix_ea_tid(_ea); } +#else +#define DEFINE_EA_HELPER_FUNCS(eh) +#endif + +/// \def{IDB_EXT32, Database file extension for 32-bit programs} +/// \def{IDB_EXT64, Database file extension for 64-bit programs} +/// \def{IDB_EXT, Database file extension} +#define IDB_EXT32 "idb" +#define IDB_EXT64 "i64" +#ifdef __EA64__ +#define IDB_EXT IDB_EXT64 +#else +#define IDB_EXT IDB_EXT32 +#endif + +//-------------------------------------------------------------------------- +/// Types of events that be hooked to with \ref hook_event_listener() +enum hook_type_t +{ + HT_IDP, ///< Hook to the processor module. + ///< The callback will receive all processor_t::event_t events. + HT_UI, ///< Hook to the user interface. + ///< The callback will receive all ::ui_notification_t events. + HT_DBG, ///< Hook to the debugger. + ///< The callback will receive all ::dbg_notification_t events. + HT_IDB, ///< Hook to the database events. + ///< These events are separated from the ::HT_IDP group + ///< to speed things up (there are too many plugins and + ///< modules hooking to the ::HT_IDP). Some essential events + ///< are still generated in th ::HT_IDP group: + ///< make_code, make_data + ///< This list is not exhaustive. + ///< A common trait of all events in this group: the kernel + ///< does not expect any reaction to the event and does not + ///< check the return code. For event names, see ::idb_event. + HT_DEV, ///< Internal debugger events. + ///< Not stable and undocumented for the moment + HT_VIEW, ///< Custom/IDA views notifications. + ///< Refer to ::view_notification_t + ///< for notification codes + HT_OUTPUT, ///< Output window notifications. + ///< Refer to ::msg_notification_t + ///< (::view_notification_t) + HT_GRAPH, ///< Handling graph operations + ///< (::graph_notification_t) + HT_IDD, ///< Hook to the debugger plugin. + ///< The callback will receive all debugger_t::event_t events. + HT_LAST +}; + + +//-------------------------------------------------------------------------- +/// Callback provided to hook_to_notification_point(). +/// A plugin can hook to a notification point and receive notifications +/// of all major events in IDA. The callback function will be called +/// for each event. +/// \param user_data data supplied in call to hook_to_notification_point() +/// \param notification_code processor_t::event_t or ::ui_notification_t or +/// debugger_t::event_t, depending on the hook type +/// \param va additional parameters supplied with the notification. +/// see the event descriptions for information +/// \retval 0 ok, the event should be processed further +/// \retval !=0 the event is blocked and should be discarded. +/// in the case of processor modules, the returned value is used +/// as the return value of processor_t::notify() + +typedef ssize_t idaapi hook_cb_t(void *user_data, int notification_code, va_list va); + + +/// Register a callback for a class of events in IDA + +idaman DEPRECATED bool ida_export hook_to_notification_point( + hook_type_t hook_type, + hook_cb_t *cb, + void *user_data = nullptr); + + +/// Unregister a callback (also see hook_to_notification_point()). +/// A plugin should unhook before being unloaded +/// (preferably in its termination function). +/// If different callbacks have the same callback function pointer +/// and user_data is not nullptr, only the callback whose associated +/// user defined data matches will be removed. +/// \return number of unhooked functions. + +idaman DEPRECATED int ida_export unhook_from_notification_point( + hook_type_t hook_type, + hook_cb_t *cb, + void *user_data = nullptr); + + +/// Generate event notification. +/// \param hook_type hook type +/// \param notification_code event code +/// \param va additional arguments +/// \return !=0: event processed + +idaman ssize_t ida_export invoke_callbacks(hook_type_t hook_type, int notification_code, va_list va); + + +/// Implements action to take when the event was processed by the notification callback +/// (i.e. when the notification callback returns non-zero code). +/// All the registered (\ref register_post_event_visitor()) post-event visitors will be +/// called in turn after the event was processed by one of the notification callbacks. + +struct post_event_visitor_t +{ + /// Action for post-event management. + /// May change the result of the processed event or data carried by the event. + /// \param code the notification callback or other post-event action + /// return code (result of the processed event) + /// \param notification_code event code + /// \param va additional arguments, the same as for processed event + /// \return result of the event processed + + virtual ssize_t idaapi handle_post_event( + ssize_t code, + int notification_code, + va_list va) = 0; + + virtual ~post_event_visitor_t() {} +}; + + +/// Register the post-event visitor. +/// The kernel will not take ownership, nor delete the VISITOR instance. +/// Therefore, it's up to the plugin to handle it. +/// In addition, the post_event_visitor_t will be automatically unregistered +/// when the owner plugin is unloaded from memory. +/// \param hook_type hook type +/// \param visitor post-event visitor +/// \param owner the owner plugin of the post_event_visitor_t type +/// \return success + +idaman bool ida_export register_post_event_visitor( + hook_type_t hook_type, + post_event_visitor_t *visitor, + const plugmod_t *owner); + + +/// Unregister the post-event visitor. +/// \param hook_type hook type +/// \param visitor post-event visitor +/// \return success + +idaman bool ida_export unregister_post_event_visitor( + hook_type_t hook_type, + post_event_visitor_t *visitor); + + +/// Get the current database context ID +/// \return the database context ID, or -1 if no current database +idaman ssize_t ida_export get_dbctx_id(); + +/// Get number of database contexts +/// \return number of database contexts +idaman size_t ida_export get_dbctx_qty(); + +/// Switch to the database with the provided context ID +/// \param idx the index of the database to switch to +/// \return the current dbctx_t instance or nullptr +idaman struct dbctx_t *ida_export switch_dbctx(size_t idx); + +/// Check if the database is busy (e.g. performing some critical operations +/// and cannot be safely accessed) +idaman bool ida_export is_database_busy(); + +/// Validate the database +/// \param vld_flags combination of VLD_.. constants +/// \return number of corrupted/fixed records +idaman size_t ida_export validate_idb(uint32 vld_flags=0); +/// \defgroup VLD_ flags used by validate_idb() +///@{ +#define VLD_AUTO_REPAIR 0x01 ///< automatically repair the database +#define VLD_DIALOG 0x02 ///< ask user to repair (this bit is mutually exclusive with VLD_AUTO_REPAIR) +#define VLD_SILENT 0x04 ///< no messages to the output window +///@} + +/// Move privrange to the specified address +/// \param new_privrange_start new start address of the privrange +/// \return success +idaman bool ida_export move_privrange(ea_t new_privrange_start); + + +//-------------------------------------------------------------------------- +/// idbattr mapping value=>string for human-readable representation of IDB attrs +struct idbattr_valmap_t +{ + uint64 value; + const char *valname; +}; + +//-------------------------------------------------------------------------- +/// access to attribute by structure/offset/width or netnode/index/width +struct idbattr_info_t +{ + const char *name; ///< human-readable name. if null, then + ///< the field will not be merged as part of INF. + uintptr_t offset; ///< field position: + ///< offset within a structure (IDI_STRUCFLD) + ///< altval or supval index (IDI_NODEVAL) + ///< hashval name (IDI_ALTVAL/IDI_SUPVAL+IDI_HASH) + size_t width; ///< field width in bytes + uint64 bitmask; ///< mask for bitfields (0-not bitfield) + uchar tag; ///< tag of node value (if IDI_NODEVAL is set) + const idbattr_valmap_t *vmap; ///< array value=>name (terminated by empty element) + const char *individual_node; ///< individual node name (nullptr - use default) + uint idi_flags; +#define IDI_STRUCFLD 0 ///< structure field (opposite to IDI_NODEVAL) +#define IDI_ALTVAL 0x00001 ///< netnode: altval +#define IDI_SUPVAL 0x00002 ///< netnode: supval +#define IDI_VALOBJ 0x00004 ///< netnode: valobj +#define IDI_BLOB 0x00008 ///< netnode: blob +#define IDI_SCALAR 0 ///< scalar value (default) +#define IDI_CSTR 0x00010 ///< string +#define IDI_QSTRING 0x00020 ///< qstring +#define IDI_BYTEARRAY 0x00040 ///< byte array: binary representation +#define IDI_EA_HEX 0 ///< default representation: hex or "BADADDR" +#define IDI_DEC 0x00080 ///< show as decimal +#define IDI_HEX 0x00100 ///< show as hexadecimal +#define IDI_INC 0x00200 ///< stored value is incremented (scalars only) +#define IDI_MAP_VAL 0x00400 ///< apply ea2node() to value +#define IDI_HASH 0x00800 ///< hashed node field, hash name in offset +#define IDI_HLPSTRUC 0x01000 ///< call helper for pointer to structure +#define IDI_READONLY 0x02000 ///< read-only field (cannot be modified) +#define IDI_BITMAP 0x04000 ///< bitmap field: interpret bitmask as bit number +#define IDI_ONOFF 0x08000 ///< show boolean as on/off (not true/false) +#define IDI_NOMERGE 0x10000 ///< field should not be merged as part of INF + +#define IDI_NODEVAL (IDI_ALTVAL|IDI_SUPVAL|IDI_VALOBJ|IDI_BLOB) +#define IDI_BUFVAR (IDI_CSTR|IDI_BYTEARRAY|IDI_QSTRING|IDI_BITMAP) + + uint32 maxsize; ///< max bytes reserved for storage in netnode + + bool is_node_altval() const { return (idi_flags & IDI_ALTVAL) != 0; } + bool is_node_supval() const { return (idi_flags & IDI_SUPVAL) != 0; } + bool is_node_valobj() const { return (idi_flags & IDI_VALOBJ) != 0; } + bool is_node_blob() const { return (idi_flags & IDI_BLOB) != 0; } + bool is_node_var() const { return (idi_flags & IDI_NODEVAL) != 0; } + bool is_struc_field() const { return !is_node_var(); } + bool is_cstr() const { return (idi_flags & IDI_CSTR) != 0; } + bool is_qstring() const { return (idi_flags & IDI_QSTRING) != 0; } + bool is_bytearray() const { return (idi_flags & IDI_BYTEARRAY) != 0; } + bool is_buf_var() const { return (idi_flags & IDI_BUFVAR) != 0; } + bool is_decimal() const { return (idi_flags & IDI_DEC) != 0; } + bool is_hexadecimal() const { return (idi_flags & IDI_HEX) != 0; } + bool is_readonly_var() const { return (idi_flags & IDI_READONLY) != 0; } + bool is_incremented() const { return (idi_flags & IDI_INC) != 0; } + bool is_val_mapped() const { return (idi_flags & IDI_MAP_VAL) != 0; } + bool is_hash() const { return (idi_flags & IDI_HASH) != 0; } + bool use_hlpstruc() const { return (idi_flags & IDI_HLPSTRUC) != 0; } + bool is_bitmap() const { return (idi_flags & IDI_BITMAP) != 0; } + bool is_onoff() const { return (idi_flags & IDI_ONOFF) != 0; } + bool is_scalar_var() const { return !is_buf_var(); } + bool is_bitfield() const { return bitmask != 0 || is_bitmap(); } + bool is_boolean() const + { + return is_bitmap() || (bitmask != 0 && is_pow2(bitmask)); + } + bool has_individual_node() const { return individual_node != nullptr; } + + const char *str_true() const { return is_onoff() ? "on" : "true"; } + const char *str_false() const { return is_onoff() ? "off" : "false"; } + + size_t ridx() const { QASSERT(1932, is_node_var()); return offset; } + const char *hashname() const { QASSERT(2040, is_hash()); return (const char *)offset; } + bool operator < (const idbattr_info_t &r) const + { + return strcmp(name, r.name) < 0; + } + +}; +DECLARE_TYPE_AS_MOVABLE(idbattr_info_t); + +#endif // _IDA_HPP + +``` + +`IdaSDK/ida_highlighter.hpp`: + +```hpp +#ifndef IDA_HIGHLIGHTER +#define IDA_HIGHLIGHTER + +#include + +idaman void ida_export code_highlight_block(void *context, highlighter_cbs_t *highlighter_cbs, const qstring &text); + +struct ida_syntax_highlighter_helper_t +{ + virtual ~ida_syntax_highlighter_helper_t() {} +// returns the length of text to colorize +// negative values may have special meaning in the future. + virtual ssize_t get_text_color(syntax_highlight_style * /*out_color*/, const char * /*str*/) { return 0; } +// returns true if identifier is colorized + virtual bool get_ident_color(syntax_highlight_style * /*out_color*/, const qstring &/*ident*/) { return false; } +}; +typedef qvector ida_syntax_highlighter_helpers_t; + +#define MLTCMTMASK 0xF +#define PREPROC_FLAG 0x10 + +// Common implementation of syntax_highlighter_t used in different parts of IDA +struct ida_syntax_highlighter_t : syntax_highlighter_t +{ +protected: + // keys of keywords_t point to memory from keyword_strings. + // once allocated, this buffer won't be moved in the memory. + qstrvec_t keyword_memory; + // helper class so that keywords_t gets sorted by string contents, not pointer values. + struct plain_char_ptr_t + { + const char *ptr; + plain_char_ptr_t(const char *p = nullptr) : ptr(p) {} + bool operator <(const plain_char_ptr_t &r) const + { + return strcmp(ptr, r.ptr) < 0; + } + bool operator ==(const plain_char_ptr_t &r) const + { + return strcmp(ptr, r.ptr) == 0; + } + }; + struct multicmt_t + { + qstring open_multicmt; + qstring close_multicmt; + multicmt_t() {} + multicmt_t(const char *_open_multicmt, const char *_close_multicmt) : + open_multicmt(_open_multicmt), + close_multicmt(_close_multicmt) + {} + }; + + // typedef std::map keywords_t; + struct keywords_style_t + { + qvector keywords; + syntax_highlight_style style; + }; + typedef qvector keywords_t; + //typedef qvector keywords_t; + typedef qvector multicmtvec_t; + keywords_t keywords; + + qstring open_cmt; // string that opens a regular line comment + multicmtvec_t multicmts; + char literal_closer; // either close_strconst or close_chrconst for the current literal + + // color mappings + syntax_highlight_style text_color = HF_DEFAULT; + syntax_highlight_style comment_color = HF_COMMENT; + syntax_highlight_style string_color = HF_STRING; + syntax_highlight_style preprocessor_color = HF_PREPROC; + + ida_syntax_highlighter_helpers_t ida_syntax_highlighter_helpers; + + // work variables + const char *input; // pointer to the start of the input buffer + const char *pending; // pointer in the input buffer + syntax_highlight_style style = HF_DEFAULT; // current highlighting style + + bool pending_nonspaces_present(const char *end) + { + for ( const char *p = pending; p != end; ++p ) + if ( !qisspace(*p) ) + return true; + return false; + } + + const char *parse_literal_const(highlighter_cbs_t *highlighter_cbs, const char *ptr, char literal_closer); + void flush_output(highlighter_cbs_t *highlighter_cbs, const char *ptr, syntax_highlight_style style); + void handle_cmt(highlighter_cbs_t *highlighter_cbs, int mcmt_idx, const char **ptr); + void handle_preproc(highlighter_cbs_t *highlighter_cbs, const char **ptr); + +public: + // if any of the following features is not needed, just zero them out: + char open_strconst; // character that opens a string constant + char close_strconst; // character that closes a string constant + char open_chrconst; // character that closes a character constant + char close_chrconst; // character that opens a character constant + char escape_char; // backslash + char preprocessor_char; // # + + ida_syntax_highlighter_t() : syntax_highlighter_t(&code_highlight_block) {} + + void highlight_block_ex(highlighter_cbs_t *highlighter_cbs, const qstring &text); + void add_ida_syntax_highlighter_helper(ida_syntax_highlighter_helper_t *sh) { ida_syntax_highlighter_helpers.push_back(sh); } + void set_open_cmt(const char *begin) { open_cmt = begin; } + void add_multi_line_comment(const char *begin, const char *end) + { + multicmt_t &mcmt = multicmts.push_back(); + mcmt.open_multicmt = begin; + mcmt.close_multicmt = end; + } + + keywords_style_t *get_keyword_style(syntax_highlight_style _style) + { + keywords_style_t *pair_p = nullptr; + for ( int i = 0; i < keywords.size(); i++ ) + { + if ( keywords[i].style == _style ) + { + pair_p = &keywords[i]; + break; + } + } + if ( pair_p == nullptr ) + { + keywords_style_t &pair = keywords.push_back(); + pair_p = &pair; + pair_p->style = _style; + } + return pair_p; + } + + void add_keyword_to_style(const char *kw, syntax_highlight_style _style) + { + keywords_style_t *pair_p = get_keyword_style(_style); + pair_p->keywords.push_back(kw); + } + + void add_keywords(const char *kwstr, syntax_highlight_style _style) + { + char *ctx; + // in order not to allocate every keyword separately, we allocate the whole + // kwstr string at once and will just store pointers to it in the map. + qstring &mem = keyword_memory.push_back(); + mem = kwstr; + for ( char *kw = qstrtok(mem.begin(), "|", &ctx); kw != nullptr; kw = qstrtok(nullptr, "|", &ctx) ) + { + // there is room for optimization here... + bool exists = false; + for ( int i = 0; i < keywords.size(); i++ ) + { + auto p = std::find(keywords[i].keywords.begin(), keywords[i].keywords.end(), kw); + if ( p != keywords[i].keywords.end() ) + { + // already exists, update the style + if ( keywords[i].style != _style ) + { + keywords[i].keywords.erase(p); + add_keyword_to_style(kw, _style); + } + exists = true; + break; + } + } + if ( !exists ) + add_keyword_to_style(kw, _style); + } + } +}; + + +#endif // IDA_HIGHLIGHTER + +``` + +`IdaSDK/idacfg.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef IDACFG_HPP +#define IDACFG_HPP + +/// Get one of config parameters defined by CC_PARMS in ida.cfg. +/// All parameters for all compilers are stored in local map during last read +/// of ida.cfg - this function just returns previously stored parameter value for +/// given compiler (nullptr if no such parameter) +idaman const char *ida_export cfg_get_cc_parm(comp_t compid, const char *name); + + +/// Get header path config parameter from ida.cfg. +/// Also see cfg_get_cc_parm() + +inline const char *cfg_get_cc_header_path(comp_t compid) +{ + return cfg_get_cc_parm(compid, "HEADER_PATH"); +} + + +/// Get predefined macros config parameter from ida.cfg. +/// Also see cfg_get_cc_parm() + +inline const char *cfg_get_cc_predefined_macros(comp_t compid) +{ + return cfg_get_cc_parm(compid, "PREDEFINED_MACROS"); +} + +/// Process one or more config directive(s). +/// \param directive the directives to process +/// \param priority priority \ref IDPOPT_RET +/// In the case of errors this function displays a message and exits. + +idaman void ida_export process_config_directive( + const char *directive, + int priority=IDPOPT_PRI_HIGH); + + +#endif // IDACFG_HPP + +``` + +`IdaSDK/idalib.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef __IDALIB_HPP +#define __IDALIB_HPP + +#include + +/*! \file idalib.hpp + + \brief Contains the IDA as library functions. +*/ + +/// \brief Initialize ida as library +/// \param argc Optional parameters count for advanced usage +/// \param argv Optional parameters list for advanced usage +/// \return 0 if successfully initialized, non zero in case of errors +idaman int ida_export init_library(int argc = 0, char *argv[] = nullptr); + + +/// \brief Open the database specified in file_path argument +/// If the database did not exist, a new database will be created and +/// the input file will be loaded +/// Note: All library functions must be called from the same thread that initialized the library +/// The library is single-threaded, and performing database operations from a different thread +/// than the initialization thread may lead to undefined behavior +/// \param file_path the file name to be loaded +/// \param run_auto if set to true, library will run also auto analysis +/// \param args optional arguments, respecting IDA's command-line arguments format +/// \return 0 if successfully opened, otherwise error code +idaman int ida_export open_database(const char *file_path, bool run_auto, const char *args = nullptr); + + +/// \brief Close the current database +/// \param save boolean value, save or discard changes +idaman void ida_export close_database(bool save); + +/// \brief Generate .sig and .pat files for the current database +/// \param only_pat Generate .pat file only +/// \return true in case of success +idaman bool ida_export make_signatures(bool only_pat); + +/// \brief Enable console messages +/// \param enable flag to activate or deactivate console messages +/// by default, console messages are disabled +idaman void ida_export enable_console_messages(bool enable); + +/// \brief Set screen ea, let the user specify the current screen ea +/// subsequent calls to get_screen_ea will return this value +/// Please note that with idalib there is no screen, this is provided +/// just for being able to use the get_screen_ea +/// \param ea screen ea +idaman void ida_export set_screen_ea(ea_t ea); + +/// \brief Get ida library version +/// \param major major version +/// \param minor minor version +/// \param build build number +/// \return true in case of success +idaman bool ida_export get_library_version(int &major, int &minor, int &build); + +#endif // __IDALIB_HPP + +``` + +`IdaSDK/idd.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _IDD_HPP +#define _IDD_HPP + +#include +#include + +/*! \file idd.hpp + + \brief Contains definition of the interface to IDD modules. + + The interface consists of structures describing the target + debugged processor and a debugging API. +*/ + +/// The IDD interface version number +#define IDD_INTERFACE_VERSION 31 + +class idc_value_t; +struct fpvalue_t; +class tinfo_t; + +//==================================================================== +// +// Process and Threads +// + +typedef int pid_t; ///< process id +typedef int thid_t; ///< thread id + +#define NO_PROCESS pid_t(-1) ///< No process +#define NO_THREAD 0 ///< No thread. + ///< in ::PROCESS_STARTED this value + ///< can be used to specify that + ///< the main thread has not been created. + ///< It will be initialized later + ///< by a ::THREAD_STARTED event. + +/// Process information +struct process_info_t +{ + pid_t pid; ///< process id + qstring name; ///< process name +}; +DECLARE_TYPE_AS_MOVABLE(process_info_t); +typedef qvector procinfo_vec_t; + +//-------------------------------------------------------------------- +/// Runtime attributes of the debugger/process. +/// It is guaranteed that these attributes are really valid after start/attach process +struct debapp_attrs_t +{ + int32 cbsize = sizeof(*this); ///< control field: size of this structure + + /// address size of the process. + /// Since 64-bit debuggers usually can debug 32-bit applications, we cannot + /// rely on sizeof(ea_t) to detect the current address size. The following + /// variable should be used instead. It is initialized with 8 for 64-bit + /// debuggers but they should adjust it as soon as they learn that a + /// 32-bit application is being debugged. + /// For 32-bit debuggers it is initialized with 4. + int addrsize; + + qstring platform; ///< platform name process is running/debugging under. + ///< (is used as a key value in exceptions.cfg) + +/// \def{DEF_ADDRSIZE, Default address size - see debapp_attrs_t::addrsize} +#ifdef __EA64__ +#define DEF_ADDRSIZE 8 +#else +#define DEF_ADDRSIZE 4 +#endif + int is_be = -1; + + debapp_attrs_t() : addrsize(DEF_ADDRSIZE) {} +}; + +//==================================================================== +// +// Registers +// + +typedef unsigned char register_class_t; ///< Each register is associated to + ///< a register class. + ///< example: "segment", "mmx", ... + +/// Debuggee register information +struct register_info_t +{ + const char *name; ///< Register name. + uint32 flags; ///< \ref REGISTER_ +/// \defgroup REGISTER_ Register info attribute flags +/// Used by register_info_t::flags +///@{ +#define REGISTER_READONLY 0x0001 ///< the user can't modify the current value of this register +#define REGISTER_IP 0x0002 ///< instruction pointer +#define REGISTER_SP 0x0004 ///< stack pointer +#define REGISTER_FP 0x0008 ///< frame pointer +#define REGISTER_ADDRESS 0x0010 ///< may contain an address +#define REGISTER_CS 0x0020 ///< code segment +#define REGISTER_SS 0x0040 ///< stack segment +#define REGISTER_NOLF 0x0080 ///< displays this register without returning to the next line, + ///< allowing the next register to be displayed to its right (on the same line) +#define REGISTER_CUSTFMT 0x0100 ///< register should be displayed using a custom data format. + ///< the format name is in bit_strings[0]; + ///< the corresponding ::regval_t will use ::bytevec_t +///@} + uchar register_class_mask; ///< mask of register classes + op_dtype_t dtype; ///< Register size (see \ref dt_) + const char *const *bit_strings; ///< strings corresponding to each bit of the register. + ///< (nullptr = no bit, same name = multi-bits mask) + uval_t default_bit_strings_mask; ///< mask of default bits +}; +DECLARE_TYPE_AS_MOVABLE(register_info_t); +typedef qvector register_info_vec_t; + +//-------------------------------------------------------------------------- +struct dynamic_register_set_t +{ + typedef qvector const_char_vec_t; + + register_info_vec_t ri_vec; + qstrvec_t strvec; + const_char_vec_t classname_ptrs; + qvector bit_strings_ptrs_vec; + int default_regclasses = 1; ///< Mask of default printed register classes, + ///< 1 is the general register set + + void clear() + { + ri_vec.clear(); + strvec.clear(); + classname_ptrs.clear(); + bit_strings_ptrs_vec.clear(); + } + + void add_register(const register_info_t &ri) + { + add_register(ri.name, + ri.flags, + ri.dtype, + ri.register_class_mask, + ri.bit_strings, + ri.default_bit_strings_mask); + } + + void add_register( + const char *name, + int flags, + op_dtype_t dtype, + register_class_t register_class, + const char *const *bit_strings, + uval_t bits_mask) + { + // Allocate bit_strings. + if ( bit_strings != nullptr ) + { + size_t num_bits = (flags & REGISTER_CUSTFMT) != 0 ? 1 + : dtype == dt_word ? 16 + : dtype == dt_dword ? 32 + : dtype == dt_qword ? 64 + : 0; + QASSERT(1795, num_bits != 0); + const_char_vec_t &ptrvec = bit_strings_ptrs_vec.push_back(); + ptrvec.resize(num_bits, nullptr); + for ( size_t i = 0; i < num_bits; i++ ) + { + if ( bit_strings[i] != nullptr ) + { + qstring &field_name = strvec.push_back(); + field_name = bit_strings[i]; + ptrvec[i] = field_name.c_str(); + } + } + bit_strings = ptrvec.begin(); + } + + // Allocate name. + qstring ®name = strvec.push_back(); + regname = name; + + // Add entry for register. + register_info_t &ri = ri_vec.push_back(); + ri.name = regname.c_str(); + ri.flags = flags; + ri.dtype = dtype; + ri.register_class_mask = register_class; + ri.bit_strings = bit_strings; + ri.default_bit_strings_mask = bits_mask; + } + + void set_regclasses(const char **register_classes, int default_regcls) + { + while ( *register_classes != nullptr ) + { + qstring ®ister_class = strvec.push_back(); + register_class = *register_classes++; + classname_ptrs.push_back(register_class.begin()); + } + classname_ptrs.push_back(nullptr); + default_regclasses = default_regcls; + } + + // Values for debugger_t. + bool empty() const { return ri_vec.empty(); } + size_t nregs() const { return ri_vec.size(); } + register_info_t *registers() { return ri_vec.begin(); } + const char **regclasses() { return classname_ptrs.begin(); } +}; + +// helper functions: +idaman THREAD_SAFE void ida_export serialize_dynamic_register_set( + bytevec_t *buf, + dynamic_register_set_t &idaregs); +idaman THREAD_SAFE void ida_export deserialize_dynamic_register_set( + dynamic_register_set_t *idaregs, + memory_deserializer_t &mmdsr); +idaman THREAD_SAFE void ida_export serialize_insn( + bytevec_t *s, + const insn_t &insn); +idaman THREAD_SAFE void ida_export deserialize_insn( + insn_t *insn, + memory_deserializer_t &mmdsr); + +//==================================================================== +// +// Memory +// + +/// Used by debugger modules to report memory are information to IDA kernel. +/// It is ok to return empty fields if information is not available. +struct memory_info_t : public range_t +{ + qstring name; ///< Memory range name + qstring sclass; ///< Memory range class name + ea_t sbase = 0; ///< Segment base (meaningful only for segmented architectures, e.g. 16-bit x86) + ///< The base is specified in paragraphs (i.e. shifted to the right by 4) + uchar bitness = 0; ///< Number of bits in segment addresses (0-16bit, 1-32bit, 2-64bit) + uchar perm = 0; ///< Memory range permissions (0-no information): see segment.hpp + + bool operator ==(const memory_info_t &r) const + { + return start_ea == r.start_ea + && end_ea == r.end_ea + && name == r.name + && sclass == r.sclass + && sbase == r.sbase + && bitness == r.bitness + && perm == r.perm; + } + bool operator !=(const memory_info_t &r) const { return !(*this == r); } +}; +DECLARE_TYPE_AS_MOVABLE(memory_info_t); +struct meminfo_vec_t : public qvector {}; ///< vector of memory info objects + +/// Used by debugger modules to keep track of images that are not mapped uniformly into memory. +struct scattered_segm_t : public range_t +{ + qstring name; ///< name of the segment +}; +DECLARE_TYPE_AS_MOVABLE(scattered_segm_t); +typedef qvector scattered_image_t; ///< vector of scattered segments + +//==================================================================== +// +// Debug events +// + +/// Used by debugger modules to launching processes with environment variables +struct launch_env_t : public qstrvec_t +{ + bool merge = true; // merge environment variables with system ones + // or replace it completely +}; + +/// Debug event codes +enum event_id_t +{ + NO_EVENT = 0, ///< Not an interesting event. This event can be + ///< used if the debugger module needs to return + ///< an event but there are no valid events. + PROCESS_STARTED = 1, ///< New process has been started. + PROCESS_EXITED = 2, ///< Process has been stopped. + THREAD_STARTED = 3, ///< New thread has been started. + THREAD_EXITED = 4, ///< Thread has been stopped. + BREAKPOINT = 5, ///< Breakpoint has been reached. IDA will complain + ///< about unknown breakpoints, they should be reported + ///< as exceptions. + STEP = 6, ///< One instruction has been executed. Spurious + ///< events of this kind are silently ignored by IDA. + EXCEPTION = 7, ///< Exception. + LIB_LOADED = 8, ///< New library has been loaded. + LIB_UNLOADED = 9, ///< Library has been unloaded. + INFORMATION = 10, ///< User-defined information. + ///< This event can be used to return empty information + ///< This will cause IDA to call get_debug_event() + ///< immediately once more. + PROCESS_ATTACHED = 11, ///< Successfully attached to running process. + PROCESS_DETACHED = 12, ///< Successfully detached from process. + PROCESS_SUSPENDED= 13, ///< Process has been suspended. + ///< This event can be used by the debugger module + ///< to signal if the process spontaneously gets + ///< suspended (not because of an exception, + ///< breakpoint, or single step). IDA will silently + ///< switch to the 'suspended process' mode without + ///< displaying any messages. + TRACE_FULL = 14, ///< The trace buffer of the tracer module is full + ///< and IDA needs to read it before continuing + + STATUS_MASK = 0xF0000000, ///< additional info about process state + BITNESS_CHANGED = 0x80000000, ///< Debugger detected the process bitness changing. +}; + +// helper functions: +struct debug_event_t; +idaman THREAD_SAFE void ida_export free_debug_event(debug_event_t *ev); +idaman THREAD_SAFE void ida_export copy_debug_event(debug_event_t *ev, const debug_event_t &r); +idaman THREAD_SAFE void ida_export set_debug_event_code(debug_event_t *ev, event_id_t id); + +/// Describes a module load event. +/// (see ::PROCESS_STARTED, ::PROCESS_ATTACHED, ::LIB_LOADED) +struct modinfo_t +{ + qstring name; ///< full name of the module + ea_t base; ///< module base address. if unknown pass #BADADDR + asize_t size; ///< module size. if unknown pass 0 + ea_t rebase_to; ///< if not #BADADDR, then rebase the program to the specified address +}; +DECLARE_TYPE_AS_MOVABLE(modinfo_t); +typedef qvector modinfovec_t; + +/// Describes a breakpoint event. +/// (see ::BREAKPOINT) +struct bptaddr_t +{ + ea_t hea = BADADDR; ///< Possible address referenced by hardware breakpoints + ea_t kea = BADADDR; ///< Address of the triggered bpt from the kernel's point + ///< of view. (for some systems with special memory mappings, + ///< the triggered ea might be different from event ea). + ///< Use to #BADADDR for flat memory model. +}; + +/// Describes an exception. +/// (see ::EXCEPTION) +struct excinfo_t +{ + uint32 code; ///< Exception code + bool can_cont; ///< Execution of the process can continue after this exception? + ea_t ea; ///< Possible address referenced by the exception + qstring info; ///< Exception message +}; + +/// This structure is used only when detailed information +/// about a debug event is needed. +struct debug_event_t +{ + pid_t pid = NO_PROCESS; ///< Process where the event occurred + thid_t tid = NO_THREAD; ///< Thread where the event occurred + ea_t ea = BADADDR; ///< Address where the event occurred + bool handled = false; ///< Is event handled by the debugger?. + ///< (from the system's point of view) + ///< Meaningful for ::EXCEPTION events +private: + event_id_t _eid = NO_EVENT; +#ifndef SWIG + char bytes[qmax(sizeof(modinfo_t), sizeof(excinfo_t))]; +#endif + +public: + debug_event_t() { memset(bytes, 0, sizeof(bytes)); } + debug_event_t(const debug_event_t &r) { copy(r); } + ~debug_event_t() { clear(); } + debug_event_t &operator =(const debug_event_t &r) { return copy(r); } + debug_event_t ©(const debug_event_t &r) { copy_debug_event(this, r); return *this; } + + /// clear the dependent information (see below), set event code to NO_EVENT + void clear() { free_debug_event(this); } + + void clear_all() + { + clear(); + pid = NO_PROCESS; + tid = NO_THREAD; + ea = BADADDR; + handled = false; + } + + /// Event code + event_id_t eid() const { return event_id_t(_eid & ~STATUS_MASK); } + + /// Set event code. + /// If the new event code is compatible with the old one + /// then the dependent information (see below) will be preserved. + /// Otherwise the event will be cleared and the new event code will be set. + void set_eid(event_id_t id) { set_debug_event_code(this, id); } + + /// process bitness + bool is_bitness_changed() const { return (_eid & BITNESS_CHANGED) != 0; } + void set_bitness_changed(bool on=true) { setflag(_eid, BITNESS_CHANGED, on); } + + /// Information that depends on the event code: + + ///< ::PROCESS_STARTED, ::PROCESS_ATTACHED, ::LIB_LOADED + modinfo_t &modinfo() + { + QASSERT(3295, eid() == PROCESS_STARTED || eid() == PROCESS_ATTACHED || eid() == LIB_LOADED); + return *(modinfo_t *)bytes; + } + ///< ::PROCESS_EXITED, ::THREAD_EXITED + int &exit_code() + { + QASSERT(3296, eid() == PROCESS_EXITED || eid() == THREAD_EXITED); + return *(int *)bytes; + } + ///< ::THREAD_STARTED (thread name) + ///< ::LIB_UNLOADED (unloaded library name) + ///< ::INFORMATION (will be displayed in the output window if not empty) + qstring &info() + { + QASSERT(3297, eid() == THREAD_STARTED || eid() == LIB_UNLOADED || eid() == INFORMATION); + return *(qstring *)bytes; + } + ///< ::BREAKPOINT + bptaddr_t &bpt() + { + QASSERT(3298, eid() == BREAKPOINT); + return *(bptaddr_t *)bytes; + } + ///< ::EXCEPTION + excinfo_t &exc() + { + QASSERT(3299, eid() == EXCEPTION); + return *(excinfo_t *)bytes; + } + + const modinfo_t &modinfo() const { return CONST_CAST(debug_event_t*)(this)->modinfo(); } + const int &exit_code() const { return CONST_CAST(debug_event_t*)(this)->exit_code(); } + const qstring &info() const { return CONST_CAST(debug_event_t*)(this)->info(); } + const bptaddr_t &bpt() const { return CONST_CAST(debug_event_t*)(this)->bpt(); } + const excinfo_t &exc() const { return CONST_CAST(debug_event_t*)(this)->exc(); } + + modinfo_t &set_modinfo(event_id_t id) + { + set_eid(id); + return modinfo(); + } + + void set_exit_code(event_id_t id, int code) + { + set_eid(id); + exit_code() = code; + } + + qstring &set_info(event_id_t id) + { + set_eid(id); + return info(); + } + + bptaddr_t &set_bpt() + { + set_eid(BREAKPOINT); + return bpt(); + } + + excinfo_t &set_exception() + { + set_eid(EXCEPTION); + return exc(); + } + + /// On some systems with special memory mappings the triggered ea might be + /// different from the actual ea. Calculate the address to use. + ea_t bpt_ea() const + { + return _eid == BREAKPOINT && bpt().kea != BADADDR ? bpt().kea : ea; + } + + friend THREAD_SAFE void ida_export free_debug_event(debug_event_t *ev); + friend THREAD_SAFE void ida_export copy_debug_event(debug_event_t *ev, const debug_event_t &r); + friend THREAD_SAFE void ida_export set_debug_event_code(debug_event_t *ev, event_id_t id); +}; +DECLARE_TYPE_AS_MOVABLE(debug_event_t); + +/// get debug event name +inline const char *get_debug_event_name(const debug_event_t &dev) +{ + switch ( dev.eid() ) + { + case NO_EVENT: return "NO_EVENT"; + case THREAD_STARTED: return "THREAD_STARTED"; + case STEP: return "STEP"; + case PROCESS_DETACHED: return "PROCESS_DETACHED"; + case PROCESS_STARTED: return "PROCESS_STARTED"; + case PROCESS_ATTACHED: return "PROCESS_ATTACHED"; + case PROCESS_SUSPENDED: return "PROCESS_SUSPENDED"; + case LIB_LOADED: return "LIB_LOADED"; + case PROCESS_EXITED: return "PROCESS_EXITED"; + case THREAD_EXITED: return "THREAD_EXITED"; + case BREAKPOINT: return "BREAKPOINT"; + case EXCEPTION: return "EXCEPTION"; + case LIB_UNLOADED: return "LIB_UNLOADED"; + case INFORMATION: return "INFORMATION"; + case TRACE_FULL: return "TRACE_FULL"; + default: return "???"; + } +} + +typedef int bpttype_t; ///< hardware breakpoint type (see \ref BPT_H) + +/// \defgroup BPT_H Hardware breakpoint ids +/// Fire the breakpoint upon one of these events +///@{ +const bpttype_t + BPT_WRITE = 1, ///< Write access + BPT_READ = 2, ///< Read access + BPT_RDWR = 3, ///< Read/write access + BPT_SOFT = 4, ///< Software breakpoint + BPT_EXEC = 8, ///< Execute instruction + BPT_DEFAULT = (BPT_SOFT|BPT_EXEC); ///< Choose bpt type automatically +///@} + +/// Exception information +struct exception_info_t +{ + uint code = 0; ///< exception code + uint32 flags = 0; ///< \ref EXC_ +/// \defgroup EXC_ Exception info flags +/// Used by exception_info_t::flags +///@{ +#define EXC_BREAK 0x0001 ///< break on the exception +#define EXC_HANDLE 0x0002 ///< should be handled by the debugger? +#define EXC_MSG 0x0004 ///< instead of a warning, log the exception to the output window +#define EXC_SILENT 0x0008 ///< do not warn or log to the output window +///@} + + /// Should we break on the exception? + bool break_on() const { return (flags & EXC_BREAK) != 0; } + + /// Should we handle the exception? + bool handle() const { return (flags & EXC_HANDLE) != 0; } + + qstring name; ///< Exception standard name + qstring desc; ///< Long message used to display info about the exception + + exception_info_t() {} + exception_info_t(uint _code, uint32 _flags, const char *_name, const char *_desc) + : code(_code), flags(_flags), name(_name), desc(_desc) {} +}; +DECLARE_TYPE_AS_MOVABLE(exception_info_t); +typedef qvector excvec_t; ///< vector of exception info objects + +/// Structure to hold a register value. +/// Small values (up to 64-bit integers and floating point values) use +/// #RVT_INT and #RVT_FLOAT types. For bigger values the bytes() vector is used. +struct regval_t +{ +/// \defgroup RVT_ Register value types +/// Used by regval_t::rvtype +///@{ +#define RVT_FLOAT (-1) ///< floating point +#define RVT_INT (-2) ///< integer +#define RVT_UNAVAILABLE (-3) ///< unavailable; + ///< other values mean custom data type +///@} + int32 rvtype = RVT_INT; ///< one of \ref RVT_ +#ifndef SWIG + // this union must be the last member (see memset below) + union + { +#endif + uint64 ival; ///< RVT_INT +#ifndef SWIG + uchar reserve[sizeof(bytevec_t)]; ///< bytevec_t: custom data type (use bytes() to access it) + ///< RVT_FLOAT: floating point value in native CPU format + ///< if there are more than one fpvalue formats with the + ///< same length, use custom types to distinguish them + ///< (only one of them can be RVT_FLOAT) + }; +#endif + bool use_bytevec() const { return rvtype >= RVT_FLOAT; } // floating and custom types + regval_t() + { + memset(reserve, 0, sizeof(regval_t) - qoffsetof(regval_t, reserve)); + ival = ~uint64(0); + } + regval_t(const regval_t &r) + { + memset(reserve, 0, sizeof(regval_t) - qoffsetof(regval_t, reserve)); + *this = r; + } + ~regval_t() { clear(); } + + /// Assign this regval to the given value + regval_t &operator = (const regval_t &r) + { + if ( this == &r ) + return *this; + if ( r.use_bytevec() ) + { + if ( use_bytevec() ) + bytes() = r.bytes(); + else + new (&bytes()) bytevec_t(r.bytes()); + } + else + { + if ( use_bytevec() ) + bytes().~bytevec_t(); + ival = r.ival; + } + rvtype = r.rvtype; + return *this; + } + + /// Clear register value + void clear() + { + if ( use_bytevec() ) + { + bytes().~bytevec_t(); + rvtype = RVT_INT; + } + } + + /// Compare two regvals with '==' + bool operator == (const regval_t &r) const + { + if ( rvtype == r.rvtype ) + { + if ( rvtype == RVT_UNAVAILABLE ) + return true; + if ( rvtype == RVT_INT ) + return ival == r.ival; + return bytes() == r.bytes(); + } + return false; + } + + /// Compare two regvals with '!=' + bool operator != (const regval_t &r) const { return !(*this == r); } + + /// Set this = r and r = this + void swap(regval_t &r) { qswap(*this, r); } + + /// Use set_int() + void _set_int(uint64 x) { ival = x; } + /// Use set_float() + void _set_float(const bytevec_t &v) { new (&bytes()) bytevec_t(v); rvtype = RVT_FLOAT; } + /// Use set_bytes(const uchar *, size_t) + void _set_bytes(const uchar *data, size_t size, int _rvtype=0) + { + new (&bytes()) bytevec_t(data, size); + rvtype = _rvtype; + QASSERT(3214, use_bytevec()); + } + /// Use set_bytes(const bytevec_t &) + void _set_bytes(const bytevec_t &v, int _rvtype=0) + { + new (&bytes()) bytevec_t(v); + rvtype = _rvtype; + QASSERT(3215, use_bytevec()); + } + /// Use set_bytes() + bytevec_t &_set_bytes(int _rvtype) + { + new (&bytes()) bytevec_t; + rvtype = _rvtype; + QASSERT(3216, use_bytevec()); + return bytes(); + } + /// Use set_unavailable() + void _set_unavailable() { ival = 0; rvtype = RVT_UNAVAILABLE; } + + /// \name Setters + /// These functions ensure that the previous value is cleared + ///@{ + /// Set int value (ival) + void set_int(uint64 x) { clear(); _set_int(x); } + /// Set float value + void set_float(const bytevec_t &v) { set_bytes(v, RVT_FLOAT); } + /// Set custom regval with raw data + void set_bytes(const uchar *data, size_t size, int _rvtype=0) { clear(); _set_bytes(data, size, _rvtype); } + /// Set custom value with existing bytevec + void set_bytes(const bytevec_t &v, int _rvtype=0) { clear(); _set_bytes(v, _rvtype); } + /// Initialize this regval to an empty custom value + bytevec_t &set_bytes(int _rvtype) { clear(); _set_bytes(_rvtype); return bytes(); } + /// Mark as unavailable + void set_unavailable() { clear(); _set_unavailable(); } + + ///@} + + /// \name Getters + ///@{ + /// Get custom value + bytevec_t &bytes() { return *(bytevec_t *)reserve; } + /// Get const custom value + const bytevec_t &bytes() const { return *(bytevec_t *)reserve; } + /// Get pointer to value + void *get_data() { return use_bytevec() ? (void *)bytes().begin() : (void *)&ival; } + /// Get const pointer to value + const void *get_data() const { return use_bytevec() ? (void *)bytes().begin() : (void *)&ival; } + /// Get size of value + size_t get_data_size() const + { + if ( use_bytevec() ) + return bytes().size(); + if ( rvtype == RVT_INT ) + return sizeof(ival); + return 0; + } + ///@} +}; +DECLARE_TYPE_AS_MOVABLE(regval_t); +typedef qvector regvals_t; ///< vector register value objects + +/// Instruction operand information +struct idd_opinfo_t +{ + bool modified = false; ///< the operand is modified (written) by the instruction + ea_t ea = BADADDR; ///< operand address (#BADADDR - no address) + regval_t value; ///< operand value. custom data is represented by 'bytes'. + int debregidx = -1; ///< for custom data: index of the corresponding register in dbg->registers + int value_size = 0; ///< size of the value in bytes +}; + +/// Call stack trace information +struct call_stack_info_t +{ + ea_t callea; ///< the address of the call instruction. + ///< for the 0th frame this is usually just the current value of EIP. + ea_t funcea; ///< the address of the called function + ea_t fp; ///< the value of the frame pointer of the called function + bool funcok; ///< is the function present? + bool operator==(const call_stack_info_t &r) const + { + return callea == r.callea + && funcea == r.funcea + && funcok == r.funcok + && fp == r.fp; + } + bool operator!=(const call_stack_info_t &r) const { return !(*this == r); } +}; +DECLARE_TYPE_AS_MOVABLE(call_stack_info_t); +struct call_stack_t : public qvector {}; ///< defined as struct so it can be forward-declared + +//------------------------------------------------------------------------- +THREAD_SAFE inline void append_regval(bytevec_t &s, const regval_t &value) +{ + s.pack_dd(value.rvtype+3); + if ( value.rvtype == RVT_INT ) + { + s.pack_dq(value.ival+1); // +1 to efficiently serialize 0xffffffff + } + else if ( value.rvtype != RVT_UNAVAILABLE ) + { + const bytevec_t &b = value.bytes(); + s.pack_dd(b.size()); + s.append(b.begin(), b.size()); + } +} + +//------------------------------------------------------------------------- +template +THREAD_SAFE inline void extract_regval(regval_t *out, T &v) +{ + out->clear(); + out->rvtype = extract_dd(v) - 3; + if ( out->rvtype == RVT_INT ) + { + out->ival = extract_dq(v) - 1; + } + else if ( out->rvtype != RVT_UNAVAILABLE ) + { + bytevec_t &b = out->_set_bytes(out->rvtype); + int size = extract_dd(v); + b.resize(size); + extract_obj(v, b.begin(), size); + } +} + +//------------------------------------------------------------------------- +template +THREAD_SAFE inline void extract_regvals( + regval_t *values, + int n, + T &v, + const uchar *regmap) +{ + for ( int i=0; i < n && !v.eof(); i++ ) + if ( regmap == nullptr || test_bit(regmap, i) ) + extract_regval(&values[i], v); +} + +//-------------------------------------------------------------------------- +THREAD_SAFE inline void unpack_regvals( + regval_t *values, + int n, + const uchar *regmap, + memory_deserializer_t &mmdsr) +{ + extract_regvals(values, n, mmdsr, regmap); +} + + +/// Call a function from the debugged application. +/// \param[out] retval function return value +/// - for #APPCALL_MANUAL, r will hold the new stack point value +/// - for #APPCALL_DEBEV, r will hold the exception information upon failure +/// and the return code will be eExecThrow +/// \param func_ea address to call +/// \param tid thread to use. #NO_THREAD means to use the current thread +/// \param ptif pointer to type of the function to call +/// \param argv array of arguments +/// \param argnum number of actual arguments +/// \return #eOk if successful, otherwise an error code + +idaman error_t ida_export dbg_appcall( + idc_value_t *retval, + ea_t func_ea, + thid_t tid, + const tinfo_t *ptif, + idc_value_t *argv, + size_t argnum); + + +/// Cleanup after manual appcall. +/// \param tid thread to use. #NO_THREAD means to use the current thread +/// The application state is restored as it was before calling the last appcall(). +/// Nested appcalls are supported. +/// \return #eOk if successful, otherwise an error code + +idaman error_t ida_export cleanup_appcall(thid_t tid); + + +/// Return values for get_debug_event() +enum gdecode_t +{ + GDE_ERROR = -1, ///< error + GDE_NO_EVENT, ///< no debug events are available + GDE_ONE_EVENT, ///< got one event, no more available yet + GDE_MANY_EVENTS, ///< got one event, more events available +}; + +/// Input argument for update_bpts() +struct update_bpt_info_t +{ + ea_t ea = BADADDR; ///< in: bpt address + bytevec_t orgbytes; ///< in(del), out(add): original bytes (only for swbpts) + bpttype_t type = BPT_SOFT; ///< in: bpt type + int size = 0; ///< in: bpt size (only for hwbpts) + uchar code = 0; ///< in: 0. #BPT_SKIP entries must be skipped by the debugger module + ///< out: \ref BPT_ + pid_t pid = NO_PROCESS; ///< in: process id + thid_t tid = NO_THREAD; ///< in: thread id + + /// facilitate update_bpt_vec_t::find() + bool operator==(const update_bpt_info_t &b) const + { + return ea == b.ea && type == b.type; + } +}; +DECLARE_TYPE_AS_MOVABLE(update_bpt_info_t); +typedef qvector update_bpt_vec_t; ///< vector of update breakpoint info objects + +/// Input argument for update_lowcnds(). +/// Server-side low-level breakpoint conditions +struct lowcnd_t +{ + ea_t ea; ///< address of the condition + qstring cndbody; ///< new condition. empty means 'remove condition' + ///< the following fields are valid only if condition is not empty: + bpttype_t type; ///< existing breakpoint type + bytevec_t orgbytes; ///< original bytes (if type==#BPT_SOFT) + insn_t cmd; ///< decoded instruction at 'ea' + ///< (used for processors without single step feature, e.g. arm) + bool compiled; ///< has 'cndbody' already been compiled? + int size; ///< breakpoint size (if type!=#BPT_SOFT) +}; +typedef qvector lowcnd_vec_t; ///< vector of low-level breakpoint conditions + +/// Output argument for ev_suspended +/// New thread names +struct thread_name_t +{ + thid_t tid; ///< thread + qstring name; ///< new thread name +}; +typedef qvector thread_name_vec_t; ///< vector of thread names + +//==================================================================== +/// How to resume the application. The corresponding bit for \ref DBG_FLAG_ +/// must be set in order to use a resume mode. +enum resume_mode_t +{ + RESMOD_NONE, ///< no stepping, run freely + RESMOD_INTO, ///< step into call (the most typical single stepping) + RESMOD_OVER, ///< step over call + RESMOD_OUT, ///< step out of the current function (run until return) + RESMOD_SRCINTO, ///< until control reaches a different source line + RESMOD_SRCOVER, ///< next source line in the current stack frame + RESMOD_SRCOUT, ///< next source line in the previous stack frame + RESMOD_USER, ///< step out to the user code + RESMOD_HANDLE, ///< step into the exception handler + RESMOD_BACKINTO, ///< step backwards into call (in time-travel debugging) + RESMOD_MAX, +}; + +//==================================================================== +// Tracing bits +#define STEP_TRACE 0x01 ///< lowest level trace. trace buffers are not maintained +#define INSN_TRACE 0x02 ///< instruction tracing +#define FUNC_TRACE 0x04 ///< function tracing +#define BBLK_TRACE 0x08 ///< basic block tracing + +//==================================================================== +/// Debugger return codes. +/// Success if positive (> DRC_NONE). +enum drc_t +{ + DRC_EVENTS = 3, ///< success, there are pending events + DRC_CRC = 2, ///< success, but the input file crc does not match + DRC_OK = 1, ///< success + DRC_NONE = 0, ///< reaction to the event not implemented + DRC_FAILED = -1, ///< failed or false + DRC_NETERR = -2, ///< network error + DRC_NOFILE = -3, ///< file not found + DRC_IDBSEG = -4, ///< use idb segmentation + DRC_NOPROC = -5, ///< the process does not exist anymore + DRC_NOCHG = -6, ///< no changes + DRC_ERROR = -7, ///< unclassified error, may be complemented by errbuf +}; + +//==================================================================== +/// This structure describes a debugger API module. +/// (functions needed to debug a process on a specific +/// operating system). +/// +/// The address of this structure must be put into the ::dbg variable by +/// the plugin_t::init() function of the debugger plugin. +struct debugger_t +{ + int version; ///< Expected kernel version, + ///< should be #IDD_INTERFACE_VERSION + const char *name; ///< Short debugger name like win32 or linux + int id; ///< one of \ref DEBUGGER_ID_ + + /// \defgroup DEBUGGER_ID_ Debugger API module id + /// Used by debugger_t::id + ///@{ + #define DEBUGGER_ID_X86_IA32_WIN32_USER 0 ///< Userland win32 processes (win32 debugging APIs) + #define DEBUGGER_ID_X86_IA32_LINUX_USER 1 ///< Userland linux processes (ptrace()) + #define DEBUGGER_ID_X86_IA32_MACOSX_USER 3 ///< Userland MAC OS X processes + #define DEBUGGER_ID_ARM_IPHONE_USER 5 ///< iPhone 1.x + #define DEBUGGER_ID_X86_IA32_BOCHS 6 ///< BochsDbg.exe 32 + #define DEBUGGER_ID_6811_EMULATOR 7 ///< MC6812 emulator (beta) + #define DEBUGGER_ID_GDB_USER 8 ///< GDB remote + #define DEBUGGER_ID_WINDBG 9 ///< WinDBG using Microsoft Debug engine + #define DEBUGGER_ID_X86_DOSBOX_EMULATOR 10 ///< Dosbox MS-DOS emulator + #define DEBUGGER_ID_ARM_LINUX_USER 11 ///< Userland arm linux + #define DEBUGGER_ID_TRACE_REPLAYER 12 ///< Fake debugger to replay recorded traces + #define DEBUGGER_ID_X86_PIN_TRACER 14 ///< PIN Tracer module + #define DEBUGGER_ID_DALVIK_USER 15 ///< Dalvik + #define DEBUGGER_ID_XNU_USER 16 ///< XNU Kernel + #define DEBUGGER_ID_ARM_MACOS_USER 17 ///< Userland arm MAC OS + ///@} + + const char *processor; ///< Required processor name. + ///< Used for instant debugging to load the correct + ///< processor module + + uint64 flags; /// \ref DBG_FLAG_ + /// may be set inside debugger_t::init_debugger() except of the severals + + /// \defgroup DBG_FLAG_ Debugger module features + /// Used by debugger_t::flags + ///@{ + #define DBG_FLAG_REMOTE 0x0000000000000001ULL ///< Remote debugger (requires remote host name unless #DBG_FLAG_NOHOST) + #define DBG_FLAG_NOHOST 0x0000000000000002ULL ///< Remote debugger with does not require network params (host/port/pass). + ///< (a unique device connected to the machine) + #define DBG_FLAG_FAKE_ATTACH 0x0000000000000004ULL ///< ::PROCESS_ATTACHED is a fake event + ///< and does not suspend the execution + #define DBG_FLAG_HWDATBPT_ONE 0x0000000000000008ULL ///< Hardware data breakpoints are + ///< one byte size by default + #define DBG_FLAG_CAN_CONT_BPT 0x0000000000000010ULL ///< Debugger knows to continue from a bpt. + ///< This flag also means that the debugger module + ///< hides breakpoints from ida upon read_memory + #define DBG_FLAG_NEEDPORT 0x0000000000000020ULL ///< Remote debugger requires port number (to be used with DBG_FLAG_NOHOST) + #define DBG_FLAG_DONT_DISTURB 0x0000000000000040ULL ///< Debugger can handle only + ///< get_debug_event(), + ///< request_pause(), + ///< exit_process() + ///< when the debugged process is running. + ///< The kernel may also call service functions + ///< (file I/O, map_address, etc) + #define DBG_FLAG_SAFE 0x0000000000000080ULL ///< The debugger is safe (probably because it just emulates the application + ///< without really running it) + #define DBG_FLAG_CLEAN_EXIT 0x0000000000000100ULL ///< IDA must suspend the application and remove + ///< all breakpoints before terminating the application. + ///< Usually this is not required because the application memory + ///< disappears upon termination. + #define DBG_FLAG_USE_SREGS 0x0000000000000200ULL ///< Take segment register values into account (non flat memory) + #define DBG_FLAG_NOSTARTDIR 0x0000000000000400ULL ///< Debugger module doesn't use startup directory + #define DBG_FLAG_NOPARAMETERS 0x0000000000000800ULL ///< Debugger module doesn't use commandline parameters + #define DBG_FLAG_NOPASSWORD 0x0000000000001000ULL ///< Remote debugger doesn't use password + #define DBG_FLAG_CONNSTRING 0x0000000000002000ULL ///< Display "Connection string" instead of "Hostname" and hide the "Port" field + #define DBG_FLAG_SMALLBLKS 0x0000000000004000ULL ///< If set, IDA uses 256-byte blocks for caching memory contents. + ///< Otherwise, 1024-byte blocks are used + #define DBG_FLAG_MANMEMINFO 0x0000000000008000ULL ///< If set, manual memory region manipulation commands + ///< will be available. Use this bit for debugger modules + ///< that cannot return memory layout information + #define DBG_FLAG_EXITSHOTOK 0x0000000000010000ULL ///< IDA may take a memory snapshot at ::PROCESS_EXITED event + #define DBG_FLAG_VIRTHREADS 0x0000000000020000ULL ///< Thread IDs may be shuffled after each debug event. + ///< (to be used for virtual threads that represent cpus for windbg kmode) + #define DBG_FLAG_LOWCNDS 0x0000000000040000ULL ///< Low level breakpoint conditions are supported. + #define DBG_FLAG_DEBTHREAD 0x0000000000080000ULL ///< Supports creation of a separate thread in ida + ///< for the debugger (the debthread). + ///< Most debugger functions will be called from debthread (exceptions are marked below) + ///< The debugger module may directly call only #THREAD_SAFE functions. + ///< To call other functions please use execute_sync(). + ///< The debthread significantly increases debugging + ///< speed, especially if debug events occur frequently. + #define DBG_FLAG_DEBUG_DLL 0x0000000000100000ULL ///< Can debug standalone DLLs. + ///< For example, Bochs debugger can debug any snippet of code + #define DBG_FLAG_FAKE_MEMORY 0x0000000000200000ULL ///< get_memory_info()/read_memory()/write_memory() work with the idb. + ///< (there is no real process to read from, as for the replayer module) + ///< the kernel will not call these functions if this flag is set. + ///< however, third party plugins may call them, they must be implemented. + #define DBG_FLAG_ANYSIZE_HWBPT 0x0000000000400000ULL ///< The debugger supports arbitrary size hardware breakpoints. + #define DBG_FLAG_TRACER_MODULE 0x0000000000800000ULL ///< The module is a tracer, not a full featured debugger module + #define DBG_FLAG_PREFER_SWBPTS 0x0000000001000000ULL ///< Prefer to use software breakpoints + #define DBG_FLAG_LAZY_WATCHPTS 0x0000000002000000ULL ///< Watchpoints are triggered before the offending instruction is executed. + ///< The debugger must temporarily disable the watchpoint and single-step + ///< before resuming. + #define DBG_FLAG_FAST_STEP 0x0000000004000000ULL ///< Do not refresh memory layout info after single stepping + #define DBG_FLAG_ADD_ENVS 0x0000000008000000ULL ///< The debugger supports launching processes with environment variables + #define DBG_FLAG_MERGE_ENVS 0x0000000010000000ULL ///< The debugger supports merge or replace setting for environment variables + ///< (only makes sense if DBG_FLAG_ADD_ENVS is set) + #define DBG_FLAG_DISABLE_ASLR 0x0000000020000000ULL ///< The debugger support ASLR disabling + ///< (Address space layout randomization) + #define DBG_FLAG_TTD 0x0000000040000000ULL ///< The debugger is a time travel debugger and supports continuing backwards + #define DBG_FLAG_FULL_INSTR_BPT 0x0000000080000000ULL ///< Setting a breakpoint in the middle of an instruction will also break + #define DBG_HAS_GET_PROCESSES 0x0000000100000000ULL ///< supports ev_get_processes + #define DBG_HAS_ATTACH_PROCESS 0x0000000200000000ULL ///< supports ev_attach_process + #define DBG_HAS_DETACH_PROCESS 0x0000000400000000ULL ///< supports ev_detach_process + #define DBG_HAS_REQUEST_PAUSE 0x0000000800000000ULL ///< supports ev_request_pause + #define DBG_HAS_SET_EXCEPTION_INFO \ + 0x0000001000000000ULL ///< supports ev_set_exception_info + #define DBG_HAS_THREAD_SUSPEND 0x0000002000000000ULL ///< supports ev_thread_suspend + #define DBG_HAS_THREAD_CONTINUE 0x0000004000000000ULL ///< supports ev_thread_continue + #define DBG_HAS_SET_RESUME_MODE 0x0000008000000000ULL ///< supports ev_set_resume_mode. + ///< Cannot be set inside the debugger_t::init_debugger() + #define DBG_HAS_THREAD_GET_SREG_BASE \ + 0x0000010000000000ULL ///< supports ev_thread_get_sreg_base + #define DBG_HAS_CHECK_BPT 0x0000020000000000ULL ///< supports ev_check_bpt + #define DBG_HAS_OPEN_FILE 0x0000040000000000ULL ///< supports ev_open_file, ev_close_file, ev_read_file, ev_write_file + #define DBG_HAS_UPDATE_CALL_STACK \ + 0x0000080000000000ULL ///< supports ev_update_call_stack + #define DBG_HAS_APPCALL 0x0000100000000000ULL ///< supports ev_appcall, ev_cleanup_appcall + #define DBG_HAS_REXEC 0x0000200000000000ULL ///< supports ev_rexec + #define DBG_HAS_MAP_ADDRESS 0x0000400000000000ULL ///< supports ev_map_address. + ///< Avoid using this bit, especially together with DBG_FLAG_DEBTHREAD + ///< because it may cause big slow downs + ///@} + + bool is_remote() const { return (flags & DBG_FLAG_REMOTE) != 0; } + bool must_have_hostname() const + { return (flags & (DBG_FLAG_REMOTE|DBG_FLAG_NOHOST)) == DBG_FLAG_REMOTE; } + bool can_continue_from_bpt() const + { return (flags & DBG_FLAG_CAN_CONT_BPT) != 0; } + bool may_disturb() const + { return (flags & DBG_FLAG_DONT_DISTURB) == 0; } + bool is_safe() const + { return (flags & DBG_FLAG_SAFE) != 0; } + bool use_sregs() const + { return (flags & DBG_FLAG_USE_SREGS) != 0; } + size_t cache_block_size() const + { return (flags & DBG_FLAG_SMALLBLKS) != 0 ? 256 : 1024; } + bool use_memregs() const + { return (flags & DBG_FLAG_MANMEMINFO) != 0; } + bool may_take_exit_snapshot() const + { return (flags & DBG_FLAG_EXITSHOTOK) != 0; } + bool virtual_threads() const + { return (flags & DBG_FLAG_VIRTHREADS) != 0; } + bool supports_lowcnds() const + { return (flags & DBG_FLAG_LOWCNDS) != 0; } + bool supports_debthread() const + { return (flags & DBG_FLAG_DEBTHREAD) != 0; } + bool can_debug_standalone_dlls() const + { return (flags & DBG_FLAG_DEBUG_DLL) != 0; } + bool fake_memory() const + { return (flags & DBG_FLAG_FAKE_MEMORY) != 0; } + bool is_ttd() const + { return (flags & DBG_FLAG_TTD) != 0; } + + bool has_get_processes() const + { return (flags & DBG_HAS_GET_PROCESSES) != 0; } + bool has_attach_process() const + { return (flags & DBG_HAS_ATTACH_PROCESS) != 0; } + bool has_detach_process() const + { return (flags & DBG_HAS_DETACH_PROCESS) != 0; } + bool has_request_pause() const + { return (flags & DBG_HAS_REQUEST_PAUSE) != 0; } + bool has_set_exception_info() const + { return (flags & DBG_HAS_SET_EXCEPTION_INFO) != 0; } + bool has_thread_suspend() const + { return (flags & DBG_HAS_THREAD_SUSPEND) != 0; } + bool has_thread_continue() const + { return (flags & DBG_HAS_THREAD_CONTINUE) != 0; } + bool has_set_resume_mode() const + { return (flags & DBG_HAS_SET_RESUME_MODE) != 0; } + bool has_thread_get_sreg_base() const + { return (flags & DBG_HAS_THREAD_GET_SREG_BASE) != 0; } + bool has_check_bpt() const + { return (flags & DBG_HAS_CHECK_BPT) != 0; } + bool has_open_file() const + { return (flags & DBG_HAS_OPEN_FILE) != 0; } + bool has_update_call_stack() const + { return (flags & DBG_HAS_UPDATE_CALL_STACK) != 0; } + bool has_appcall() const + { return (flags & DBG_HAS_APPCALL) != 0; } + bool has_rexec() const + { return (flags & DBG_HAS_REXEC) != 0; } + bool has_map_address() const + { return (flags & DBG_HAS_MAP_ADDRESS) != 0; } + bool has_soft_bpt() const + { return bpt_bytes != nullptr && bpt_size > 0; } + + const char **regclasses; ///< Array of register class names + int default_regclasses; ///< Mask of default printed register classes + register_info_t *registers; ///< Array of registers. Use regs() to access it + int nregisters; ///< Number of registers + + // A function for accessing the 'registers' array + inline register_info_t ®s(int idx) + { + return registers[idx]; + } + + int memory_page_size; ///< Size of a memory page. Usually 4K + + const uchar *bpt_bytes; ///< A software breakpoint instruction + uchar bpt_size; ///< Size of the software breakpoint instruction in bytes + uchar filetype; ///< Input file type for the instant debugger. + ///< This value will be used after attaching to a new process. + ushort resume_modes; ///< \ref DBG_RESMOD_ + /// \defgroup DBG_RESMOD_ Resume modes + /// Used by debugger_t::resume_modes + ///@{ + #define DBG_RESMOD_STEP_INTO 0x0001 ///< ::RESMOD_INTO is available + #define DBG_RESMOD_STEP_OVER 0x0002 ///< ::RESMOD_OVER is available + #define DBG_RESMOD_STEP_OUT 0x0004 ///< ::RESMOD_OUT is available + #define DBG_RESMOD_STEP_SRCINTO 0x0008 ///< ::RESMOD_SRCINTO is available + #define DBG_RESMOD_STEP_SRCOVER 0x0010 ///< ::RESMOD_SRCOVER is available + #define DBG_RESMOD_STEP_SRCOUT 0x0020 ///< ::RESMOD_SRCOUT is available + #define DBG_RESMOD_STEP_USER 0x0040 ///< ::RESMOD_USER is available + #define DBG_RESMOD_STEP_HANDLE 0x0080 ///< ::RESMOD_HANDLE is available + #define DBG_RESMOD_STEP_BACKINTO 0x0100 ///< ::RESMOD_BACKINTO is available + ///@} + bool is_resmod_avail(int resmod) const + { return (resume_modes & (1 << (resmod - 1))) != 0; } + +#if !defined(_MSC_VER) // this compiler complains :( + static const int default_port_number = 23946; +#define DEBUGGER_PORT_NUMBER debugger_t::default_port_number +#else +#define DEBUGGER_PORT_NUMBER 23946 +#endif + + /// Callback notification codes. + /// + /// They are passed to notify() when certain events occur in the kernel, + /// allowing the debugger plugin to take appropriate actions. + /// + /// Debugger plugins must implement the desired reaction to these events + /// in the notify() function. + /// + /// The notify() function should not be called directly. See inline functions + /// below. + enum event_t + { + /// Initialize debugger. + /// This event is generated in the main thread. + /// \param hostname (const char *) + /// \param portnum (int) + /// \param password (const char *) + /// \param errbuf (::qstring *) may be nullptr + /// \return ::DRC_OK, ::DRC_FAILED + ev_init_debugger, + + /// Terminate debugger. + /// This event is generated in the main thread. + /// \return ::DRC_OK, ::DRC_FAILED + ev_term_debugger, + + /// Return information about the running processes. + /// This event is generated in the main thread. + /// Available if \ref DBG_HAS_GET_PROCESSES is set + /// \param procs (::procinfo_vec_t *) + /// \param errbuf (::qstring *) may be nullptr + /// \return ::DRC_NONE, ::DRC_OK, ::DRC_FAILED, ::DRC_NETERR + ev_get_processes, + + /// Start an executable to debug. + /// This event is generated in debthread. + /// Must be implemented. + /// \param path (const char *) path to executable + /// \param args (const char *) arguments to pass to executable + /// \param startdir (const char *) initial working directory of new process + /// \param dbg_proc_flags (uint32) \ref DBG_PROC_ + /// \param input_path (const char *) path to the file that was used to create the idb file + /// It is not always the same as 'path' - e.g. if we are analyzing + /// a dll and want to launch an executable that loads it. + /// \param input_file_crc32 (uint32) CRC value for 'input_path' + /// \param errbuf (::qstring *) may be nullptr + /// \param envs (launch_env_t *) environment variables for debugged process + /// \return ::DRC_OK, ::DRC_CRC, ::DRC_FAILED, ::DRC_NETERR, ::DRC_NOFILE + ev_start_process, + + /// Attach to an existing running process. + /// event_id should be equal to -1 if not attaching to a crashed process. + /// This event is generated in debthread. + /// Available if \ref DBG_HAS_ATTACH_PROCESS is set + /// \param pid (::pid_t) process id to attach + /// \param event_id (int) event to trigger upon attaching + /// \param dbg_proc_flags (uint32) \ref DBG_PROC_ + /// \param errbuf (::qstring *) may be nullptr + /// \return ::DRC_NONE, ::DRC_OK, ::DRC_FAILED, ::DRC_NETERR + ev_attach_process, + + /// Detach from the debugged process. + /// May be generated while the process is running or suspended. + /// Must detach from the process in any case. + /// The kernel will repeatedly call get_debug_event() until ::PROCESS_DETACHED is received. + /// In this mode, all other events will be automatically handled and process will be resumed. + /// This event is generated from debthread. + /// Available if \ref DBG_HAS_DETACH_PROCESS is set + /// \return ::DRC_NONE, ::DRC_OK, ::DRC_FAILED, ::DRC_NETERR + ev_detach_process, + + /// Retrieve process- and debugger-specific runtime attributes. + /// This event is generated in the main thread. + /// \param out_pattrs (::debapp_attrs_t *) + /// \return ::DRC_NONE, ::DRC_OK + ev_get_debapp_attrs, + + /// Rebase database if the debugged program has been rebased by the system. + /// This event is generated in the main thread. + /// \param new_base (::ea_t) + /// \return ::DRC_NONE, ::DRC_OK + ev_rebase_if_required_to, + + /// Prepare to pause the process. + /// Normally the next get_debug_event() will pause the process + /// If the process is sleeping, + /// then the pause will not occur until the process wakes up. + /// If the debugger module does not react to this event, + /// then it will be impossible to pause the program. + /// This event is generated in debthread. + /// Available if \ref DBG_HAS_REQUEST_PAUSE is set + /// \param errbuf (::qstring *) may be nullptr + /// \return ::DRC_NONE, ::DRC_OK, ::DRC_FAILED, ::DRC_NETERR + ev_request_pause, + + /// Stop the process. + /// May be generated while the process is running or suspended. + /// Must terminate the process in any case. + /// The kernel will repeatedly call get_debug_event() until ::PROCESS_EXITED is received. + /// In this mode, all other events will be automatically handled and process will be resumed. + /// This event is generated in debthread. + /// Must be implemented. + /// \param errbuf (::qstring *) may be nullptr + /// \return ::DRC_NONE, ::DRC_OK, ::DRC_FAILED, ::DRC_NETERR + ev_exit_process, + + /// Get a pending debug event and suspend the process. + /// This event will be generated regularly by IDA. + /// This event is generated in debthread. + /// IMPORTANT: the BREAKPOINT/EXCEPTION/STEP events must be reported + /// only after reporting other pending events for a thread. + /// Must be implemented. + /// \param code (::gdecode_t *) + /// \param event (::debug_event_t *) + /// \param timeout_ms (int) + /// \retval ignored + ev_get_debug_event, + + /// Continue after handling the event. + /// This event is generated in debthread. + /// Must be implemented. + /// \param event (::debug_event_t *) + /// \return ::DRC_OK, ::DRC_FAILED, ::DRC_NETERR + ev_resume, + + /// Set whether the debugger should continue backwards or forwards. + /// This event is generated in debthread. + /// Available if \ref DBG_FLAG_TTD is set + ev_set_backwards, + + /// Set exception handling. + /// This event is generated in debthread or the main thread. + /// Available if \ref DBG_HAS_SET_EXCEPTION_INFO is set + /// \param info (::exception_info_t *) + /// \param qty (int) + /// \return ::DRC_NONE, ::DRC_OK + ev_set_exception_info, + + /// This event will be generated by the kernel each time + /// it has suspended the debuggee process and refreshed the database. + /// The debugger module may add information to the database if necessary. + /// + /// The reason for introducing this event is that when an event like + /// LOAD_DLL happens, the database does not reflect the memory state yet + /// and therefore we can't add information about the dll into the database + /// in the get_debug_event() function. + /// Only when the kernel has adjusted the database we can do it. + /// Example: for loaded PE DLLs we can add the exported function + /// names to the list of debug names (see set_debug_names()). + /// + /// This event is generated in the main thread. + /// \param dlls_added (bool) + /// \param thr_names (::thread_name_vec_t *) (for the kernel only, must be nullptr) + /// \return ::DRC_NONE, ::DRC_OK + ev_suspended, + + /// \name Threads + /// The following events manipulate threads. + /// These events are generated in debthread. + /// \return ::DRC_OK, ::DRC_FAILED, ::DRC_NETERR + ///@{ + + /// Suspend a running thread + /// Available if \ref DBG_HAS_THREAD_SUSPEND is set + /// \param tid (::thid_t) + ev_thread_suspend, + + /// Resume a suspended thread + /// Available if \ref DBG_HAS_THREAD_CONTINUE is set + /// \param tid (::thid_t) + ev_thread_continue, + + /// Specify resume action + /// Available if \ref DBG_HAS_SET_RESUME_MODE is set + /// \param tid (::thid_t) + /// \param resmod (::resume_mode_t) + ev_set_resume_mode, + + ///@} + + /// Read thread registers. + /// This event is generated in debthread. + /// Must be implemented. + /// \param tid (::thid_t) thread id + /// \param clsmask (int) bitmask of register classes to read + /// \param values (::regval_t *) pointer to vector of regvals for all registers. + /// regval must have debugger_t::nregisters elements + /// \param errbuf (::qstring *) may be nullptr + /// \return ::DRC_OK, ::DRC_FAILED, ::DRC_NETERR + ev_read_registers, + + /// Write one thread register. + /// This event is generated in debthread. + /// Must be implemented. + /// \param tid (::thid_t) thread id + /// \param regidx (int) register index + /// \param value (const ::regval_t *) new value of the register + /// \param errbuf (::qstring *) may be nullptr + /// \return ::DRC_OK, ::DRC_FAILED, ::DRC_NETERR + ev_write_register, + + /// Get information about the base of a segment register. + /// Currently used by the IBM PC module to resolve references like fs:0. + /// This event is generated in debthread. + /// Available if \ref DBG_HAS_THREAD_GET_SREG_BASE is set + /// \param answer (::ea_t *) pointer to the answer. can't be nullptr. + /// \param tid (::thid_t) thread id + /// \param sreg_value (int) value of the segment register (returned by get_reg_val()) + /// \param errbuf (::qstring *) may be nullptr + /// \return ::DRC_NONE, ::DRC_OK, ::DRC_FAILED, ::DRC_NETERR + ev_thread_get_sreg_base, + + /// \name Memory manipulation + /// The following events manipulate bytes in the memory. + ///@{ + + /// Get information on the memory ranges. + /// The debugger module fills 'ranges'. The returned vector must be sorted. + /// This event is generated in debthread. + /// Must be implemented. + /// \param ranges (::meminfo_vec_t *) + /// \param errbuf (::qstring *) may be nullptr + /// \retval ::DRC_OK new memory layout is returned + /// \retval ::DRC_FAILED, ::DRC_NETERR, ::DRC_NOPROC, ::DRC_NOCHG, ::DRC_IDBSEG + ev_get_memory_info, + + /// Read process memory. + /// This event is generated in debthread. + /// \param nbytes (size_t *) number of read bytes + /// \param ea (::ea_t) + /// \param buffer (void *) + /// \param size (::size_t) + /// \param errbuf (::qstring *) may be nullptr + /// \return ::DRC_OK, ::DRC_FAILED, ::DRC_NOPROC + ev_read_memory, + + /// Write process memory. + /// This event is generated in debthread. + /// \param nbytes (size_t *) number of written bytes + /// \param ea (::ea_t) + /// \param buffer (const void *) + /// \param size (::size_t) + /// \param errbuf (::qstring *) may be nullptr + /// \retval ::DRC_OK, ::DRC_FAILED, ::DRC_NOPROC + ev_write_memory, + + ///@} + + /// Is it possible to set breakpoint? + /// This event is generated in debthread or in the main thread if debthread + /// is not running yet. + /// It is generated to verify hardware breakpoints. + /// Available if \ref DBG_HAS_CHECK_BPT is set + /// \param bptvc (int *) breakpoint verification codes \ref BPT_ + /// \param type (::bpttype_t) \ref BPT_H + /// \param ea (::ea_t) + /// \param len (int) + /// \return ::DRC_OK, ::DRC_NONE + ev_check_bpt, + + /// Add/del breakpoints. + /// bpts array contains nadd bpts to add, followed by ndel bpts to del. + /// This event is generated in debthread. + /// \param nbpts (int *) number of updated breakpoints + /// \param bpts (::update_bpt_info_t *) + /// \param nadd (int) + /// \param ndel (int) + /// \param errbuf (::qstring *) may be nullptr + /// \return ::DRC_OK, ::DRC_FAILED, ::DRC_NETERR + ev_update_bpts, + + /// Update low-level (server side) breakpoint conditions. + /// This event is generated in debthread. + /// \param nupdated (int *) number of updated conditions + /// \param lowcnds (const ::lowcnd_t *) + /// \param nlowcnds (int) + /// \param errbuf (::qstring *) may be nullptr + /// \return ::DRC_OK, ::DRC_NETERR + ev_update_lowcnds, + + /// \name Remote file + /// Open/close/read/write a remote file. + /// These events are generated in the main thread + /// Available if \ref DBG_HAS_OPEN_FILE is set + ///@{ + + /// \param file (const char *) + /// \param fsize (::uint64 *) + /// \param readonly (bool) + /// \param errbuf (::qstring *) may be nullptr + /// \retval (int) handle + /// \retval -1 error + ev_open_file, + + /// \param fn (int) handle + /// \return ignored + ev_close_file, + + /// \param fn (int) handle + /// \param off (::qoff64_t) + /// \param buf (void *) + /// \param size (size_t) + /// \param errbuf (::qstring *) may be nullptr + /// \retval number of read bytes + ev_read_file, + + /// \param fn (int) handle + /// \param off (::qoff64_t) + /// \param buf (const void *) + /// \param size (size_t) + /// \param errbuf (::qstring *) may be nullptr + /// \retval number of written bytes + ev_write_file, + + ///@} + + /// Map process address. + /// The debugger module may ignore this event. + /// This event is generated in debthread. + /// IDA will generate this event only if \ref DBG_HAS_MAP_ADDRESS is set. + /// \param mapped (::ea_t *) mapped address or #BADADDR + /// \param off (::ea_t) offset to map + /// \param regs (const ::regval_t *) current register values. + /// if regs == nullptr, then perform global mapping, + /// which is independent on used registers + /// usually such a mapping is a trivial identity mapping + /// \param regnum (int) required mapping. + /// May be specified as a segment register number or a regular + /// register number if the required mapping can be deduced from it. + /// For example, esp implies that ss should be used. + /// \return ::DRC_NONE, ::DRC_OK see MAPPED + ev_map_address, + + /// Get pointer to debugger specific events. + /// This event returns a pointer to a structure that holds pointers to + /// debugger module specific events. For information on the structure + /// layout, please check the corresponding debugger module. Most debugger + /// modules return nullptr because they do not have any extensions. Available + /// extensions may be generated from plugins. + /// This event is generated in the main thread. + /// \param ext (void **) + /// \return ::DRC_NONE, ::DRC_OK see EXT + ev_get_debmod_extensions, + + /// Calculate the call stack trace for the given thread. + /// This event is generated when the process is suspended and should fill the 'trace' object + /// with the information about the current call stack. If this event returns DRC_NONE, IDA + /// will try to invoke a processor-specific mechanism (see processor_t::ev_update_call_stack). + /// If the current processor module does not implement stack tracing, then IDA will fall back + /// to a generic algorithm (based on the frame pointer chain) to calculate the trace. + /// This event is ideal if the debugging targets manage stack frames in a peculiar way, + /// requiring special analysis. + /// This event is generated in the main thread. + /// Available if \ref DBG_HAS_UPDATE_CALL_STACK is set + /// \param tid (::thid_t) + /// \param trace (::call_stack_t *) + /// \retval ::DRC_NONE false or not implemented + /// \return ::DRC_OK success + ev_update_call_stack, + + /// Call application function. + /// This event calls a function from the debugged application. + /// This event is generated in debthread + /// Available if \ref HAS_APPCALL is set + /// \param[out] blob_ea (::ea_t *) ea of stkargs blob, + /// #BADADDR if failed and errbuf is filled + /// \param func_ea (::ea_t) address to call + /// \param tid (::thid_t) thread to use + /// \param fti (const ::func_type_data_t *) type information for the generated event + /// \param nargs (int) number of actual arguments + /// \param regargs (const ::regobjs_t *) information about register arguments + /// \param stkargs (::relobj_t *) memory blob to pass as stack arguments + /// (usually contains pointed data) + /// it must be relocated by the callback but not changed otherwise + /// \param retregs (::regobjs_t *) event return registers. + /// \param[out] errbuf (::qstring *) the error message. if empty on failure, see EVENT. + /// should not be filled if an appcall exception + /// happened but #APPCALL_DEBEV is set + /// \param[out] event (::debug_event_t *) the last debug event that occurred during appcall execution + /// filled only if the appcall execution fails and #APPCALL_DEBEV is set + /// \param options (int) appcall options, usually taken from \inf{appcall_options}. + /// possible values: combination of \ref APPCALL_ or 0 + /// \retval ::DRC_NONE + /// \retval ::DRC_OK, see BLOB_EA + ev_appcall, + + /// Cleanup after appcall(). + /// The debugger module must keep the stack blob in the memory until this event + /// is generated. It will be generated by the kernel for each successful appcall(). + /// There is an exception: if #APPCALL_MANUAL, IDA may not call cleanup_appcall. + /// If the user selects to terminate a manual appcall, then cleanup_appcall will be generated. + /// Otherwise, the debugger module should terminate the appcall when the generated + /// event returns. + /// This event is generated in debthread. + /// Available if \ref HAS_APPCALL is set + /// \param tid (::thid_t) + /// \retval ::DRC_EVENTS success, there are pending events + /// \retval ::DRC_OK success + /// \retval ::DRC_FAILED failed + /// \retval ::DRC_NETERR network error + ev_cleanup_appcall, + + /// Evaluate a low level breakpoint condition at 'ea'. + /// Other evaluation errors are displayed in a dialog box. + /// This call is used by IDA when the process has already been temporarily + /// suspended for some reason and IDA has to decide whether the process + /// should be resumed or definitely suspended because of a breakpoint + /// with a low level condition. + /// This event is generated in debthread. + /// \param tid (::thid_t) + /// \param ea (::ea_t) + /// \param errbuf (::qstring *) may be nullptr + /// \retval ::DRC_OK condition is satisfied + /// \retval ::DRC_FAILED not satisfied + /// \retval ::DRC_NETERR network error + ev_eval_lowcnd, + + /// Perform a debugger-specific event. + /// This event is generated in debthread + /// \param fn (int) + /// \param buf (const void *) + /// \param size (size_t) + /// \param poutbuf (void **) + /// \param poutsize (ssize_t *) + /// \param errbuf (::qstring *) may be nullptr + /// \retval DRC_... + ev_send_ioctl, + + /// Enable/Disable tracing. + /// The kernel will generated this event if the debugger plugin set DBG_FLAG_TRACER_MODULE. + /// TRACE_FLAGS can be a set of #STEP_TRACE, #INSN_TRACE, #BBLK_TRACE or #FUNC_TRACE. + /// This event is generated in the main thread. + /// \param tid (::thid_t) + /// \param enable (bool) + /// \param trace_flags (int) + /// \return ::DRC_OK, ::DRC_FAILED, ::DRC_NONE + ev_dbg_enable_trace, + + /// Is tracing enabled? + /// The kernel will generated this event if the debugger plugin set DBG_FLAG_TRACER_MODULE. + /// TRACE_BIT can be one of the following: #STEP_TRACE, #INSN_TRACE, #BBLK_TRACE or #FUNC_TRACE + /// \param tid (::thid_t) + /// \param tracebit (int) + /// \retval ::DRC_OK bit is set + /// \retval ::DRC_NONE bit is not set or not implemented + ev_is_tracing_enabled, + + /// Execute a command on the remote computer. + /// Available if \ref DBG_HAS_REXEC is set + /// \param cmdline (const char *) + /// \return (int) exit code + ev_rexec, + + /// Get the path to a file containing source debug info for the given module. + /// This allows srcinfo providers to call into the debugger when looking for debug info. + /// It is useful in certain cases like the iOS debugger, which is a remote debugger but + /// the remote debugserver does not provide dwarf info. So, we allow the debugger client + /// to decide where to look for debug info locally. + /// \param path (qstring *) output path (file might not exist) + /// \param base (::ea_t) base address of a module in the target process + /// \return ::DRC_NONE, ::DRC_OK result stored in PATH + ev_get_srcinfo_path, + + /// Search for a binary pattern in the program. + /// \param out (::ea_t *) binary pattern address + /// \param start_ea (::ea_t) linear address, start of range to search + /// \param end_ea (::ea_t) linear address, end of range to search (exclusive) + /// \param data (const ::compiled_binpat_vec_t *) + /// the prepared data to search for (see parse_binpat_str()) + /// \param srch_flags (int) combination of \ref BIN_SEARCH_ + /// \param errbuf (::qstring *) may be nullptr + /// \return ::DRC_OK EA contains the binary pattern address + /// \retval ::DRC_FAILED not found + /// \retval ::DRC_NONE not implemented + /// \retval ::DRC_NETERR, ::DRC_ERROR + ev_bin_search, + + /// Ask debuger to send dynamic register set + /// \param[out] regset (::dynamic_register_set_t *) dynamic register set + /// \return ::DRC_OK REGSET is ready + /// \retval ::DRC_NETERR, ::DRC_ERROR + ev_get_dynamic_register_set, + + /// Set debugger options (parameters that are specific to the debugger module). + /// \param[out] code (const char **) one of \ref IDPOPT_RET, + /// otherwise a pointer to an error message, + /// may be nullptr + /// \param keyword (const char *) keyword encountered in IDA.CFG/user config file. + /// if nullptr, then an interactive dialog form should be displayed + /// \param pri (int) option priority, one of \ref IDAOPT_PRIO values + /// \param value_type (int) type of value of the keyword - one of \ref IDPOPT_T + /// \param value (const void *) pointer to value + /// \return ::DRC_OK event is implemented + /// \return ::DRC_NONE event is not implemented + /// See the convenience function in dbg.hpp if you need to call it. + /// The kernel will generate this event after reading the debugger specific + /// config file (arguments are: keyword="", type=#IDPOPT_STR, value="") + /// The kernel uses this event with keyword=" " to detect implementation. + /// This event is optional. + /// This event is generated in the main thread + ev_set_dbg_options, + }; + + /// Event notification handler + ssize_t notify(event_t event_code, ...) + { + va_list va; + va_start(va, event_code); + ssize_t code = invoke_callbacks(HT_IDD, event_code, va); + va_end(va); + return code; + } + drc_t notify_drc(event_t event_code, ...) + { + va_list va; + va_start(va, event_code); + drc_t code = drc_t(invoke_callbacks(HT_IDD, event_code, va)); + va_end(va); + return code; + } + + /// \defgroup DBG_PROC_ Debug process flags + /// Passed as 'dbg_proc_flags' parameter to debugger_t::start_process + ///@{ + #define DBG_PROC_IS_DLL 0x01 ///< database contains a dll (not exe) + #define DBG_PROC_IS_GUI 0x02 ///< using gui version of ida + #define DBG_PROC_32BIT 0x04 ///< application is 32-bit + #define DBG_PROC_64BIT 0x08 ///< application is 64-bit + #define DBG_NO_TRACE 0x10 ///< do not trace the application (mac/linux) + #define DBG_HIDE_WINDOW 0x20 ///< application should be hidden on startup (windows) + #define DBG_SUSPENDED 0x40 ///< application should be suspended on startup (mac) + #define DBG_NO_ASLR 0x80 ///< disable ASLR (linux) + ///@} + + /// \defgroup BPT_ Breakpoint verification codes + /// Return values for debugger_t::check_bpt + ///@{ + #define BPT_OK 0 ///< breakpoint can be set + #define BPT_INTERNAL_ERR 1 ///< interr occurred when verifying breakpoint + #define BPT_BAD_TYPE 2 ///< bpt type is not supported + #define BPT_BAD_ALIGN 3 ///< alignment is invalid + #define BPT_BAD_ADDR 4 ///< ea is invalid + #define BPT_BAD_LEN 5 ///< bpt len is invalid + #define BPT_TOO_MANY 6 ///< reached max number of supported breakpoints + #define BPT_READ_ERROR 7 ///< failed to read memory at bpt ea + #define BPT_WRITE_ERROR 8 ///< failed to write memory at bpt ea + #define BPT_SKIP 9 ///< update_bpts(): do not process bpt + #define BPT_PAGE_OK 10 ///< update_bpts(): ok, added a page bpt + ///@} + + /// \defgroup APPCALL_ Appcall options + /// Passed as 'options' parameter to debugger_t::appcall + ///@{ + #define APPCALL_MANUAL 0x0001 ///< Only set up the appcall, do not run. + ///< debugger_t::cleanup_appcall will not be generated by ida! + #define APPCALL_DEBEV 0x0002 ///< Return debug event information + #define APPCALL_TIMEOUT 0x0004 ///< Appcall with timeout. + ///< If timed out, errbuf will contain "timeout". + ///< See #SET_APPCALL_TIMEOUT and #GET_APPCALL_TIMEOUT + /// Set appcall timeout in milliseconds + #define SET_APPCALL_TIMEOUT(msecs) ((uint(msecs) << 16)|APPCALL_TIMEOUT) + /// Timeout value is contained in high 2 bytes of 'options' parameter + #define GET_APPCALL_TIMEOUT(options) (uint(options) >> 16) + ///@} + + // Notification helpers, should be used instead of direct dbg->notify(...) calls + inline bool init_debugger(const char *hostname, int portnum, const char *password, qstring *errbuf=nullptr); + inline bool term_debugger(); + inline drc_t get_processes(procinfo_vec_t *procs, qstring *errbuf=nullptr); + inline drc_t start_process(const char *path, + const char *args, + launch_env_t *envs, + const char *startdir, + uint32 dbg_proc_flags, + const char *input_path, + uint32 input_file_crc32, + qstring *errbuf=nullptr); + inline drc_t attach_process(pid_t pid, int event_id, uint32 dbg_proc_flags, qstring *errbuf=nullptr); + inline drc_t detach_process(qstring *errbuf=nullptr); + inline bool get_debapp_attrs(debapp_attrs_t *out_pattrs); + inline void rebase_if_required_to(ea_t new_base); + inline drc_t request_pause(qstring *errbuf=nullptr); + inline drc_t exit_process(qstring *errbuf=nullptr); + inline gdecode_t get_debug_event(debug_event_t *event, int timeout_ms); + inline drc_t resume(const debug_event_t *event, qstring *errbuf=nullptr); + inline drc_t set_backwards(bool backwards); + inline void set_exception_info(const exception_info_t *info, int qty); + inline void suspended(bool dlls_added, thread_name_vec_t *thr_names=nullptr); + inline drc_t thread_suspend(thid_t tid, qstring *errbuf=nullptr); + inline drc_t thread_continue(thid_t tid, qstring *errbuf=nullptr); + inline drc_t set_resume_mode(thid_t tid, resume_mode_t resmod, qstring *errbuf=nullptr); + inline drc_t read_registers(thid_t tid, int clsmask, regval_t *values, qstring *errbuf=nullptr); + inline drc_t write_register(thid_t tid, int regidx, const regval_t *value, qstring *errbuf=nullptr); + inline drc_t thread_get_sreg_base(ea_t *answer, thid_t tid, int sreg_value, qstring *errbuf=nullptr); + inline drc_t get_memory_info(meminfo_vec_t &ranges, qstring *errbuf=nullptr); + inline drc_t read_memory(size_t *nbytes, ea_t ea, void *buffer, size_t size, qstring *errbuf=nullptr); + inline drc_t write_memory(size_t *nbytes, ea_t ea, const void *buffer, size_t size, qstring *errbuf=nullptr); + inline drc_t check_bpt(int *bptvc, bpttype_t type, ea_t ea, int len); + inline drc_t update_bpts(int *nbpts, update_bpt_info_t *bpts, int nadd, int ndel, qstring *errbuf=nullptr); + inline drc_t update_lowcnds(int *nupdated, const lowcnd_t *lowcnds, int nlowcnds, qstring *errbuf=nullptr); + inline int open_file(const char *file, uint64 *fsize, bool readonly, qstring *errbuf=nullptr); + inline void close_file(int fn); + inline ssize_t read_file(int fn, qoff64_t off, void *buf, size_t size, qstring *errbuf=nullptr); + inline ssize_t write_file(int fn, qoff64_t off, const void *buf, size_t size, qstring *errbuf=nullptr); + inline ea_t map_address(ea_t off, const regval_t *regs, int regnum); + inline const void *get_debmod_extensions(); + inline drc_t update_call_stack(thid_t tid, call_stack_t *trace); + inline ea_t appcall( + ea_t func_ea, + thid_t tid, + const struct func_type_data_t *fti, + int nargs, + const struct regobjs_t *regargs, + struct relobj_t *stkargs, + struct regobjs_t *retregs, + qstring *errbuf, + debug_event_t *event, + int options); + inline drc_t cleanup_appcall(thid_t tid); + inline drc_t eval_lowcnd(thid_t tid, ea_t ea, qstring *errbuf=nullptr); + inline drc_t send_ioctl(int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize, qstring *errbuf=nullptr); + inline bool dbg_enable_trace(thid_t tid, bool enable, int trace_flags); + inline bool is_tracing_enabled(thid_t tid, int tracebit); + inline int rexec(const char *cmdline); + inline bool get_srcinfo_path(qstring *path, ea_t base); + inline drc_t bin_search( + ea_t *out, + ea_t start_ea, + ea_t end_ea, + const compiled_binpat_vec_t &data, + int srch_flags, + qstring *errbuf=nullptr); + inline bool get_dynamic_register_set(dynamic_register_set_t *regset); + inline drc_t set_dbg_options(const char **res, const char *keyword, int pri, int value_type, const void *value); + inline bool have_set_options() const; +}; + + +#define RQ_MASKING 0x0001 ///< masking step handler: unless errors, tmpbpt handlers won't be generated + ///< should be used only with request_internal_step() +#define RQ_SUSPEND 0x0002 ///< suspending step handler: suspends the app + ///< handle_debug_event: suspends the app +#define RQ_NOSUSP 0x0000 ///< running step handler: continues the app +#define RQ_IGNWERR 0x0004 ///< ignore breakpoint write failures +#define RQ_SILENT 0x0008 ///< all: no dialog boxes +#define RQ_VERBOSE 0x0000 ///< all: display dialog boxes +#define RQ_SWSCREEN 0x0010 ///< handle_debug_event: switch screens +#define RQ__NOTHRRF 0x0020 ///< handle_debug_event: do not refresh threads +#define RQ_PROCEXIT 0x0040 ///< snapshots: the process is exiting +#define RQ_IDAIDLE 0x0080 ///< handle_debug_event: ida is idle +#define RQ_SUSPRUN 0x0100 ///< handle_debug_event: suspend at PROCESS_STARTED +#define RQ_RESUME 0x0200 ///< handle_debug_event: resume application +#define RQ_RESMOD 0xF000 ///< resume_mode_t +#define RQ_RESMOD_SHIFT 12 +#define RQ_INTO (RESMOD_INTO << RQ_RESMOD_SHIFT) +#define RQ_BACKINTO (RESMOD_BACKINTO << RQ_RESMOD_SHIFT) + +inline bool debugger_t::init_debugger(const char *hostname, int portnum, const char *password, qstring *errbuf) +{ + return notify_drc(ev_init_debugger, hostname, portnum, password, errbuf) == DRC_OK; +} +inline bool debugger_t::term_debugger() +{ + return notify_drc(ev_term_debugger) == DRC_OK; +} +inline drc_t debugger_t::get_processes(procinfo_vec_t *procs, qstring *errbuf) +{ + return notify_drc(ev_get_processes, procs, errbuf); +} +inline drc_t debugger_t::start_process( + const char *path, + const char *args, + launch_env_t *envs, + const char *startdir, + uint32 dbg_proc_flags, + const char *input_path, + uint32 input_file_crc32, + qstring *errbuf) +{ + return notify_drc(ev_start_process, path, args, startdir, dbg_proc_flags, input_path, input_file_crc32, errbuf, envs); +} +inline drc_t debugger_t::attach_process(pid_t pid, int event_id, uint32 dbg_proc_flags, qstring *errbuf) +{ + return notify_drc(ev_attach_process, pid, event_id, dbg_proc_flags, errbuf); +} +inline drc_t debugger_t::detach_process(qstring *errbuf) +{ + return notify_drc(ev_detach_process, errbuf); +} +inline bool debugger_t::get_debapp_attrs(debapp_attrs_t *out_pattrs) +{ + return notify_drc(ev_get_debapp_attrs, out_pattrs) != DRC_NONE; +} +inline void debugger_t::rebase_if_required_to(ea_t new_base) +{ + notify_drc(ev_rebase_if_required_to, new_base); +} +inline drc_t debugger_t::request_pause(qstring *errbuf) +{ + return notify_drc(ev_request_pause, errbuf); +} +inline drc_t debugger_t::exit_process(qstring *errbuf) +{ + return notify_drc(ev_exit_process, errbuf); +} +inline gdecode_t debugger_t::get_debug_event(debug_event_t *event, int timeout_ms) +{ + gdecode_t code = GDE_ERROR; + notify_drc(ev_get_debug_event, &code, event, timeout_ms); + return code; +} +inline drc_t debugger_t::resume(const debug_event_t *event, qstring *errbuf) +{ + return notify_drc(ev_resume, event, errbuf); +} +inline drc_t debugger_t::set_backwards(bool backwards) +{ + return notify_drc(ev_set_backwards, backwards); +} +inline void debugger_t::set_exception_info(const exception_info_t *info, int qty) +{ + notify_drc(ev_set_exception_info, info, qty); +} +inline void debugger_t::suspended(bool dlls_added, thread_name_vec_t *thr_names) +{ + notify_drc(ev_suspended, dlls_added, thr_names); +} +inline drc_t debugger_t::thread_suspend(thid_t tid, qstring *errbuf) +{ + return notify_drc(ev_thread_suspend, tid, errbuf); +} +inline drc_t debugger_t::thread_continue(thid_t tid, qstring *errbuf) +{ + return notify_drc(ev_thread_continue, tid, errbuf); +} +inline drc_t debugger_t::set_resume_mode(thid_t tid, resume_mode_t resmod, qstring *errbuf) +{ + return notify_drc(ev_set_resume_mode, tid, resmod, errbuf); +} +inline drc_t debugger_t::read_registers(thid_t tid, int clsmask, regval_t *values, qstring *errbuf) +{ + return notify_drc(ev_read_registers, tid, clsmask, values, errbuf); +} +inline drc_t debugger_t::write_register(thid_t tid, int regidx, const regval_t *value, qstring *errbuf) +{ + return notify_drc(ev_write_register, tid, regidx, value, errbuf); +} +inline drc_t debugger_t::thread_get_sreg_base(ea_t *answer, thid_t tid, int sreg_value, qstring *errbuf) +{ + return notify_drc(ev_thread_get_sreg_base, answer, tid, sreg_value, errbuf); +} +inline drc_t debugger_t::get_memory_info(meminfo_vec_t &ranges, qstring *errbuf) +{ + return notify_drc(ev_get_memory_info, &ranges, errbuf); +} +inline drc_t debugger_t::read_memory(size_t *nbytes, ea_t ea, void *buffer, size_t size, qstring *errbuf) +{ + return notify_drc(ev_read_memory, nbytes, ea, buffer, size, errbuf); +} +inline drc_t debugger_t::write_memory(size_t *nbytes, ea_t ea, const void *buffer, size_t size, qstring *errbuf) +{ + return notify_drc(ev_write_memory, nbytes, ea, buffer, size, errbuf); +} +inline drc_t debugger_t::check_bpt(int *bptvc, bpttype_t type, ea_t ea, int len) +{ + return notify_drc(ev_check_bpt, bptvc, type, ea, len); +} +inline drc_t debugger_t::update_bpts(int *nbpts, update_bpt_info_t *bpts, int nadd, int ndel, qstring *errbuf) +{ + return notify_drc(ev_update_bpts, nbpts, bpts, nadd, ndel, errbuf); +} +inline drc_t debugger_t::update_lowcnds(int *nupdated, const lowcnd_t *lowcnds, int nlowcnds, qstring *errbuf) +{ + return notify_drc(ev_update_lowcnds, nupdated, lowcnds, nlowcnds, errbuf); +} +inline int debugger_t::open_file(const char *file, uint64 *fsize, bool readonly, qstring *errbuf) +{ + return int(notify(ev_open_file, file, fsize, readonly, errbuf)); +} +inline void debugger_t::close_file(int fn) +{ + notify(ev_close_file, fn); +} +inline ssize_t debugger_t::read_file(int fn, qoff64_t off, void *buf, size_t size, qstring *errbuf) +{ + return notify(ev_read_file, fn, off, buf, size, errbuf); +} +inline ssize_t debugger_t::write_file(int fn, qoff64_t off, const void *buf, size_t size, qstring *errbuf) +{ + return notify(ev_write_file, fn, off, buf, size, errbuf); +} +inline ea_t debugger_t::map_address(ea_t off, const regval_t *rvs, int regnum) +{ + ea_t mapped; + return notify_drc(ev_map_address, &mapped, off, rvs, regnum) == DRC_OK + ? mapped + : off; +} +inline const void *debugger_t::get_debmod_extensions() +{ + void *ext; + if ( notify_drc(ev_get_debmod_extensions, &ext) != DRC_OK ) + ext = nullptr; + return ext; +} +inline drc_t debugger_t::update_call_stack(thid_t tid, call_stack_t *trace) +{ + return notify_drc(ev_update_call_stack, tid, trace); +} +inline ea_t debugger_t::appcall( + ea_t func_ea, + thid_t tid, + const struct func_type_data_t *fti, + int nargs, + const struct regobjs_t *regargs, + struct relobj_t *stkargs, + struct regobjs_t *retregs, + qstring *errbuf, + debug_event_t *event, + int options) +{ + ea_t blob_ea; + if ( notify_drc(ev_appcall, &blob_ea, func_ea, tid, fti, nargs, regargs, stkargs, retregs, errbuf, event, options) != DRC_OK ) + { + blob_ea = BADADDR; + if ( errbuf != nullptr ) + *errbuf = "Debugger plugin does not support an application function call"; + } + return blob_ea; +} +inline drc_t debugger_t::cleanup_appcall(thid_t tid) +{ + return notify_drc(ev_cleanup_appcall, tid); +} +inline drc_t debugger_t::eval_lowcnd(thid_t tid, ea_t ea, qstring *errbuf) +{ + return notify_drc(ev_eval_lowcnd, tid, ea, errbuf); +} +inline drc_t debugger_t::send_ioctl(int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize, qstring *errbuf) +{ + return notify_drc(ev_send_ioctl, fn, buf, size, poutbuf, poutsize, errbuf); +} +inline bool debugger_t::dbg_enable_trace(thid_t tid, bool enable, int trace_flags) +{ + return notify_drc(ev_dbg_enable_trace, tid, enable, trace_flags) == DRC_OK; +} +inline bool debugger_t::is_tracing_enabled(thid_t tid, int tracebit) +{ + return notify_drc(ev_is_tracing_enabled, tid, tracebit) == DRC_OK; +} +inline int debugger_t::rexec(const char *cmdline) +{ + return int(notify(ev_rexec, cmdline)); +} +inline bool debugger_t::get_srcinfo_path(qstring *path, ea_t base) +{ + return notify_drc(ev_get_srcinfo_path, path, base) == DRC_OK; +} +inline drc_t debugger_t::bin_search( + ea_t *out, + ea_t start_ea, + ea_t end_ea, + const compiled_binpat_vec_t &data, + int srch_flags, + qstring *errbuf) +{ + return notify_drc(ev_bin_search, out, start_ea, end_ea, &data, srch_flags, errbuf); +} +inline bool debugger_t::get_dynamic_register_set(dynamic_register_set_t *regset) +{ + return notify_drc(ev_get_dynamic_register_set, regset) == DRC_OK; +} +inline drc_t debugger_t::set_dbg_options(const char **res, const char *keyword, int pri, int value_type, const void *value) +{ + return notify_drc(ev_set_dbg_options, res, keyword, pri, value_type, value); +} +inline bool debugger_t::have_set_options() const +{ + return const_cast(this)->notify_drc(ev_set_dbg_options, nullptr, " ", 0, 0, nullptr) != DRC_NONE; +} + +/// Convert a floating point number in CPU native format to IDA's internal format. +/// \param ieee_out output buffer +/// \param cpu_fpval floating point number in CPU native format +/// \param size size of cpu_fpval in bytes (size of the input buffer) +/// \return \ref REAL_ERROR_ +idaman int ida_export cpu2ieee(fpvalue_t *ieee_out, const void *cpu_fpval, int size); + +/// Convert a floating point number in IDA's internal format to CPU native format. +/// \param cpu_fpval_out output buffer +/// \param ieee floating point number of IDA's internal format +/// \param size size of cpu_fpval in bytes (size of the output buffer) +/// \return \ref REAL_ERROR_ +idaman int ida_export ieee2cpu(void *cpu_fpval_out, const fpvalue_t &ieee, int size); + +#endif // _IDD_HPP + +``` + +`IdaSDK/idp.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _IDP_HPP +#define _IDP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +/*! \file idp.hpp + + \brief Contains definition of the interface to IDP modules. + + The interface consists of two structures: + - definition of target assembler: ::ash + - definition of current processor: ::ph + + These structures contain information about target processor and assembler features. + + It also defines two groups of kernel events: + - processor_t::event_t processor related events + - idb_event:event_code_t database related events + + The processor related events are used to communicate with the processor module. + The database related events are used to inform any interested parties, like + plugins or processor modules, about the changes in the database. + +*/ + +typedef int help_t; ///< message id from ida.hlp + +struct outctx_t; +struct regval_t; +struct stkpnts_t; +struct simd_info_t; +struct reg_accesses_t; +struct call_stack_t; +struct stkarg_area_info_t; +struct reg_finder_t; +class merge_data_t; +class idasgn_t; + +/// The interface version number. +/// \note see also #IDA_SDK_VERSION from pro.h + +#define IDP_INTERFACE_VERSION 900 + +//----------------------------------------------------------------------- +/// Structure used to describe byte streams +/// (for "ret" instruction and empirics) +struct bytes_t +{ + uchar len; + const uchar *bytes; +}; + +//------------------------------------------------------------------------- +/// \defgroup CF_ Instruction feature bits +/// Used by instruc_t::feature +///@{ +#define CF_STOP 0x00001 ///< Instruction doesn't pass execution to the + ///< next instruction +#define CF_CALL 0x00002 ///< CALL instruction (should make a procedure here) +#define CF_CHG1 0x00004 ///< The instruction modifies the first operand +#define CF_CHG2 0x00008 ///< The instruction modifies the second operand +#define CF_CHG3 0x00010 ///< The instruction modifies the third operand +#define CF_CHG4 0x00020 ///< The instruction modifies the fourth operand +#define CF_CHG5 0x00040 ///< The instruction modifies the fifth operand +#define CF_CHG6 0x00080 ///< The instruction modifies the sixth operand +#define CF_USE1 0x00100 ///< The instruction uses value of the first operand +#define CF_USE2 0x00200 ///< The instruction uses value of the second operand +#define CF_USE3 0x00400 ///< The instruction uses value of the third operand +#define CF_USE4 0x00800 ///< The instruction uses value of the fourth operand +#define CF_USE5 0x01000 ///< The instruction uses value of the fifth operand +#define CF_USE6 0x02000 ///< The instruction uses value of the sixth operand +#define CF_JUMP 0x04000 ///< The instruction passes execution using indirect + ///< jump or call (thus needs additional analysis) +#define CF_SHFT 0x08000 ///< Bit-shift instruction (shl,shr...) +#define CF_HLL 0x10000 ///< Instruction may be present in a high level + ///< language function +#define CF_CHG7 0x020000 ///< The instruction modifies the seventh operand +#define CF_CHG8 0x040000 ///< The instruction modifies the eighth operand +#define CF_USE7 0x080000 ///< The instruction uses value of the seventh operand +#define CF_USE8 0x100000 ///< The instruction uses value of the eighth operand +///@} + +//----------------------------------------------------------------------- +/// Internal representation of processor instructions. +/// Definition of all internal instructions are kept in special arrays. +/// One of such arrays describes instruction names and features. +struct instruc_t +{ + const char *name; ///< instruction name + uint32 feature; ///< combination of \ref CF_ +}; + + +/// Does an instruction with the specified feature modify the i-th operand? + +inline THREAD_SAFE bool has_cf_chg(uint32 feature, uint opnum) +{ + static const int bits[] = + { + CF_CHG1, CF_CHG2, CF_CHG3, CF_CHG4, + CF_CHG5, CF_CHG6, CF_CHG7, CF_CHG8, + }; + CASSERT(qnumber(bits) == UA_MAXOP); + return opnum < UA_MAXOP && (feature & bits[opnum]) != 0; +} + + +/// Does an instruction with the specified feature use a value of the i-th operand? + +inline THREAD_SAFE bool has_cf_use(uint32 feature, uint opnum) +{ + static const int bits[] = + { + CF_USE1, CF_USE2, CF_USE3, CF_USE4, + CF_USE5, CF_USE6, CF_USE7, CF_USE8, + }; + CASSERT(qnumber(bits) == UA_MAXOP); + return opnum < UA_MAXOP && (feature & bits[opnum]) != 0; +} + + +/// Does the specified instruction have the specified feature? + +idaman bool ida_export has_insn_feature(uint16 icode, uint32 bit); + + + +/// Is the instruction a "call"? + +idaman bool ida_export is_call_insn(const insn_t &insn); + + +/// Is the instruction a "return"? + +///@{ +#define IRI_EXTENDED 0x00 ///< include instructions like "leave" + ///< that begin the function epilog +#define IRI_RET_LITERALLY 0x01 ///< report only 'ret' instructions +#define IRI_SKIP_RETTARGET 0x02 ///< exclude 'ret' instructions that have + ///< special targets (see set_ret_target in PC) +#define IRI_STRICT (IRI_RET_LITERALLY|IRI_SKIP_RETTARGET) +///@} +idaman bool ida_export is_ret_insn(const insn_t &insn, uchar flags=IRI_STRICT); + + +/// Is the instruction an indirect jump? + +idaman bool ida_export is_indirect_jump_insn(const insn_t &insn); + + +/// Is the instruction the end of a basic block? + +idaman bool ida_export is_basic_block_end(const insn_t &insn, bool call_insn_stops_block); + + +//===================================================================== +/// Describes the target assembler. +/// An IDP module may have several target assemblers. +/// In this case you should create a structure for each supported +/// assembler. +struct asm_t +{ + uint32 flag; ///< \ref AS_ +/// \defgroup AS_ Assembler feature bits +/// Used by asm_t::flag. +///@{ +#define AS_OFFST 0x00000001 ///< offsets are 'offset xxx' ? +#define AS_COLON 0x00000002 ///< create colons after data names ? +#define AS_UDATA 0x00000004 ///< can use '?' in data directives + +#define AS_2CHRE 0x00000008 ///< double char constants are: "xy +#define AS_NCHRE 0x00000010 ///< char constants are: 'x +#define AS_N2CHR 0x00000020 ///< can't have 2 byte char consts + + // String literals: +#define AS_1TEXT 0x00000040 ///< 1 text per line, no bytes +#define AS_NHIAS 0x00000080 ///< no characters with high bit +#define AS_NCMAS 0x00000100 ///< no commas in ascii directives + +#define AS_HEXFM 0x00000E00 ///< mask - hex number format +#define ASH_HEXF0 0x00000000 ///< 34h +#define ASH_HEXF1 0x00000200 ///< h'34 +#define ASH_HEXF2 0x00000400 ///< 34 +#define ASH_HEXF3 0x00000600 ///< 0x34 +#define ASH_HEXF4 0x00000800 ///< $34 +#define ASH_HEXF5 0x00000A00 ///< <^R > (radix) +#define AS_DECFM 0x00003000 ///< mask - decimal number format +#define ASD_DECF0 0x00000000 ///< 34 +#define ASD_DECF1 0x00001000 ///< #34 +#define ASD_DECF2 0x00002000 ///< 34. +#define ASD_DECF3 0x00003000 ///< .34 +#define AS_OCTFM 0x0001C000 ///< mask - octal number format +#define ASO_OCTF0 0x00000000 ///< 123o +#define ASO_OCTF1 0x00004000 ///< 0123 +#define ASO_OCTF2 0x00008000 ///< 123 +#define ASO_OCTF3 0x0000C000 ///< @123 +#define ASO_OCTF4 0x00010000 ///< o'123 +#define ASO_OCTF5 0x00014000 ///< 123q +#define ASO_OCTF6 0x00018000 ///< ~123 +#define ASO_OCTF7 0x0001C000 ///< q'123 +#define AS_BINFM 0x000E0000 ///< mask - binary number format +#define ASB_BINF0 0x00000000 ///< 010101b +#define ASB_BINF1 0x00020000 ///< ^B010101 +#define ASB_BINF2 0x00040000 ///< %010101 +#define ASB_BINF3 0x00060000 ///< 0b1010101 +#define ASB_BINF4 0x00080000 ///< b'1010101 +#define ASB_BINF5 0x000A0000 ///< b'1010101' + +#define AS_UNEQU 0x00100000 ///< replace undefined data items with EQU (for ANTA's A80) +#define AS_ONEDUP 0x00200000 ///< One array definition per line +#define AS_NOXRF 0x00400000 ///< Disable xrefs during the output file generation +#define AS_XTRNTYPE 0x00800000 ///< Assembler understands type of extern symbols as ":type" suffix +#define AS_RELSUP 0x01000000 ///< Checkarg: 'and','or','xor' operations with addresses are possible +#define AS_LALIGN 0x02000000 ///< Labels at "align" keyword are supported. +#define AS_NOCODECLN 0x04000000 ///< don't create colons after code names +#define AS_NOSPACE 0x10000000 ///< No spaces in expressions +#define AS_ALIGN2 0x20000000 ///< .align directive expects an exponent rather than a power of 2 + ///< (.align 5 means to align at 32byte boundary) +#define AS_ASCIIC 0x40000000 ///< ascii directive accepts C-like escape sequences + ///< (\\n,\\x01 and similar) +#define AS_ASCIIZ 0x80000000 ///< ascii directive inserts implicit zero byte at the end +///@} + uint16 uflag; ///< user defined flags (local only for IDP) + ///< you may define and use your own bits + const char *name; ///< Assembler name (displayed in menus) + help_t help; ///< Help screen number, 0 - no help + const char *const *header; ///< array of automatically generated header lines + ///< they appear at the start of disassembled text + const char *origin; ///< org directive + const char *end; ///< end directive + const char *cmnt; ///< comment string (see also cmnt2) + char ascsep; ///< string literal delimiter + char accsep; ///< char constant delimiter + const char *esccodes; ///< special chars that cannot appear + ///< as is in string and char literals + + // Data representation (db,dw,...): + const char *a_ascii; ///< string literal directive + const char *a_byte; ///< byte directive + const char *a_word; ///< word directive + const char *a_dword; ///< nullptr if not allowed + const char *a_qword; ///< nullptr if not allowed + const char *a_oword; ///< nullptr if not allowed + const char *a_float; ///< float; 4bytes; nullptr if not allowed + const char *a_double; ///< double; 8bytes; nullptr if not allowed + const char *a_tbyte; ///< long double; nullptr if not allowed + const char *a_packreal; ///< packed decimal real nullptr if not allowed + const char *a_dups; ///< array keyword. the following + ///< sequences may appear: + ///< - #h header + ///< - #d size + ///< - #v value + ///< - #s(b,w,l,q,f,d,o) size specifiers + ///< for byte,word, + ///< dword,qword, + ///< float,double,oword + const char *a_bss; ///< uninitialized data directive + ///< should include '%s' for the + ///< size of data + const char *a_equ; ///< 'equ' Used if AS_UNEQU is set + const char *a_seg; ///< 'seg ' prefix (example: push seg seg001) + + const char *a_curip; ///< current IP (instruction pointer) symbol in assembler + + /// Generate function header lines. + /// If nullptr, then function headers are displayed as normal lines + void (idaapi *out_func_header)(outctx_t &ctx, func_t *); + + /// Generate function footer lines. + /// If nullptr, then a comment line is displayed + void (idaapi *out_func_footer)(outctx_t &ctx, func_t *); + + const char *a_public; ///< "public" name keyword. nullptr-use default, ""-do not generate + const char *a_weak; ///< "weak" name keyword. nullptr-use default, ""-do not generate + const char *a_extrn; ///< "extern" name keyword + const char *a_comdef; ///< "comm" (communal variable) + + /// Get name of type of item at ea or id. + /// (i.e. one of: byte,word,dword,near,far,etc...) + ssize_t (idaapi *get_type_name)( + qstring *buf, + flags64_t flag, + ea_t ea_or_id); + + const char *a_align; ///< "align" keyword + + char lbrace; ///< left brace used in complex expressions + char rbrace; ///< right brace used in complex expressions + + const char *a_mod; ///< % mod assembler time operation + const char *a_band; ///< & bit and assembler time operation + const char *a_bor; ///< | bit or assembler time operation + const char *a_xor; ///< ^ bit xor assembler time operation + const char *a_bnot; ///< ~ bit not assembler time operation + const char *a_shl; ///< << shift left assembler time operation + const char *a_shr; ///< >> shift right assembler time operation + const char *a_sizeof_fmt; ///< size of type (format string) + + uint32 flag2; ///< \ref AS2_ +/// \defgroup AS2_ Secondary assembler feature bits +/// Used by asm_t::flag2 +///@{ +#define AS2_BRACE 0x00000001 ///< Use braces for all expressions +#define AS2_STRINV 0x00000002 ///< Invert meaning of \inf{wide_high_byte_first} for text strings + ///< (for processors with bytes bigger than 8 bits) +#define AS2_BYTE1CHAR 0x00000004 ///< One symbol per processor byte. + ///< Meaningful only for wide byte processors +#define AS2_IDEALDSCR 0x00000008 ///< Description of struc/union is in + ///< the 'reverse' form (keyword before name), + ///< the same as in borland tasm ideal +#define AS2_TERSESTR 0x00000010 ///< 'terse' structure initialization form; + ///< NAME is supported +#define AS2_COLONSUF 0x00000020 ///< addresses may have ":xx" suffix; + ///< this suffix must be ignored when extracting + ///< the address under the cursor +#define AS2_YWORD 0x00000040 ///< a_yword field is present and valid +#define AS2_ZWORD 0x00000080 ///< a_zword field is present and valid +///@} + + const char *cmnt2; ///< comment close string (usually nullptr) + ///< this is used to denote a string which + ///< closes comments, for example, if the + ///< comments are represented with (* ... *) + ///< then cmnt = "(*" and cmnt2 = "*)" + const char *low8; ///< low8 operation, should contain %s for the operand + const char *high8; ///< high8 + const char *low16; ///< low16 + const char *high16; ///< high16 + const char *a_include_fmt; ///< the include directive (format string) + const char *a_vstruc_fmt; ///< if a named item is a structure and displayed + ///< in the verbose (multiline) form then display the name + ///< as printf(a_strucname_fmt, typename) + ///< (for asms with type checking, e.g. tasm ideal) + const char *a_rva; ///< 'rva' keyword for image based offsets + ///< (see #REFINFO_RVAOFF) + const char *a_yword; ///< 32-byte (256-bit) data; nullptr if not allowed + ///< requires #AS2_YWORD + const char *a_zword; ///< 64-byte (512-bit) data; nullptr if not allowed + ///< requires #AS2_ZWORD +}; +#ifndef __X86__ +CASSERT(sizeof(asm_t) == 416); +#else +CASSERT(sizeof(asm_t) == 212); +#endif + +// forward declarations for notification helpers +struct proc_def_t; +struct elf_loader_t; +class reader_t; +struct extlang_t; +class qflow_chart_t; +struct libfunc_t; +struct fixup_data_t; +struct idd_opinfo_t; +class argloc_t; +struct func_type_data_t; +struct regobjs_t; +class callregs_t; +struct funcarg_t; + +//-------------------------------------------------------------------------- +struct event_listener_t; + +/// Install an event listener. +/// The installed listener will be called for all kernel events of the specified +/// type (\ref hook_type_t). +/// \param hook_type one of \ref hook_type_t constants +/// \param cb The event listener object +/// \param owner The listener owner. Points to an instance of: plugin_t, +/// processor_t, or loader_t. Can be nullptr, which means +/// undefined owner. The owner is used by the kernel for +/// automatic removal of the event listener when the owner is +/// unloaded from the memory. +/// \param hkcb_flags combination of \ref HKCB_ bits +/// \return success +idaman bool ida_export hook_event_listener( + hook_type_t hook_type, + event_listener_t *cb, + const void *owner, + int hkcb_flags=0); +/// \defgroup HKCB_ Hook installation bits. +///@{ +#define HKCB_GLOBAL 0x0001 ///< is global event listener? if true, the listener + ///< will survive database closing and opening. it + ///< will stay in the memory until explicitly + ///< unhooked. otherwise the kernel will delete it + ///< as soon as the owner is unloaded. + ///< should be used only with PLUGIN_FIX plugins. +///@} + +/// Uninstall an event listener. +/// \param hook_type one of \ref hook_type_t constants +/// \param cb the listener object +/// \return success +/// A listener is uninstalled automatically when the owner module is unloaded +/// or when the listener object is being destroyed +idaman bool ida_export unhook_event_listener( + hook_type_t hook_type, + event_listener_t *cb); + +/// remove all hooks in all databases for specified event_listener object +idaman void ida_export remove_event_listener(event_listener_t *cb); + +struct event_listener_t +{ + size_t listener_flags = 0; // reserved + /// Callback to handle events. The event code depends on the event + /// group where the callback is attached to. + virtual ssize_t idaapi on_event(ssize_t code, va_list va) = 0; + virtual ~event_listener_t() { remove_event_listener(this); } +}; + +/// Declare listener with a context +#define DECLARE_LISTENER(listener_type, ctx_type, ctx_name) \ + struct listener_type : public event_listener_t \ + { \ + ctx_type &ctx_name; \ + listener_type(ctx_type &_ctx) : ctx_name(_ctx) {} \ + virtual ssize_t idaapi on_event(ssize_t code, va_list va) override; \ + } + +//------------------------------------------------------------------------- +/// \defgroup PLFM_ Processor IDs +/// Used by processor_t::id. +/// Numbers above 0x8000 are reserved for the third-party modules +///@{ +#define PLFM_386 0 ///< Intel 80x86 +#define PLFM_Z80 1 ///< 8085, Z80 +#define PLFM_I860 2 ///< Intel 860 +#define PLFM_8051 3 ///< 8051 +#define PLFM_TMS 4 ///< Texas Instruments TMS320C5x +#define PLFM_6502 5 ///< 6502 +#define PLFM_PDP 6 ///< PDP11 +#define PLFM_68K 7 ///< Motorola 680x0 +#define PLFM_JAVA 8 ///< Java +#define PLFM_6800 9 ///< Motorola 68xx +#define PLFM_ST7 10 ///< SGS-Thomson ST7 +#define PLFM_MC6812 11 ///< Motorola 68HC12 +#define PLFM_MIPS 12 ///< MIPS +#define PLFM_ARM 13 ///< Advanced RISC Machines +#define PLFM_TMSC6 14 ///< Texas Instruments TMS320C6x +#define PLFM_PPC 15 ///< PowerPC +#define PLFM_80196 16 ///< Intel 80196 +#define PLFM_Z8 17 ///< Z8 +#define PLFM_SH 18 ///< Renesas (formerly Hitachi) SuperH +#define PLFM_NET 19 ///< Microsoft Visual Studio.Net +#define PLFM_AVR 20 ///< Atmel 8-bit RISC processor(s) +#define PLFM_H8 21 ///< Hitachi H8/300, H8/2000 +#define PLFM_PIC 22 ///< Microchip's PIC +#define PLFM_SPARC 23 ///< SPARC +#define PLFM_ALPHA 24 ///< DEC Alpha +#define PLFM_HPPA 25 ///< Hewlett-Packard PA-RISC +#define PLFM_H8500 26 ///< Hitachi H8/500 +#define PLFM_TRICORE 27 ///< Tasking Tricore +#define PLFM_DSP56K 28 ///< Motorola DSP5600x +#define PLFM_C166 29 ///< Siemens C166 family +#define PLFM_ST20 30 ///< SGS-Thomson ST20 +#define PLFM_IA64 31 ///< Intel Itanium IA64 +#define PLFM_I960 32 ///< Intel 960 +#define PLFM_F2MC 33 ///< Fujistu F2MC-16 +#define PLFM_TMS320C54 34 ///< Texas Instruments TMS320C54xx +#define PLFM_TMS320C55 35 ///< Texas Instruments TMS320C55xx +#define PLFM_TRIMEDIA 36 ///< Trimedia +#define PLFM_M32R 37 ///< Mitsubishi 32bit RISC +#define PLFM_NEC_78K0 38 ///< NEC 78K0 +#define PLFM_NEC_78K0S 39 ///< NEC 78K0S +#define PLFM_M740 40 ///< Mitsubishi 8bit +#define PLFM_M7700 41 ///< Mitsubishi 16bit +#define PLFM_ST9 42 ///< ST9+ +#define PLFM_FR 43 ///< Fujitsu FR Family +#define PLFM_MC6816 44 ///< Motorola 68HC16 +#define PLFM_M7900 45 ///< Mitsubishi 7900 +#define PLFM_TMS320C3 46 ///< Texas Instruments TMS320C3 +#define PLFM_KR1878 47 ///< Angstrem KR1878 +#define PLFM_AD218X 48 ///< Analog Devices ADSP 218X +#define PLFM_OAKDSP 49 ///< Atmel OAK DSP +#define PLFM_TLCS900 50 ///< Toshiba TLCS-900 +#define PLFM_C39 51 ///< Rockwell C39 +#define PLFM_CR16 52 ///< NSC CR16 +#define PLFM_MN102L00 53 ///< Panasonic MN10200 +#define PLFM_TMS320C1X 54 ///< Texas Instruments TMS320C1x +#define PLFM_NEC_V850X 55 ///< NEC V850 and V850ES/E1/E2 +#define PLFM_SCR_ADPT 56 ///< Processor module adapter for processor modules written in scripting languages +#define PLFM_EBC 57 ///< EFI Bytecode +#define PLFM_MSP430 58 ///< Texas Instruments MSP430 +#define PLFM_SPU 59 ///< Cell Broadband Engine Synergistic Processor Unit +#define PLFM_DALVIK 60 ///< Android Dalvik Virtual Machine +#define PLFM_65C816 61 ///< 65802/65816 +#define PLFM_M16C 62 ///< Renesas M16C +#define PLFM_ARC 63 ///< Argonaut RISC Core +#define PLFM_UNSP 64 ///< SunPlus unSP +#define PLFM_TMS320C28 65 ///< Texas Instruments TMS320C28x +#define PLFM_DSP96K 66 ///< Motorola DSP96000 +#define PLFM_SPC700 67 ///< Sony SPC700 +#define PLFM_AD2106X 68 ///< Analog Devices ADSP 2106X +#define PLFM_PIC16 69 ///< Microchip's 16-bit PIC +#define PLFM_S390 70 ///< IBM's S390 +#define PLFM_XTENSA 71 ///< Tensilica Xtensa +#define PLFM_RISCV 72 ///< RISC-V +#define PLFM_RL78 73 ///< Renesas RL78 +#define PLFM_RX 74 ///< Renesas RX +#define PLFM_WASM 75 ///< WASM +///@} + +//------------------------------------------------------------------------- +/// \defgroup PR_ Processor feature bits +/// Used by processor_t::flag +///@{ +#define PR_SEGS 0x000001 ///< has segment registers? +#define PR_USE32 0x000002 ///< supports 32-bit addressing? +#define PR_DEFSEG32 0x000004 ///< segments are 32-bit by default +#define PR_RNAMESOK 0x000008 ///< allow user register names for location names +//#define PR_DB2CSEG 0x0010 // .byte directive in code segments +// // should define even number of bytes +// // (used by AVR processor) +#define PR_ADJSEGS 0x000020 ///< IDA may adjust segments' starting/ending addresses. +#define PR_DEFNUM 0x0000C0 ///< mask - default number representation +#define PRN_HEX 0x000000 ///< hex +#define PRN_OCT 0x000040 ///< octal +#define PRN_DEC 0x000080 ///< decimal +#define PRN_BIN 0x0000C0 ///< binary +#define PR_WORD_INS 0x000100 ///< instruction codes are grouped 2bytes in binary line prefix +#define PR_NOCHANGE 0x000200 ///< The user can't change segments and code/data attributes + ///< (display only) +#define PR_ASSEMBLE 0x000400 ///< Module has a built-in assembler and will react to ev_assemble +#define PR_ALIGN 0x000800 ///< All data items should be aligned properly +#define PR_TYPEINFO 0x001000 ///< the processor module fully supports type information callbacks; + ///< without full support, function argument locations and other things + ///< will probably be wrong. +#define PR_USE64 0x002000 ///< supports 64-bit addressing? +#define PR_SGROTHER 0x004000 ///< the segment registers don't contain the segment selectors. +#define PR_STACK_UP 0x008000 ///< the stack grows up +#define PR_BINMEM 0x010000 ///< the processor module provides correct segmentation for binary files + ///< (i.e.\ it creates additional segments). + ///< The kernel will not ask the user to specify the RAM/ROM sizes +#define PR_SEGTRANS 0x020000 ///< the processor module supports the segment translation feature + ///< (meaning it calculates the code + ///< addresses using the map_code_ea() function) +#define PR_CHK_XREF 0x040000 ///< don't allow near xrefs between segments with different bases +#define PR_NO_SEGMOVE 0x080000 ///< the processor module doesn't support move_segm() + ///< (i.e. the user can't move segments) +//#define PR_FULL_HIFXP 0x100000 // ::REF_VHIGH operand value contains full operand +// // (not only the high bits) Meaningful if \ph{high_fixup_bits} +#define PR_USE_ARG_TYPES 0x200000 ///< use \ph{use_arg_types} callback +#define PR_SCALE_STKVARS 0x400000 ///< use \ph{get_stkvar_scale} callback +#define PR_DELAYED 0x800000 ///< has delayed jumps and calls. + ///< If this flag is set, \ph{is_basic_block_end}, \ph{delay_slot_insn} + ///< should be implemented +#define PR_ALIGN_INSN 0x1000000 ///< allow ida to create alignment instructions arbitrarily. + ///< Since these instructions might lead to other wrong instructions + ///< and spoil the listing, IDA does not create them by default anymore +#define PR_PURGING 0x2000000 ///< there are calling conventions which may purge bytes from the stack +#define PR_CNDINSNS 0x4000000 ///< has conditional instructions +#define PR_USE_TBYTE 0x8000000 ///< ::BTMT_SPECFLT means _TBYTE type +#define PR_DEFSEG64 0x10000000 ///< segments are 64-bit by default +#define PR_OUTER 0x20000000 ///< has outer operands (currently only mc68k) +///@} + +//------------------------------------------------------------------------- +/// \defgroup PR2_ Processor additional feature bits +/// Used by processor_t::flag2 +///@{ +#define PR2_MAPPINGS 0x000001 ///< the processor module uses memory mapping +#define PR2_IDP_OPTS 0x000002 ///< the module has processor-specific configuration options +#define PR2_CODE16_BIT 0x000008 ///< low bit of code addresses has special meaning + ///< e.g. ARM Thumb, MIPS16 +#define PR2_MACRO 0x000010 ///< processor supports macro instructions +#define PR2_USE_CALCREL 0x000020 ///< (Lumina) the module supports calcrel info +#define PR2_REL_BITS 0x000040 ///< (Lumina) calcrel info has bits granularity, not bytes - construction flag only +#define PR2_FORCE_16BIT 0x000080 ///< use 16-bit basic types despite of 32-bit segments (used by c166) +///@} + +//------------------------------------------------------------------------- +/// \defgroup OP_FP_SP SP/FP operand flags +/// Return values for processor_t::is_sp_based() +///@{ +#define OP_FP_BASED 0x00000000 ///< operand is FP based +#define OP_SP_BASED 0x00000001 ///< operand is SP based +#define OP_SP_ADD 0x00000000 ///< operand value is added to the pointer +#define OP_SP_SUB 0x00000002 ///< operand value is subtracted from the pointer +///@} + +//------------------------------------------------------------------------- +/// Custom instruction codes defined by processor extension plugins +/// must be greater than or equal to this +#define CUSTOM_INSN_ITYPE 0x8000 + +//------------------------------------------------------------------------- +/// processor_t::use_regarg_type uses this bit in the return value +/// to indicate that the register value has been spoiled +#define REG_SPOIL 0x80000000 + +//===================================================================== +/// Describes a processor module (IDP). +/// An IDP file may have only one such structure called LPH. +/// The kernel will copy it to dbctx_t::ph. +struct processor_t +{ + int32 version; ///< Expected kernel version, + ///< should be #IDP_INTERFACE_VERSION + int32 id; ///< one of \ref PLFM_ + uint32 flag; ///< an ORed combination of \ref PR_ + uint32 flag2; ///< an ORed combination of \ref PR2_ + + bool has_idp_opts(void) const { return (flag2 & PR2_IDP_OPTS) != 0; } ///< #PR_IDP_OPTS + bool has_segregs(void) const { return (flag & PR_SEGS) != 0; } ///< #PR_SEGS + bool use32(void) const { return (flag & (PR_USE64|PR_USE32)) != 0; } ///< #PR_USE64 or #PR_USE32 + bool use64(void) const { return (flag & PR_USE64) != 0; } ///< #PR_USE64 + bool ti(void) const { return (flag & PR_TYPEINFO) != 0; } ///< #PR_TYPEINFO + bool stkup(void) const { return (flag & PR_STACK_UP) != 0; } ///< #PR_STACK_UP + bool use_tbyte(void) const { return (flag & PR_USE_TBYTE) != 0; } ///< #PR_USE_TBYTE + bool use_mappings(void) const { return (flag2 & PR2_MAPPINGS) != 0; } ///< #PR2_MAPPINGS + bool has_code16_bit(void) const { return (flag2 & PR2_CODE16_BIT) != 0; } ///< #PR2_CODE16_BIT + bool supports_macros(void) const { return (flag2 & PR2_MACRO) != 0; } ///< #PR2_MACRO + bool supports_calcrel(void) const { return (flag2 & PR2_USE_CALCREL) != 0; } ///< #PR2_USE_CALCREL + bool calcrel_in_bits(void) const { return (flag2 & PR2_REL_BITS) != 0; } ///< #PR2_REL_BITS + + /// Get default segment bitness + /// \retval 2 #PR_DEFSEG64 + /// \retval 1 #PR_DEFSEG32 + /// \retval 0 none specified + + int get_default_segm_bitness(bool is_64bit_app) const + { + return is_64bit_app && (flag & PR_DEFSEG64) != 0 ? 2 : (flag & PR_DEFSEG32) != 0; + } + + int32 cnbits; ///< Number of bits in a byte + ///< for code segments (usually 8). + ///< IDA supports values up to supported + ///< address bits size + int32 dnbits; ///< Number of bits in a byte + ///< for non-code segments (usually 8). + ///< IDA supports values up to supported + ///< address bit size + + /// \name Byte size + /// Number of 8bit bytes required to hold one byte of the target processor. + ///@{ + int cbsize(void) { return (cnbits+7)/8; } ///< for code segments + int dbsize(void) { return (dnbits+7)/8; } ///< for non-code segments + ///@} + + /// \name Names + /// IDP module may support several compatible processors. + /// The following arrays define processor names: + ///@{ + const char *const *psnames; ///< short processor names (nullptr terminated). + ///< Each name should be shorter than 9 characters + const char *const *plnames; ///< long processor names (nullptr terminated). + ///< No restriction on name lengths. + ///@} + + inline int get_proc_index(); ///< \retval currently selected processor subtype (index into psnames/plnames) + + const asm_t *const *assemblers; ///< pointer to array of target + ///< assembler definitions. You may + ///< change this array when current + ///< processor is changed. + ///< (nullptr terminated) + + typedef const regval_t &(idaapi regval_getter_t)( + const char *name, + const regval_t *regvalues); + + // + + /// Callback notification codes. + /// + /// These are passed to notify() when certain events occur in the kernel, + /// allowing the processor module to take the appropriate action. + /// + /// If you are not developing a processor module, you do not need to + /// use the codes directly many of them already have a corresponding function + /// to use instead (\idpcode{is_call_insn} vs is_call_insn(ea_t), for example). + /// + /// If you are developing a processor module, your notify() function + /// must implement the desired behavior when called with a given code. + /// Not all events need to be handled, some of them are optional. + enum event_t + { + ev_init, ///< The IDP module is just loaded. + ///< \param idp_modname (const char *) processor module name + ///< \retval <0 on failure + + ev_term, ///< The IDP module is being unloaded + + ev_newprc, ///< Before changing processor type. + ///< \param pnum (int) processor number in the array of processor names + ///< \param keep_cfg (bool) true: do not modify kernel configuration + ///< \retval 1 ok + ///< \retval <0 prohibit + + ev_newasm, ///< Before setting a new assembler. + ///< \param asmnum (int) + ///< See also ev_asm_installed + + ev_newfile, ///< A new file has been loaded. + ///< \param fname (char *) input file name + + ev_oldfile, ///< An old file has been loaded. + ///< \param fname (char *) input file name + + ev_newbinary, ///< IDA is about to load a binary file. + ///< \param filename (char *) binary file name + ///< \param fileoff (::qoff64_t) offset in the file + ///< \param basepara (::ea_t) base loading paragraph + ///< \param binoff (::ea_t) loader offset + ///< \param nbytes (::uint64) number of bytes to load + + ev_endbinary, ///< IDA has loaded a binary file. + ///< \param ok (bool) file loaded successfully? + + ev_set_idp_options, ///< Set IDP-specific configuration option + ///< Also see set_options_t in config.hpp + ///< \param keyword (const char *) + ///< \param value_type (int) + ///< \param value (const void *) + ///< \param errbuf (const char **) - a error message will be returned here (can be nullptr) + ///< \param idb_loaded (bool) true if the ev_oldfile/ev_newfile events have been generated + ///< \retval 1 ok + ///< \retval 0 not implemented + ///< \retval -1 error (and message in errbuf) + + ev_set_proc_options, ///< Called if the user specified an option string in the command line: + ///< -p:. + ///< Can be used for setting a processor subtype. + ///< Also called if option string is passed to set_processor_type() + ///< and IDC's SetProcessorType(). + ///< \param options (const char *) + ///< \param confidence (int) + ///< 0: loader's suggestion + ///< 1: user's decision + ///< \retval <0 if bad option string + + ev_ana_insn, ///< Analyze one instruction and fill 'out' structure. + ///< This function shouldn't change the database, flags or anything else. + ///< All these actions should be performed only by emu_insn() function. + ///< \insn_t{ea} contains address of instruction to analyze. + ///< \param out (::insn_t *) + ///< \return length of the instruction in bytes, 0 if instruction can't be decoded. + ///< \retval 0 if instruction can't be decoded. + + ev_emu_insn, ///< Emulate instruction, create cross-references, plan to analyze + ///< subsequent instructions, modify flags etc. Upon entrance to this function, + ///< all information about the instruction is in 'insn' structure. + ///< \param insn (const ::insn_t *) + ///< \retval 1 ok + ///< \retval -1 the kernel will delete the instruction + + ev_out_header, ///< Function to produce start of disassembled text + ///< \param outctx (::outctx_t *) + ///< \retval void + + ev_out_footer, ///< Function to produce end of disassembled text + ///< \param outctx (::outctx_t *) + ///< \retval void + + ev_out_segstart, ///< Function to produce start of segment + ///< \param outctx (::outctx_t *) + ///< \param seg (::segment_t *) + ///< \retval 1 ok + ///< \retval 0 not implemented + + ev_out_segend, ///< Function to produce end of segment + ///< \param outctx (::outctx_t *) + ///< \param seg (::segment_t *) + ///< \retval 1 ok + ///< \retval 0 not implemented + + ev_out_assumes, ///< Function to produce assume directives + ///< when segment register value changes. + ///< \param outctx (::outctx_t *) + ///< \retval 1 ok + ///< \retval 0 not implemented + + ev_out_insn, ///< Generate text representation of an instruction in 'ctx.insn' + ///< outctx_t provides functions to output the generated text. + ///< This function shouldn't change the database, flags or anything else. + ///< All these actions should be performed only by emu_insn() function. + ///< \param outctx (::outctx_t *) + ///< \retval void + + ev_out_mnem, ///< Generate instruction mnemonics. + ///< This callback should append the colored mnemonics to ctx.outbuf + ///< Optional notification, if absent, out_mnem will be called. + ///< \param outctx (::outctx_t *) + ///< \retval 1 if appended the mnemonics + ///< \retval 0 not implemented + + ev_out_operand, ///< Generate text representation of an instruction operand + ///< outctx_t provides functions to output the generated text. + ///< All these actions should be performed only by emu_insn() function. + ///< \param outctx (::outctx_t *) + ///< \param op (const ::op_t *) + ///< \retval 1 ok + ///< \retval -1 operand is hidden + + ev_out_data, ///< Generate text representation of data items + ///< This function may change the database and create cross-references + ///< if analyze_only is set + ///< \param outctx (::outctx_t *) + ///< \param analyze_only (bool) + ///< \retval 1 ok + ///< \retval 0 not implemented + + ev_out_label, ///< The kernel is going to generate an instruction + ///< label line or a function header. + ///< \param outctx (::outctx_t *) + ///< \param colored_name (const char *) + ///< \retval <0 if the kernel should not generate the label + ///< \retval 0 not implemented or continue + + ev_out_special_item, ///< Generate text representation of an item in a special segment + ///< i.e. absolute symbols, externs, communal definitions etc + ///< \param outctx (::outctx_t *) + ///< \param segtype (uchar) + ///< \retval 1 ok + ///< \retval 0 not implemented + ///< \retval -1 overflow + + ev_gen_regvar_def, ///< Generate register variable definition line. + ///< \param outctx (::outctx_t *) + ///< \param v (::regvar_t *) + ///< \retval >0 ok, generated the definition text + ///< \retval 0 not implemented + + ev_gen_src_file_lnnum, ///< Callback: generate analog of: + ///< \code + ///< #line "file.c" 123 + ///< \endcode + ///< directive. + ///< \param outctx (::outctx_t *) output context + ///< \param file (const char *) source file (may be nullptr) + ///< \param lnnum (size_t) line number + ///< \retval 1 directive has been generated + ///< \retval 0 not implemented + + ev_creating_segm, ///< A new segment is about to be created. + ///< \param seg (::segment_t *) + ///< \retval 1 ok + ///< \retval <0 segment should not be created + + ev_moving_segm, ///< May the kernel move the segment? + ///< \param seg (::segment_t *) segment to move + ///< \param to (::ea_t) new segment start address + ///< \param flags (int) combination of \ref MSF_ + ///< \retval 0 yes + ///< \retval <0 the kernel should stop + + ev_coagulate, ///< Try to define some unexplored bytes. + ///< This notification will be called if the + ///< kernel tried all possibilities and could + ///< not find anything more useful than to + ///< convert to array of bytes. + ///< The module can help the kernel and convert + ///< the bytes into something more useful. + ///< \param start_ea (::ea_t) + ///< \return number of converted bytes + + ev_undefine, ///< An item in the database (insn or data) is being deleted. + ///< \param ea (ea_t) + ///< \retval 1 do not delete srranges at the item end + ///< \retval 0 srranges can be deleted + + ev_treat_hindering_item, ///< An item hinders creation of another item. + ///< \param hindering_item_ea (::ea_t) + ///< \param new_item_flags (::flags64_t) (0 for code) + ///< \param new_item_ea (::ea_t) + ///< \param new_item_length (::asize_t) + ///< \retval 0 no reaction + ///< \retval !=0 the kernel may delete the hindering item + + ev_rename, ///< The kernel is going to rename a byte. + ///< \param ea (::ea_t) + ///< \param new_name (const char *) + ///< \param flags (int) \ref SN_ + ///< \retval <0 if the kernel should not rename it. + ///< \retval 2 to inhibit the notification. I.e., + ///< the kernel should not rename, but + ///< 'set_name()' should return 'true'. + ///< also see \idpcode{renamed} + ///< the return value is ignored when kernel is going to delete name + + ev_is_far_jump, ///< is indirect far jump or call instruction? + ///< meaningful only if the processor has 'near' and 'far' reference types + ///< \param icode (int) + ///< \retval 0 not implemented + ///< \retval 1 yes + ///< \retval -1 no + + ev_is_sane_insn, ///< Is the instruction sane for the current file type?. + ///< \param insn (const ::insn_t*) the instruction + ///< \param no_crefs (int) + ///< 1: the instruction has no code refs to it. + ///< ida just tries to convert unexplored bytes + ///< to an instruction (but there is no other + ///< reason to convert them into an instruction) + ///< 0: the instruction is created because + ///< of some coderef, user request or another + ///< weighty reason. + ///< \retval >=0 ok + ///< \retval <0 no, the instruction isn't + ///< likely to appear in the program + + ev_is_cond_insn, ///< Is conditional instruction? + ///< \param insn (const ::insn_t *) instruction address + ///< \retval 1 yes + ///< \retval -1 no + ///< \retval 0 not implemented or not instruction + + ev_is_call_insn, ///< Is the instruction a "call"? + ///< \param insn (const ::insn_t *) instruction + ///< \retval 0 unknown + ///< \retval <0 no + ///< \retval 1 yes + + ev_is_ret_insn, ///< Is the instruction a "return"? + ///< \param insn (const ::insn_t *) instruction + ///< \param flags (uchar), combination of IRI_... flags (see above) + ///< \retval 0 unknown + ///< \retval <0 no + ///< \retval 1 yes + + ev_may_be_func, ///< Can a function start here? + ///< \param insn (const ::insn_t*) the instruction + ///< \param state (int) autoanalysis phase + ///< 0: creating functions + ///< 1: creating chunks + ///< \return probability 1..100 + ///< \note Actually IDA uses 3 intervals of a probability: + ///< 0..50 not a function, + ///< 51..99 a function (IDA needs another proof), + ///< 100 a function (no other proofs needed) + + ev_is_basic_block_end, ///< Is the current instruction end of a basic block?. + ///< This function should be defined for processors + ///< with delayed jump slots. + ///< \param insn (const ::insn_t*) the instruction + ///< \param call_insn_stops_block (bool) + ///< \retval 0 unknown + ///< \retval <0 no + ///< \retval 1 yes + + ev_is_indirect_jump, ///< Determine if instruction is an indirect jump. + ///< If #CF_JUMP bit cannot describe all jump types + ///< jumps, please define this callback. + ///< \param insn (const ::insn_t*) the instruction + ///< \retval 0 use #CF_JUMP + ///< \retval 1 no + ///< \retval 2 yes + + ev_is_insn_table_jump, ///< Reserved + + ev_is_switch, ///< Find 'switch' idiom or override processor module's decision. + ///< It will be called for instructions marked with #CF_JUMP. + ///< \param si (switch_info_t *), out + ///< \param insn (const ::insn_t *) instruction possibly belonging to a switch + ///< \retval 1 switch is found, 'si' is filled. + ///< IDA will create the switch using the filled 'si' + ///< \retval -1 no switch found. + ///< This value forbids switch creation by the processor module + ///< \retval 0 not implemented + + ev_calc_switch_cases, ///< Calculate case values and targets for a custom jump table. + ///< \param casevec (::casevec_t *) vector of case values (may be nullptr) + ///< \param targets (::eavec_t *) corresponding target addresses (my be nullptr) + ///< \param insn_ea (::ea_t) address of the 'indirect jump' instruction + ///< \param si (::switch_info_t *) switch information + ///< \retval 1 ok + ///< \retval <=0 failed + + ev_create_switch_xrefs, ///< Create xrefs for a custom jump table. + ///< \param jumpea (::ea_t) address of the jump insn + ///< \param si (const ::switch_info_t *) switch information + ///< \return must return 1 + ///< Must be implemented if module uses custom jump tables, \ref SWI_CUSTOM + + ev_is_align_insn, ///< Is the instruction created only for alignment purposes?. + ///< Do not directly call this function, use ::is_align_insn() + ///< \param ea (ea_t) - instruction address + ///< \retval number of bytes in the instruction + + ev_is_alloca_probe, ///< Does the function at 'ea' behave as __alloca_probe? + ///< \param ea (::ea_t) + ///< \retval 1 yes + ///< \retval 0 no + + ev_delay_slot_insn, ///< Get delay slot instruction + ///< \param ea (::ea_t *) in: instruction address in question, + ///< out: (if the answer is positive) + ///< if the delay slot contains valid insn: + ///< the address of the delay slot insn + ///< else: + ///< BADADDR (invalid insn, e.g. a branch) + ///< \param bexec (bool *) execute slot if jumping, + ///< initially set to 'true' + ///< \param fexec (bool *) execute slot if not jumping, + ///< initally set to 'true' + ///< \retval 1 positive answer + ///< \retval <=0 ordinary insn + ///< \note Input EA may point to the instruction with a delay slot or + ///< to the delay slot instruction itself. + + ev_is_sp_based, ///< Check whether the operand is relative to stack pointer or frame pointer + ///< This event is used to determine how to output a stack variable + ///< If not implemented, then all operands are sp based by default. + ///< Implement this event only if some stack references use frame pointer + ///< instead of stack pointer. + ///< \param mode (int *) out, combination of \ref OP_FP_SP + ///< \param insn (const insn_t *) + ///< \param op (const op_t *) + ///< \retval 0 not implemented + ///< \retval 1 ok + + ev_can_have_type, ///< Can the operand have a type as offset, segment, decimal, etc? + ///< (for example, a register AX can't have a type, meaning that the user can't + ///< change its representation. see bytes.hpp for information about types and flags) + ///< \param op (const ::op_t *) + ///< \retval 0 unknown + ///< \retval <0 no + ///< \retval 1 yes + + ev_cmp_operands, ///< Compare instruction operands + ///< \param op1 (const ::op_t*) + ///< \param op2 (const ::op_t*) + ///< \retval 1 equal + ///< \retval -1 not equal + ///< \retval 0 not implemented + + ev_adjust_refinfo, ///< Called from apply_fixup before converting operand to reference. + ///< Can be used for changing the reference info. + ///< (e.g. the PPC module adds REFINFO_NOBASE for some references) + ///< \param ri (refinfo_t *) + ///< \param ea (::ea_t) instruction address + ///< \param n (int) operand number + ///< \param fd (const fixup_data_t *) + ///< \retval <0 do not create an offset + ///< \retval 0 not implemented or refinfo adjusted + + ev_get_operand_string, ///< Request text string for operand (cli, java, ...). + ///< \param buf (qstring *) + ///< \param insn (const ::insn_t*) the instruction + ///< \param opnum (int) operand number, -1 means any string operand + ///< \retval 0 no string (or empty string) + ///< \retval >0 original string length without terminating zero + + ev_get_reg_name, ///< Generate text representation of a register. + ///< Most processor modules do not need to implement this callback. + ///< It is useful only if \ph{reg_names}[reg] does not provide + ///< the correct register name. + ///< \param buf (qstring *) output buffer + ///< \param reg (int) internal register number as defined in the processor module + ///< \param width (size_t) register width in bytes + ///< \param reghi (int) if not -1 then this function will return the register pair + ///< \retval -1 if error + ///< \retval strlen(buf) if success + + ev_str2reg, ///< Convert a register name to a register number. + ///< The register number is the register index in the \ph{reg_names} array + ///< Most processor modules do not need to implement this callback + ///< It is useful only if \ph{reg_names}[reg] does not provide + ///< the correct register names + ///< \param regname (const char *) + ///< \retval register number + 1 + ///< \retval 0 not implemented or could not be decoded + + ev_get_autocmt, ///< Callback: get dynamic auto comment. + ///< Will be called if the autocomments are enabled + ///< and the comment retrieved from ida.int starts with + ///< '$!'. 'insn' contains valid info. + ///< \param buf (qstring *) output buffer + ///< \param insn (const ::insn_t*) the instruction + ///< \retval 1 new comment has been generated + ///< \retval 0 callback has not been handled. + ///< the buffer must not be changed in this case + + ev_get_bg_color, ///< Get item background color. + ///< Plugins can hook this callback to color disassembly lines dynamically + ///< \param color (::bgcolor_t *), out + ///< \param ea (::ea_t) + ///< \retval 0 not implemented + ///< \retval 1 color set + + ev_is_jump_func, ///< Is the function a trivial "jump" function?. + ///< \param pfn (::func_t *) + ///< \param jump_target (::ea_t *) + ///< \param func_pointer (::ea_t *) + ///< \retval <0 no + ///< \retval 0 don't know + ///< \retval 1 yes, see 'jump_target' and 'func_pointer' + + ev_func_bounds, ///< find_func_bounds() finished its work. + ///< The module may fine tune the function bounds + ///< \param possible_return_code (int *), in/out + ///< \param pfn (::func_t *) + ///< \param max_func_end_ea (::ea_t) (from the kernel's point of view) + ///< \retval void + + ev_verify_sp, ///< All function instructions have been analyzed. + ///< Now the processor module can analyze the stack pointer + ///< for the whole function + ///< \param pfn (::func_t *) + ///< \retval 0 ok + ///< \retval <0 bad stack pointer + + ev_verify_noreturn, ///< The kernel wants to set 'noreturn' flags for a function. + ///< \param pfn (::func_t *) + ///< \retval 0: ok. any other value: do not set 'noreturn' flag + + ev_create_func_frame, ///< Create a function frame for a newly created function + ///< Set up frame size, its attributes etc + ///< \param pfn (::func_t *) + ///< \retval 1 ok + ///< \retval 0 not implemented + + ev_get_frame_retsize, ///< Get size of function return address in bytes + ///< If this event is not implemented, the kernel will assume + ///< - 8 bytes for 64-bit function + ///< - 4 bytes for 32-bit function + ///< - 2 bytes otherwise + ///< + ///< \param frsize (int *) frame size (out) + ///< \param pfn (const ::func_t *), can't be nullptr + ///< \retval 1 ok + ///< \retval 0 not implemented + + ev_get_stkvar_scale_factor, ///< Should stack variable references be multiplied by + ///< a coefficient before being used in the stack frame?. + ///< Currently used by TMS320C55 because the references into + ///< the stack should be multiplied by 2 + ///< \return scaling factor + ///< \retval 0 not implemented + ///< \note #PR_SCALE_STKVARS should be set to use this callback + + ev_demangle_name, ///< Demangle a C++ (or another language) name into a user-readable string. + ///< This event is called by ::demangle_name() + ///< \param res (int32 *) value to return from ::demangle_name() + ///< \param out (::qstring *) output buffer. may be nullptr + ///< \param name (const char *) mangled name + ///< \param disable_mask (uint32) flags to inhibit parts of output or compiler info/other (see MNG_) + ///< \param demreq (demreq_type_t) operation to perform + ///< \retval 1 if success + ///< \retval 0 not implemented + ///< \note if you call ::demangle_name() from the handler, protect against recursion! + + // the following 5 events are very low level + // take care of possible recursion + ev_add_cref, ///< A code reference is being created. + ///< \param from (::ea_t) + ///< \param to (::ea_t) + ///< \param type (::cref_t) + ///< \retval <0 cancel cref creation + ///< \retval 0 not implemented or continue + + ev_add_dref, ///< A data reference is being created. + ///< \param from (::ea_t) + ///< \param to (::ea_t) + ///< \param type (::dref_t) + ///< \retval <0 cancel dref creation + ///< \retval 0 not implemented or continue + + ev_del_cref, ///< A code reference is being deleted. + ///< \param from (::ea_t) + ///< \param to (::ea_t) + ///< \param expand (bool) + ///< \retval <0 cancel cref deletion + ///< \retval 0 not implemented or continue + + ev_del_dref, ///< A data reference is being deleted. + ///< \param from (::ea_t) + ///< \param to (::ea_t) + ///< \retval <0 cancel dref deletion + ///< \retval 0 not implemented or continue + + ev_coagulate_dref, ///< Data reference is being analyzed. + ///< plugin may correct 'code_ea' (e.g. for thumb mode refs, we clear the last bit) + ///< \param from (::ea_t) + ///< \param to (::ea_t) + ///< \param may_define (bool) + ///< \param code_ea (::ea_t *) + ///< \retval <0 failed dref analysis, >0 done dref analysis + ///< \retval 0 not implemented or continue + + ev_may_show_sreg, ///< The kernel wants to display the segment registers + ///< in the messages window. + ///< \param current_ea (::ea_t) + ///< \retval <0 if the kernel should not show the segment registers. + ///< (assuming that the module has done it) + ///< \retval 0 not implemented + + ev_loader_elf_machine, ///< ELF loader machine type checkpoint. + ///< A plugin check of the 'machine_type'. If it is the desired one, + ///< the the plugin fills 'p_procname' with the processor name + ///< (one of the names present in \ph{psnames}). + ///< 'p_pd' is used to handle relocations, otherwise can be left untouched. + ///< This event occurs for each newly loaded ELF file + ///< \param li (linput_t *) + ///< \param machine_type (int) + ///< \param p_procname (const char **) + ///< \param p_pd (proc_def_t **) (see ldr\elf.h) + ///< \param loader (elf_loader_t *) (see ldr\elf.h) + ///< \param reader (reader_t *) (see ldr\elf.h) + ///< \retval e_machine value (if it is different from the + ///< original e_machine value, procname and 'p_pd' will be ignored + ///< and the new value will be used) + ///< before replacing pd it is a good idea to delete the previous instance + ///< using 'delete pd;' + ///< The 'loader' and 'reader' arguments are available starting from IDA v7.7. + + ev_auto_queue_empty, ///< One analysis queue is empty. + ///< \param type (::atype_t) + ///< \retval void + ///< see also \ref idb_event::auto_empty_finally + + ev_validate_flirt_func, ///< Flirt has recognized a library function. + ///< This callback can be used by a plugin or proc module + ///< to intercept it and validate such a function. + ///< \param start_ea (::ea_t) + ///< \param funcname (const char *) + ///< \retval -1 do not create a function, + ///< \retval 0 function is validated + + ev_adjust_libfunc_ea, ///< Called when a signature module has been matched against + ///< bytes in the database. This is used to compute the + ///< offset at which a particular module's libfunc should + ///< be applied. + ///< \param sig (const idasgn_t *) + ///< \param libfun (const libfunc_t *) + ///< \param ea (::ea_t *) \note 'ea' initially contains the ea_t of the + ///< start of the pattern match + ///< \retval 1 the ea_t pointed to by the third argument was modified. + ///< \retval <=0 not modified. use default algorithm. + + ev_assemble, ///< Assemble an instruction. + ///< (display a warning if an error is found). + ///< \param bin (::uchar *) pointer to output opcode buffer + ///< \param ea (::ea_t) linear address of instruction + ///< \param cs (::ea_t) cs of instruction + ///< \param ip (::ea_t) ip of instruction + ///< \param use32 (bool) is 32bit segment? + ///< \param line (const char *) line to assemble + ///< \return size of the instruction in bytes + + ev_extract_address, ///< Extract address from a string. + ///< \param out_ea (ea_t *), out + ///< \param screen_ea (ea_t) + ///< \param string (const char *) + ///< \param position (size_t) + ///< \retval 1 ok + ///< \retval 0 kernel should use the standard algorithm + ///< \retval -1 error + + ev_realcvt, ///< Floating point -> IEEE conversion + ///< \param m (void *) ptr to processor-specific floating point value + ///< \param e (fpvalue_t *) IDA representation of a floating point value + ///< \param swt (uint16) operation (see realcvt() in ieee.h) + ///< \retval 0 not implemented + ///< \retval \ref REAL_ERROR_ otherwise + + ev_gen_asm_or_lst, ///< Callback: generating asm or lst file. + ///< The kernel calls this callback twice, at the beginning + ///< and at the end of listing generation. The processor + ///< module can intercept this event and adjust its output + ///< \param starting (bool) beginning listing generation + ///< \param fp (FILE *) output file + ///< \param is_asm (bool) true:assembler, false:listing + ///< \param flags (int) flags passed to gen_file() + ///< \param outline (html_line_cb_t **) ptr to ptr to outline callback. + ///< if this callback is defined for this code, it will be + ///< used by the kernel to output the generated lines + ///< \retval void + + ev_gen_map_file, ///< Generate map file. If not implemented + ///< the kernel itself will create the map file. + ///< \param nlines (int *) number of lines in map file (-1 means write error) + ///< \param fp (FILE *) output file + ///< \retval 0 not implemented + ///< \retval 1 ok + ///< \retval -1 write error + + ev_create_flat_group, ///< Create special segment representing the flat group. + ///< \param image_base (::ea_t) + ///< \param bitness (int) + ///< \param dataseg_sel (::sel_t) + ///< return value is ignored + + ev_getreg, ///< IBM PC only internal request, + ///< should never be used for other purpose + ///< Get register value by internal index + ///< \param regval (uval_t *), out + ///< \param regnum (int) + ///< \retval 1 ok + ///< \retval 0 not implemented + ///< \retval -1 failed (undefined value or bad regnum) + + ev_analyze_prolog, ///< Analyzes function prolog, epilog, and updates + ///< purge, and function attributes + ///< \param ea (::ea_t) start of function + ///< \retval 1 ok + ///< \retval 0 not implemented + + ev_calc_spdelta, ///< Calculate amount of change to sp for the given insn. + ///< This event is required to decompile code snippets. + ///< \param spdelta (::sval_t *) + ///< \param insn (const ::insn_t *) + ///< \retval 1 ok + ///< \retval 0 not implemented + + ev_calcrel, ///< Reserved + ev_find_reg_value, ///< Find register value via a register tracker. + ///< The returned value in 'out' is valid + ///< before executing the instruction. + ///< \param out (uval_t *) pointer to the found value + ///< \param pinsn (const ::insn_t *) instruction + ///< \param reg (int) register index + ///< \retval 1 if implemented, and value was found + ///< \retval 0 not implemented, -1 decoding failed, or no value found + + ev_find_op_value, ///< Find operand value via a register tracker. + ///< The returned value in 'out' is valid + ///< before executing the instruction. + ///< \param out (uval_t *) pointer to the found value + ///< \param pinsn (const ::insn_t *) instruction + ///< \param opn (int) operand index + ///< \retval 1 if implemented, and value was found + ///< \retval 0 not implemented, -1 decoding failed, or no value found + + ev_replaying_undo, ///< Replaying an undo/redo buffer + ///< \param action_name (const char *) action that we perform undo/redo for. may be nullptr for intermediary buffers. + ///< \param vec (const undo_records_t *) + ///< \param is_undo (bool) true if performing undo, false if performing redo + ///< This event may be generated multiple times per undo/redo + ev_ending_undo, ///< Ended undoing/redoing an action + ///< \param action_name (const char *) action that we finished undoing/redoing. is not nullptr. + ///< \param is_undo (bool) true if performing undo, false if performing redo + + ev_set_code16_mode, ///< Some processors have ISA 16-bit mode + ///< e.g. ARM Thumb mode, PPC VLE, MIPS16 + ///< Set ISA 16-bit mode + ///< \param ea (ea_t) address to set new ISA mode + ///< \param code16 (bool) true for 16-bit mode, false for 32-bit mode + ev_get_code16_mode, ///< Get ISA 16-bit mode + ///< \param ea (ea_t) address to get the ISA mode + ///< \retval 1 16-bit mode + ///< \retval 0 not implemented or 32-bit mode + + ev_get_procmod, ///< Get pointer to the processor module object. + ///< All processor modules must implement this. + ///< The pointer is returned as size_t. + + ev_asm_installed, ///< After setting a new assembler + ///< \param asmnum (int) + ///< See also ev_newasm + + ev_get_reg_accesses, ///< Get info about the registers that are used/changed by an instruction. + ///< \param accvec (::reg_accesses_t*) out: info about accessed registers + ///< \param insn (const ::insn_t *) instruction in question + ///< \param flags (int) reserved, must be 0 + ///< \retval -1 if accvec is nullptr + ///< \retval 1 found the requested access (and filled accvec) + ///< \retval 0 not implemented + + ev_is_control_flow_guard, ///< Detect if an instruction is a "thunk call" to a flow guard function (equivalent to call reg/return/nop) + ///< \param p_reg (int *) indirect register number, may be -1 + ///< \param insn (const ::insn_t *) call/jump instruction + ///< \retval -1 no thunk detected + ///< \retval 1 indirect call + ///< \retval 2 security check routine call (NOP) + ///< \retval 3 return thunk + ///< \retval 0 not implemented + + ev_broadcast, ///< Broadcast call + ///< \param magic (::int64) a magic number + ///< Other parameters and the return value depend on the magic + + ev_create_merge_handlers, ///< Create merge handlers, if needed + ///< \param md (::merge_data_t *) + ///< This event is generated immediately after + ///< opening idbs. + ///< \return must be 0 + + ev_privrange_changed, ///< Privrange interval has been moved to + ///< a new location. Most common actions + ///< to be done by module in this case: + ///< fix indices of netnodes used by module + ///< \param old_privrange (const ::range_t *) - old privrange interval + ///< \param delta (::adiff_t) + ///< \param errbuf (::qstring *) - a error message will be returned here (can be nullptr) + ///< \retval 0 Ok + ///< \retval -1 error (and message in errbuf) + + + ev_cvt64_supval, ///< perform 32-64 conversion for a netnode array element + ///< \param node (::nodeidx_t) + ///< \param tag (::uchar) + ///< \param idx (::nodeidx_t) + ///< \param data (const ::uchar *) + ///< \param datlen (::size_t) + ///< \param errbuf (::qstring *) - a error message will be returned here (can be nullptr) + ///< \retval 0 nothing was done + ///< \retval 1 converted successfully + ///< \retval -1 error (and message in errbuf) + + ev_cvt64_hashval, ///< perform 32-64 conversion for a hash value + ///< \param node (::nodeidx_t) + ///< \param tag (::uchar) + ///< \param name (const ::char *) + ///< \param data (const ::uchar *) + ///< \param datlen (::size_t) + ///< \param errbuf (::qstring *) - a error message will be returned here (can be nullptr) + ///< \retval 0 nothing was done + ///< \retval 1 converted successfully + ///< \retval -1 error (and message in errbuf) + + ev_get_regfinder, ///< Get pointer to the regfinder object. + ///< This event is called by ::find_reg_value() when it detects that the + ///< processor module does not support the ev_find_reg_value event. + ///< the regfinder object must be initialized by the ev_init event + ///< and terminated by the ev_term event. + ///< \return (::reg_finder_t *) + ///< \retval 0 reg_finder_t is not supported + + ev_gen_stkvar_def, ///< Generate stack variable definition line + ///< Default line is + ///< varname = type ptr value, + ///< where 'type' is one of byte,word,dword,qword,tbyte + ///< \param outctx (::outctx_t *) + ///< \param stkvar (const ::udm_t *) + ///< \param v (sval_t) + ///< \param tid (::tid_t) stkvar TID + ///< \retval 1 ok + ///< \retval 0 not implemented + + ev_is_addr_insn, ///< Does the instruction calculate some address using an immediate operand? + ///< e.g. in PC such operand may be o_displ: 'lea eax, [esi+4]' + ///< \param[out] type (int *) pointer to the returned instruction type: + ///< - 0 the "add" instruction (the immediate + ///< operand is a relative value) + ///< - 1 the "move" instruction (the immediate + ///< operand is an absolute value) + ///< - 2 the "sub" instruction (the immediate + ///< operand is a relative value) + ///< \param insn (const ::insn_t *) instruction + ///< \return >0 the operand number+1 + ///< \retval 0 not implemented + + ev_last_cb_before_debugger, ///< START OF DEBUGGER CALLBACKS + + ev_next_exec_insn = 1000, ///< Get next address to be executed + ///< This function must return the next address to be executed. + ///< If the instruction following the current one is executed, then it must return #BADADDR + ///< Usually the instructions to consider are: jumps, branches, calls, returns. + ///< This function is essential if the 'single step' is not supported in hardware. + ///< \param target (::ea_t *), out: pointer to the answer + ///< \param ea (::ea_t) instruction address + ///< \param tid (int) current therad id + ///< \param getreg (::processor_t::regval_getter_t *) function to get register values + ///< \param regvalues (const ::regval_t *) register values array + ///< \retval 0 unimplemented + ///< \retval 1 implemented + + ev_calc_step_over, ///< Calculate the address of the instruction which will be + ///< executed after "step over". The kernel will put a breakpoint there. + ///< If the step over is equal to step into or we cannot calculate + ///< the address, return #BADADDR. + ///< \param target (::ea_t *) pointer to the answer + ///< \param ip (::ea_t) instruction address + ///< \retval 0 unimplemented + ///< \retval 1 implemented + + ev_calc_next_eas, ///< Calculate list of addresses the instruction in 'insn' + ///< may pass control to. + ///< This callback is required for source level debugging. + ///< \param res (::eavec_t *), out: array for the results. + ///< \param insn (const ::insn_t*) the instruction + ///< \param over (bool) calculate for step over (ignore call targets) + ///< \retval <0 incalculable (indirect jumps, for example) + ///< \retval >=0 number of addresses of called functions in the array. + ///< They must be put at the beginning of the array (0 if over=true) + + ev_get_macro_insn_head, ///< Calculate the start of a macro instruction. + ///< This notification is called if IP points to the middle of an instruction + ///< \param head (::ea_t *), out: answer, #BADADDR means normal instruction + ///< \param ip (::ea_t) instruction address + ///< \retval 0 unimplemented + ///< \retval 1 implemented + + ev_get_dbr_opnum, ///< Get the number of the operand to be displayed in the + ///< debugger reference view (text mode). + ///< \param opnum (int *) operand number (out, -1 means no such operand) + ///< \param insn (const ::insn_t*) the instruction + ///< \retval 0 unimplemented + ///< \retval 1 implemented + + ev_insn_reads_tbit, ///< Check if insn will read the TF bit. + ///< \param insn (const ::insn_t*) the instruction + ///< \param getreg (::processor_t::regval_getter_t *) function to get register values + ///< \param regvalues (const ::regval_t *) register values array + ///< \retval 2 yes, will generate 'step' exception + ///< \retval 1 yes, will store the TF bit in memory + ///< \retval 0 no + + ev_clean_tbit, ///< Clear the TF bit after an insn like pushf stored it in memory. + ///< \param ea (::ea_t) instruction address + ///< \param getreg (::processor_t::regval_getter_t *) function to get register values + ///< \param regvalues (const ::regval_t *) register values array + ///< \retval 1 ok + ///< \retval 0 failed + + ev_get_idd_opinfo, ///< Get operand information. + ///< This callback is used to calculate the operand + ///< value for double clicking on it, hints, etc. + ///< \param opinf (::idd_opinfo_t *) the output buffer + ///< \param ea (::ea_t) instruction address + ///< \param n (int) operand number + ///< \param thread_id (int) current thread id + ///< \param getreg (::processor_t::regval_getter_t *) function to get register values + ///< \param regvalues (const ::regval_t *) register values array + ///< \retval 1 ok + ///< \retval 0 failed + + ev_get_reg_info, ///< Get register information by its name. + ///< example: "ah" returns: + ///< - main_regname="eax" + ///< - bitrange_t = { offset==8, nbits==8 } + ///< + ///< This callback may be unimplemented if the register + ///< names are all present in \ph{reg_names} and they all have + ///< the same size + ///< \param main_regname (const char **), out + ///< \param bitrange (::bitrange_t *), out: position and size of the value within 'main_regname' (empty bitrange == whole register) + ///< \param regname (const char *) + ///< \retval 1 ok + ///< \retval -1 failed (not found) + ///< \retval 0 unimplemented + + ev_update_call_stack, ///< Calculate the call stack trace for the given thread. + ///< This callback is invoked when the process is suspended and should fill + ///< the 'trace' object with the information about the current call stack. + ///< Note that this callback is NOT invoked if the current debugger backend + ///< implements stack tracing via debugger_t::event_t::ev_update_call_stack. + ///< The debugger-specific algorithm takes priority. Implementing this callback + ///< in the processor module is useful when multiple debugging platforms follow + ///< similar patterns, and thus the same processor-specific algorithm can be + ///< used for different platforms. + ///< \param stack (::call_stack_t *) result + ///< \param tid (int) thread id + ///< \param getreg (::processor_t::regval_getter_t *) function to get register values + ///< \param regvalues (const ::regval_t *) register values array + ///< \retval 1 ok + ///< \retval -1 failed + ///< \retval 0 unimplemented + + // END OF DEBUGGER CALLBACKS + + // START OF TYPEINFO CALLBACKS TODO: get this into doxygen output + // The codes below will be called only if #PR_TYPEINFO is set. + // Please note that some codes are optional but the more codes + // are implemented, the better the analysis. + + ev_last_cb_before_type_callbacks, + + ev_setup_til = 2000, ///< Setup default type libraries. (called after loading + ///< a new file into the database). + ///< The processor module may load tils, setup memory + ///< model and perform other actions required to set up + ///< the type system. + ///< This is an optional callback. + ///< \param none + ///< \retval void + + ev_get_abi_info, ///< Get all possible ABI names and optional extensions for given compiler + ///< abiname/option is a string entirely consisting of letters, digits and underscore + ///< \param abi_names (qstrvec_t *) - all possible ABis each in form abiname-opt1-opt2-... + ///< \param abi_opts (qstrvec_t *) - array of all possible options in form "opt:description" or opt:hint-line#description + ///< \param comp (comp_t) - compiler ID + ///< \retval 0 not implemented + ///< \retval 1 ok + + ev_max_ptr_size, ///< Get maximal size of a pointer in bytes. + ///< \param none + ///< \return max possible size of a pointer + + ev_get_default_enum_size, ///< Get default enum size. Not generated anymore. + ///< inf_get_cc_size_e() is used instead + + ev_get_cc_regs, ///< Get register allocation convention for given calling convention + ///< \param regs (::callregs_t *), out + ///< \param cc (::callcnv_t) + ///< \retval 1 + ///< \retval 0 not implemented + ev_get_simd_types, ///< Get SIMD-related types according to given attributes ant/or argument location + ///< \param out (::simd_info_vec_t *) + ///< \param simd_attrs (const ::simd_info_t *), may be nullptr + ///< \param argloc (const ::argloc_t *), may be nullptr + ///< \param create_tifs (bool) return valid tinfo_t objects, create if neccessary + ///< \retval number of found types + ///< \retval -1 error + ///< If name==nullptr, initialize all SIMD types + + ev_calc_cdecl_purged_bytes, + ///< Calculate number of purged bytes after call. + ///< \param ea (::ea_t) address of the call instruction + ///< \return number of purged bytes (usually add sp, N) + + ev_calc_purged_bytes, ///< Calculate number of purged bytes by the given function type. + ///< \param[out] p_purged_bytes (int *) ptr to output + ///< \param fti (const ::func_type_data_t *) func type details + ///< \retval 1 + ///< \retval 0 not implemented + + ev_calc_retloc, ///< Calculate return value location. + ///< \param[out] retloc (::argloc_t *) + ///< \param rettype (const tinfo_t *) + ///< \param cc (::callcnv_t) + ///< \retval 0 not implemented + ///< \retval 1 ok, + ///< \retval -1 error + + ev_calc_arglocs, ///< Calculate function argument locations. + ///< This callback should fill retloc, all arglocs, and stkargs. + ///< This callback is never called for ::CM_CC_SPECIAL functions. + ///< \param fti (::func_type_data_t *) points to the func type info + ///< \retval 0 not implemented + ///< \retval 1 ok + ///< \retval -1 error + + ev_calc_varglocs, ///< Calculate locations of the arguments that correspond to '...'. + ///< \param ftd (::func_type_data_t *), inout: info about all arguments (including varargs) + ///< \param[out] aux_regs (::regobjs_t *) buffer for hidden register arguments, may be nullptr + ///< \param[out] aux_stkargs (::relobj_t *) buffer for hidden stack arguments, may be nullptr + ///< \param nfixed (int) number of fixed arguments + ///< \retval 0 not implemented + ///< \retval 1 ok + ///< \retval -1 error + ///< On some platforms variadic calls require + ///< passing additional information: for example, + ///< number of floating variadic arguments must + ///< be passed in rax on gcc-x64. The locations + ///< and values that constitute this additional + ///< information are returned in the buffers + ///< pointed by aux_regs and aux_stkargs + + ev_adjust_argloc, ///< Adjust argloc according to its type/size + ///< and platform endianess + ///< \param argloc (argloc_t *), inout + ///< \param type (const tinfo_t *), may be nullptr + ///< nullptr means primitive type of given size + ///< \param size (int) + ///< 'size' makes no sense if type != nullptr + ///< (type->get_size() should be used instead) + ///< \retval 0 not implemented + ///< \retval 1 ok + ///< \retval -1 error + + ev_lower_func_type, ///< Get function arguments which should be converted to pointers when lowering function prototype. + ///< The processor module can also modify 'fti' in order to make non-standard conversion + ///< of some arguments. + ///< \param argnums (intvec_t *), out - numbers of arguments to be converted to pointers in acsending order + ///< \param fti (::func_type_data_t *), inout func type details + ///< \retval 0 not implemented + ///< \retval 1 argnums was filled + ///< \retval 2 argnums was filled and made substantial changes to fti + ///< argnums[0] can contain a special negative value indicating that + ///< the return value should be passed as a hidden 'retstr' argument: + ///< -1 this argument is passed as the first one and + ///< the function returns a pointer to the argument, + ///< -2 this argument is passed as the last one and + ///< the function returns a pointer to the argument, + ///< -3 this argument is passed as the first one and + ///< the function returns 'void'. + + ev_equal_reglocs, ///< Are 2 register arglocs the same?. + ///< We need this callback for the pc module. + ///< \param a1 (::argloc_t *) + ///< \param a2 (::argloc_t *) + ///< \retval 1 yes + ///< \retval -1 no + ///< \retval 0 not implemented + + ev_use_stkarg_type, ///< Use information about a stack argument. + ///< \param ea (::ea_t) address of the push instruction which + ///< pushes the function argument into the stack + ///< \param arg (const ::funcarg_t *) argument info + ///< \retval 1 ok + ///< \retval <=0 failed, the kernel will create a comment with the + ///< argument name or type for the instruction + + ev_use_regarg_type, ///< Use information about register argument. + ///< \param[out] idx (int *) pointer to the returned value, may contain: + ///< - idx of the used argument, if the argument is defined + ///< in the current instruction, a comment will be applied by the kernel + ///< - idx | #REG_SPOIL - argument is spoiled by the instruction + ///< - -1 if the instruction doesn't change any registers + ///< - -2 if the instruction spoils all registers + ///< \param ea (::ea_t) address of the instruction + ///< \param rargs (const ::funcargvec_t *) vector of register arguments + ///< (including regs extracted from scattered arguments) + ///< \retval 1 + ///< \retval 0 not implemented + + ev_use_arg_types, ///< Use information about callee arguments. + ///< \param ea (::ea_t) address of the call instruction + ///< \param fti (::func_type_data_t *) info about function type + ///< \param rargs (::funcargvec_t *) array of register arguments + ///< \retval 1 (and removes handled arguments from fti and rargs) + ///< \retval 0 not implemented + + ev_arg_addrs_ready, ///< Argument address info is ready. + ///< \param caller (::ea_t) + ///< \param n (int) number of formal arguments + ///< \param tif (tinfo_t *) call prototype + ///< \param addrs (::ea_t *) argument intilization addresses + ///< \retval <0 do not save into idb; other values mean "ok to save" + + ev_decorate_name, ///< Decorate/undecorate a C symbol name. + ///< \param outbuf (::qstring *) output buffer + ///< \param name (const char *) name of symbol + ///< \param mangle (bool) true-mangle, false-unmangle + ///< \param cc (::callcnv_t) calling convention + ///< \param type (const ::tinfo_t *) name type (nullptr-unknown) + ///< \retval 1 if success + ///< \retval 0 not implemented or failed + + ev_arch_changed, ///< The loader is done parsing arch-related + ///< information, which the processor module + ///< might want to use to finish its + ///< initialization. + ///< \retval 1 if success + ///< \retval 0 not implemented or failed + + ev_get_stkarg_area_info, ///< Get some metrics of the stack argument area. + ///< \param[out] out (::stkarg_area_info_t *) ptr to stkarg_area_info_t + ///< \param cc (::callcnv_t) calling convention + ///< \retval 1 if success + ///< \retval 0 not implemented + + ev_last_cb_before_loader, + + // END OF TYPEINFO CALLBACKS + + ev_loader=3000, ///< This code and higher ones are reserved + ///< for the loaders. + ///< The arguments and the return values are + ///< defined by the loaders + }; + + /// Event notification handler + hook_cb_t *_notify; + static ssize_t notify(event_t event_code, ...) + { + va_list va; + va_start(va, event_code); + ssize_t code = invoke_callbacks(HT_IDP, event_code, va); + va_end(va); + return code; + } + + // Notification helpers, should be used instead of direct ph.notify(...) calls + inline static ssize_t init(const char *idp_modname); + inline static ssize_t term(); + inline static ssize_t newprc(int pnum, bool keep_cfg); + inline static ssize_t newasm(int asmnum); + inline static ssize_t asm_installed(int asmnum); + inline static ssize_t newfile(const char *fname); + inline static ssize_t oldfile(const char *fname); + inline static ssize_t newbinary(const char *filename, qoff64_t fileoff, ea_t basepara, ea_t binoff, uint64 nbytes); + inline static ssize_t endbinary(bool ok); + inline static ssize_t creating_segm(segment_t *seg); + inline static ssize_t assemble(uchar *_bin, ea_t ea, ea_t cs, ea_t ip, bool _use32, const char *line); + inline static ssize_t ana_insn(insn_t *out); + inline static ssize_t emu_insn(const insn_t &insn); + inline static ssize_t out_header(outctx_t &ctx); + inline static ssize_t out_footer(outctx_t &ctx); + inline static ssize_t out_segstart(outctx_t &ctx, segment_t *seg); + inline static ssize_t out_segend(outctx_t &ctx, segment_t *seg); + inline static ssize_t out_assumes(outctx_t &ctx); + inline static ssize_t out_insn(outctx_t &ctx); + inline static ssize_t out_mnem(outctx_t &ctx); + inline static ssize_t out_operand(outctx_t &ctx, const op_t &op); + inline static ssize_t out_data(outctx_t &ctx, bool analyze_only); + inline static ssize_t out_label(outctx_t &ctx, const char *colored_name); + inline static ssize_t out_special_item(outctx_t &ctx, uchar segtype); + inline static ssize_t gen_stkvar_def(outctx_t &ctx, const struct udm_t *mptr, sval_t v, tid_t tid); + inline static ssize_t gen_regvar_def(outctx_t &ctx, regvar_t *v); + inline static ssize_t gen_src_file_lnnum(outctx_t &ctx, const char *file, size_t lnnum); + inline static ssize_t rename(ea_t ea, const char *new_name, int flags); + inline static ssize_t may_show_sreg(ea_t current_ea); + inline static ssize_t coagulate(ea_t start_ea); + inline static void auto_queue_empty(/*atype_t*/ int type); + + inline static ssize_t func_bounds(int *possible_return_code, func_t *pfn, ea_t max_func_end_ea); + inline static ssize_t may_be_func(const insn_t &insn, int state); + inline static ssize_t is_sane_insn(const insn_t &insn, int no_crefs); + inline static ssize_t cmp_operands(const op_t &op1, const op_t &op2); + inline static ssize_t is_jump_func(func_t *pfn, ea_t *jump_target, ea_t *func_pointer); + inline static ssize_t is_basic_block_end(const insn_t &insn, bool call_insn_stops_block); + inline static ssize_t getreg(uval_t *rv, int regnum); + inline static ssize_t undefine(ea_t ea); + inline static ssize_t moving_segm(segment_t *seg, ea_t to, int flags); + inline static ssize_t is_sp_based(const insn_t &insn, const op_t &x); + inline static ssize_t is_far_jump(int icode); + inline static ssize_t is_call_insn(const insn_t &insn); + inline static ssize_t is_ret_insn(const insn_t &insn, uchar iri_flags); + inline static ssize_t is_align_insn(ea_t ea); + inline static ssize_t is_addr_insn(int *type, const insn_t &insn); + inline static ssize_t can_have_type(const op_t &op); + inline static ssize_t get_stkvar_scale_factor(); + inline static ssize_t demangle_name(int32 *res, qstring *out, const char *name, uint32 disable_mask, /*demreq_type_t*/ int demreq); + inline static ssize_t create_flat_group(ea_t image_base, int bitness, sel_t dataseg_sel); + inline static ssize_t is_alloca_probe(ea_t ea); + inline static ssize_t get_reg_name(qstring *buf, int reg, size_t width, int reghi); + inline static ssize_t gen_asm_or_lst(bool starting, FILE *fp, bool is_asm, int flags, /*html_line_cb_t ** */ void *outline); + inline static ssize_t gen_map_file(int *nlines, FILE *fp); + inline static ssize_t get_autocmt(qstring *buf, const insn_t &insn); + inline static ssize_t loader_elf_machine(linput_t *li, int machine_type, const char **p_procname, proc_def_t **p_pd, elf_loader_t *ldr, reader_t *reader); + inline static ssize_t is_indirect_jump(const insn_t &insn); + inline static ssize_t verify_noreturn(func_t *pfn); + inline static ssize_t verify_sp(func_t *pfn); + inline static ssize_t create_func_frame(func_t *pfn); + inline static ssize_t get_frame_retsize(int *retsize, const func_t *pfn); + inline static ssize_t analyze_prolog(ea_t fct_ea); + inline static ssize_t calc_spdelta(sval_t *spdelta, const insn_t &ins); + inline static ssize_t calcrel(bytevec_t *out_relbits, size_t *out_consumed, ea_t ea); + inline static ssize_t get_reg_accesses(reg_accesses_t *accvec, const insn_t &insn, int flags); + inline static ssize_t is_control_flow_guard(int *p_reg, const insn_t *insn); + inline static ssize_t find_reg_value(uval_t *out, const insn_t &insn, int reg); + inline static ssize_t find_op_value(uval_t *out, const insn_t &insn, int op); + inline static ssize_t treat_hindering_item(ea_t hindering_item_ea, flags64_t new_item_flags, ea_t new_item_ea, asize_t new_item_length); + inline static ssize_t extract_address(ea_t *out_ea, ea_t screen_ea, const char *string, size_t x); + inline static ssize_t str2reg(const char *regname); + inline static ssize_t is_switch(switch_info_t *si, const insn_t &insn); + inline static ssize_t create_switch_xrefs(ea_t jumpea, const switch_info_t &si); + inline static ssize_t calc_switch_cases(/*casevec_t * */void *casevec, eavec_t *targets, ea_t insn_ea, const switch_info_t &si); + inline static ssize_t get_bg_color(bgcolor_t *color, ea_t ea); + inline static ssize_t validate_flirt_func(ea_t start_ea, const char *funcname); + inline static ssize_t get_operand_string(qstring *buf, const insn_t &insn, int opnum); + inline static ssize_t add_cref(ea_t from, ea_t to, cref_t type); + inline static ssize_t add_dref(ea_t from, ea_t to, dref_t type); + inline static ssize_t del_cref(ea_t from, ea_t to, bool expand); + inline static ssize_t del_dref(ea_t from, ea_t to); + inline static ssize_t coagulate_dref(ea_t from, ea_t to, bool may_define, ea_t *code_ea); + inline static const char *set_idp_options(const char *keyword, int vtype, const void *value, bool idb_loaded = true); + inline static ssize_t set_proc_options(const char *options, int confidence); + inline static ssize_t adjust_libfunc_ea(const idasgn_t &sig, const libfunc_t &libfun, ea_t *ea); + inline static fpvalue_error_t realcvt(void *m, fpvalue_t *e, uint16 swt); + inline bool delay_slot_insn(ea_t *ea, bool *bexec, bool *fexec); + inline static ssize_t adjust_refinfo(refinfo_t *ri, ea_t ea, int n, const fixup_data_t &fd); + inline static ssize_t is_cond_insn(const insn_t &insn); + inline static ssize_t set_code16_mode(ea_t ea, bool code16 = true); + inline static bool get_code16_mode(ea_t ea); + inline static ssize_t next_exec_insn(ea_t *target, ea_t ea, int tid, regval_getter_t *_getreg, const regval_t ®values); + inline static ssize_t calc_step_over(ea_t *target, ea_t ip); + inline static ssize_t get_macro_insn_head(ea_t *head, ea_t ip); + inline static ssize_t get_dbr_opnum(int *opnum, const insn_t &insn); + inline static ssize_t insn_reads_tbit(const insn_t &insn, regval_getter_t *_getreg, const regval_t ®values); + inline static ssize_t get_idd_opinfo(idd_opinfo_t *opinf, ea_t ea, int n, int thread_id, regval_getter_t *_getreg, const regval_t ®values); + inline static ssize_t calc_next_eas(eavec_t *res, const insn_t &insn, bool over); + inline static ssize_t clean_tbit(ea_t ea, regval_getter_t *_getreg, const regval_t ®values); + inline static const char *get_reg_info(const char *regname, bitrange_t *bitrange); + inline static ssize_t update_call_stack(call_stack_t *stack, int tid, regval_getter_t *_getreg, const regval_t ®values); + inline static ssize_t setup_til(); + inline static ssize_t max_ptr_size(); + inline static ssize_t calc_cdecl_purged_bytes(ea_t ea); + inline static ssize_t equal_reglocs(const argloc_t &a1, const argloc_t &a2); + inline static ssize_t _decorate_name(qstring *outbuf, const char *name, bool mangle, callcnv_t cc, const tinfo_t &type); + inline static ssize_t _calc_retloc(argloc_t *retloc, const tinfo_t &rettype, callcnv_t cc); + inline static ssize_t _calc_varglocs(func_type_data_t *ftd, regobjs_t *regs, relobj_t *stkargs, int nfixed); + inline static ssize_t _calc_arglocs(func_type_data_t *fti); + inline static ssize_t use_stkarg_type(ea_t ea, const funcarg_t &arg); + inline static ssize_t use_regarg_type(int *idx, ea_t ea, /*const funcargvec_t * */void *rargs); + inline static ssize_t use_arg_types(ea_t ea, func_type_data_t *fti, /*funcargvec_t * */void *rargs); + inline static ssize_t calc_purged_bytes(int *p_purged_bytes, const func_type_data_t &fti); + inline static ssize_t get_cc_regs(callregs_t *regs, callcnv_t cc); + inline static ssize_t get_simd_types(/*simd_info_vec_t * */void *out, const simd_info_t *simd_attrs, const argloc_t *argloc, bool create_tifs); + inline static ssize_t arg_addrs_ready(ea_t caller, int n, const tinfo_t &tif, ea_t *addrs); + inline static ssize_t adjust_argloc(argloc_t *argloc, const tinfo_t *type, int size); + inline static ssize_t lower_func_type(intvec_t *argnums, func_type_data_t *fti); + inline static ssize_t get_abi_info(qstrvec_t *abi_names, qstrvec_t *abi_opts, comp_t comp); + inline static ssize_t arch_changed(); + inline static ssize_t create_merge_handlers(merge_data_t *md); + inline ssize_t privrange_changed(const range_t &old_privrange, adiff_t delta, qstring *errbuf=nullptr); + inline ssize_t cvt64_supval(nodeidx_t node, uchar tag, nodeidx_t idx, const uchar *data, size_t datlen, qstring *errbuf = nullptr); + inline ssize_t cvt64_hashval(nodeidx_t node, uchar tag, const char *name, const uchar *data, size_t datlen, qstring *errbuf = nullptr); + inline static reg_finder_t *get_regfinder(); + + /// Get the stack variable scaling factor. + /// Useful for processors who refer to the stack with implicit scaling factor. + /// TMS320C55 for example: SP(#1) really refers to (SP+2) + int get_stkvar_scale(void) + { + if ( (flag & PR_SCALE_STKVARS) == 0 ) + return 1; + int scale = notify(ev_get_stkvar_scale_factor); + if ( scale == 0 ) + error("Request ph.get_stkvar_scale_factor should be implemented"); + else if ( scale <= 0 ) + error("Invalid return code from ph.get_stkvar_scale_factor request"); + return scale; + } + + // Processor register information: + const char *const *reg_names; ///< array of register names + int32 regs_num; ///< number of registers + + /// \name Segment registers + /// Segment register information (use virtual CS and DS registers if your + /// processor doesn't have segment registers): + ///@{ + int32 reg_first_sreg; ///< number of first segment register + int32 reg_last_sreg; ///< number of last segment register + int32 segreg_size; ///< size of a segment register in bytes + ///@} + + /// \name Virtual segment registers + /// If your processor doesn't have segment registers, + /// you should define 2 virtual segment registers for CS and DS. + /// Let's call them rVcs and rVds. + ///@{ + int32 reg_code_sreg; ///< number of CS register + int32 reg_data_sreg; ///< number of DS register + ///@} + + + /// \name Empirics + ///@{ + const bytes_t *codestart; ///< Array of typical code start sequences. + ///< This array is used when a new file + ///< is loaded to find the beginnings of code + ///< sequences. + ///< This array is terminated with + ///< a zero length item. + const bytes_t *retcodes; ///< Array of 'return' instruction opcodes. + ///< This array is used to determine + ///< form of autogenerated locret_... + ///< labels. + ///< The last item of it should be { 0, nullptr } + ///< This array may be nullptr + ///< Better way of handling return instructions + ///< is to define the \idpcode{is_ret_insn} callback in + ///< the notify() function + ///@} + + /// \name Instruction set + ///@{ + int32 instruc_start; ///< icode of the first instruction + int32 instruc_end; ///< icode of the last instruction + 1 + + /// Does the given value specify a valid instruction for this instruction set?. + /// See #instruc_start and #instruc_end + bool is_canon_insn(uint16 itype) const { return itype >= instruc_start && itype < instruc_end; } + + const instruc_t *instruc; ///< Array of instructions + + /// Get the instruction name + const char *get_canon_mnem(uint16 itype) const + { + return is_canon_insn(itype) ? instruc[itype-instruc_start].name : nullptr; + } + + /// Get the instruction features (combination of \ref CF_) + uint32 get_canon_feature(uint16 itype) const + { + return is_canon_insn(itype) ? instruc[itype-instruc_start].feature : 0; + } + ///@} + + /// Size of long double (tbyte) for this processor + /// (meaningful only if \ash{a_tbyte} != nullptr) + size_t tbyte_size; + + /// Number of digits in floating numbers after the decimal point. + /// If an element of this array equals 0, then the corresponding + /// floating point data is not used for the processor. + /// This array is used to align numbers in the output. + /// - real_width[0] - number of digits for short floats (only PDP-11 has them) + /// - real_width[1] - number of digits for "float" + /// - real_width[2] - number of digits for "double" + /// - real_width[3] - number of digits for "long double" + /// + /// Example: IBM PC module has { 0,7,15,19 } + char real_width[4]; + + /// Icode of return instruction. It is ok to give any of possible return instructions + int32 icode_return; + + /// Reserved, currently equals to nullptr + void *unused_slot; + + inline void ensure_processor(void); + inline size_t sizeof_ldbl() const; + inline bool is_funcarg_off(const func_t *pfn, uval_t frameoff) const; + inline sval_t lvar_off(const func_t *pfn, uval_t frameoff) const; + inline bool is_lumina_usable() const; +}; +#ifndef __X86__ +CASSERT(sizeof(processor_t) == 144); +#else +CASSERT(sizeof(processor_t) == 104); +#endif + +// The following two structures contain information about the current +// processor and assembler. + +idaman processor_t *ida_export get_ph(); +#define PH (*get_ph()) +idaman asm_t *ida_export get_ash(); +#define ASH (*get_ash()) +idaman ea_helper_t *ida_export get_eah(); +#define EAH (*get_eah()) + +/// Hex-Rays decompiler dispatcher. +/// All interaction with the decompiler is carried out by the intermediary of this dispatcher. +typedef void *hexdsp_t(int code, ...); +idaman hexdsp_t *ida_export get_hexdsp(); +#ifndef HEXDSP + #define HEXDSP get_hexdsp() +#endif + + +#ifndef SWIG +// ignore_micro manager: can be used in modules/plugins as a base class +struct ignore_micro_t +{ + //-------------------------------------------------------------------------- + /// \name Ignore micro + /// netnode to keep information about various kinds of instructions + ///@{ + netnode ignore_micro; + +#define IM_NONE 0 // regular instruction +#define IM_PROLOG 1 // prolog instruction +#define IM_EPILOG 2 // epilog instruction +#define IM_SWITCH 3 // switch instruction (the indirect jump should not be marked) + + inline void init_ignore_micro(void) { ignore_micro.create("$ ignore micro"); } + inline void term_ignore_micro(void) { ignore_micro = BADNODE; } + inline char get_ignore_micro(ea_t ea) const { return ignore_micro.charval_ea(ea, 0); } + inline void set_ignore_micro(ea_t ea, uchar im_type) { ignore_micro.charset_ea(ea, im_type, 0); } + inline void clr_ignore_micro(ea_t ea) { ignore_micro.chardel_ea(ea, 0); } + inline ea_t next_marked_insn(ea_t ea) { return node2ea(ignore_micro.charnext(ea2node(ea), 0)); } + inline void mark_prolog_insn(ea_t ea) { set_ignore_micro(ea, IM_PROLOG); } + inline void mark_epilog_insn(ea_t ea) { set_ignore_micro(ea, IM_EPILOG); } + inline void mark_switch_insn(ea_t ea) { set_ignore_micro(ea, IM_SWITCH); } + inline bool is_prolog_insn(ea_t ea) const { return get_ignore_micro(ea) == IM_PROLOG; } + inline bool is_epilog_insn(ea_t ea) const { return get_ignore_micro(ea) == IM_EPILOG; } + inline bool is_switch_insn(ea_t ea) const { return get_ignore_micro(ea) == IM_SWITCH; } + inline bool should_ignore_micro(ea_t ea) const { return get_ignore_micro(ea) != IM_NONE; } + ///@} +}; +#endif // SWIG + +struct modctx_t +{ + processor_t &ph; + asm_t &ash; + ea_helper_t &_eah; + size_t reserved[8] = { 0 }; + modctx_t() : ph(PH), ash(ASH), _eah(EAH) {} +#ifndef SWIG + DEFINE_EA_HELPER_FUNCS(_eah) +#endif + // this class is freed by IDA kernel on unload so must be allocated by it + DEFINE_MEMORY_ALLOCATION_FUNCS() +}; + +// Each processor module subclasses this class and reacts to HT_IDP events +struct procmod_t : public modctx_t, public event_listener_t, public ignore_micro_t +{ + size_t procmod_flags = 0; + + procmod_t() {} + procmod_t(const procmod_t &) = delete; + + /// The helper methods for \ref reg_finder_t + const op_t *make_op_reg(op_t *op, int reg, int8 dtype = -1) const + { + op->type = o_reg; + op->reg = reg; + op->dtype = calc_op_dtype(dtype); + return op; + } + const op_t *make_op_imm(op_t *op, uval_t val, int8 dtype = -1) const + { + op->type = o_imm; + op->value = val; + op->dtype = calc_op_dtype(dtype); + return op; + } + const op_t *make_op_displ( + op_t *op, + int base_reg, + uval_t displ, + int8 dtype = -1) const + { + op->type = o_displ; + op->phrase = base_reg; + op->addr = displ; + op->dtype = calc_op_dtype(dtype); + return op; + } + /// This method puts the index register into the VALUE member + const op_t *make_op_phrase( + op_t *op, + int base_reg, + int index_reg, + int8 dtype = -1) const + { + op->type = o_phrase; + op->phrase = base_reg; + op->value = index_reg; + op->dtype = calc_op_dtype(dtype); + return op; + } + +protected: + op_dtype_t calc_op_dtype(sint8 dtype) const + { + return dtype != -1 ? op_dtype_t(dtype) + : eah().ea_size == 4 ? dt_dword + : dt_qword; + } +}; + + +struct plugmod_t : public modctx_t +{ + size_t owner = 0; // internal info used by the kernel + + /// Invoke the plugin. + virtual bool idaapi run(size_t arg) = 0; + + /// Helper function to hook event listeners. + bool hook_event_listener( + hook_type_t hook_type, + event_listener_t *cb, + int hkcb_flags=0) + { + return ::hook_event_listener(hook_type, cb, this, hkcb_flags); + } + + /// Virtual destructor. + virtual ~plugmod_t() {} +}; + +inline ssize_t processor_t::init(const char *idp_modname) +{ + return notify(ev_init, idp_modname); +} +inline ssize_t processor_t::term() +{ + return notify(ev_term); +} +inline ssize_t processor_t::newprc(int pnum, bool keep_cfg) +{ + return notify(ev_newprc, pnum, keep_cfg); +} +inline ssize_t processor_t::newasm(int asmnum) +{ + return notify(ev_newasm, asmnum); +} +inline ssize_t processor_t::asm_installed(int asmnum) +{ + return notify(ev_asm_installed, asmnum); +} +inline ssize_t processor_t::get_reg_accesses(reg_accesses_t *accvec, const insn_t &insn, int flags) +{ + return notify(ev_get_reg_accesses, accvec, &insn, flags); +} +inline ssize_t processor_t::is_control_flow_guard(int *p_reg, const insn_t *insn) +{ + return processor_t::notify(ev_is_control_flow_guard, p_reg, insn); +} +inline ssize_t processor_t::newfile(const char *fname) +{ + return notify(ev_newfile, fname); +} +inline ssize_t processor_t::oldfile(const char *fname) +{ + return notify(ev_oldfile, fname); +} +inline ssize_t processor_t::newbinary(const char *filename, qoff64_t fileoff, ea_t basepara, ea_t binoff, uint64 nbytes) +{ + return notify(ev_newbinary, filename, fileoff, basepara, binoff, nbytes); +} +inline ssize_t processor_t::endbinary(bool ok) +{ + return notify(ev_endbinary, ok); +} +inline ssize_t processor_t::creating_segm(segment_t *seg) +{ + return notify(ev_creating_segm, seg); +} +inline ssize_t processor_t::assemble(uchar *_bin, ea_t ea, ea_t cs, ea_t ip, bool _use32, const char *line) +{ + return notify(ev_assemble, _bin, ea, cs, ip, _use32, line, _bin); +} +inline ssize_t processor_t::ana_insn(insn_t *out) +{ + return notify(ev_ana_insn, out); +} +inline ssize_t processor_t::emu_insn(const insn_t &insn) +{ + return notify(ev_emu_insn, &insn); +} +inline ssize_t processor_t::out_header(outctx_t &ctx) +{ + return notify(ev_out_header, &ctx); +} +inline ssize_t processor_t::out_footer(outctx_t &ctx) +{ + return notify(ev_out_footer, &ctx); +} +inline ssize_t processor_t::out_segstart(outctx_t &ctx, segment_t *seg) +{ + return notify(ev_out_segstart, &ctx, seg); +} +inline ssize_t processor_t::out_segend(outctx_t &ctx, segment_t *seg) +{ + return notify(ev_out_segend, &ctx, seg); +} +inline ssize_t processor_t::out_assumes(outctx_t &ctx) +{ + return notify(ev_out_assumes, &ctx); +} +inline ssize_t processor_t::out_insn(outctx_t &ctx) +{ + return notify(ev_out_insn, &ctx); +} +inline ssize_t processor_t::out_mnem(outctx_t &ctx) +{ + return notify(ev_out_mnem, &ctx); +} +inline ssize_t processor_t::out_operand(outctx_t &ctx, const op_t &op) +{ + return notify(ev_out_operand, &ctx, &op); +} +inline ssize_t processor_t::out_data(outctx_t &ctx, bool analyze_only) +{ + return notify(ev_out_data, &ctx, analyze_only); +} +inline ssize_t processor_t::out_label(outctx_t &ctx, const char *colored_name) +{ + return notify(ev_out_label, &ctx, colored_name); +} +inline ssize_t processor_t::out_special_item(outctx_t &ctx, uchar segtype) +{ + return notify(ev_out_special_item, &ctx, segtype); +} +inline ssize_t processor_t::gen_stkvar_def(outctx_t &ctx, const struct udm_t *stkvar, sval_t v, tid_t tid) +{ + return notify(ev_gen_stkvar_def, &ctx, stkvar, v, tid); +} +inline ssize_t processor_t::gen_regvar_def(outctx_t &ctx, regvar_t *v) +{ + return notify(ev_gen_regvar_def, &ctx, v); +} +inline ssize_t processor_t::gen_src_file_lnnum(outctx_t &ctx, const char *file, size_t lnnum) +{ + return notify(ev_gen_src_file_lnnum, &ctx, file, lnnum); +} +inline ssize_t processor_t::rename(ea_t ea, const char *new_name, int flags) +{ + return notify(ev_rename, ea, new_name, flags); +} +inline ssize_t processor_t::may_show_sreg(ea_t current_ea) +{ + return notify(ev_may_show_sreg, current_ea); +} +inline ssize_t processor_t::coagulate(ea_t start_ea) +{ + return notify(ev_coagulate, start_ea); +} +inline void processor_t::auto_queue_empty(/*atype_t*/ int type) +{ + notify(ev_auto_queue_empty, type); +} +inline ssize_t processor_t::func_bounds(int *possible_return_code, func_t *pfn, ea_t max_func_end_ea) +{ + return notify(ev_func_bounds, possible_return_code, pfn, max_func_end_ea); +} +inline ssize_t processor_t::may_be_func(const insn_t &insn, int state) +{ + return notify(ev_may_be_func, &insn, state); +} +inline ssize_t processor_t::is_sane_insn(const insn_t &insn, int no_crefs) +{ + return notify(ev_is_sane_insn, &insn, no_crefs); +} +inline ssize_t processor_t::cmp_operands(const op_t &op1, const op_t &op2) +{ + return notify(ev_cmp_operands, &op1, &op2); +} +inline ssize_t processor_t::is_jump_func(func_t *pfn, ea_t *jump_target, ea_t *func_pointer) +{ + return notify(ev_is_jump_func, pfn, jump_target, func_pointer); +} +inline ssize_t processor_t::is_basic_block_end(const insn_t &insn, bool call_insn_stops_block) +{ + return notify(ev_is_basic_block_end, &insn, call_insn_stops_block); +} +inline ssize_t processor_t::getreg(uval_t *rv, int regnum) +{ + return notify(ev_getreg, rv, regnum); +} +inline ssize_t processor_t::undefine(ea_t ea) +{ + return notify(ev_undefine, ea); +} +inline ssize_t processor_t::moving_segm(segment_t *seg, ea_t to, int flags) +{ + return notify(ev_moving_segm, seg, to, flags); +} +inline ssize_t processor_t::is_sp_based(const insn_t &insn, const op_t &x) +{ + int mode; + int code = notify(ev_is_sp_based, &mode, &insn, &x); + return code == 0 ? OP_SP_BASED : mode; +} +inline ssize_t processor_t::is_far_jump(int icode) +{ + return notify(ev_is_far_jump, icode); +} +inline ssize_t processor_t::is_call_insn(const insn_t &insn) +{ + return notify(ev_is_call_insn, &insn); +} +inline ssize_t processor_t::is_ret_insn(const insn_t &insn, uchar iri_flags) +{ + return notify(ev_is_ret_insn, &insn, iri_flags); +} +inline ssize_t processor_t::is_align_insn(ea_t ea) +{ + return notify(ev_is_align_insn, ea); +} +inline ssize_t processor_t::is_addr_insn(int *type, const insn_t &insn) +{ + return notify(ev_is_addr_insn, type, &insn); +} +inline ssize_t processor_t::can_have_type(const op_t &op) +{ + ssize_t code = notify(ev_can_have_type, &op); + return code != 0 ? code + : op.type == o_void || op.type == o_reg ? -1 + : 1; +} +inline ssize_t processor_t::get_stkvar_scale_factor() +{ + return notify(ev_get_stkvar_scale_factor); +} +inline ssize_t processor_t::demangle_name(int32 *res, qstring *out, const char *name, uint32 disable_mask, /*demreq_type_t*/ int demreq) +{ + return notify(ev_demangle_name, res, out, name, disable_mask, demreq); +} +inline ssize_t processor_t::create_flat_group(ea_t image_base, int bitness, sel_t dataseg_sel) +{ + return notify(ev_create_flat_group, image_base, bitness, dataseg_sel); +} +inline ssize_t processor_t::is_alloca_probe(ea_t ea) +{ + return notify(ev_is_alloca_probe, ea); +} +inline ssize_t processor_t::get_reg_name(qstring *buf, int reg, size_t width, int reghi) +{ + return notify(ev_get_reg_name, buf, reg, width, reghi); +} +inline ssize_t processor_t::gen_asm_or_lst(bool starting, FILE *fp, bool is_asm, int flags, /*html_line_cb_t ** */ void *outline) +{ + return notify(ev_gen_asm_or_lst, starting, fp, is_asm, flags, outline); +} +inline ssize_t processor_t::gen_map_file(int *nlines, FILE *fp) +{ + return notify(ev_gen_map_file, nlines, fp); +} +inline ssize_t processor_t::get_autocmt(qstring *buf, const insn_t &insn) +{ + return notify(ev_get_autocmt, buf, &insn); +} +inline ssize_t processor_t::loader_elf_machine(linput_t *li, int machine_type, const char **p_procname, proc_def_t **p_pd, elf_loader_t *ldr, reader_t *reader) +{ + return notify(ev_loader_elf_machine, li, machine_type, p_procname, p_pd, ldr, reader); +} +inline ssize_t processor_t::is_indirect_jump(const insn_t &insn) +{ + return notify(ev_is_indirect_jump, &insn); +} +inline ssize_t processor_t::verify_noreturn(func_t *pfn) +{ + return notify(ev_verify_noreturn, pfn); +} +inline ssize_t processor_t::verify_sp(func_t *pfn) +{ + return notify(ev_verify_sp, pfn); +} +inline ssize_t processor_t::create_func_frame(func_t *pfn) +{ + return notify(ev_create_func_frame, pfn); +} +inline ssize_t processor_t::get_frame_retsize(int *retsize, const func_t *pfn) +{ + return notify(ev_get_frame_retsize, retsize, pfn); +} +inline ssize_t processor_t::analyze_prolog(ea_t fct_ea) +{ + return notify(ev_analyze_prolog, fct_ea); +} +inline ssize_t processor_t::calc_spdelta(sval_t *spdelta, const insn_t &insn) +{ + return notify(ev_calc_spdelta, spdelta, &insn); +} +inline ssize_t processor_t::calcrel(bytevec_t *out_relbits, size_t *out_consumed, ea_t ea) +{ + return notify(ev_calcrel, out_relbits, out_consumed, ea); +} +inline ssize_t processor_t::find_reg_value(uval_t *out, const insn_t &insn, int reg) +{ + return notify(ev_find_reg_value, out, &insn, reg); +} +inline ssize_t processor_t::find_op_value(uval_t *out, const insn_t &insn, int opn) +{ + return notify(ev_find_op_value, out, &insn, opn); +} +inline ssize_t processor_t::treat_hindering_item(ea_t hindering_item_ea, flags64_t new_item_flags, ea_t new_item_ea, asize_t new_item_length) +{ + return notify(ev_treat_hindering_item, hindering_item_ea, new_item_flags, new_item_ea, new_item_length); +} +inline ssize_t processor_t::extract_address(ea_t *out_ea, ea_t screen_ea, const char *string, size_t x) +{ + return notify(ev_extract_address, out_ea, screen_ea, string, x); +} +inline ssize_t processor_t::str2reg(const char *regname) +{ + return notify(ev_str2reg, regname); +} +inline ssize_t processor_t::is_switch(switch_info_t *si, const insn_t &insn) +{ + return notify(ev_is_switch, si, &insn); +} +inline ssize_t processor_t::create_switch_xrefs(ea_t jumpea, const switch_info_t &si) +{ + return notify(ev_create_switch_xrefs, jumpea, &si); +} +inline ssize_t processor_t::calc_switch_cases(/*casevec_t * */void *casevec, eavec_t *targets, ea_t insn_ea, const switch_info_t &si) +{ + return notify(ev_calc_switch_cases, casevec, targets, insn_ea, &si); +} +inline ssize_t processor_t::get_bg_color(bgcolor_t *color, ea_t ea) +{ + return notify(ev_get_bg_color, color, ea); +} +inline ssize_t processor_t::validate_flirt_func(ea_t start_ea, const char *funcname) +{ + return notify(ev_validate_flirt_func, start_ea, funcname); +} +inline ssize_t processor_t::get_operand_string(qstring *buf, const insn_t &insn, int opnum) +{ + return notify(ev_get_operand_string, buf, &insn, opnum); +} +inline ssize_t processor_t::add_cref(ea_t from, ea_t to, cref_t type) +{ + return notify(ev_add_cref, from, to, type); +} +inline ssize_t processor_t::add_dref(ea_t from, ea_t to, dref_t type) +{ + return notify(ev_add_dref, from, to, type); +} +inline ssize_t processor_t::del_cref(ea_t from, ea_t to, bool expand) +{ + return notify(ev_del_cref, from, to, expand); +} +inline ssize_t processor_t::del_dref(ea_t from, ea_t to) +{ + return notify(ev_del_dref, from, to); +} +inline ssize_t processor_t::coagulate_dref(ea_t from, ea_t to, bool may_define, ea_t *code_ea) +{ + return notify(ev_coagulate_dref, from, to, may_define, code_ea); +} +inline const char *processor_t::set_idp_options(const char *keyword, int vtype, const void *value, bool idb_loaded) +{ + const char *errmsg = IDPOPT_BADKEY; + int code = notify(ev_set_idp_options, keyword, vtype, value, &errmsg, idb_loaded); + return code == 1 ? IDPOPT_OK : code == 0 ? IDPOPT_BADKEY : errmsg; +} +inline ssize_t processor_t::set_proc_options(const char *options, int confidence) +{ + return notify(ev_set_proc_options, options, confidence); +} +inline ssize_t processor_t::adjust_libfunc_ea(const idasgn_t &sig, const libfunc_t &libfun, ea_t *ea) +{ + return notify(ev_adjust_libfunc_ea, &sig, &libfun, ea); +} +inline fpvalue_error_t processor_t::realcvt(void *m, fpvalue_t *e, uint16 swt) +{ + return (fpvalue_error_t)notify(ev_realcvt, m, e, swt); +} +inline ssize_t processor_t::adjust_refinfo(refinfo_t *ri, ea_t ea, int n, const fixup_data_t &fd) +{ + return notify(ev_adjust_refinfo, ri, ea, n, &fd); +} +inline ssize_t processor_t::is_cond_insn(const insn_t &insn) +{ + return notify(ev_is_cond_insn, &insn); +} +inline ssize_t processor_t::set_code16_mode(ea_t ea, bool code16) +{ + return notify(ev_set_code16_mode, ea, code16); +} +inline bool processor_t::get_code16_mode(ea_t ea) +{ + return notify(ev_get_code16_mode, ea) == 1; +} +inline ssize_t processor_t::next_exec_insn(ea_t *target, ea_t ea, int tid, regval_getter_t *_getreg, const regval_t ®values) +{ + return notify(ev_next_exec_insn, target, ea, tid, _getreg, ®values); +} +inline ssize_t processor_t::calc_step_over(ea_t *target, ea_t ip) +{ + return notify(ev_calc_step_over, target, ip); +} +inline ssize_t processor_t::get_macro_insn_head(ea_t *head, ea_t ip) +{ + return notify(ev_get_macro_insn_head, head, ip); +} +inline ssize_t processor_t::get_dbr_opnum(int *opnum, const insn_t &insn) +{ + return notify(ev_get_dbr_opnum, opnum, &insn); +} +inline ssize_t processor_t::insn_reads_tbit(const insn_t &insn, regval_getter_t *_getreg, const regval_t ®values) +{ + return notify(ev_insn_reads_tbit, &insn, _getreg, ®values); +} +inline ssize_t processor_t::get_idd_opinfo(idd_opinfo_t *opinf, ea_t ea, int n, int thread_id, regval_getter_t *_getreg, const regval_t ®values) +{ + return notify(ev_get_idd_opinfo, opinf, ea, n, thread_id, _getreg, ®values); +} +inline ssize_t processor_t::update_call_stack(call_stack_t *stack, int thread_id, regval_getter_t *_getreg, const regval_t ®values) +{ + return notify(ev_update_call_stack, stack, thread_id, _getreg, ®values); +} +inline ssize_t processor_t::calc_next_eas(eavec_t *res, const insn_t &insn, bool over) +{ + return notify(ev_calc_next_eas, res, &insn, over); +} +inline ssize_t processor_t::clean_tbit(ea_t ea, regval_getter_t *_getreg, const regval_t ®values) +{ + return notify(ev_clean_tbit, ea, _getreg, ®values); +} +inline ssize_t processor_t::setup_til() +{ + return notify(ev_setup_til); +} +inline ssize_t processor_t::max_ptr_size() +{ + ssize_t code = notify(ev_max_ptr_size); + if ( code == 0 ) + code = 4; + return code; +} +inline ssize_t processor_t::calc_cdecl_purged_bytes(ea_t ea) +{ + return notify(ev_calc_cdecl_purged_bytes, ea); +} +// Use global calc_retloc() instead of this: +inline ssize_t processor_t::_calc_retloc(argloc_t *retloc, const tinfo_t &rettype, callcnv_t cc) +{ + return notify(ev_calc_retloc, retloc, &rettype, cc); +} +// Use global calc_varglocs() instead of this: +inline ssize_t processor_t::_calc_varglocs(func_type_data_t *ftd, regobjs_t *regs, relobj_t *stkargs, int nfixed) +{ + return notify(ev_calc_varglocs, ftd, regs, stkargs, nfixed); +} +// Use global calc_arglocs() instead of this: +inline ssize_t processor_t::_calc_arglocs(func_type_data_t *fti) +{ + return notify(ev_calc_arglocs, fti); +} +inline ssize_t processor_t::use_stkarg_type(ea_t ea, const funcarg_t &arg) +{ + return notify(ev_use_stkarg_type, ea, &arg); +} +inline ssize_t processor_t::use_regarg_type(int *idx, ea_t ea, /*const funcargvec_t * */void *rargs) +{ + return notify(ev_use_regarg_type, idx, ea, rargs); +} +inline ssize_t processor_t::use_arg_types(ea_t ea, func_type_data_t *fti, /*funcargvec_t * */void *rargs) +{ + return notify(ev_use_arg_types, ea, fti, rargs); +} +inline ssize_t processor_t::calc_purged_bytes(int *p_purged_bytes, const func_type_data_t &fti) +{ + return notify(ev_calc_purged_bytes, p_purged_bytes, &fti); +} +inline ssize_t processor_t::get_cc_regs(callregs_t *regs, callcnv_t cc) +{ + return notify(ev_get_cc_regs, regs, cc); +} +inline ssize_t processor_t::get_simd_types(/*simd_info_vec_t * */void *out, const simd_info_t *simd_attrs, const argloc_t *argloc, bool create_tifs) +{ + return notify(ev_get_simd_types, out, simd_attrs, argloc, create_tifs); +} +inline ssize_t processor_t::arg_addrs_ready(ea_t caller, int n, const tinfo_t &tif, ea_t *addrs) +{ + return notify(ev_arg_addrs_ready, caller, n, &tif, addrs); +} +inline ssize_t processor_t::adjust_argloc(argloc_t *argloc, const tinfo_t *type, int size) +{ + return notify(ev_adjust_argloc, argloc, type, size); +} +inline ssize_t processor_t::lower_func_type(intvec_t *argnums, func_type_data_t *fti) +{ + return notify(ev_lower_func_type, argnums, fti); +} +inline ssize_t processor_t::get_abi_info(qstrvec_t *abi_names, qstrvec_t *abi_opts, comp_t comp) +{ + return notify(ev_get_abi_info, abi_names, abi_opts, comp); +} +inline ssize_t processor_t::arch_changed() +{ + return notify(ev_arch_changed); +} +inline ssize_t processor_t::create_merge_handlers(merge_data_t *md) +{ + return notify(ev_create_merge_handlers, md); +} +inline ssize_t processor_t::privrange_changed(const range_t &old_privrange, adiff_t delta, qstring *errbuf) +{ + return notify(ev_privrange_changed, &old_privrange, delta, errbuf); +} +inline ssize_t processor_t::cvt64_supval(nodeidx_t node, uchar tag, nodeidx_t idx, const uchar *data, size_t datlen, qstring *errbuf) +{ + return notify(ev_cvt64_supval, node, tag, idx, data, datlen, errbuf); +} +inline ssize_t processor_t::cvt64_hashval(nodeidx_t node, uchar tag, const char *name, const uchar *data, size_t datlen, qstring *errbuf) +{ + return notify(ev_cvt64_hashval, node, tag, name, data, datlen, errbuf); +} +inline reg_finder_t *processor_t::get_regfinder() +{ + return (reg_finder_t *)notify(ev_get_regfinder); +} + +idaman int ida_export str2reg(const char *p); ///< Get any register number (-1 on error) + + +/// If the instruction at 'ea' looks like an alignment instruction, +/// return its length in bytes. Otherwise return 0. + +idaman int ida_export is_align_insn(ea_t ea); + + +/// Get text representation of a register. +/// For most processors this function will just return \ph{reg_names}[reg]. +/// If the processor module has implemented processor_t::get_reg_name, it will be +/// used instead +/// \param buf output buffer +/// \param reg internal register number as defined in the processor module +/// \param width register width in bytes +/// \param reghi if specified, then this function will return the register pair +/// \return length of register name in bytes or -1 if failure + +idaman ssize_t ida_export get_reg_name(qstring *buf, int reg, size_t width, int reghi = -1); + + +/// Get register information - useful for registers like al, ah, dil, etc. +/// Example: this function for "al" returns "eax" in 32bit mode +/// \return main register name (nullptr no such register) + +inline const char *processor_t::get_reg_info(const char *regname, bitrange_t *bitrange) +{ + const char *r2; + int code = notify(ev_get_reg_info, &r2, bitrange, regname); + if ( code == 0 ) // not implemented? + { + if ( ::str2reg(regname) != -1 ) + { + if ( bitrange != nullptr ) + bitrange->reset(); + return regname; + } + return nullptr; + } + return code == 1 ? r2 : nullptr; +} + +/// Get register number and size from register name +struct reg_info_t +{ + int reg; ///< register number + int size; ///< register size + DECLARE_COMPARISONS(reg_info_t) + { + if ( reg != r.reg ) + return reg > r.reg ? 1 : -1; + if ( size != r.size ) + return size > r.size ? 1 : -1; + return 0; + } +}; +DECLARE_TYPE_AS_MOVABLE(reg_info_t); +typedef qvector reginfovec_t; ///< vector of register info objects + + +/// Get register info by name. +/// \param[out] ri result +/// \param regname name of register +/// \return success + +idaman bool ida_export parse_reg_name(reg_info_t *ri, const char *regname); + + +/// Possible memory and register access types. +enum access_type_t ENUM_SIZE(uchar) +{ + NO_ACCESS = 0, + WRITE_ACCESS = 1, + READ_ACCESS = 2, + RW_ACCESS = WRITE_ACCESS | READ_ACCESS, +}; + +/// Information about a register accessed by an instruction. +struct reg_access_t +{ + int regnum = 0; ///< register number (only entire registers) + bitrange_t range; ///< bitrange inside the register + access_type_t access_type = NO_ACCESS; + uchar opnum = 0; ///< operand number + + bool have_common_bits(const reg_access_t &r) const + { + return regnum == r.regnum && range.has_common(r.range); + } + + bool operator==(const reg_access_t &r) const + { + return regnum == r.regnum + && range == r.range + && access_type == r.access_type + && opnum == r.opnum; + } + + bool operator!=(const reg_access_t &r) const + { + return !(*this == r); + } +}; +DECLARE_TYPE_AS_MOVABLE(reg_access_t); +typedef qvector reg_access_vec_t; + +struct reg_accesses_t : public reg_access_vec_t {}; + + +inline bool insn_t::is_canon_insn(const processor_t &_ph) const // see ::insn_t in ua.hpp +{ + return _ph.is_canon_insn(itype); +} + +inline const char *insn_t::get_canon_mnem(const processor_t &_ph) const // see ::insn_t in ua.hpp +{ + return _ph.get_canon_mnem(itype); +} + +inline uint32 insn_t::get_canon_feature(const processor_t &_ph) const // ::insn_t in ua.hpp +{ + return _ph.get_canon_feature(itype); +} + + +/// Get size of long double +inline size_t processor_t::sizeof_ldbl() const +{ + return inf_get_cc_size_ldbl() ? inf_get_cc_size_ldbl() : tbyte_size; +} + +/// Flags passed as 'level' parameter to set_processor_type() +enum setproc_level_t +{ + SETPROC_IDB = 0, ///< set processor type for old idb + SETPROC_LOADER = 1, ///< set processor type for new idb; + ///< if the user has specified a compatible processor, + ///< return success without changing it. + ///< if failure, call loader_failure() + SETPROC_LOADER_NON_FATAL = 2, ///< the same as SETPROC_LOADER but non-fatal failures. + SETPROC_USER = 3, ///< set user-specified processor + ///< used for -p and manual processor change at later time +}; + +/// Set target processor type. +/// Once a processor module is loaded, it cannot be replaced until we close the idb. +/// \param procname name of processor type (one of names present in \ph{psnames}) +/// \param level \ref SETPROC_ +/// \return success + +idaman bool ida_export set_processor_type( + const char *procname, + setproc_level_t level); + + +/// Get name of the current processor module. +/// The name is derived from the file name. +/// For example, for IBM PC the module is named "pc.w32" (windows version), +/// then the module name is "PC" (uppercase). +/// If no processor module is loaded, this function will return nullptr +/// \param buf the output buffer, should be at least #QMAXFILE length +/// \param bufsize size of output buffer + +idaman char *ida_export get_idp_name(char *buf, size_t bufsize); + + +/// Set target assembler. +/// \param asmnum number of assembler in the current processor module +/// \return success + +idaman bool ida_export set_target_assembler(int asmnum); + + +/// Helper function to get the delay slot instruction +inline bool processor_t::delay_slot_insn(ea_t *ea, bool *bexec, bool *fexec) +{ + bool ok = (flag & PR_DELAYED) != 0; + if ( ok ) + { + bool be = true; + bool fe = true; + ok = notify(ev_delay_slot_insn, ea, &be, &fe) == 1; + if ( ok ) + { + if ( bexec != nullptr ) + *bexec = be; + if ( fexec != nullptr ) + *fexec = fe; + } + } + return ok; +} + +enum local_type_change_t +{ + LTC_NONE, ///< no event (internal use) + LTC_ADDED, ///< added a local type + LTC_DELETED, ///< deleted a local type + LTC_EDITED, ///< edited a local type + LTC_ALIASED, ///< added a type alias + LTC_COMPILER, ///< changed the compiler and calling convention + LTC_TIL_LOADED, ///< loaded a til file + LTC_TIL_UNLOADED, ///< unloaded a til file + LTC_TIL_COMPACTED,///< numbered types have been compacted \ref compact_numbered_types() +}; + +/// IDB event group. Some events are still in the processor group, so you will +/// need to hook to both groups. These events do not return anything. +/// +/// The callback function should return 0 but the kernel won't check it. +/// Use the hook_event_listener() function to install your callback. +namespace idb_event +{ + // + + /// IDB event codes + enum event_code_t + { + closebase, ///< The database will be closed now + + savebase, ///< The database is being saved + + upgraded, ///< The database has been upgraded + ///< and the receiver can upgrade its info as well + ///< \param from (int) - old IDB version + + auto_empty, ///< Info: all analysis queues are empty. + ///< This callback is called once when the + ///< initial analysis is finished. If the queue is + ///< not empty upon the return from this callback, + ///< it will be called later again. + + auto_empty_finally, ///< Info: all analysis queues are empty definitively. + ///< This callback is called only once. + + determined_main, ///< The main() function has been determined. + ///< \param main (::ea_t) address of the main() function + + extlang_changed, ///< The list of extlangs or the default extlang was changed. + ///< \param kind (int) + ///< 0: extlang installed + ///< 1: extlang removed + ///< 2: default extlang changed + ///< \param el (::extlang_t *) pointer to the extlang affected + ///< \param idx (int) extlang index + + idasgn_loaded, ///< FLIRT signature has been loaded + ///< for normal processing (not for + ///< recognition of startup sequences). + ///< \param short_sig_name (const char *) + + kernel_config_loaded, ///< This event is issued when ida.cfg is parsed. + ///< \param pass_number (int) + + loader_finished, ///< External file loader finished its work. + ///< Use this event to augment the existing loader functionality. + ///< \param li (linput_t *) + ///< \param neflags (::uint16) \ref NEF_ + ///< \param filetypename (const char *) + + flow_chart_created, ///< Gui has retrieved a function flow chart. + ///< Plugins may modify the flow chart in this callback. + ///< \param fc (qflow_chart_t *) + + compiler_changed, ///< The kernel has changed the compiler information. + ///< (\inf{cc} structure; \ref get_abi_name) + ///< \param adjust_inf_fields (::bool) may change inf fields? + + changing_ti, ///< An item typestring (c/c++ prototype) is to be changed. + ///< \param ea (::ea_t) + ///< \param new_type (const ::type_t *) + ///< \param new_fnames (const ::p_list *) + + ti_changed, ///< An item typestring (c/c++ prototype) has been changed. + ///< \param ea (::ea_t) + ///< \param type (const ::type_t *) + ///< \param fnames (const ::p_list *) + + changing_op_ti, ///< An operand typestring (c/c++ prototype) is to be changed. + ///< \param ea (::ea_t) + ///< \param n (int) + ///< \param new_type (const ::type_t *) + ///< \param new_fnames (const ::p_list *) + op_ti_changed, ///< An operand typestring (c/c++ prototype) has been changed. + ///< \param ea (::ea_t) + ///< \param n (int) + ///< \param type (const ::type_t *) + ///< \param fnames (const ::p_list *) + + changing_op_type, ///< An operand type (offset, hex, etc...) is to be changed. + ///< \param ea (::ea_t) + ///< \param n (int) eventually or'ed with OPND_OUTER or OPND_ALL + ///< \param opinfo (const opinfo_t *) additional operand info + op_type_changed, ///< An operand type (offset, hex, etc...) has been set or deleted. + ///< \param ea (::ea_t) + ///< \param n (int) eventually or'ed with OPND_OUTER or OPND_ALL + + segm_added, ///< A new segment has been created. + ///< \param s (::segment_t *) + ///< See also adding_segm + + deleting_segm, ///< A segment is to be deleted. + ///< \param start_ea (::ea_t) + segm_deleted, ///< A segment has been deleted. + ///< \param start_ea (::ea_t) + ///< \param end_ea (::ea_t) + ///< \param flags (int) + + changing_segm_start, ///< Segment start address is to be changed. + ///< \param s (::segment_t *) + ///< \param new_start (::ea_t) + ///< \param segmod_flags (int) + segm_start_changed, ///< Segment start address has been changed. + ///< \param s (::segment_t *) + ///< \param oldstart (::ea_t) + + changing_segm_end, ///< Segment end address is to be changed. + ///< \param s (::segment_t *) + ///< \param new_end (::ea_t) + ///< \param segmod_flags (int) + segm_end_changed, ///< Segment end address has been changed. + ///< \param s (::segment_t *) + ///< \param oldend (::ea_t) + + changing_segm_name, ///< Segment name is being changed. + ///< \param s (::segment_t *) + ///< \param oldname (const char *) + segm_name_changed, ///< Segment name has been changed. + ///< \param s (::segment_t *) + ///< \param name (const char *) + + changing_segm_class, ///< Segment class is being changed. + ///< \param s (::segment_t *) + segm_class_changed, ///< Segment class has been changed. + ///< \param s (::segment_t *) + ///< \param sclass (const char *) + + segm_attrs_updated, ///< Segment attributes has been changed. + ///< \param s (::segment_t *) + ///< This event is generated for secondary segment + ///< attributes (examples: color, permissions, etc) + + segm_moved, ///< Segment has been moved. + ///< \param from (::ea_t) + ///< \param to (::ea_t) + ///< \param size (::asize_t) + ///< \param changed_netmap (bool) + ///< See also \ref idb_event::allsegs_moved + + allsegs_moved, ///< Program rebasing is complete. + ///< This event is generated after series of + ///< segm_moved events + ///< \param info (::segm_move_infos_t *) + + func_added, ///< The kernel has added a function. + ///< \param pfn (::func_t *) + + func_updated, ///< The kernel has updated a function. + ///< \param pfn (::func_t *) + + set_func_start, ///< Function chunk start address will be changed. + ///< \param pfn (::func_t *) + ///< \param new_start (::ea_t) + + set_func_end, ///< Function chunk end address will be changed. + ///< \param pfn (::func_t *) + ///< \param new_end (::ea_t) + + deleting_func, ///< The kernel is about to delete a function. + ///< \param pfn (::func_t *) + // + frame_deleted, ///< The kernel has deleted a function frame. + ///< \param pfn (::func_t *) + ///< \ref idb_event::frame_created + + thunk_func_created, ///< A thunk bit has been set for a function. + ///< \param pfn (::func_t *) + + func_tail_appended, ///< A function tail chunk has been appended. + ///< \param pfn (::func_t *) + ///< \param tail (::func_t *) + + deleting_func_tail, ///< A function tail chunk is to be removed. + ///< \param pfn (::func_t *) + ///< \param tail (const ::range_t *) + + func_tail_deleted, ///< A function tail chunk has been removed. + ///< \param pfn (::func_t *) + ///< \param tail_ea (::ea_t) + + tail_owner_changed, ///< A tail chunk owner has been changed. + ///< \param tail (::func_t *) + ///< \param owner_func (::ea_t) + ///< \param old_owner (::ea_t) + + func_noret_changed, ///< #FUNC_NORET bit has been changed. + ///< \param pfn (::func_t *) + + stkpnts_changed, ///< Stack change points have been modified. + ///< \param pfn (::func_t *) + + updating_tryblks, ///< About to update tryblk information + ///< \param tbv (const ::tryblks_t *) + tryblks_updated, ///< Updated tryblk information + ///< \param tbv (const ::tryblks_t *) + + deleting_tryblks, ///< About to delete tryblk information in given range + ///< \param range (const ::range_t *) + // + sgr_changed, ///< The kernel has changed a segment register value. + ///< \param start_ea (::ea_t) + ///< \param end_ea (::ea_t) + ///< \param regnum (int) + ///< \param value (::sel_t) + ///< \param old_value (::sel_t) + ///< \param tag (::uchar) \ref SR_ + + make_code, ///< An instruction is being created. + ///< \param insn (const ::insn_t*) + + make_data, ///< A data item is being created. + ///< \param ea (::ea_t) + ///< \param flags (::flags64_t) + ///< \param tid (::tid_t) + ///< \param len (::asize_t) + + destroyed_items, ///< Instructions/data have been destroyed in [ea1,ea2). + ///< \param ea1 (::ea_t) + ///< \param ea2 (::ea_t) + ///< \param will_disable_range (bool) + + renamed, ///< The kernel has renamed a byte. + ///< See also the \idpcode{rename} event + ///< \param ea (::ea_t) + ///< \param new_name (const char *) can be nullptr + ///< \param local_name (bool) + ///< \param old_name (const char *) can be nullptr + + byte_patched, ///< A byte has been patched. + ///< \param ea (::ea_t) + ///< \param old_value (::uint32) + + changing_cmt, ///< An item comment is to be changed. + ///< \param ea (::ea_t) + ///< \param repeatable_cmt (bool) + ///< \param newcmt (const char *) + cmt_changed, ///< An item comment has been changed. + ///< \param ea (::ea_t) + ///< \param repeatable_cmt (bool) + + changing_range_cmt, ///< Range comment is to be changed. + ///< \param kind (::range_kind_t) + ///< \param a (const ::range_t *) + ///< \param cmt (const char *) + ///< \param repeatable (bool) + range_cmt_changed, ///< Range comment has been changed. + ///< \param kind (::range_kind_t) + ///< \param a (const ::range_t *) + ///< \param cmt (const char *) + ///< \param repeatable (bool) + + extra_cmt_changed, ///< An extra comment has been changed. + ///< \param ea (::ea_t) + ///< \param line_idx (int) + ///< \param cmt (const char *) + + item_color_changed, ///< An item color has been changed. + ///< \param ea (::ea_t) + ///< \param color (::bgcolor_t) + ///< if color==DEFCOLOR, the the color is deleted. + + callee_addr_changed, ///< Callee address has been updated by the user. + ///< \param ea (::ea_t) + ///< \param callee (::ea_t) + + bookmark_changed, ///< Boomarked position changed. + ///< \param index (::uint32) + ///< \param pos (::const lochist_entry_t *) + ///< \param desc (::const char *) + ///< \param operation (int) 0-added, 1-updated, 2-deleted + ///< if desc==nullptr, then the bookmark was deleted. + + sgr_deleted, ///< The kernel has deleted a segment register value. + ///< \param start_ea (::ea_t) + ///< \param end_ea (::ea_t) + ///< \param regnum (int) + + adding_segm, ///< A segment is being created. + ///< \param s (::segment_t *) + + func_deleted, ///< A function has been deleted. + ///< \param func_ea (::ea_t) + + dirtree_mkdir, ///< Dirtree: a directory has been created. + ///< \param dt (::dirtree_t *) + ///< \param path (::const char *) + + dirtree_rmdir, ///< Dirtree: a directory has been deleted. + ///< \param dt (::dirtree_t *) + ///< \param path (::const char *) + + dirtree_link, ///< Dirtree: an item has been linked/unlinked. + ///< \param dt (::dirtree_t *) + ///< \param path (::const char *) + ///< \param link (::bool) + + dirtree_move, ///< Dirtree: a directory or item has been moved. + ///< \param dt (::dirtree_t *) + ///< \param from (::const char *) + ///< \param to (::const char *) + + dirtree_rank, ///< Dirtree: a directory or item rank has been changed. + ///< \param dt (::dirtree_t *) + ///< \param path (::const char *) + ///< \param rank (::size_t) + + dirtree_rminode, ///< Dirtree: an inode became unavailable. + ///< \param dt (::dirtree_t *) + ///< \param inode (::inode_t) + + dirtree_segm_moved, ///< Dirtree: inodes were changed due to + ///< a segment movement or a program rebasing + ///< \param dt (::dirtree_t *) + + local_types_changed, ///< Local types have been changed + ///< \param ltc (::local_type_change_t) + ///< \param ordinal (::uint32) 0 means ordinal is unknown + ///< \param name (const char *) nullptr means name is unknown + + lt_udm_created, ///< local type udt member has been added + ///< \param udtname (::const char *) + ///< \param udm (::const udm_t *) + ///< \note udm_t::offset may not be calculated yet except of the fixed udt + + lt_udm_deleted, ///< local type udt member has been deleted + ///< \param udtname (::const char *) + ///< \param udm_tid (::tid_t) + ///< \param udm (::const udm_t *) + + lt_udm_renamed, ///< local type udt member has been renamed + ///< \param udtname (::const char *) + ///< \param udm (::const udm_t *) + ///< \param oldname (::const char *) + + lt_udm_changed, ///< local type udt member has been changed + ///< \param udtname (::const char *) + ///< \param udm_tid (::tid_t) + ///< \param udmold (::const udm_t *) + ///< \param udmnew (::const udm_t *) + ///< \note udm_t::offset may not be calculated yet except of the fixed udt + + lt_udt_expanded, ///< A structure type has been expanded/shrank. + ///< \param udtname (::const char *) + ///< \param udm_tid (::tid_t) the gap was added/removed before this member + ///< \param delta (::adiff_t) number of added/removed bytes + + frame_created, ///< A function frame has been created. + ///< \param func_ea (::ea_t) + ///< \ref idb_event::frame_deleted + + frame_udm_created, ///< Frame member has been added. + ///< \param func_ea (::ea_t) + ///< \param udm (::const udm_t *) + + frame_udm_deleted, ///< Frame member has been deleted. + ///< \param func_ea (::ea_t) + ///< \param udm_tid (::tid_t) + ///< \param udm (::const udm_t *) + + frame_udm_renamed, ///< Frame member has been renamed. + ///< \param func_ea (::ea_t) + ///< \param udm (::const udm_t *) + ///< \param oldname (::const char *) + + frame_udm_changed, ///< Frame member has been changed. + ///< \param func_ea (::ea_t) + ///< \param udm_tid (::tid_t) + ///< \param udmold (::const udm_t *) + ///< \param udmnew (::const udm_t *) + + frame_expanded, ///< A frame type has been expanded/shrank. + ///< \param func_ea (::ea_t) + ///< \param udm_tid (::tid_t) the gap was added/removed before this member + ///< \param delta (::adiff_t) number of added/removed bytes + + idasgn_matched_ea, ///< A FLIRT match has been found + ///< \param ea (::ea_t) the matching address + ///< \param name (::const char *) the matched name + ///< \param lib_name (::const char *) library name extracted from signature file + + lt_edm_created, ///< local type enum member has been added + ///< \param enumname (::const char *) + ///< \param edm (::const edm_t *) + + lt_edm_deleted, ///< local type enum member has been deleted + ///< \param enumname (::const char *) + ///< \param edm_tid (::tid_t) + ///< \param edm (::const edm_t *) + + lt_edm_renamed, ///< local type enum member has been renamed + ///< \param enumname (::const char *) + ///< \param edm (::const edm_t *) + ///< \param oldname (::const char *) + + lt_edm_changed, ///< local type enum member has been changed + ///< \param enumname (::const char *) + ///< \param edm_tid (::tid_t) + ///< \param edmold (::const edm_t *) + ///< \param edmnew (::const edm_t *) + + local_type_renamed, ///< Local type has been renamed + ///< \param ordinal (::uint32) 0 means ordinal is unknown + ///< \param oldname (const char *) nullptr means name is unknown + ///< \param newname (const char *) nullptr means name is unknown + + }; +} + + +/// the kernel will use this function to generate idb_events + +inline void gen_idb_event(idb_event::event_code_t code, ...) +{ + if ( errorexit ) + return; + va_list va; + va_start(va, code); + invoke_callbacks(HT_IDB, code, va); + va_end(va); +} + +inline int processor_t::get_proc_index(void) +{ + qstring curproc = inf_get_procname(); + for ( size_t i = 0; psnames[i] != nullptr; ++i ) + { + const char *p = psnames[i]; + if ( p[0] == '-' ) // obsolete processor names start with a '-' + ++p; + if ( curproc == p ) + return i; + } + // should not reach here + INTERR(10336); +} + +/// Starting from IDA v7.5 all modules should use the following 3 functions +/// to handle idb specific static data because now the kernel supports +/// opening and working with multiple idbs files simultaneously. +/// See the source code of the processor modules in the SDK for the usage examples. + +/// Register pointer to database specific module data. +/// \param data_id initially the pointed-to value must be 0, the kernel will fill +/// it with a unique id. once assigned, the data_id does not change. +/// \param data_ptr pointer to the data to register +/// \return data_ptr. +/// The registered pointer can later be retrieved using get_module_data() + +idaman void *ida_export set_module_data(int *data_id, void *data_ptr); + + +/// Unregister pointer to database specific module data. +/// \param data_id an data_id that was assigned by set_module_data() +/// \return previously registered pointer for the current database. +/// it can be deallocated now. +/// Multiple calls to this function with the same id are forbidden. + +idaman void *ida_export clr_module_data(int data_id); + + + +/// Get pointer to the database specific module data. +/// \param data_id data id that was initialized by set_module_data() +/// \return previously registered pointer for the current database + +idaman void *ida_export get_module_data(int data_id); + +// Convenience macros to handle the module data. +// They assume the existence of a global variable "int data_id" +#define SET_MODULE_DATA(type) (type *)set_module_data(&data_id, new type) +#define GET_MODULE_DATA(type) ((type *)get_module_data(data_id)) + + +#endif // _IDP_HPP + +``` + +`IdaSDK/ieee.h`: + +```h +/* + * Interactive disassembler (IDA) + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + + * Floating Point Number Libary. + * Copyright (c) 1995-2006 by Iouri Kharon. + * E-mail: yjh@styx.cabel.net + * + */ + +#ifndef _IEEE_H_ +#define _IEEE_H_ + +/*! \file ieee.h + + \brief IEEE floating point functions + +*/ + +struct fpvalue_t; // processor-independent representation of floats + +#define FPVAL_NWORDS 8 ///< number of words in fpvalue_t + +/// Floating value kinds. +/// They are useful when checking for NaN/Inf +enum fpvalue_kind_t +{ + FPV_BADARG, ///< wrong value of max_exp + FPV_NORM, ///< regular value + FPV_NAN, ///< NaN + FPV_PINF, ///< positive infinity + FPV_NINF, ///< negative infinity +}; + +/// \name max_exp values +/// Common values for max_exp (for IEEE floating point values) +///@{ +const uint32 + MAXEXP_FLOAT = 0x80, ///< maximum exponent for 32-bit float + MAXEXP_DOUBLE = 0x400, ///< maximum exponent for 64-bit double + MAXEXP_LNGDBL = 0x4000; ///< maximum exponent for 80-bit long double +///@} + +/// \defgroup REAL_ERROR_ Floating point/IEEE Conversion codes +/// Return values for ieee_realcvt and processor_t::realcvt request +enum fpvalue_error_t +{ + REAL_ERROR_OK = 1, ///< no error + REAL_ERROR_FORMAT = -1, ///< realcvt: not supported format for current .idp + REAL_ERROR_RANGE = -2, ///< realcvt: number too big (small) for store (mem NOT modified) + REAL_ERROR_BADDATA = -3, ///< realcvt: illegal real data for load (IEEE data not filled) + REAL_ERROR_FPOVER = 2, ///< floating overflow or underflow + REAL_ERROR_BADSTR = 3, ///< asctoreal: illegal input string + REAL_ERROR_ZERODIV = 4, ///< ediv: divide by 0 + REAL_ERROR_INTOVER = 5, ///< eetol*: integer overflow +}; + +/// Standard IEEE 754 floating point conversion function +/// \param m pointer to data +/// \param out internal IEEE format data +/// \param swt operation: +/// - 000: load trunc. float (DEC ^F) 2 bytes (m->e) +/// - 001: load float 4 bytes (m->e) +/// - 003: load double 8 bytes (m->e) +/// - 004: load long double 10 bytes (m->e) +/// - 005: load long double 12 bytes (m->e) +/// - 010: store trunc. float (DEC ^F) 2 bytes (e->m) +/// - 011: store float 4 bytes (e->m) +/// - 013: store double 8 bytes (e->m) +/// - 014: store long double 10 bytes (e->m) +/// - 015: store long double 12 bytes (e->m) +/// bit 0x80 forces little endian even for big endian processors +/// \return fpvalue_error_t + +idaman THREAD_SAFE fpvalue_error_t ida_export ieee_realcvt(void *m, fpvalue_t *out, uint16 swt); + +// Helper functions. Better use members of fpvalue_t, they are nicer. +idaman THREAD_SAFE void ida_export realtoasc(char *buf, size_t bufsize, const fpvalue_t &x, uint mode); +idaman THREAD_SAFE fpvalue_error_t ida_export asctoreal(const char **sss, fpvalue_t *out); +idaman THREAD_SAFE void ida_export eltoe(sval_t l, fpvalue_t *vout); +idaman THREAD_SAFE void ida_export eltoe64(int64 l, fpvalue_t *vout); +idaman THREAD_SAFE void ida_export eltoe64u(uint64 l, fpvalue_t *vout); +idaman THREAD_SAFE fpvalue_error_t ida_export eetol(sval_t *out, const fpvalue_t &a, bool roundflg); +idaman THREAD_SAFE fpvalue_error_t ida_export eetol64(int64 *out, const fpvalue_t &a, bool roundflg); +idaman THREAD_SAFE fpvalue_error_t ida_export eetol64u(uint64 *out, const fpvalue_t &a, bool roundflg); +idaman THREAD_SAFE fpvalue_error_t ida_export eldexp(const fpvalue_t &a, int32 pwr2, fpvalue_t *zout); +idaman THREAD_SAFE fpvalue_error_t ida_export eadd(const fpvalue_t &a, const fpvalue_t &b, fpvalue_t *zout, bool subflg); +idaman THREAD_SAFE fpvalue_error_t ida_export emul(const fpvalue_t &a, const fpvalue_t &b, fpvalue_t *zout); +idaman THREAD_SAFE fpvalue_error_t ida_export ediv(const fpvalue_t &a, const fpvalue_t &b, fpvalue_t *zout); +idaman THREAD_SAFE int ida_export ecmp(const fpvalue_t &a, const fpvalue_t &b); +idaman THREAD_SAFE fpvalue_kind_t ida_export get_fpvalue_kind(const fpvalue_t &a, uint16 reserved = 0); + +//------------------------------------------------------------------------ +/// Processor-independent representation of a floating point value. +/// IDA uses this structure to store and manipulate floating point values. +struct fpvalue_t +{ + uint16 w[FPVAL_NWORDS]; + + void clear(void) { memset(this, 0, sizeof(*this)); } + DECLARE_COMPARISONS(fpvalue_t) { return ecmp(*this, r); } + + /// Convert to the processor-independent representation. + fpvalue_error_t from_half(uint16 fpval) { return ieee_realcvt(&fpval, this, sizeof(fpval)/2-1); } + fpvalue_error_t from_float(float fpval) { return ieee_realcvt(&fpval, this, sizeof(fpval)/2-1); } + fpvalue_error_t from_double(double fpval) { return ieee_realcvt(&fpval, this, sizeof(fpval)/2-1); } + + /// Convert from the processor-independent representation. + fpvalue_error_t to_half(uint16 *fpval) const { return ieee_realcvt(fpval, (fpvalue_t*)this, 8|(sizeof(*fpval)/2-1)); } + fpvalue_error_t to_float(float *fpval) const { return ieee_realcvt(fpval, (fpvalue_t*)this, 8|(sizeof(*fpval)/2-1)); } + fpvalue_error_t to_double(double *fpval) const { return ieee_realcvt(fpval, (fpvalue_t*)this, 8|(sizeof(*fpval)/2-1)); } + + /// Conversions for 10-byte floating point values. + fpvalue_error_t from_10bytes(const void *fpval) { return ieee_realcvt((void *)fpval, this, 4); } + fpvalue_error_t to_10bytes(void *fpval) const { return ieee_realcvt(fpval, (fpvalue_t*)this, 8|4); } + + /// Conversions for 12-byte floating point values. + fpvalue_error_t from_12bytes(const void *fpval) { return ieee_realcvt((void*)fpval, this, 5); } + fpvalue_error_t to_12bytes(void *fpval) const { return ieee_realcvt(fpval, (fpvalue_t*)this, 8|5); } + + /// Convert string to IEEE. + /// \param p_str pointer to pointer to string. it will advanced. + fpvalue_error_t from_str(const char **p_str) { return asctoreal(p_str, this); } + + /// Convert IEEE to string. + /// \param buf the output buffer + /// \param bufsize the size of the output buffer + /// \param mode broken down into: + /// - low byte: number of digits after '.' + /// - second byte: FPNUM_LENGTH + /// - third byte: FPNUM_DIGITS + void to_str(char *buf, size_t bufsize, uint mode) const { realtoasc(buf, bufsize, *this, mode); } + + /// Convert integer to IEEE + void from_sval(sval_t x) { eltoe(x, this); } + void from_int64(int64 x) { eltoe64(x, this); } + void from_uint64(uint64 x) { eltoe64u(x, this); } + + /// Convert IEEE to integer (+-0.5 if round) + fpvalue_error_t to_sval(sval_t *out, bool round=false) const { return eetol(out, *this, round); } + fpvalue_error_t to_int64(int64 *out, bool round=false) const { return eetol64(out, *this, round); } + fpvalue_error_t to_uint64(uint64 *out, bool round=false) const { return eetol64u(out, *this, round); } + + /// Arithmetic operations + fpvalue_error_t fadd(const fpvalue_t &y) { return eadd(*this, y, this, false); } + fpvalue_error_t fsub(const fpvalue_t &y) { return eadd(*this, y, this, true); } + fpvalue_error_t fmul(const fpvalue_t &y) { return emul(*this, y, this); } + fpvalue_error_t fdiv(const fpvalue_t &y) { return ediv(*this, y, this); } + + /// Multiply by a power of 2. + fpvalue_error_t mul_pow2(int32 power_of_2) { return eldexp(*this, power_of_2, this); } + + /// Calculate absolute value. + void eabs() { w[FPVAL_NWORDS-1] &= 0x7fff; } + + /// Is negative value? + bool is_negative() const { return (w[FPVAL_NWORDS-1] & 0x8000) != 0; } + + /// Negate. + void negate() + { + if ( w[FPVAL_NWORDS-1] != 0 ) + w[FPVAL_NWORDS-1] ^= 0x8000; + } + + /// Get value kind. + fpvalue_kind_t get_kind() const { return get_fpvalue_kind(*this, 0); } +}; + +//------------------------------------------------------------------------ +/// The exponent of 1.0 +#define IEEE_EXONE (0x3FFF) +/// Exponent in fpvalue_t for NaN and Inf +#define E_SPECIAL_EXP 0x7FFF +#if !defined(NO_OBSOLETE_FUNCS) || defined(IEEE_SOURCE) +#define IEEE_NI (FPVAL_NWORDS+3) ///< Number of 16 bit words in ::eNI +#define IEEE_E 1 ///< Array offset to exponent +#define IEEE_M 2 ///< Array offset to high guard word +/// There is one more internal format used by IDA to store intermediate values. +/// - 0 : sign (0/1) +/// - 1 : exponent (based of #IEEE_EXONE). If exp = 0, value = 0. +/// - 2 : high word of mantissa (always zero after normalize) +typedef uint16 eNI[IEEE_NI]; + +#ifdef IEEE_SOURCE +# define IEEE_DEPRECATED +#else +# define IEEE_DEPRECATED DEPRECATED +#endif +inline IEEE_DEPRECATED void ecleaz(eNI x) +{ + if ( x != nullptr ) + memset(x, 0, sizeof(eNI)); +} +idaman IEEE_DEPRECATED THREAD_SAFE void ida_export emovo(const eNI a, fpvalue_t *vout); /// Move eNI => eNE +idaman IEEE_DEPRECATED THREAD_SAFE void ida_export emovi(const fpvalue_t &a, eNI vout); /// Move eNE => eNI +idaman IEEE_DEPRECATED THREAD_SAFE int ida_export eshift(eNI x, int sc); /// Shift NI format up (+) or down +/// Normalize and round off. +/// \param s the internal format number to be rounded +/// \param lost indicates whether or not the number is exact. +/// this is the so-called sticky bit. +/// \param subflg indicates whether the number was obtained +/// by a subtraction operation. In that case if lost is nonzero +/// then the number is slightly smaller than indicated. +/// \param exp the biased exponent, which may be negative. +/// the exponent field of "s" is ignored but is replaced by +/// "exp" as adjusted by normalization and rounding. +/// \param rndbase if 0 => is the rounding control. +/// else is processor defined base (rndprc) +/// \return success +idaman IEEE_DEPRECATED THREAD_SAFE bool ida_export emdnorm(eNI s, bool lost, bool subflg, int32 exp, int rndbase); + +#endif + +#endif + +``` + +`IdaSDK/intel.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _INTEL_HPP +#define _INTEL_HPP +#include +#include +#include + +#define PROC_MAXOP 5 // max number of operands +CASSERT(PROC_MAXOP <= UA_MAXOP); + +//--------------------------------- +// Intel 80x86 insn_t.auxpref bits +#define aux_lock 0x00000001 +#define aux_rep 0x00000002 +#define aux_repne 0x00000004 +#define aux_use32 0x00000008 // segment type is 32-bits +#define aux_use64 0x00000010 // segment type is 64-bits +#define aux_large 0x00000020 // offset field is 32-bit (16-bit is not enough) +#define aux_short 0x00000040 // short (byte) displacement used +#define aux_sgpref 0x00000080 // a segment prefix byte is not used +#define aux_oppref 0x00000100 // operand size prefix byte is not used +#define aux_adpref 0x00000200 // address size prefix byte is not used +#define aux_basess 0x00000400 // SS based instruction +#define aux_natop 0x00000800 // operand size is not overridden by prefix +#define aux_natad 0x00001000 // addressing mode is not overridden by prefix +#define aux_fpemu 0x00002000 // FP emulator instruction +#define aux_vexpr 0x00004000 // VEX-encoded instruction +#define aux_bnd 0x00008000 // MPX-encoded instruction +#define aux_evex 0x00010000 // EVEX-encoded instruction +#define aux_xop 0x00020000 // XOP-encoded instruction +#define aux_xacquire 0x00040000 // HLE prefix hints +#define aux_xrelease 0x00080000 // HLE prefix hints + +//--------------------------------- +// operand types and other customization: +#define o_trreg o_idpspec0 // IDP specific type +#define o_dbreg o_idpspec1 // IDP specific type +#define o_crreg o_idpspec2 // IDP specific type +#define o_fpreg o_idpspec3 // IDP specific type +#define o_mmxreg o_idpspec4 // IDP specific type +#define o_xmmreg o_idpspec5 // xmm register +#define o_ymmreg o_idpspec5+1 // ymm register +#define o_zmmreg o_idpspec5+2 // zmm register +#define o_kreg o_idpspec5+3 // opmask register + +// 04.10.97: For o_mem,o_near,o_far we keep segment information as +// segrg - number of segment register to use +// if it is == SEGREG_IMM, then the segment was specified as an immediate +// value, look at segsel. + +#define segrg specval_shorts.high +#define SEGREG_IMM 0xFFFF // this value of segrg means that + // segment selector value is in + // "segsel": +#define segsel specval_shorts.low +#define hasSIB specflag1 +#define sib specflag2 +#define rex insnpref // REX byte for 64-bit mode, or bits from the VEX byte if vexpr() + +// Op6 is used for opmask registers in EVEX. +// specflags from Op6 are used to extend insn_t. +#define evex_flags Op6.specflag2 // bits from the EVEX byte if evexpr() + +#define cr_suff specflag1 // o_crreg: D suffix for cr registers (used for CR8D) + +// bits in insn_t.evex_flags: +const int EVEX_R = 0x01; // High-16 register specifier modifier +const int EVEX_L = 0x02; // Vector length/RC +const int EVEX_z = 0x04; // Zeroing/Merging +const int EVEX_b = 0x08; // Broadcast/RC/SAE Context +const int EVEX_V = 0x10; // High-16 NDS/VIDX register specifier + +// bits in insn_t.rex: +const int REX_W = 8; // 64-bit operand size +const int REX_R = 4; // modrm reg field extension +const int REX_X = 2; // sib index field extension +const int REX_B = 1; // modrm r/m, sib base, or opcode reg fields extension +const int VEX_L = 0x80; // 256-bit operation (YMM register) + +typedef short regnum_t; + +enum RegNo +{ + R_none = -1, + R_ax = 0, + R_cx, // 1 + R_dx, // 2 + R_bx, // 3 + R_sp, // 4 + R_bp, // 5 + R_si, // 6 + R_di, // 7 + R_r8, // 8 + R_r9, // 9 + R_r10, // 10 + R_r11, // 11 + R_r12, // 12 + R_r13, // 13 + R_r14, // 14 + R_r15, // 15 + + R_al, + R_cl, + R_dl, + R_bl, + R_ah, + R_ch, + R_dh, + R_bh, + + R_spl, + R_bpl, + R_sil, + R_dil, + + R_ip, + + R_es, // 0 + R_cs, // 1 + R_ss, // 2 + R_ds, // 3 + R_fs, + R_gs, + + R_cf, // main cc's + R_zf, + R_sf, + R_of, + + R_pf, // additional cc's + R_af, + R_tf, + R_if, + R_df, + + R_efl, // eflags + + // the following registers will be used in the disassembly + // starting from ida v5.7 + + R_st0, // floating point registers (not used in disassembly) + R_st1, + R_st2, + R_st3, + R_st4, + R_st5, + R_st6, + R_st7, + R_fpctrl,// fpu control register + R_fpstat,// fpu status register + R_fptags,// fpu tags register + + R_mm0, // mmx registers + R_mm1, + R_mm2, + R_mm3, + R_mm4, + R_mm5, + R_mm6, + R_mm7, + + R_xmm0, // xmm registers + R_xmm1, + R_xmm2, + R_xmm3, + R_xmm4, + R_xmm5, + R_xmm6, + R_xmm7, + R_xmm8, + R_xmm9, + R_xmm10, + R_xmm11, + R_xmm12, + R_xmm13, + R_xmm14, + R_xmm15, + R_mxcsr, + + R_ymm0, // AVX 256-bit registers + R_ymm1, + R_ymm2, + R_ymm3, + R_ymm4, + R_ymm5, + R_ymm6, + R_ymm7, + R_ymm8, + R_ymm9, + R_ymm10, + R_ymm11, + R_ymm12, + R_ymm13, + R_ymm14, + R_ymm15, + + R_bnd0, // MPX registers + R_bnd1, + R_bnd2, + R_bnd3, + + R_xmm16, // AVX-512 extended XMM registers + R_xmm17, + R_xmm18, + R_xmm19, + R_xmm20, + R_xmm21, + R_xmm22, + R_xmm23, + R_xmm24, + R_xmm25, + R_xmm26, + R_xmm27, + R_xmm28, + R_xmm29, + R_xmm30, + R_xmm31, + + R_ymm16, // AVX-512 extended YMM registers + R_ymm17, + R_ymm18, + R_ymm19, + R_ymm20, + R_ymm21, + R_ymm22, + R_ymm23, + R_ymm24, + R_ymm25, + R_ymm26, + R_ymm27, + R_ymm28, + R_ymm29, + R_ymm30, + R_ymm31, + + R_zmm0, // AVX-512 ZMM registers + R_zmm1, + R_zmm2, + R_zmm3, + R_zmm4, + R_zmm5, + R_zmm6, + R_zmm7, + R_zmm8, + R_zmm9, + R_zmm10, + R_zmm11, + R_zmm12, + R_zmm13, + R_zmm14, + R_zmm15, + R_zmm16, + R_zmm17, + R_zmm18, + R_zmm19, + R_zmm20, + R_zmm21, + R_zmm22, + R_zmm23, + R_zmm24, + R_zmm25, + R_zmm26, + R_zmm27, + R_zmm28, + R_zmm29, + R_zmm30, + R_zmm31, + + R_k0, // AVX-512 opmask registers + R_k1, + R_k2, + R_k3, + R_k4, + R_k5, + R_k6, + R_k7, + + R_last, +}; + +CASSERT(R_last == 173); + +inline bool is_segreg(int r) { return r >= R_es && r <= R_gs; } +inline bool is_fpureg(int r) { return r >= R_st0 && r <= R_st7; } +inline bool is_mmxreg(int r) { return r >= R_mm0 && r <= R_mm7; } +inline bool is_xmmreg(int r) { return r >= R_xmm0 && r <= R_xmm15 || r >= R_xmm16 && r <= R_xmm31; } +inline bool is_ymmreg(int r) { return r >= R_ymm0 && r <= R_ymm15; } + +int cvt_to_wholereg(int _reg, bool allow_high_byte_regs); // byte reg -> whole reg +int calc_dbg_reg_index(const char *name); + +//------------------------------------------------------------------------- +// is conditional branch? +inline bool insn_jcc(const insn_t &insn) +{ + switch ( insn.itype ) + { + case NN_ja: + case NN_jae: + case NN_jb: + case NN_jbe: + case NN_jc: + case NN_je: + case NN_jg: + case NN_jge: + case NN_jl: + case NN_jle: + case NN_jna: + case NN_jnae: + case NN_jnb: + case NN_jnbe: + case NN_jnc: + case NN_jne: + case NN_jng: + case NN_jnge: + case NN_jnl: + case NN_jnle: + case NN_jno: + case NN_jnp: + case NN_jns: + case NN_jnz: + case NN_jo: + case NN_jp: + case NN_jpe: + case NN_jpo: + case NN_js: + case NN_jz: + return true; + } + return false; +} + +//------------------------------------------------------------------------- +inline bool insn_default_opsize_64(const insn_t &insn) +{ + if ( insn_jcc(insn) ) + return true; + switch ( insn.itype ) + { + // use ss + case NN_pop: + case NN_popf: + case NN_popfq: + case NN_push: + case NN_pushf: + case NN_pushfq: + case NN_retn: + case NN_retf: + case NN_retnq: + case NN_retfq: + case NN_call: + case NN_callfi: + case NN_callni: + case NN_enter: + case NN_enterq: + case NN_leave: + case NN_leaveq: + + // near branches + case NN_jcxz: + case NN_jecxz: + case NN_jrcxz: + case NN_jmp: + case NN_jmpni: + case NN_jmpshort: + case NN_loop: + case NN_loopq: + case NN_loope: + case NN_loopqe: + case NN_loopne: + case NN_loopqne: + return true; + } + return false; +} + +inline bool mode16(const insn_t &insn) { return (insn.auxpref & (aux_use32|aux_use64)) == 0; } // 16-bit mode? +inline bool mode32(const insn_t &insn) { return (insn.auxpref & aux_use32) != 0; } // 32-bit mode? +inline bool mode64(const insn_t &insn) { return (insn.auxpref & aux_use64) != 0; } // 64-bit mode? +inline bool natad(const insn_t &insn) { return (insn.auxpref & aux_natad) != 0; } // natural address size (no prefixes)? +inline bool natop(const insn_t &insn) { return (insn.auxpref & aux_natop) != 0; } // natural operand size (no prefixes)? +inline bool vexpr(const insn_t &insn) { return (insn.auxpref & aux_vexpr) != 0; } // VEX encoding used +inline bool evexpr(const insn_t &insn) { return (insn.auxpref & aux_evex) != 0; } // EVEX encoding used +inline bool xopexpr(const insn_t &insn) { return (insn.auxpref & aux_xop) != 0; } // XOP encoding used + +inline bool ad16(const insn_t &insn) // is current addressing 16-bit? +{ + int p = insn.auxpref & (aux_use32|aux_use64|aux_natad); + return p == aux_natad || p == aux_use32; +} + +inline bool ad32(const insn_t &insn) // is current addressing 32-bit? +{ + int p = insn.auxpref & (aux_use32|aux_use64|aux_natad); + return p == (aux_natad|aux_use32) + || p == 0 + || p == aux_use64; +} + +inline bool ad64(const insn_t &insn) // is current addressing 64-bit? +{ +#ifdef __EA64__ + int p = insn.auxpref & (aux_use32|aux_use64|aux_natad); + return p == (aux_natad|aux_use64); +#else + qnotused(insn); + return false; +#endif +} + +inline bool op16(const insn_t &insn) // is current operand size 16-bit? +{ + int p = insn.auxpref & (aux_use32|aux_use64|aux_natop); + return p == aux_natop // 16-bit segment, no prefixes + || p == aux_use32 // 32-bit segment, 66h + || p == aux_use64 && (insn.rex & REX_W) == 0; // 64-bit segment, 66h, no rex.w +} + +inline bool op32(const insn_t &insn) // is current operand size 32-bit? +{ + int p = insn.auxpref & (aux_use32|aux_use64|aux_natop); + return p == 0 // 16-bit segment, 66h + || p == (aux_use32|aux_natop) // 32-bit segment, no prefixes + || p == (aux_use64|aux_natop) && (insn.rex & REX_W) == 0; // 64-bit segment, 66h, no rex.w +} + +inline bool op64(const insn_t &insn) // is current operand size 64-bit? +{ +#ifdef __EA64__ + return mode64(insn) + && ((insn.rex & REX_W) != 0 + || natop(insn) && insn_default_opsize_64(insn)); // 64-bit segment, rex.w or insns-64 +#else + qnotused(insn); + return false; +#endif +} + +inline bool op256(const insn_t &insn) // is VEX.L == 1 or EVEX.L'L == 01? +{ + return (insn.rex & VEX_L) != 0 + && (vexpr(insn) + || xopexpr(insn) + || evexpr(insn) && (insn.evex_flags & EVEX_L) == 0); +} + +inline bool op512(const insn_t &insn) // is EVEX.L'L == 10? +{ + return evexpr(insn) && (insn.rex & VEX_L) == 0 && (insn.evex_flags & EVEX_L) != 0; +} + +inline bool is_vsib(const insn_t &insn) // does instruction use VSIB variant of the sib byte? +{ + switch ( insn.itype ) + { + case NN_vgatherdps: + case NN_vgatherdpd: + case NN_vgatherqps: + case NN_vgatherqpd: + case NN_vpgatherdd: + case NN_vpgatherdq: + case NN_vpgatherqd: + case NN_vpgatherqq: + + case NN_vscatterdps: + case NN_vscatterdpd: + case NN_vscatterqps: + case NN_vscatterqpd: + case NN_vpscatterdd: + case NN_vpscatterdq: + case NN_vpscatterqd: + case NN_vpscatterqq: + + case NN_vgatherpf0dps: + case NN_vgatherpf0qps: + case NN_vgatherpf0dpd: + case NN_vgatherpf0qpd: + case NN_vgatherpf1dps: + case NN_vgatherpf1qps: + case NN_vgatherpf1dpd: + case NN_vgatherpf1qpd: + + case NN_vscatterpf0dps: + case NN_vscatterpf0qps: + case NN_vscatterpf0dpd: + case NN_vscatterpf0qpd: + case NN_vscatterpf1dps: + case NN_vscatterpf1qps: + case NN_vscatterpf1dpd: + case NN_vscatterpf1qpd: + return true; + } + return false; +} + +inline regnum_t vsib_index_fixreg(const insn_t &insn, regnum_t index) +{ + switch ( insn.itype ) + { + case NN_vscatterdps: + case NN_vscatterqps: + case NN_vscatterqpd: + case NN_vpscatterdd: + case NN_vpscatterqd: + case NN_vpscatterqq: + + case NN_vpgatherdd: + case NN_vpgatherqd: + case NN_vpgatherqq: + case NN_vgatherdps: + case NN_vgatherqps: + case NN_vgatherqpd: + if ( index > 15 ) + index += op512(insn) ? R_zmm0 : op256(insn) ? (R_ymm16 - 16) : (R_xmm16 - 16); + else + index += op512(insn) ? R_zmm0 : op256(insn) ? R_ymm0 : R_xmm0; + break; + + case NN_vscatterdpd: + case NN_vpscatterdq: + + case NN_vgatherdpd: + case NN_vpgatherdq: + if ( index > 15 ) + index += op512(insn) ? (R_ymm16 - 16) : (R_xmm16 - 16); + else + index += op512(insn) ? R_ymm0 : R_xmm0; + break; + + case NN_vgatherpf0dps: + case NN_vgatherpf0qps: + case NN_vgatherpf0qpd: + case NN_vgatherpf1dps: + case NN_vgatherpf1qps: + case NN_vgatherpf1qpd: + + case NN_vscatterpf0dps: + case NN_vscatterpf0qps: + case NN_vscatterpf0qpd: + case NN_vscatterpf1dps: + case NN_vscatterpf1qps: + case NN_vscatterpf1qpd: + index += R_zmm0; + break; + + case NN_vgatherpf0dpd: + case NN_vgatherpf1dpd: + case NN_vscatterpf0dpd: + case NN_vscatterpf1dpd: + if ( index > 15 ) + index += R_ymm16 - 16; + else + index += R_ymm0; + break; + } + return index; +} + +inline int sib_base(const insn_t &insn, const op_t &x) // get extended sib base +{ + int base = x.sib & 7; +#ifdef __EA64__ + if ( mode64(insn) && (insn.rex & REX_B) != 0 ) + base |= 8; +#else + qnotused(insn); +#endif + return base; +} + +inline regnum_t sib_index(const insn_t &insn, const op_t &x) // get extended sib index +{ + regnum_t index = regnum_t((x.sib >> 3) & 7); +#ifdef __EA64__ + if ( mode64(insn) && (insn.rex & REX_X) != 0 ) + index |= 8; +#endif + if ( is_vsib(insn) ) + { + if ( (insn.evex_flags & EVEX_V) != 0 ) + index |= 16; + index = vsib_index_fixreg(insn, index); + } + return index; +} + +inline int sib_scale(const op_t &x) +{ + int scale = (x.sib >> 6) & 3; + return scale; +} + +// get the base register of the operand with a displacement +// NB: for 16-bit code, returns a phrase number +// use x86_base_reg() if you need to handle 16-bit instructions +inline int x86_base(const insn_t &insn, const op_t &x) +{ + return x.hasSIB ? sib_base(insn, x) : x.phrase; +} + +// get the base register of the operand with a displacement +// returns correct register for 16-bit code too +inline int x86_base_reg(const insn_t &insn, const op_t &x) +{ + if ( x.hasSIB ) + { + if ( x.type == o_mem ) + return R_none; + return sib_base(insn, x); // base register is encoded in the SIB + } + else if ( !ad16(insn) ) + { + return x.phrase; // 'phrase' contains the base register number + } + else if ( x.phrase == ushort(R_none) ) + { + return R_sp; + } + switch ( x.phrase ) + { + case 0: // [BX+SI] + case 1: // [BX+DI] + case 7: // [BX] + return R_bx; + case 2: // [BP+SI] + case 3: // [BP+DI] + case 6: // [BP] + return R_bp; + case 4: // [SI] + return R_si; + case 5: // [DI] + return R_di; + default: + INTERR(10259); + } +} + +const int INDEX_NONE = 4; // no index register is present +// get the index register of the operand with a displacement +inline int x86_index(const insn_t &insn, const op_t &x) +{ + return x.hasSIB ? sib_index(insn, x) : INDEX_NONE; +} + +inline int x86_index_reg(const insn_t &insn, const op_t &x) +{ + if ( x.hasSIB ) + { + int idx = sib_index(insn, x); + if ( idx != INDEX_NONE ) + return idx; + return R_none; + } + if ( !ad16(insn) ) + return R_none; + switch ( x.phrase ) + { + case 0: // [BX+SI] + case 2: // [BP+SI] + return R_si; + case 1: // [BX+DI] + case 3: // [BP+DI] + return R_di; + case 4: // [SI] + case 5: // [DI] + case 7: // [BX] + case 6: // [BP] + return R_none; + default: + INTERR(10260); + } +} +// get the scale factor of the operand with a displacement +inline int x86_scale(const op_t &x) +{ + return x.hasSIB ? sib_scale(x) : 0; +} + +// does the operand have a displacement? +inline int has_displ(const op_t &x) +{ + return x.type == o_displ || x.type == o_mem && x.hasSIB; +} + +// does the insn refer to the TLS variable? +inline bool has_tls_segpref(const insn_t &insn) +{ + if ( insn.segpref == 0 ) + return false; + return mode64(insn) && insn.segpref == R_fs + || mode32(insn) && insn.segpref == R_gs; +} + +// should we treat the memory operand as a displacement? +inline bool mem_as_displ(const insn_t &insn, const op_t &x) +{ + // the operand should be an offset and it should be the TLS variable + // or the second operand of "lea" instruction + // .text:08000000 mov eax, gs:(ti1 - static_TP) + // .text:08000E8F lea ecx, (_ZN4dmngL4sessE - _GLOBAL_OFFSET_TABLE_) + return (has_tls_segpref(insn) || insn.itype == NN_lea) + && is_off(get_flags(insn.ea), x.n); +} + +// does the operand refer to stack? (sp or bp based) +bool is_stack_ref(const insn_t &insn, const op_t &x, int breg); + +// return addressing width in form of dt_... constant +inline op_dtype_t address_dtype(const insn_t &insn) +{ + return char(ad64(insn) ? dt_qword : ad32(insn) ? dt_dword : dt_word); +} + +// return operand width in form of dt_... constant +inline op_dtype_t operand_dtype(const insn_t &insn) +{ + return char(op64(insn) ? dt_qword : op32(insn) ? dt_dword : op16(insn) ? dt_word : dt_byte); +} + +inline bool is_io_insn(const insn_t &insn) +{ + return insn.itype == NN_ins + || insn.itype == NN_outs + || insn.itype == NN_out + || insn.itype == NN_in; +} + +//--------------------------------- +#define PROCMOD_NAME pc +#define PROCMOD_NODE_NAME "$ vmm functions" +#define IDPFLAGS_NODE_NAME "$ idpflags" +#define EXC_NODE_NAME "$ ExceptionInfo $" +#define BP_NODE_NAME "$ Bdsc $" +#define WRONG_DECISIONS_NODE_NAME "$ handled wrong decisions" + +const char callee_tag = 'A'; +const char fbase_tag = 'b'; +const char frame_tag = 'f'; +const char purge_tag = 'p'; +const char ret_tag = 'r'; +const char pushinfo_tag = 's'; +const char is_ptr_tag = 'P'; +const char finally_tag = 'F'; +const char handler_tag = 'h'; +const char vxd_tag1 = 'V'; +const char vxd_tag2 = 'W'; +const char maybe_switch_tag = 'j'; + +// fbase reg is a register used to access data for the current function +// it is usually initialized by __i686_get_pc_thunk() function + +struct fbase_reg_t +{ + ea_t value; + ea_t minea; // address where the fbase reg is defined + int16 reg; +}; + +// the second operand of lea instruction should not be treated as memory reference +// unless there is cs: prefix or the user has specified 'offset' flag +// in other cases lea is used for arbirary calculations +inline bool is_arith_lea(const insn_t &insn, const op_t &x) +{ + return insn.itype == NN_lea + && x.segrg != R_cs + && !is_off(get_flags(insn.ea), x.n); +} + +inline bool is_push_ecx(uchar b) +{ + return b == 0x51; // push ecx +} + +inline bool is_push_eax(uchar b) +{ + return b == 0x50; // push eax +} + +inline bool is_push_edx(uchar b) +{ + return b == 0x52; // push edx +} + +inline bool is_push_ebx(uchar b) +{ + return b == 0x53; // push ebx +} + +inline bool is_volatile_reg(int r) +{ + return r != R_bx + && r != R_bp + && r != R_si + && r != R_di + && r != R_r12 + && r != R_r13 + && r != R_r14 + && r != R_r15; +} + +//------------------------------------------------------------------ +struct pushreg_t +{ + ea_t ea; // instruction ea + sval_t off; // offset from the frame top (sp delta) + sval_t width; // register width (or number of allocated bytes) + regnum_t reg; // register number (R_none means stack space allocation) + uint16 flags; // additional flags +#define PRF_NONE 0x0000 // Entry describes a push or an allocation +#define PRF_MOVE 0x0001 // Entry describes a register save by a move instruction +#define PRF_SPILL 0x0002 // Indicates that entry is located before local stack region +#define PRF_MASK (PRF_MOVE | PRF_SPILL) +}; + +struct pushinfo_t +{ + enum { PUSHINFO_VERSION = 4 }; + int flags; +#define PINF_SEHCALL 0x0001 // call to SEH_prolog is present +#define PINF_SEHMAN 0x0002 // Manual SEH setup +#define PINF_COOKIE 0x0004 // Has security cookie +#define PINF_ALIGNED 0x0008 // Lvars are align stred (visual studio) +#define PINF_VARARG 0x0010 // Vararg prolog (currently used for gcc64) +#define PINF_BPOFF 0x0020 // xmm_stkoff/reg_stkoff are from rbp (otherwise from rsp) +#define PINF_HAVE_SSIZE 0x0040 // pushinfo_t structure contains its own size (field 'cb') +#define PINF_PSI_FLAGS 0x0080 // pushreg_t structure contains flags field + qvector psi; // stack allocation instructions + ssize_t bpidx = -1; // index into psi + uint32 spoiled = 0; // bitmask of spoiled registers at the end of prolog + + eavec_t prolog_insns; // additional prolog instruction addresses + // (in addition to instructions from psi) + + typedef qvector pop_info_t; + pop_info_t pops; // pop insns for pushs (indexes shifted by one) + // in other words, this is epilog instructions + // index 0: epilog insns not linked to a push insn + // 1..psi.size(): epilog insns for each push insn + // usually there will be only one pop for each push. + // but there might be several pops for each push. + // (because the function has several returns) + + int eh_type; // function has exception handling + // low 16 bits: type, high 16 bits: version +#define EH_NONE 0 // no EH found +#define EH_VCSEH 1 // SEH (__except_handlerN, __SEH_prologN) +#define EH_VCCPPEH 2 // MSVC C++ EH (_EH_prolog[N]) + int seh_ver() + { + if ( (eh_type & 0xFFFF) == EH_VCSEH ) + return (eh_type >> 16) & 0xFFFF; + return 0; + } + int eh_ver() + { + if ( (eh_type & 0xFFFF) == EH_VCCPPEH ) + return (eh_type >> 16) & 0xFFFF; + return 0; + } + + ea_t eh_info = BADADDR; // for SEH: scopetable address, for C++ EH: __ehhandler address + + // for gcc64 vararg (see PINF_VARARG): + sval_t xmm_stkoff = 0; // offset from ebp to xmm savearea + sval_t reg_stkoff = 0; // offset from ebp to gpreg savearea + // these 2 offsets are either from rsp or rbp + // see PINF_BPOFF for that + int xmm_nsaved = 0; // number of saved xmm regs + int reg_nsaved = 0; // number of saved general purpose regs + + int cb = sizeof(pushinfo_t); // size of this structure + + pushinfo_t(void) : flags(PINF_HAVE_SSIZE|PINF_PSI_FLAGS), eh_type(EH_NONE) {} +}; + +enum spec_func_type_t +{ + SF_NONE, + SF_EH_PROLOG, + SF_SEH_PROLOG, + SF_SEH_EPILOG, + SF_ALLOCA, + SF_CHK, + SF_SYSINIT, + SF_EH_EPILOG, + SF_LSTRCATN, +}; + +inline bool is_mingw_abi(void) +{ + if ( default_compiler() != COMP_MS ) + return false; // "mingw" abi can be defined only for MSVC + qstring abiname; + get_abi_name(&abiname); + return abiname == "mingw"; +} + +inline bool is_msabi() +{ + comp_t comp = default_compiler(); + return comp == COMP_MS || comp == COMP_UNK && inf_get_filetype() == f_PE; +} + +inline bool is_msabi(callcnv_t cc) +{ + return cc == CM_CC_STDCALL || is_msabi(); +} + +inline int pc_shadow_area_size(callcnv_t cc) +{ + return inf_is_64bit() && is_msabi(cc) ? 4 * 8 : 0; +} + +struct regval_t; +typedef const regval_t &idaapi getreg_t(const char *name, const regval_t *regvalues); + +// Structure where information about a mmx/xmm/ymm type is returned +struct mmtype_t +{ + const char *name; + const type_t *type; + const type_t *fields; + tinfo_t tif; +}; + +//---------------------------------------------------------------------- +// The following events are supported by the PC module in the processor_t::notify() function +namespace pc_module_t +{ + enum event_codes_t + { + ev_set_difbase = processor_t::ev_loader, + // set AFIDP_DIFBASE flag + // in: int onoff + // Returns: nothing + ev_restore_pushinfo,// Restore function prolog info from the database + // in: pushinfo_t *pi + // ea_t func_start + // Returns: 1-ok, otherwise-failed + ev_save_pushinfo, // Save function prolog info to the database + // in: ea_t func_start + // pushinfo_t *pi + // Returns: 1-ok, otherwise-failed + ev_prolog_analyzed, // This event is generated by the PC module + // at the end of prolog analysis. Plugins may + // hook to it and improve the analysis. + // in: ea_t first_past_prolog_insn + // pushinfo_t *pi + // Returns: 1-ok, 2-ok but do not automatically verify epilog + ev_verify_epilog, // Verify function epilog + // in: int *answer + // pushinfo_t *pi + // const insn_t *insn + // 'insn' structure must be filled with the first epilog instruction + // number of verified epilog instructions will be in the 'answer' + // returns: 1-ok, otherwise-failed + obsolete_ev_find_reg_value, // not used anymore, use ev_find_reg_value + ev_dbgtools_path, // Returns the configuration value of the debugging tools path (from IDA.CFG) + // in: char *path + // size_t path_size + // returns: 1-if value is set, 0-if value not set in IDA.CFG + ev_is_get_pc_thunk, // Detect get_pc_thunk calls + // in: RegNo *p_reg, + // ea_t *p_end + // const insn_t *ins + // returns: 1-found, -1-not found, 0-not implemented + + ev_vxd_loaded, // notification: a virtual device driver (Vxd) is loaded + + ev_get_borland_template_node, + // out: netnode *node + // returns: 1-found, -1-not found + ev_clear_borland_template_node, + // returns: nothing + ev_borland_template,// Applies Borland RTTI template for the given address + // in: ea_t ea, + // bool bp_mode if false - bc + // bool recursive + // returns: 1-created, -1-not created + ev_get_segval, // Get segment for the specified instruction operand + // in: ea_t *out, + // const insn_t *insn, + // const op_t *x + // returns: 1-success + ev_get_idpflags, // Get idpflags + // in: uint32 *idpflags + // returns: 1 success, fill IDPFLAGS + ev_get_ret_target, // Some 'ret' insns do not return from the function but are used for short jumps + // (for example: push off; ret). The following functions mark such 'ret' instructions. + // in: ea_t ea + // ea_t *target + // returns: 1 success, fill TARGET + ev_set_ret_target, // in: ea_t ea + // ea_t target + ev_del_ret_target, // in: ea_t ea + }; + + inline processor_t::event_t idp_ev(event_codes_t ev) + { + return processor_t::event_t(ev); + } + + inline void set_difbase(int onoff) + { + processor_t::notify(idp_ev(ev_set_difbase), onoff); + } + + inline bool restore_pushinfo(pushinfo_t *pi, ea_t func_start) + { + return processor_t::notify(idp_ev(ev_restore_pushinfo), pi, func_start) == 1; + } + + inline bool save_pushinfo(ea_t func_start, pushinfo_t *pi) + { + return processor_t::notify(idp_ev(ev_save_pushinfo), func_start, pi) == 1; + } + + inline int prolog_analyzed(ea_t first_past_prolog_insn, pushinfo_t *pi) + { + return processor_t::notify(idp_ev(ev_prolog_analyzed), first_past_prolog_insn, pi); + } + + inline bool verify_epilog(int *answer, pushinfo_t *pi, const insn_t &insn) + { + return processor_t::notify(idp_ev(ev_verify_epilog), answer, pi, &insn) == 1; + } + + inline bool dbgtools_path(char *path, size_t path_size) + { + return processor_t::notify(idp_ev(ev_dbgtools_path), path, path_size) == 1; + } + + inline int is_get_pc_thunk(RegNo *p_reg, ea_t *p_end, const insn_t &insn) + { + return processor_t::notify(idp_ev(ev_is_get_pc_thunk), p_reg, p_end, &insn); + } + + inline int vxd_loaded() + { + return processor_t::notify(idp_ev(ev_vxd_loaded)); + } + + inline bool get_borland_template_node(netnode *node) + { + return processor_t::notify(idp_ev(ev_get_borland_template_node), node) > 0; + } + + inline void clear_borland_template_node(void) + { + processor_t::notify(idp_ev(ev_clear_borland_template_node)); + } + + inline bool borland_template(ea_t ea, bool bp_mode, bool recursive) + { + return processor_t::notify(idp_ev(ev_borland_template), + ea, + bp_mode, + recursive) > 0; + } + + inline ea_t get_segval(const insn_t &insn, const op_t &x) + { + ea_t ea = BADADDR; + processor_t::notify(idp_ev(ev_get_segval), &ea, &insn, &x); + return ea; + } + + inline uint32 get_idpflags() + { + uint32 idpflags; + processor_t::notify(idp_ev(ev_get_idpflags), &idpflags); + return idpflags; + } + + inline bool get_ret_target(ea_t ea, ea_t *target) + { + return processor_t::notify(idp_ev(ev_get_ret_target), ea, target) == 1; + } + + inline void set_ret_target(ea_t ea, ea_t target) + { + processor_t::notify(idp_ev(ev_set_ret_target), ea, target); + } + + inline void del_ret_target(ea_t ea) + { + processor_t::notify(idp_ev(ev_del_ret_target), ea); + } + +} + +//------------------------------------------------------------------------- +#define AFIDP_PUSH 0x0001 // push seg; push num; is converted to offset +#define AFIDP_NOP 0x0002 // db 90h after jmp is converted to nop + +#define AFIDP_MOVOFF 0x0004 // mov reg, numoff <- convert to offset + // mov segreg, immseg + +#define AFIDP_MOVOFF2 0x0008 // mov z, numoff <- convert to offset + // mov z, immseg + // where z - o_mem, o_displ +#define AFIDP_ZEROINS 0x0010 // allow zero opcode instructions: + // add [bx+si], al (16bit) + // add [eax], al (32bit) + // add [rax], al (64bit) + +#define AFIDP_BRTTI 0x0020 // Advanced analysis of Borlands RTTI +#define AFIDP_UNKRTTI 0x0040 // -"- with 'unknown_libname' +#define AFIDP_EXPFUNC 0x0080 // for PE? bc(ms?) - expanding + // function (exception subblock) +#define AFIDP_DIFBASE 0x0100 // Allow references with different segment bases +#define AFIDP_NOPREF 0x0200 // Don't display superfluous prefixes +#define AFIDP_NOVXD 0x0400 // Don't interpret int 20 as VxDcall +#define AFIDP_NOFPEMU 0x0800 // Disable FPU emulation instructions +#define AFIDP_SHOWRIP 0x1000 // Explicit RIP-addressing +#define AFIDP_NOSEH 0x2000 // Disable SEH/EH analysis +#define AFIDP_INT3STOP 0x4000 // int 3 may stop code flow + // call + // int 3 <- this is likely a no-return guard +#define AFIDP_NOAGGRJMPS 0x8000 // Don't aggressively convert jumps to thunk functions + // 'NO' is used to simplify upgrading existing idbs +#define AFIDP_NO_USE_ANY_IND_OP 0x10000 // resolve indirect operands of 'jmpni/callni' + // only for read-only memory + // 'NO' is used to simplify upgrading existing idbs + +inline bool should_af_push(void) { return (pc_module_t::get_idpflags() & AFIDP_PUSH) != 0; } +inline bool should_af_nop(void) { return (pc_module_t::get_idpflags() & AFIDP_NOP) != 0; } +inline bool should_af_movoff(void) { return (pc_module_t::get_idpflags() & AFIDP_MOVOFF) != 0; } +inline bool should_af_movoff2(void) { return (pc_module_t::get_idpflags() & AFIDP_MOVOFF2) != 0; } +inline bool should_af_zeroins(void) { return (pc_module_t::get_idpflags() & AFIDP_ZEROINS) != 0; } +inline bool should_af_brtti(void) { return (pc_module_t::get_idpflags() & AFIDP_BRTTI) != 0; } +inline bool should_af_urtti(void) { return (pc_module_t::get_idpflags() & AFIDP_UNKRTTI) != 0; } +inline bool should_af_fexp(void) { return (pc_module_t::get_idpflags() & AFIDP_EXPFUNC) != 0; } +inline bool should_af_difbase(void) { return (pc_module_t::get_idpflags() & AFIDP_DIFBASE) != 0; } +inline bool should_af_nopref(void) { return (pc_module_t::get_idpflags() & AFIDP_NOPREF) != 0; } +inline bool should_af_vxd(void) { return (pc_module_t::get_idpflags() & AFIDP_NOVXD) == 0; } +inline bool should_af_fpemu(void) { return (pc_module_t::get_idpflags() & AFIDP_NOFPEMU) == 0; } +inline bool should_af_showrip(void) { return (pc_module_t::get_idpflags() & AFIDP_SHOWRIP) != 0; } +inline bool should_af_seh(void) { return (pc_module_t::get_idpflags() & AFIDP_NOSEH) == 0; } +inline bool should_af_int3stop(void) { return (pc_module_t::get_idpflags() & AFIDP_INT3STOP) != 0; } +inline bool should_af_aggrjmps(void) { return (pc_module_t::get_idpflags() & AFIDP_NOAGGRJMPS) == 0; } +inline bool should_af_use_any_ind_op(void) { return (pc_module_t::get_idpflags() & AFIDP_NO_USE_ANY_IND_OP) == 0; } + +//------------------------------------------------------------------------- +inline bool get_ret_target(ea_t ea, ea_t *target) { return pc_module_t::get_ret_target(ea, target); } +inline void set_ret_target(ea_t ea, ea_t target) { return pc_module_t::set_ret_target(ea, target); } +inline void del_ret_target(ea_t ea) { return pc_module_t::del_ret_target(ea); } + +//------------------------------------------------------------------------- +// Don't use the following define's with underscores at the start! +#define _PT_486p 0x00000001 +#define _PT_486r 0x00000002 +#define _PT_386p 0x00000004 +#define _PT_386r 0x00000008 +#define _PT_286p 0x00000010 +#define _PT_286r 0x00000020 +#define _PT_086 0x00000040 +#define _PT_586p 0x00000080 // Pentium real mode +#define _PT_586r 0x00000100 // Pentium protected mode +#define _PT_686r 0x00000200 // Pentium Pro real +#define _PT_686p 0x00000400 // Pentium Pro protected +#define _PT_mmx 0x00000800 // MMX extensions +#define _PT_pii 0x00001000 // Pentium II +#define _PT_3d 0x00002000 // 3DNow! extensions +#define _PT_piii 0x00004000 // Pentium III +#define _PT_k7 0x00008000 // AMD K7 +#define _PT_p4 0x00010000 // Pentium 4 +#define _PT_sse3 0x00020000 // SSE3 + SSSE3 +#define _PT_sse4 0x00040000 // SSE4.1 + SSE4.2 + +// +// The following values mean 'is XXX processor or better?' +// + +#define PT_sse4 _PT_sse4 +#define PT_sse3 (_PT_sse3 | _PT_sse4 ) +#define PT_p4 ( PT_sse3 | _PT_p4 ) +#define PT_k7 ( PT_p4 | _PT_k7 ) +#define PT_piii ( PT_k7 | _PT_piii ) +#define PT_k62 ( PT_piii | _PT_3d ) +#define PT_3d _PT_3d +#define PT_pii ( PT_piii | _PT_pii ) +#define PT_mmx (_PT_mmx | _PT_3d ) +#define PT_686p ( PT_pii | _PT_686p ) +#define PT_686r ( PT_686p | _PT_686r ) +#define PT_586p ( PT_686r | _PT_586p ) +#define PT_586r ( PT_586p | _PT_586r ) +#define PT_486p ( PT_586r | _PT_486p ) +#define PT_486r ( PT_486p | _PT_486r ) +#define PT_386p ( PT_486r | _PT_386p ) +#define PT_386r ( PT_386p | _PT_386r ) +#define PT_286p ( PT_386r | _PT_286p ) +#define PT_286r ( PT_286p | _PT_286r ) +#define PT_086 ( PT_286r | _PT_086 ) + +// +// The following values mean 'is exactly XXX processor?' +// + +#define PT_ismmx (_PT_mmx ) +#define PT_is686 (_PT_686r | _PT_686p) +#define PT_is586 (_PT_586r | _PT_586p) +#define PT_is486 (_PT_486r | _PT_486p) +#define PT_is386 (_PT_386r | _PT_386p) +#define PT_is286 (_PT_286r | _PT_286p) +#define PT_is086 (_PT_086) + +//--------------------------------------------------------------------- +inline bool isProtected(uint32 type) +{ + return (type + & (_PT_286p + | _PT_386p + | _PT_486p + | _PT_586p + | _PT_686p + | _PT_pii)) != 0; +} + +inline bool isAMD(uint32 type) { return (type & PT_k7 ) != 0; } +inline bool isp4(uint32 type) { return (type & PT_p4 ) != 0; } +inline bool isp3(uint32 type) { return (type & PT_piii) != 0; } +inline bool is3dnow(uint32 type) { return (type & PT_3d ) != 0; } +inline bool ismmx(uint32 type) { return (type & PT_mmx ) != 0; } +inline bool isp2(uint32 type) { return (type & PT_pii ) != 0; } +inline bool is686(uint32 type) { return (type & PT_686r) != 0; } +inline bool is586(uint32 type) { return (type & PT_586r) != 0; } +inline bool is486(uint32 type) { return (type & PT_486r) != 0; } +inline bool is386(uint32 type) { return (type & PT_386r) != 0; } // is 386 or better ? +inline bool is286(uint32 type) { return (type & PT_286r) != 0; } // is 286 or better ? + +#endif // _INTEL_HPP + +``` + +`IdaSDK/jumptable.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef __JUMPTABLE_HPP +#define __JUMPTABLE_HPP + +#include +#include // op_t +#include // switch_info_t, jumptable_info_t + +// Class to check for a jump table sequence. +// This class should be used in preference to the hard encoding of jump table sequences +// because it allows for: +// - instruction rescheduling +// - intermingling the jump sequence with other instructions +// - sequence variants +// +// For this class: +// all instructions of the sequence are numbered starting from the last instruction. +// The last instruction has the number 0. +// The instruction before the last instruciton has the number 1, etc. +// There is a virtual function jpiN() for each instruction of the sequence +// These functions return true if 'insn' is filled with the required instruction +// +// The comparison is made in the match() function: +// +// ea points to the last instruction of the sequence (instruction #0) +// +// the 'depends' array contains dependencies between the instructions of the sequence. +// For example: +// ARM thumb LDRH switch +// 7 SUB Ra, #minv (optional) +// 6 CMP Ra, #size +// 5 BCS defea +// 4 ADR Rb, jt +// 3 ADD Rb, Rb, Ra +// 2 LDRH Rb, [Rb,Ra] +// 1 LSL Rb, Rb, #1 +// 0 ADD PC, Rb +// In this sequence, instruction #0 depends on the value of Rb which is produced +// by the instruction #1. So, the instruction #0 depends on #1. Therefore, depends[0] +// will contain '1' as its element. +// The instruction #3 depends on 2 registers: Ra and Rb, or in other words, +// it depends on the instructions #4 and #6. Therefore, depends[2] will contain { 4, 6 } +// Maximum 4 dependencies per instruction are allowed. +// +// FIXME +// The 'roots' array contains the first instruction of the dependency chains. +// In our case we can say that there are 2 dependency chains: +// 0 -> 1 -> 2 -> 3 -> 4 +// -> 6 -> 7 +// 5 -> 6 +// Therefore the roots array will consist of {1, 5}. +// 0 denotes the end of the chain and cannot be the root of a dependency chain +// Usually 1 is a root of any jump sequence. +// +// The dependency array allows for checking for optimized sequences of instructions. +// If 2 instructions are not dependent on each other, they may appear in any order. +// (for example, the instruction #4 and the instruction sequence #5-6-7 may appear +// in any order because they do not depend on each other) +// Also any other instructions not modifying the register values may appear between +// the instructions of the sequence (due to the instruction rescheduling performed +// by the compiler). +// +// Provision for optional instructions: +// The presence of an optional instruction in the sequence (like #7) is signalled +// by a negative number of the dependency in the 'depends' array. +// +// Provision for variable instructions: +// In some cases several variants of the same instructions may be supported. +// For example, the instruction #5 might be BCS as well as BGE. It is the job of +// the jpi5() function to check for all variants. +// +// In order to use the 'jump_pattern_t' class you should derive another class from it +// and define the jpiN() virtual functions. +// Then you have to define the 'depends' and 'roots' arrays and call the match() +// function. +// If you processor contains instructions who modify registers in peculiar ways +// you might want to override the check_spoiled() function. + + +//---------------------------------------------------------------------- +// Macro to declare implementation of methods of jump_pattern_t +class jump_pattern_t; +// tracked registers +// We use the 'size' term to denote the number of bits involved in the insn. +// E.g. an operand of type dt_byte has 8-bit size. +// We store the current size (the number of used bits) in the DTYPE field +// of the 'op_t' structure. It may differ from the size of operand in the +// insn. See the comment for set_moved(). +// We extend the 'op_dtype_t' type by some negative constants to denote +// sizes from 2 to 7 bits. +typedef qvector tracked_regs_t; +#define DECLARE_JUMP_PATTERN_HELPERS(decl)\ +decl void ida_export check_spoiled_jpt(const jump_pattern_t *_this, tracked_regs_t *_regs); \ +decl bool ida_export match_jpt(jump_pattern_t *_this);\ +decl bool ida_export same_value_jpt(jump_pattern_t *_this, const op_t &op, int r_i);\ +decl bool ida_export track_value_until_address_jpt(jump_pattern_t *_this, op_t *op, ea_t ea);\ +decl void ida_export combine_regs_jpt(jump_pattern_t *_this, tracked_regs_t *dst, const tracked_regs_t &src, ea_t ea);\ +decl void ida_export mark_switch_insns_jpt(const jump_pattern_t *_this, int last, int);\ +decl bool ida_export set_moved_jpt(const jump_pattern_t *_this, const op_t &dst, const op_t &src, tracked_regs_t &_regs, op_dtype_t real_dst_dtype, op_dtype_t real_src_dtype); + +DECLARE_JUMP_PATTERN_HELPERS(idaman) + +class jump_pattern_t +{ +protected: + // 32-bit operand generates a 32-bit result, zero- or sign-extended to a + // 64-bit result. This flag may be overwritten in processor modules. + // For example: + // ARM: MOV W8, #0x3C will clear the upper 32 bits of X8, + // PC : mov eax, 3Ch will clear the upper 32 bits of rax + bool modifying_r32_spoils_r64; + +public: + typedef bool (jump_pattern_t::*check_insn_t)(void); + inline jump_pattern_t( + switch_info_t *si, // may be nullptr + const char (*depends)[4], + int last_reg); + + insn_t insn; // current instruction + switch_info_t *si; // answers will be here + + enum + { + NINS = 16, // the maximum length of the sequence + INS_MASK = 0x0F, + }; + ea_t eas[NINS]; + bool skip[NINS]; // do not check the Nth insn if skip[N] is true + int non_spoiled_reg; // if non_spoiled_reg was spoiled then we stop + // matching + check_insn_t check[NINS]; + // this is the hidden return value of the jpiN() methods. If it is set and + // jpiN() returned 'true' then we stop processing the dependency chain. If + // it is set and jpiN() returned 'false' then we stop checking the insns + // in the current basic block and we are switching to the next one (and we + // fail if there is no such block). + bool stop_matching; + // this flag can be analyzed by jpiN(). It means that the current insn is + // in the linear flow from the previous insn. It is always 'true' if the + // insn has JPT_NEAR flag. + bool in_linear_flow; + // this address can be analyzed by jpiN(). It means the end of the current + // block. It may help if we want to check in-block jumps. + ea_t block_end; + + #define JPT_OPT 0x10 // the dependent insn might be missing + #define JPT_NEAR 0x20 // the dependent insn must be in the linear flow + + const char (*depends)[4]; // instruction, on which we depend, and + // additional JPT_... flags + + // mark swith instructions to be ignored by the decompiler + // do not mark the indirect jmp (eas[0]) as ignored + // it will be used to recognize switch idioms + // unmark NLOWCASE insns after LAST (in the case of SWI_HXNOLOWCASE flag) + void mark_switch_insns(int last = NINS - 1, int nlowcase = 0) const + { + mark_switch_insns_jpt(this, last, nlowcase); + } + + // for fragmented switch idioms, cmp/jbe might be located in a separate + // fragment. we must not mark these instructions as part of the switch + // idiom because doing so would spoil the program logic for the decompiler + // and make the switch operator unreachable. the following vector keeps + // addresses of all instructions which must not be marked. this vector is + // maintained by derived classes. + eavec_t remote_code; + // extra insns used to calculate values (discovered by find_op_value) + eavec_t extra_insn_eas; + // tracked registers + tracked_regs_t regs; + + // handle a possible delay slot situation + // while walking backwards in the execution flow + // if is false and is in a delay + // slot of a branch likely instruction + // then set to the branch instruction + // (=annul the delay slot) + // if is true and the instruction at + // has a delay slot then set to the delay slot + // (=execute the delay slot) + virtual void process_delay_slot(ea_t &/*ea*/, bool /*branch*/) const {} + + enum + { + // an artificial operand type to track the address of the conditional jump + // .addr - address of the conditional jump + // .specval - address of the default case + // .specflag1 - the conditional jump flags (see below cc_*) + // the derived class can use .reg to track the condition register + o_condjump = 99, + cc_inc_ncases = 0x01, // increment ncases + cc_check_max_ncases = 0x02, // comparison with the maximum value + + // an artificial operand type to count something + // .value - the counter + o_count = 100, + }; + + // compare supported operands + virtual bool equal_ops(const op_t &x, const op_t &y) const + { + if ( x.type != y.type ) + return false; + switch ( x.type ) + { + case o_void: + // consider spoiled values as not equal + return false; + case o_reg: + // ignore difference in the data size of registers + return x.reg == y.reg; + case o_count: + return x.value == y.value; + case o_condjump: + // we do not track the condition flags + return true; + } + return false; + } + + // return true if the instruction `insn' is a move one, + // there is no need check spoiled registers in this case + virtual bool handle_mov(tracked_regs_t & /*_regs*/ ) + { + return false; + } + // does the instruction `insn' spoil `_regs' ? + virtual void check_spoiled(tracked_regs_t *_regs) const + { + check_spoiled_jpt(this, _regs); + } + // some binaries use the following pattern + // xor eax, eax | mov al, cl + // so we can extend dtype of the operand from dt_byte to dt_dword + virtual op_dtype_t extend_dtype(const op_t &op) const + { + return op.dtype; // do not extend + } + + // these methods are not virtual and should be used in processor + // module only + inline void track(int reg, int r_i, op_dtype_t dtype); + inline void trackop(const op_t &op, int r_i); + inline bool is_spoiled(int r_i) { return regs[r_i].type == o_void; } + inline bool is_equal(int reg, int r_i, op_dtype_t dtype); + inline bool is_equal(const op_t &op, int r_i); + inline bool same_value(const op_t &op, int r_i); + inline bool track_value_until_address(op_t *op, ea_t ea); + + virtual bool jpi0(void) = 0; + virtual bool jpi1(void) { return false; } + virtual bool jpi2(void) { return false; } + virtual bool jpi3(void) { return false; } + virtual bool jpi4(void) { return false; } + virtual bool jpi5(void) { return false; } + virtual bool jpi6(void) { return false; } + virtual bool jpi7(void) { return false; } + virtual bool jpi8(void) { return false; } + virtual bool jpi9(void) { return false; } + virtual bool jpia(void) { return false; } + virtual bool jpib(void) { return false; } + virtual bool jpic(void) { return false; } + virtual bool jpid(void) { return false; } + virtual bool jpie(void) { return false; } + virtual bool jpif(void) { return false; } + // jpi will be called if pre_jpi returns true + virtual bool pre_jpi(int /*n*/) { return true; } + + bool match(const insn_t &_insn) { insn = _insn; return match_jpt(this); } + + // remove compiler warnings -- class with virtual functions MUST have virtual destructor + virtual ~jump_pattern_t() {} + + // helpers for mov instruction tracing (see methods handle_mov(), + // check_spoiled() above) + inline static void set_spoiled(tracked_regs_t *_regs); + inline void set_spoiled(tracked_regs_t *_regs, const op_t &op) const; + // track 'mov' insn: dst <- src + // it returns 'true' if insn changes any of the tracked registers + // REAL_DST_DTYPE is the size that will be changed in the DST operand by + // the insn. It can be greater than the operand size because some insns + // clear the upper bits. For example: + // xor eax, eax | mov ax, cx REAL_DST_DTYPE is 32 + // xor bh, bh | mov bl, cl REAL_DST_DTYPE is 16 + // Extending of the 32-bit register to 64 bits is performed automatically + // based on the modifying_r32_spoils_r64 flag. + // REAL_SRC_DTYPE is the size that will be used in the SRC operand by the + // insn. It can be less than the operand size. For example: + // ARM: AND W8, W8, #0xFF will use 8 bits of X8, + // PC : cwde will use 16 bits of rax. + bool set_moved( + const op_t &dst, + const op_t &src, + tracked_regs_t &_regs, + op_dtype_t real_dst_dtype = dt_void, + op_dtype_t real_src_dtype = dt_void) const + { + return set_moved_jpt(this, dst, src, _regs, real_dst_dtype, real_src_dtype); + } + // calculate state of registers before a conditional jump as the + // combination of states of each branch + void combine_regs( + tracked_regs_t *dst, + const tracked_regs_t &src, + ea_t ea) + { + combine_regs_jpt(this, dst, src, ea); + } + +protected: + bool match_tree(); + bool follow_tree(ea_t ea, int n); + bool same_value_impl(const op_t &op, int r_i); + bool track_value_until_address_impl(op_t *op, ea_t ea); + + inline bool equal_ops_dtype(const op_t &op, const op_t ®) const; + static inline bool is_narrower(op_dtype_t dt1, op_dtype_t dt2); + enum + { + dt_7bit = 255, + dt_6bit = 254, + dt_5bit = 253, + dt_4bit = 252, + dt_3bit = 251, + dt_2bit = 250, + }; + static inline int get_dtype_nbits(op_dtype_t dtype); + + // helper for check_spoiled() + // TODO introduce new virtual methods spoils() and spoils_flags() and + // replace check_spoiled() by non-virtual method + inline void check_spoiled_not_reg( + tracked_regs_t *_regs, + uint maxop = UA_MAXOP) const; + + DECLARE_JUMP_PATTERN_HELPERS(friend) +}; + +//---------------------------------------------------------------------- +// kinds of jump tables +enum { JT_NONE = 0, JT_SWITCH, JT_CALL }; +// It returns a nonzero JT_... kind if it found a jump pattern. This kind is +// passed to the check_table() function. +typedef int is_pattern_t(switch_info_t *si, const insn_t &insn, procmod_t *procmod); +// It returns a refined kind. For example, JT_NONE if the found jump pattern +// is not a switch, or JT_CALL if it is a call of a func from an array +typedef int table_checker_t( + switch_info_t *si, + ea_t jump_ea, + int is_pattern_res, + procmod_t *pm); +// check a flat 32/16/8 bit jump table -- the most common case +idaman int ida_export check_flat_jump_table( + switch_info_t *si, + ea_t jump_ea, + int is_pattern_res = JT_SWITCH); + +// This function finds a switch. It calls functions from the PATTERNS +// array in turn until the first one returns a nonzero value. +// If a suitable pattern is found, it calls check_table() for the final +// check, passing a nonzero result code of the 'is_pattern_t' function. +// If the CHECK_TABLE parameter is nullptr then check_flat_jump_table() is +// called. +// NAME is used for a debug output. +// It returns 'false' if INSN is not a switch or it is a call of a func from +// an array. In the latter case it defines this array. +idaman bool ida_export check_for_table_jump( + switch_info_t *si, + const insn_t &insn, + is_pattern_t *const patterns[], + size_t qty, + table_checker_t *check_table = nullptr, + const char *name = nullptr); + +//---------------------------------------------------------------------- +// sometimes the size of the jump table is misdetected +// check if any of the would-be targets point into the table +// and if so, truncate it +// if 'ignore_refs' is false, also stop at first data reference +idaman void ida_export trim_jtable( + switch_info_t *si, + ea_t jump_ea, + bool ignore_refs = false); + +//---------------------------------------------------------------------- +// this function find the size of the jump table for indirect switches +// (indirect switches have the values table which contains indexes into +// the jump table) +// in: si->ncases has the size of the values table +// out: si->jcases is initialized +idaman bool ida_export find_jtable_size(switch_info_t *si); + +//---------------------------------------------------------------------- +// get default jump address from the jump table. +// This method can be used only for a sparse nonindirect switch with default +// case in the jump table. +idaman ea_t ida_export find_defjump_from_table( + ea_t jump_ea, + const switch_info_t &si); + +//---------------------------------------------------------------------- +// get the specified target from the jump table. +idaman ea_t ida_export get_jtable_target( + ea_t jump_ea, + const switch_info_t &si, + int i); + + +//---------------------------------------------------------------------- +// iterate instructions in the backward execution flow +//lint -esym(1512,backward_flow_iterator_t*) destructor is not virtual +template +// State: default constructor, operator= +// Ctrl: combine_regs(State *, const State& ,ea_t) +// process_delay_slot(ea_t &/*ea*/, bool /*branch*/) +struct backward_flow_iterator_t +{ +public: + ea_t cur_ea; // current address + State ®s; // current state of the tracked registers + Ctrl &ctrl; // to combine state + bool only_near; // should we follow only the linear flow? + uint max_insn_cnt; + +protected: + //lint --e{958} padding is required + func_t *pfn; // to check bounds + const segment_t *seg; + ea_t start_ea; + ea_t cur_end; // end of current basic block + uint insn_cnt; + // visited basic blocks: + // key_type - start of the block, mapped_type - end of the block; + typedef std::map visited_t; + visited_t visited; + // waiting basic blocks: + // key_type - end of the block, mapped_type - state at the end; + struct state_t + { + State regs; + uint insn_cnt; + state_t() : regs(), insn_cnt(UINT_MAX) {} + }; + typedef std::map waiting_t; + waiting_t waiting; + +public: + backward_flow_iterator_t( + ea_t start_ea_, + State &start_regs, + Ctrl &ctrl_, + bool only_near_, + uint max_insn_cnt_ = 0) + : cur_ea(start_ea_), + regs(start_regs), + ctrl(ctrl_), + only_near(only_near_), + max_insn_cnt(max_insn_cnt_), + pfn(nullptr), + seg(nullptr), + start_ea(start_ea_), + cur_end(BADADDR), + insn_cnt(0), + visited(), + waiting() + { + // to check bounds + pfn = get_func(start_ea); + if ( pfn == nullptr ) + { + seg = getseg(start_ea); + QASSERT(10183, seg != nullptr); + } + } + + // fl_U : no previous instruction (start of a function or a cycle, + // or non linear flow if ONLY_NEAR is true), + // fl_F : got previous instruction by linear flow, + // fl_JF: got previous instruction by jump; + inline cref_t prev_insn(); + // stop iterating the current basic block, switch to the lowest waiting + // block + inline cref_t skip_block(); + + inline ea_t get_cur_end() const + { + return cur_end == BADADDR ? cur_ea : cur_end; + } + +protected: + // find visited basic block containing the address + // it returns the pointer to the address of the block end or nullptr + inline ea_t *find_visited(ea_t ea); + // get the lowest to start_ea waiting block + inline cref_t get_waiting(); + // combine insn counter - count the shortest path + static inline void combine_insn_cnt(uint *dst, uint src) + { + if ( src < *dst ) + *dst = src; + } + + bool check_bounds() const + { + if ( pfn != nullptr ) + return func_contains(pfn, cur_ea); + return seg->contains(cur_ea); + } +}; + +//------------------------------------------------------------------------- +// simple backward flow iterator +struct no_regs_t {}; +struct simple_bfi_t + : public backward_flow_iterator_t +{ + typedef backward_flow_iterator_t base_t; + +protected: + no_regs_t regs_; + +public: + simple_bfi_t(ea_t ea) + : base_t(ea, regs_, *this, false) {} + static void combine_regs(no_regs_t *, const no_regs_t &, ea_t) {} + static void process_delay_slot(ea_t &, bool) {} +}; + + +//====================================================================== +// inline implementation +//---------------------------------------------------------------------- + +inline jump_pattern_t::jump_pattern_t( + switch_info_t *_si, + const char (*_depends)[4], + int last_reg) + : modifying_r32_spoils_r64(true), + si(_si), + non_spoiled_reg(-1), + in_linear_flow(false), + depends(_depends), + regs() +{ + if ( si != nullptr ) + si->clear(); + regs.resize(last_reg + 1); +} + +//---------------------------------------------------------------------- +inline bool jump_pattern_t::equal_ops_dtype( + const op_t &op, + const op_t ®) const +{ + if ( !equal_ops(op, reg) ) + return false; + // operand should be wider than a tracked register + // e.g. after 'cmp cl, imm' we cannot use cx + if ( !is_narrower(op.dtype, reg.dtype) ) + return true; + // we believe that dword is widened to qword + if ( modifying_r32_spoils_r64 && op.dtype == dt_dword ) + return true; + // try to extend + if ( !is_narrower(extend_dtype(op), reg.dtype) ) + return true; + return false; +} + +//---------------------------------------------------------------------- +// return true if size1 is narrow than size2 +inline bool jump_pattern_t::is_narrower(op_dtype_t dt1, op_dtype_t dt2) +{ + if ( dt1 < dt_2bit ) + return dt2 < dt_2bit && dt1 < dt2; + else + return dt2 < dt_2bit || dt1 < dt2; +} + +//---------------------------------------------------------------------- +inline int jump_pattern_t::get_dtype_nbits(op_dtype_t dtype) +{ + switch ( dtype ) + { + case dt_byte: return 8; + case dt_word: return 16; + case dt_dword: return 32; + case dt_qword: return 64; + case dt_7bit: return 7; + case dt_6bit: return 6; + case dt_5bit: return 5; + case dt_4bit: return 4; + case dt_3bit: return 3; + case dt_2bit: return 2; + default: return -1; + } +} + +//---------------------------------------------------------------------- +inline void jump_pattern_t::check_spoiled_not_reg( + tracked_regs_t *_regs, + uint maxop) const +{ + uint32 feature = insn.get_canon_feature(PH); + if ( feature == 0 ) + return; + for ( uint i = 0; i < maxop; ++i ) + { + if ( has_cf_chg(feature, i) + && insn.ops[i].type != o_void + && insn.ops[i].type != o_reg ) + { + set_spoiled(_regs, insn.ops[i]); + } + } +} + +//---------------------------------------------------------------------- +inline void jump_pattern_t::track(int reg, int r_i, op_dtype_t dtype) +{ + regs[r_i].type = o_reg; + regs[r_i].reg = reg; + regs[r_i].dtype = dtype; +} +inline void jump_pattern_t::trackop(const op_t &op, int r_i) +{ + regs[r_i] = op; +} + +//---------------------------------------------------------------------- +inline bool jump_pattern_t::is_equal(int reg, int r_i, op_dtype_t dtype) +{ + op_t op; + op.type = o_reg; + op.reg = reg; + op.dtype = dtype; + return is_equal(op, r_i); +} +inline bool jump_pattern_t::is_equal(const op_t &op, int r_i) +{ + if ( regs[r_i].type == o_void ) + { + // there is no reason to continue match + stop_matching = true; + return false; + } + return equal_ops_dtype(op, regs[r_i]); +} + +//---------------------------------------------------------------------- +inline bool jump_pattern_t::same_value(const op_t &op, int r_i) +{ + return same_value_jpt(this, op, r_i); +} + +//---------------------------------------------------------------------- +inline bool jump_pattern_t::track_value_until_address(op_t *op, ea_t ea) +{ + return track_value_until_address_jpt(this, op, ea); +} + +//---------------------------------------------------------------------- +inline void jump_pattern_t::set_spoiled(tracked_regs_t *__regs) +{ + tracked_regs_t &_regs = *__regs; + // spoil all registers + for ( size_t i = 0; i < _regs.size(); ++i ) + _regs[i].type = o_void; +} +inline void jump_pattern_t::set_spoiled(tracked_regs_t *__regs, const op_t &op) const +{ + tracked_regs_t &_regs = *__regs; + for ( size_t i = 0; i < _regs.size(); ++i ) + if ( equal_ops(_regs[i], op) ) + _regs[i].type = o_void; // spoil register +} + +//---------------------------------------------------------------------- +// find the previous instruction in code flow +// take into account branches and potential delay slots +template +inline cref_t backward_flow_iterator_t::prev_insn() +{ + size_t refcnt = 0; + // check visited basic block + ea_t *visited_end = find_visited(cur_ea); + if ( visited_end == nullptr ) + { + // analyze references to the current address + flags64_t F32 = get_flags32(cur_ea); + if ( is_flow(F32) ) + ++refcnt; + if ( has_xref(F32) && !is_func(F32) ) // do not count jumps to function + { + xrefblk_t xb; + for ( bool ok = xb.first_to(cur_ea, XREF_NOFLOW|XREF_CODE); + ok; + ok = xb.next_to() ) + { + // count only xrefs from jumps + if ( xb.type == fl_JF || xb.type == fl_JN ) + { + if ( only_near ) + { + if ( refcnt > 0 ) + return fl_U; + // do not consider the flow through another switch as linear + if ( (get_flags32(xb.from) & FF_JUMP) != 0 ) + return fl_U; + } + ++refcnt; + ea_t ea = xb.from; + ctrl.process_delay_slot(ea, true); + // ignore jumps from already visited blocks + if ( find_visited(ea) != nullptr ) + continue; + // add basic block to the waiting set (combine state of the + // tracked registers at the jump source) + state_t &src_state = waiting[ea]; + ctrl.combine_regs(&src_state.regs, regs, ea); + combine_insn_cnt(&src_state.insn_cnt, insn_cnt); + } + } + } + + if ( cur_end == BADADDR ) + cur_end = cur_ea; + + // try ordinary flow + if ( is_flow(F32) ) + { + ea_t prev_ea = prev_not_tail(cur_ea); + if ( prev_ea != BADADDR ) + { + cur_ea = prev_ea; + if ( check_bounds() + && (max_insn_cnt == 0 || insn_cnt < max_insn_cnt) ) + { + ++insn_cnt; + // remove reached waiting basic block + typename waiting_t::iterator w = waiting.find(cur_ea); + if ( w != waiting.end() ) + { + ctrl.combine_regs(®s, w->second.regs, cur_ea); + combine_insn_cnt(&insn_cnt, w->second.insn_cnt); + waiting.erase(w); + } + else + { + ctrl.process_delay_slot(cur_ea, false); + } + return fl_F; + } + } + // choose another branch + } + + // save block [cur_ea, cur_end] as visited + visited[cur_ea] = cur_end; + } + else if ( cur_end != BADADDR ) + { + // reach visited basic block => extend it + *visited_end = cur_end; + } + + // get the lowest waiting block + cref_t ret = get_waiting(); + // consider one xref as a linear flow + if ( ret == fl_JF && refcnt == 1 && waiting.empty() ) + ret = fl_F; + return ret; +} + +//---------------------------------------------------------------------- +template +inline cref_t backward_flow_iterator_t::skip_block() +{ + // check visited basic block + ea_t *visited_end = find_visited(cur_ea); + if ( visited_end == nullptr ) + { + if ( cur_end == BADADDR ) + cur_end = cur_ea; + // save block [cur_ea, cur_end] as visited + visited[cur_ea] = cur_end; + } + else if ( cur_end != BADADDR ) + { + // reach visited basic block => extend it + *visited_end = cur_end; + } + + // get the lowest waiting block + return get_waiting(); +} + +//---------------------------------------------------------------------- +template +inline cref_t backward_flow_iterator_t::get_waiting() +{ + while ( !waiting.empty() ) + { + typename waiting_t::iterator w = waiting.upper_bound(start_ea); + if ( w != waiting.begin() ) + --w; + cur_ea = w->first; + if ( check_bounds() ) + { + cur_end = BADADDR; + regs = w->second.regs; + insn_cnt = w->second.insn_cnt; + waiting.erase(w); + return fl_JF; + } + waiting.erase(w); + } + return fl_U; +} + +//---------------------------------------------------------------------- +template +inline ea_t *backward_flow_iterator_t::find_visited(ea_t ea) +{ + visited_t::iterator v = visited.upper_bound(ea); + // assert: v == visited.end() || v->first > ea + if ( v == visited.begin() ) + return nullptr; + --v; + // assert: v->first <= ea + if ( ea > v->second ) + return nullptr; + return &v->second; +} + + +#endif + +``` + +`IdaSDK/kernwin.hpp`: + +```hpp + /* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +/*! \file kernwin.hpp + + \brief Defines the interface between the kernel and the UI. + + It contains: + - the UI dispatcher notification codes (::ui_notification_t) + - convenience functions for UI services + - structures which hold information about the + lines (disassembly, structures, enums) generated + by the kernel + - functions to interact with the user (dialog boxes) + - some string and conversion functions. +*/ + +#ifndef __KERNWIN_HPP +#define __KERNWIN_HPP + +#ifndef SWIG +typedef uchar color_t; ///< see +struct rangevec_t; ///< see +class location_t; ///< see +struct lochist_entry_t; ///< see +struct strwinsetup_t; ///< see +struct renderer_info_t; ///< see +struct segm_move_infos_t; ///< see +struct load_info_t; ///< see +#endif // SWIG + +/// Message box kinds +enum mbox_kind_t +{ + mbox_internal, ///< internal error + mbox_info, + mbox_warning, + mbox_error, + mbox_nomem, + mbox_feedback, + mbox_readerror, + mbox_writeerror, + mbox_filestruct, + mbox_wait, + mbox_hide, + mbox_replace, +}; + + +/// List chooser types +enum choose_type_t +{ + chtype_generic, ///< the generic choose() function + chtype_idasgn, ///< see choose_idasgn() + chtype_entry, ///< see choose_entry() + chtype_name, ///< see choose_name() + chtype_stkvar_xref, ///< see choose_stkvar_xref() + chtype_xref, ///< see choose_xref() + chtype_func, ///< see choose_func() + chtype_segm, ///< see choose_segm() + chtype_strpath, ///< see choose_struc_path() + chtype_idatil, ///< see choose_til() + chtype_srcp, ///< see choose_srcp() + chtype_struct, ///< see choose_struct() + chtype_enum, ///< see choose_enum() + chtype_enum_by_value_and_size, ///< see choose_enum_by_value() +}; + + +enum beep_t ///< Beep types +{ + beep_default = 0 +}; + + +// Notify UI about various events. The kernel will call this function +// when something interesting for the UI happens. +// The UI should avoid calling the kernel from this callback. + +class func_t; +class segment_t; +struct sreg_range_t; +class plugin_t; +struct plugmod_t; +struct procmod_t; +class minsn_t; +class idc_value_t; +class linput_t; +class snapshot_t; +class tinfo_t; +struct til_t; + +/// TWidget renderer type +enum tcc_renderer_type_t +{ + TCCRT_INVALID = 0, ///< invalid + TCCRT_FLAT, ///< flat view + TCCRT_GRAPH, ///< graph view + TCCRT_PROXIMITY ///< proximity view +}; + +/// TWidget ::place_t type +enum tcc_place_type_t +{ + TCCPT_INVALID = 0, ///< invalid + TCCPT_PLACE, ///< ::place_t + TCCPT_SIMPLELINE_PLACE, ///< ::simpleline_place_t + TCCPT_IDAPLACE, ///< ::idaplace_t + TCCPT_TIPLACE, ///< ::tiplace_t +}; + +/// Represents mouse button for view_mouse_event_t objects +enum vme_button_t +{ + VME_UNKNOWN, ///< unknown mouse button + VME_LEFT_BUTTON, ///< left mouse button + VME_RIGHT_BUTTON, ///< right mouse button + VME_MID_BUTTON, ///< middle mouse button +}; + +//------------------------------------------------------------------------- +/// \defgroup SETMENU_ Set menu flags +/// Passed as 'flags' parameter to attach_action_to_menu() +/// In case menupath == nullptr new item will be added to the end of menu even when +/// SETMENU_APP is not set. SETMENU_FIRST can be used to change this behaviour +/// Note: The upper 16 bits are reserved for UI internal use. +///@{ +#define SETMENU_POSMASK 0x3 +#define SETMENU_INS 0x0 ///< add menu item before the specified path (default) +#define SETMENU_APP 0x1 ///< add menu item after the specified path +#define SETMENU_FIRST 0x2 ///< add item to the beginning of menu +#define SETMENU_ENSURE_SEP 0x8 ///< make sure there is a separator before the action +///@} + +/// \defgroup CREATETB_ create toolbar flags +/// Passed as 'flags' parameter to create_toolbar() +///@{ +#define CREATETB_ADV 0x1 ///< toolbar is for 'advanced mode' only +///@} + +//------------------------------------------------------------------------- +/// \defgroup HIF_ set_highlight flags +/// Passed as 'flags' parameter to set_highlight() +///@{ +#define HIF_IDENTIFIER 0x1 ///< text is an identifier (i.e., when searching for the current highlight, SEARCH_IDENT will be used) +#define HIF_REGISTER 0x2 ///< text represents a register (aliases/subregisters will be highlit as well) +#define HIF_LOCKED 0x4 ///< locked; clicking/moving the cursor around doesn't change the highlight +#define HIF_NOCASE 0x8 ///< case insensitive + + // bits 27-31 reserved +#define HIF_USE_SLOT (1 << 27) ///< use the given number, or just use the "floating" highlight +#define HIF_SLOT_SHIFT 28 ///< position of the 3 top bits specifying which highlight to use +#define HIF_GET_SLOT(flags) (((flags) >> HIF_SLOT_SHIFT) & 0x7) ///< retrieve the highlight number to use (if HIF_USE_SLOT) + + // convenience constants + // (not a macro HIF_SLOT(n) because SWIG won't pick it up for IDAPython) +#define HIF_SLOT_0 (HIF_USE_SLOT | (0 << HIF_SLOT_SHIFT)) ///< operate on slot 0 +#define HIF_SLOT_1 (HIF_USE_SLOT | (1 << HIF_SLOT_SHIFT)) ///< operate on slot 1 +#define HIF_SLOT_2 (HIF_USE_SLOT | (2 << HIF_SLOT_SHIFT)) ///< operate on slot 2 +#define HIF_SLOT_3 (HIF_USE_SLOT | (3 << HIF_SLOT_SHIFT)) ///< operate on slot 3 +#define HIF_SLOT_4 (HIF_USE_SLOT | (4 << HIF_SLOT_SHIFT)) ///< operate on slot 4 +#define HIF_SLOT_5 (HIF_USE_SLOT | (5 << HIF_SLOT_SHIFT)) ///< operate on slot 5 +#define HIF_SLOT_6 (HIF_USE_SLOT | (6 << HIF_SLOT_SHIFT)) ///< operate on slot 6 +#define HIF_SLOT_7 (HIF_USE_SLOT | (7 << HIF_SLOT_SHIFT)) ///< operate on slot 7 +///@} + +#define REG_HINTS_MARKER SCOLOR_ON "\x7F" +#define REG_HINTS_MARKER_LEN 2 +#define SRCDBG_HINTS_MARKER SCOLOR_ON "\x7E" +#define SRCDBG_HINTS_MARKER_LEN 2 + + +/// \defgroup CDVF_ Code viewer flags +/// passed as 'flags' parameter to create_code_viewer() +///@{ +#define CDVF_NOLINES 0x0001 ///< don't show line numbers +#define CDVF_LINEICONS 0x0002 ///< icons can be drawn over the line control +#define CDVF_STATUSBAR 0x0004 ///< keep the status bar in the custom viewer +///@} + +/// \defgroup IDCHK_ IDC hotkey error codes +/// return values for add_idc_hotkey() +///@{ +#define IDCHK_OK 0 ///< ok +#define IDCHK_ARG -1 ///< bad argument(s) +#define IDCHK_KEY -2 ///< bad hotkey name +#define IDCHK_MAX -3 ///< too many IDC hotkeys +///@} + +/// \defgroup WIDGET_CLOSE Form close flags +/// passed as options to close_widget() +///@{ +#define WCLS_SAVE 0x1 ///< save state in desktop config +#define WCLS_NO_CONTEXT 0x2 ///< don't change the current context (useful for toolbars) +#define WCLS_DONT_SAVE_SIZE 0x4 ///< don't save size of the window +#define WCLS_DELETE_LATER 0x8 ///< assign the deletion of the widget to the UI loop ///< \return void +#define WCLS_CLOSE_LATER WCLS_DELETE_LATER +///@} + +/// \defgroup DP_ Docking positions +/// passed as 'orient' parameter to set_dock_pos() +///@{ +#define DP_LEFT 0x0001 ///< Dock src_form to the left of dest_form +#define DP_TOP 0x0002 ///< Dock src_form above dest_form +#define DP_RIGHT 0x0004 ///< Dock src_form to the right of dest_form +#define DP_BOTTOM 0x0008 ///< Dock src_form below dest_form +#define DP_INSIDE 0x0010 ///< Create a new tab bar with both src_form and dest_form +#define DP_TAB 0x0040 ///< Place src_form into a tab next to dest_form, + ///< if dest_form is in a tab bar + ///< (otherwise the same as #DP_INSIDE) +#define DP_BEFORE 0x0020 ///< Place src_form before dst_form in the tab bar instead of after; + ///< used with #DP_INSIDE or #DP_TAB. +#define DP_FLOATING 0x0080 ///< Make src_form floating +#define DP_SZHINT 0x0100 ///< When floating or in a splitter (i.e., not tabbed), + ///< use the widget's size hint to determine the best + ///< geometry (Qt only) +///@} + +/// \defgroup CDVF_ Code viewer flags +/// passed as 'flags' parameter to create_code_viewer() +///@{ +#define CDVF_NOLINES 0x0001 ///< don't show line numbers +#define CDVF_LINEICONS 0x0002 ///< icons can be drawn over the line control +#define CDVF_STATUSBAR 0x0004 ///< keep the status bar in the custom viewer +///@} + +/// \defgroup SVF_ Source viewer creation flags +/// passed as 'flags' parameter to callback for ::ui_create_source_viewer +///@{ +#define SVF_COPY_LINES 0x0000 ///< keep a local copy of '*lines' +#define SVF_LINES_BYPTR 0x0001 ///< remember the 'lines' ptr. do not make a copy of '*lines' +///@} + +/// \defgroup CVNF_ Custom viewer navigation flags +/// passed as 'flags' parameter to custom_viewer_jump() +///@{ +#define CVNF_LAZY (1 << 0) ///< try and move the cursor to a line displaying the + ///< place_t if possible. This might disregard the Y + ///< position in case of success +#define CVNF_JUMP (1 << 1) ///< push the current position in this viewer's + ///< lochist_t before going to the new location +#define CVNF_ACT (1 << 2) ///< activate (i.e., switch to) the viewer. + ///< Activation is performed before the new + ///< lochist_entry_t instance is actually copied + ///< to the viewer's lochist_t (otherwise, if the + ///< viewer was invisible its on_location_changed() + ///< handler wouldn't be called.) +///@} +/// \defgroup WIDGET_OPEN Widget open flags +/// passed as options to open_form() and display_widget() +///@{ +// +#define WOPN_RESTORE 0x00000004u ///< if the widget was the only widget in a floating area the + ///< last time it was closed, it will be restored as + ///< floating, with the same position+size as before +#define WOPN_PERSIST 0x00000040u ///< widget will remain available when starting or stopping debugger sessions +#define WOPN_CLOSED_BY_ESC 0x00000080u ///< override idagui.cfg:CLOSED_BY_ESC: esc will close +#define WOPN_NOT_CLOSED_BY_ESC 0x00000100u ///< override idagui.cfg:CLOSED_BY_ESC: esc will not close +#define WOPN_DP_MASK 0x0FFF0000u +#define WOPN_DP_SHIFT 16 +#define WOPN_DP_LEFT (DP_LEFT << WOPN_DP_SHIFT) + ///< Dock widget to the left of dest_ctrl +#define WOPN_DP_TOP (DP_TOP << WOPN_DP_SHIFT) + ///< Dock widget above dest_ctrl +#define WOPN_DP_RIGHT (DP_RIGHT << WOPN_DP_SHIFT) + ///< Dock widget to the right of dest_ctrl +#define WOPN_DP_BOTTOM (DP_BOTTOM << WOPN_DP_SHIFT) + ///< Dock widget below dest_ctrl +#define WOPN_DP_INSIDE (DP_INSIDE << WOPN_DP_SHIFT) + ///< Create a new tab bar with both widget and dest_ctrl +#define WOPN_DP_TAB (DP_TAB << WOPN_DP_SHIFT) + ///< Place widget into a tab next to dest_ctrl, + ///< if dest_ctrl is in a tab bar + ///< (otherwise the same as #WOPN_DP_INSIDE) +#define WOPN_DP_BEFORE (DP_BEFORE << WOPN_DP_SHIFT) + ///< Place widget before dst_form in the tab bar instead of after; + ///< used with #WOPN_DP_INSIDE and #WOPN_DP_TAB +#define WOPN_DP_FLOATING (DP_FLOATING << WOPN_DP_SHIFT) + ///< Make widget floating +#define WOPN_DP_SZHINT (DP_SZHINT << WOPN_DP_SHIFT) + ///< when floating or in a splitter (i.e., not tabbed), + ///< use the widget's size hint to determine the best + ///< geometry (Qt only) +#define WOPN_DP_INSIDE_BEFORE (WOPN_DP_INSIDE | WOPN_DP_BEFORE) +#define WOPN_DP_TAB_BEFORE (WOPN_DP_TAB | WOPN_DP_BEFORE) +#define WOPN_GET_DP(v) (((v) & WOPN_DP_MASK) >> WOPN_DP_SHIFT) +///@} + +/// \defgroup RENADDR_DIALOGS Dialogs for "Rename address" +///@{ +#define RENADDR_IDA 0 ///< dialog for "IDA View" +#define RENADDR_HR 1 ///< dialog for "Pseudocode"; + ///< additional flags: + ///< - 0x01 Library function + ///< - 0x02 Mark as decompiled +///@} + +/// \defgroup CVLF_ Custom viewer location-retrieving flags +/// passed as 'flags' parameter to get_custom_viewer_location() +///@{ +#define CVLF_USE_MOUSE 0x1 ///< Fetch the location from the mouse, instead of caret in the listing +///@} + +#ifndef SWIG +/// Callui return codes. +/// The size of this type should be 4 bytes at most, +/// otherwise different compilers return it differently +union callui_t +{ + bool cnd; + char i8; + int i; + short i16; + int32 i32; + uchar u8; + ushort u16; + uint32 u32; + char *cptr; + void *vptr; + ssize_t ssize; + func_t *fptr; + segment_t *segptr; + plugin_t *pluginptr; + sreg_range_t *sraptr; +}; + +//------------------------------------------------------------------------- +enum execute_sync_availability_t +{ + esa_unknown = 0, + esa_available, + esa_unavailable, + esa_release, + esa_app_unavailable, +}; + +//------------------------------------------------------------------------- +enum execute_sync_ctl_t +{ + esctl_none = 0, + esctl_cancel_request, + esctl_cancel_thread_requests, + esctl_set_availability, +}; + +/// Events marked as 'ui:' should be used as a parameter to callui(). +/// (See convenience functions like get_screen_ea()) +/// Events marked as 'cb:' are designed to be callbacks and should not +/// be used in callui(). The user may hook to ::HT_UI events to catch them + +enum ui_notification_t +{ + ui_null = 0, + + ui_range, ///< cb: The disassembly range has been changed (\inf{min_ea} ... \inf{max_ea}). + ///< UI should redraw the scrollbars. See also: ::ui_lock_range_refresh + ///< \param none + ///< \return void + + ui_refresh_choosers, ///< cb: The list (chooser) window contents have been changed (names, signatures, etc). + ///< UI should redraw them. Please consider request_refresh() instead + ///< \param none + ///< \return void + + ui_idcstart, ///< cb: Start of IDC engine work. + ///< \param none + ///< \return void + + ui_idcstop, ///< cb: Stop of IDC engine work. + ///< \param none + ///< \return void + + ui_suspend, ///< cb: Suspend graphical interface. + ///< Only the text version. + ///< Interface should respond to it. + ///< \param none + ///< \return void + + ui_resume, ///< cb: Resume the suspended graphical interface. + ///< Only the text version. + ///< Interface should respond to it + ///< \param none + ///< \return void + + ui_broadcast, ///< cb: broadcast call + ///< \param magic (::int64) a magic number + ///< \param ... other parameters depend on the given magic + ///< modules may hook to this event and reply to the caller. + ///< for example, the decompiler uses it to communicate + ///< its entry point to other plugins + + ui_read_selection, ///< ui: see read_selection() + + ui_read_range_selection, ///< ui: see read_range_selection() + + ui_unmarksel, ///< ui: see unmark_selection() + + ui_screenea, ///< ui: see get_screen_ea() + + ui_saving, ///< cb: The kernel is flushing its buffers to the disk. + ///< The user interface should save its state. + ///< Parameters: none + ///< Returns: none + + ui_saved, ///< cb: The kernel has saved the database. + ///< This callback just informs the interface. + ///< Note that at the time this notification is sent, + ///< the internal paths are not updated yet, + ///< and calling get_path(PATH_TYPE_IDB) will return + ///< the previous path. + ///< \param path (const char *) the database path + ///< \return void + + ui_refreshmarked, ///< ui: see refresh_idaview() + + ui_refresh, ///< ui: see refresh_idaview_anyway() + + ui_choose, ///< ui: Allow the user to choose an object. + ///< Always use the helper inline functions for this code. + ///< See \ref ui_choose_funcs for a list of such functions. + ///< \param type (::choose_type_t) type of chooser to display + ///< \param ... other parameters depend on the given type + ///< \return depends on the given type + + ui_close_chooser, ///< ui: see close_chooser() + + ui_banner, ///< ui: see banner() + + ui_setidle, ///< ui: Set a function to call at idle times. + ///< \param func (int (*)()) pointer to function that will be called + ///< \return void + + ui_database_closed, ///< cb: The database has been closed. + ///< See also processor_t::closebase, it occurs earlier. + ///< See also ui_initing_database. + ///< This is not the same as IDA exiting. If you need + ///< to perform cleanup at the exiting time, use qatexit(). + ///< \return void + + ui_beep, ///< ui: see beep() + + ui_is_msg_inited, ///< ui: see is_msg_inited() + + ui_msg, ///< ui: Show a message in the message window. + ///< \param format (const char *) format of message body + ///< \param va (va_list) format args + ///< \return number of bytes output + + ui_mbox, ///< ui: Show a message box. + ///< \param kind (::mbox_kind_t) + ///< \param format (const char *) format of message body + ///< \param va (va_list]) format args + ///< \return void + + ui_clr_cancelled, ///< ui: see clr_cancelled() + + ui_set_cancelled, ///< ui: see set_cancelled() + + ui_test_cancelled, ///< ui: see user_cancelled() + + ui_ask_buttons, ///< ui: see ask_yn() and ask_buttons() + + ui_ask_file, ///< ui: see ask_file() + + ui_ask_form, ///< ui: see \ref FORM_C + + ui_ask_text, ///< ui: see ask_text() + + ui_ask_str, ///< ui: see ask_str() + + ui_ask_addr, ///< ui: see ask_addr() + + ui_ask_seg, ///< ui: see ask_seg() + + ui_ask_long, ///< ui: see ask_long() + + ui_add_idckey, ///< ui: see add_idc_hotkey() + + ui_obsolete_del_idckey, + ///< ui: see ui_del_idckey() + + ui_analyzer_options, ///< ui: see analyzer_options() + + ui_load_file, ///< ui: see ui_load_new_file() + + ui_run_dbg, ///< ui: see ui_run_debugger() + + ui_get_cursor, ///< ui: see get_cursor() + + ui_get_curline, ///< ui: see get_curline() + + ui_copywarn, ///< ui: see display_copyright_warning() + + ui_noabort, ///< ui: Disable 'abort' menu item - the database was not compressed. + ///< \param none + ///< \return void + + ui_lock_range_refresh,///< ui: Lock the ui_range refreshes. + ///< The ranges will not be refreshed until the corresponding + ///< ::ui_unlock_range_refresh is issued. + ///< \param none + ///< \return void + + ui_unlock_range_refresh,///< ui: Unlock the ::ui_range refreshes. + ///< If the number of locks is back to zero, then refresh the ranges. + ///< \param none + ///< \return void + + ui_genfile_callback, ///< cb: handle html generation. + ///< \param html_header_cb_t ** + ///< \param html_footer_cb_t ** + ///< \param html_line_cb_t ** + ///< \return void + + ui_open_url, ///< ui: see open_url() + + ui_hexdumpea, ///< ui: Return the current address in a hex view. + ///< \param result (::ea_t *) + ///< \param hexdump_num (int) + ///< \return void + + ui_get_key_code, ///< ui: see get_key_code() + + ui_setup_plugins_menu,///< ui: setup plugins submenu + ///< \param none + ///< \return void + + ui_get_kernel_version,///< ui: see get_kernel_version() + + ui_is_idaq, ///< ui: see is_idaq() + + ui_refresh_navband, ///< ui: see refresh_navband() + + ui_debugger_menu_change, ///< cb: debugger menu modification detected + ///< \param enable (bool) + ///< true: debugger menu has been added, or a different debugger has been selected + ///< false: debugger menu will be removed (user switched to "No debugger") + ///< \return void + + ui_get_curplace, ///< ui: see get_custom_viewer_place(). See also ui_get_custom_viewer_location + + ui_obsolete_display_widget, + ui_close_widget, ///< ui: see close_widget() + + ui_activate_widget, ///< ui: see activate_widget() + + ui_find_widget, ///< ui: see find_widget() + + ui_get_current_widget, + ///< ui: see get_current_widget() + + ui_widget_visible, ///< TWidget is displayed on the screen. + ///< Use this event to populate the window with controls + ///< \param widget (TWidget *) + ///< \return void + + ui_widget_closing, ///< TWidget is about to close. + ///< This event precedes ui_widget_invisible. Use this + ///< to perform some possible actions relevant to + ///< the lifecycle of this widget + ///< \param widget (TWidget *) + ///< \return void + + ui_widget_invisible, ///< TWidget is being closed. + ///< Use this event to destroy the window controls + ///< \param widget (TWidget *) + ///< \return void + + ui_get_ea_hint, ///< cb: ui wants to display a simple hint for an address. + ///< Use this event to generate a custom hint + ///< See also more generic ::ui_get_item_hint + ///< \param buf (::qstring *) + ///< \param ea (::ea_t) + ///< \return true if generated a hint + + ui_get_item_hint, ///< cb: ui wants to display multiline hint for an item. + ///< See also more generic ::ui_get_custom_viewer_hint + ///< \param[out] hint (::qstring *) the output string + ///< \param ea (ea_t) or item id like a structure or enum member + ///< \param max_lines (int) maximal number of lines + ///< \param[out] important_lines (int *) number of important lines. if zero, output is ignored + ///< \return true if generated a hint + + ui_refresh_custom_viewer, + ///< ui: see refresh_custom_viewer() + + ui_destroy_custom_viewer, + ///< ui: see destroy_custom_viewer() + + ui_jump_in_custom_viewer, + ///< ui: see jumpto() + + ui_get_custom_viewer_curline, + ///< ui: see get_custom_viewer_curline() + + ui_get_current_viewer,///< ui: see get_current_viewer() + + ui_is_idaview, ///< ui: see is_idaview() + + ui_get_custom_viewer_hint, + ///< cb: ui wants to display a hint for a viewer (idaview or custom). + ///< Every subscriber is supposed to append the hint lines + ///< to HINT and increment IMPORTANT_LINES accordingly. + ///< Completely overwriting the existing lines in HINT + ///< is possible but not recommended. + ///< If the REG_HINTS_MARKER sequence is found in the + ///< returned hints string, it will be replaced with the + ///< contents of the "regular" hints. + ///< If the SRCDBG_HINTS_MARKER sequence is found in the + ///< returned hints string, it will be replaced with the + ///< contents of the source-level debugger-generated hints. + ///< The following keywords might appear at the beginning of the + ///< returned hints: + ///< HIGHLIGHT text\n + ///< where text will be highlighted + ///< CAPTION caption\n + ///< caption for the hint widget + ///< \param[out] hint (::qstring *) the output string, + ///< on input contains hints from the previous subscribes + ///< \param viewer (TWidget*) viewer + ///< \param place (::place_t *) current position in the viewer + ///< \param[out] important_lines (int *) number of important lines, + ///< should be incremented, + ///< if zero, the result is ignored + ///< \retval 0 continue collecting hints with other subscribers + ///< \retval 1 stop collecting hints + + ui_set_custom_viewer_range, + ///< ui: set_custom_viewer_range() + + ui_database_inited, ///< cb: database initialization has completed. + ///< the kernel is about to run idc scripts + ///< \param is_new_database (int) + ///< \param idc_script (const char *) - may be nullptr + ///< \return void + ///< See also ui_initing_database. + ///< This event is called for both new and old databases. + + ui_ready_to_run, ///< cb: all UI elements have been initialized. + ///< Automatic plugins may hook to this event to + ///< perform their tasks. + ///< \param none + ///< \return void + + ui_set_custom_viewer_handler, + ///< ui: see set_custom_viewer_handler(). + ///< also see other examples in \ref ui_scvh_funcs + + ui_refresh_chooser, ///< ui: see refresh_chooser() + + ui_open_builtin, ///< ui: open a window of a built-in type. see \ref ui_open_builtin_funcs + + ui_preprocess_action, ///< cb: ida ui is about to handle a user action. + ///< \param name (const char *) ui action name. + ///< these names can be looked up in ida[tg]ui.cfg + ///< \retval 0 ok + ///< \retval nonzero a plugin has handled the command + + ui_postprocess_action,///< cb: an ida ui action has been handled + + ui_set_custom_viewer_mode, + ///< ui: switch between graph/text modes. + ///< \param custom_viewer (TWidget *) + ///< \param graph_view (bool) + ///< \param silent (bool) + ///< \return bool success + + ui_gen_disasm_text, ///< ui: see gen_disasm_text() + + ui_gen_idanode_text, ///< cb: generate disassembly text for a node. + ///< Plugins may intercept this event and provide + ///< custom text for an IDA graph node + ///< They may use gen_disasm_text() for that. + ///< \param text (text_t *) + ///< \param fc (qflow_chart_t *) + ///< \param node (int) + ///< \return bool text_has_been_generated + + ui_install_cli, ///< ui: see: + ///< install_command_interpreter(), + ///< remove_command_interpreter() + + ui_execute_sync, ///< ui: see execute_sync() + + ui_execute_sync_ctl, ///< ui: see cancel_thread_exec_requests(), cancel_exec_request(), set_execute_sync_availability() + + ui_get_chooser_obj, ///< ui: see get_chooser_obj() + + ui_enable_chooser_item_attrs, + ///< ui: see enable_chooser_item_attrs() + + ui_get_chooser_item_attrs, + ///< cb: get item-specific attributes for a chooser. + ///< This callback is generated only after enable_chooser_attrs() + ///< \param chooser (const ::chooser_base_t *) + ///< \param n (::size_t) + ///< \param attrs (::chooser_item_attrs_t *) + ///< \return void + + ui_set_dock_pos, ///< ui: see set_dock_pos() + + ui_get_opnum, ///< ui: see get_opnum() + + ui_install_custom_datatype_menu, + ///< ui: install/remove custom data type menu item. + ///< \param dtid (int) data type id + ///< \param install (bool) + ///< \return success + + ui_install_custom_optype_menu, + ///< ui: install/remove custom operand type menu item. + ///< \param fid (int) format id + ///< \param install (bool) + ///< \return success + + ui_get_range_marker, ///< ui: Get pointer to function. + ///< see mark_range_for_refresh(ea_t, asize_t). + ///< This function will be called by the kernel when the + ///< database is changed + ///< \param none + ///< \return vptr: (idaapi*marker)(ea_t ea, asize_t) or nullptr + + ui_lookup_key_code, ///< ui: see lookup_key_code() + + ui_load_custom_icon_file, + ///< ui: see load_custom_icon(const char *) + + ui_load_custom_icon, ///< ui: see load_custom_icon(const void *, unsigned int, const char *) + + ui_free_custom_icon, ///< ui: see free_custom_icon() + + ui_process_action, ///< ui: see process_ui_action() + + ui_create_code_viewer,///< ui: see create_code_viewer() + + ui_addons, ///< ui: see \ref ui_addons_funcs + + ui_execute_ui_requests, + ///< ui: see execute_ui_requests(ui_request_t, ...) + + ui_execute_ui_requests_list, + ///< ui: see execute_ui_requests(ui_requests_t) + + ui_register_timer, ///< ui: see register_timer() + + ui_unregister_timer, ///< ui: see unregister_timer() + + ui_take_database_snapshot, + ///< ui: see take_database_snapshot() + + ui_restore_database_snapshot, + ///< ui: see restore_database_snapshot() + + ui_set_code_viewer_line_handlers, + ///< ui: see set_code_viewer_line_handlers() + + ui_obsolete_refresh_custom_code_viewer, + + ui_create_source_viewer, + ///< ui: Create new source viewer. + ///< \param top_tl (TWidget **) toplevel widget of created source viewer (can be nullptr) + ///< \param parent (TWidget *) + ///< \param custview (TWidget *) + ///< \param path (const char *) + ///< \param lines (strvec_t *) + ///< \param lnnum (int) + ///< \param colnum (int) + ///< \param flags (int) (\ref SVF_) + ///< \return source_view_t * + + ui_get_tab_size, ///< ui: see get_tab_size() + + ui_repaint_qwidget, ///< ui: see repaint_custom_viewer() + + ui_custom_viewer_set_userdata, + ///< ui: Change ::place_t user data for a custom view. + ///< \param custom_viewer (TWidget *) + ///< \param user_data (void *) + ///< \return old user_data + + ui_jumpto, ///< ui: see jumpto(ea_t, int, int) + + ui_open_form, ///< ui: see vopen_form() + + ui_unrecognized_config_directive, + ///< ui: Possibly handle an extra config directive, + ///< passed through '-d' or '-D'. + ///< \param directive (const char *) The config directive + ///< \return char * - one of \ref IDPOPT_RET + ///< See also register_cfgopts, which is better + + ui_get_output_cursor, ///< ui: see get_output_cursor() + + ui_get_output_curline,///< ui: see get_output_curline() + + ui_get_output_selected_text, + ///< ui: see get_output_selected_text() + + ui_get_renderer_type, ///< ui: see get_view_renderer_type() + + ui_set_renderer_type, ///< ui: see set_view_renderer_type() + + ui_get_viewer_user_data, + ///< ui: see get_viewer_user_data() + + ui_get_viewer_place_type, + ///< ui: see get_viewer_place_type() + + ui_ea_viewer_history_push_and_jump, + ///< ui: see ea_viewer_history_push_and_jump() + + ui_ea_viewer_history_info, + ///< ui: see get_ea_viewer_history_info() + + ui_register_action, + ///< ui: see register_action() + + ui_unregister_action, + ///< ui: see unregister_action() + + ui_attach_action_to_menu, + ///< ui: see attach_action_to_menu() + + ui_detach_action_from_menu, + ///< ui: see detach_action_from_menu() + + ui_attach_action_to_popup, + ///< ui: see attach_action_to_popup() + + ui_detach_action_from_popup, + ///< ui: see detach_action_from_popup() + + ui_attach_dynamic_action_to_popup, + ///< ui: see create attach_dynamic_action_to_popup() + + ui_attach_action_to_toolbar, + ///< ui: see attach_action_to_toolbar() + + ui_detach_action_from_toolbar, + ///< ui: see detach_action_from_toolbar() + + ui_updating_actions, ///< cb: IDA is about to update all actions. If your plugin + ///< needs to perform expensive operations more than once + ///< (e.g., once per action it registers), you should do them + ///< only once, right away. + ///< \param ctx (::action_update_ctx_t *) + ///< \return void + + ui_updated_actions, ///< cb: IDA is done updating actions. + ///< \param none + ///< \return void + + ui_populating_widget_popup, + ///< cb: IDA is populating the context menu for a widget. + ///< This is your chance to attach_action_to_popup(). + ///< + ///< Have a look at ui_finish_populating_widget_popup, + ///< if you want to augment the + ///< context menu with your own actions after the menu + ///< has had a chance to be properly populated by the + ///< owning component or plugin (which typically does it + ///< on ui_populating_widget_popup.) + ///< + ///< \param widget (TWidget *) + ///< \param popup_handle (TPopupMenu *) + ///< \param ctx (const action_activation_ctx_t *) + ///< \return void + ///< + ///< ui: see ui_finish_populating_widget_popup + + ui_finish_populating_widget_popup, + ///< cb: IDA is about to be done populating the + ///< context menu for a widget. + ///< This is your chance to attach_action_to_popup(). + ///< + ///< \param widget (TWidget *) + ///< \param popup_handle (TPopupMenu *) + ///< \param ctx (const action_activation_ctx_t *) + ///< \return void + ///< + ///< ui: see ui_populating_widget_popup + + ui_update_action_attr, + ///< ui: see \ref ui_uaa_funcs + + ui_get_action_attr, ///< ui: see \ref ui_gaa_funcs + + ui_plugin_loaded, ///< cb: The plugin was loaded in memory. + ///< \param plugin_info (const ::plugin_info_t *) + + ui_plugin_unloading, ///< cb: The plugin is about to be unloaded + ///< \param plugin_info (const ::plugin_info_t *) + + ui_get_widget_type, ///< ui: see get_widget_type() + + ui_current_widget_changed, + ///< cb: The currently-active TWidget changed. + ///< \param widget (TWidget *) + ///< \param prev_widget (TWidget *) + ///< \return void + + ui_get_widget_title, ///< ui: see get_widget_title() + + ui_obsolete_get_user_strlist_options, + ///< ui: see get_user_strlist_options() + + ui_create_custom_viewer, + ///< ui: see create_custom_viewer() + + ui_custom_viewer_jump,///< ui: set the current location, and have the viewer display it + ///< \param v (TWidget *) + ///< \param loc (const lochist_entry_t *) + ///< \param flags (uint32) or'ed combination of CVNF_* values + ///< \return success + + ui_set_custom_viewer_handlers, + ///< ui: see set_custom_viewer_handlers() + + ui_get_registered_actions, + ///< ui: see get_registered_actions() + + ui_create_toolbar, ///< ui: see create_toolbar() + ui_delete_toolbar, ///< ui: see delete_toolbar() + ui_create_menu, ///< ui: see create_menu() + ui_delete_menu, ///< ui: see delete_menu() + ui_obsolete_set_nav_colorizer, + ui_get_chooser_data, ///< ui: see get_chooser_data() + ui_obsolete_get_highlight, ///< now ui_get_highlight_2 + ui_set_highlight, ///< ui: see set_highlight() + + ui_set_mappings, ///< ui: Show current memory mappings + ///< and allow the user to change them. + ui_create_empty_widget, + ///< ui: see create_empty_widget() + + ui_msg_clear, ///< ui: see msg_clear() + ui_msg_save, ///< ui: see msg_save() + ui_msg_get_lines, ///< ui: see msg_get_lines() + + ui_chooser_default_enter, + ///< ui: jump to the address returned by get_ea() callback in the + ///< case of the non-modal choosers + ///< \param chooser (const ::chooser_base_t *) + ///< \param n/sel (::size_t *) for chooser_t + ///< (::sizevec_t *) for chooser_multi_t + ///< \return int chooser_t::cbres_t + + ui_screen_ea_changed, + ///< cb: The "current address" changed + ///< \param ea (ea_t) + ///< \param prev_ea (ea_t) + ///< \return void + + ui_get_active_modal_widget, + ///< ui: see get_active_modal_widget() + + ui_navband_pixel, ///< ui: see get_navband_pixel + ui_navband_ea, ///< ui: see get_navband_ea + ui_get_window_id, ///< ui: set get_window_id (GUI only) + + ui_create_desktop_widget, + ///< cb: create a widget, to be placed in the widget tree (at desktop-creation time.) + ///< \param title (const char *) + ///< \param cfg (const jobj_t *) + ///< \return TWidget * the created widget, or null + + ui_strchoose, ///< ui: undocumented + + + ui_set_nav_colorizer, ///< ui: see set_nav_colorizer() + ui_display_widget, ///< ui: see display_widget() + + ui_get_lines_rendering_info, + ///< cb: get lines rendering information + ///< \param out (lines_rendering_output_t *) + ///< \param widget (const TWidget *) + ///< \param info (const lines_rendering_input_t *) + ///< \return void + + ui_sync_sources, + ///< ui: [un]synchronize sources + ///< \param what (const sync_source_t *) + ///< \param with (const sync_source_t *) + ///< \param sync (bool) + ///< \return success + + ui_get_widget_config, ///< cb: retrieve the widget configuration (it will be passed + ///< back at ui_create_desktop_widget-, and ui_set_widget_config-time) + ///< \param widget (const TWidget *) + ///< \param cfg (jobj_t *) + ///< \return void + + ui_set_widget_config, ///< cb: set the widget configuration + ///< \param widget (const TWidget *) + ///< \param cfg (const jobj_t *) + ///< \return void + + ui_obsolete_get_custom_viewer_location, // ui_get_custom_viewer_location_2 + + ui_initing_database, ///< cb: database initialization has started. + ///< \return void + ///< See also ui_database_inited. + ///< This event is called for both new and old databases. + + ui_destroying_procmod, ///< cb: The processor module is about to be destroyed + ///< \param procmod (const ::procmod_t *) + + ui_destroying_plugmod, ///< cb: The plugin object is about to be destroyed + ///< \param plugmod (const ::plugmod_t *) + ///< \param entry (const ::plugin_t *) + + ui_update_file_history, ///< ui: manipulate the file history + ///< \param add_path (const char *) + ///< \param del_path (const char *) + + ui_get_synced_group, + ///< ui: see get_synced_group() + + ui_show_rename_dialog, ///< ui: undocumented + ///< \return success + + ui_desktop_applied, ///< cb: a desktop has been applied + ///< \param name (const char *) the desktop name + ///< \param from_idb (bool) the desktop was stored in the IDB (false if it comes from the registry) + ///< \param type (int) the desktop type (1-disassembly, 2-debugger, 3-merge) + + ui_choose_bookmark, + ///< ui: modal chooser (legacy) + ///< \param n (uint32 *) input: default slot, output: chosen bookmark index + ///< \param entry (const lochist_entry_t *) entry with place type + ///< \param ud (void *) user data + + ui_get_custom_viewer_place_xcoord, + ///< ui: see get_custom_viewer_place_xcoord() + + ui_get_user_input_event, + ///< ui: see get_user_input_event() + + ui_get_highlight_2, ///< ui: see get_highlight() + + ui_get_custom_viewer_location_2, + ///< ui: see get_custom_viewer_location() + ///< \param out (listing_location_t *) + ///< \param custom_viewer (TWidget *) + ///< \param flags (uint32) or'ed combination of CVLF_* values + + ui_get_chooser_rows, ///< ui: see get_chooser_rows() + ///< \param out (chooser_row_info_vec_t *) column data for each returned row + ///< \param chooser_caption (const char *) + ///< \param what (size_t) row number, or one of the GCRF_ flags + ui_get_icon_id_by_name, ///< ui: see get_icon_id_by_name() + /// \param icon_name full name of the icon + /// \return icon id + ui_open_builtin2, ///< ui: open a window of a built-in type. see \ref ui_open_builtin_funcs + ///< \param bwn the builtin widget type + ///< \param arg0 the first argument (e.g., ordinal for BWN_TICSR) + ///< \param arg1 the second argument (e.g., a tif_cursor_t * for BWN_TICSR) + + ui_get_last_widget, ///< ui: see get_last_widget() + + ui_prompt_function_prototype, + ///< ui: open Function Prototype Editor and return new type for function + + ui_parse_tagged_line_sections, + ///< ui: see parse_tagged_line_sections() + ui_last, ///< the last notification code + + ui_dbg_begin = 1000, ///< debugger callgates. should not be used directly, see dbg.hpp for details + ui_dbg_run_requests = ui_dbg_begin, + ui_dbg_get_running_request, + ui_dbg_get_running_notification, + ui_dbg_clear_requests_queue, + ui_dbg_get_process_state, + ui_dbg_start_process, + ui_dbg_request_start_process, + ui_dbg_suspend_process, + ui_dbg_request_suspend_process, + ui_dbg_continue_process, + ui_dbg_request_continue_process, + ui_dbg_exit_process, + ui_dbg_request_exit_process, + ui_dbg_get_thread_qty, + ui_dbg_getn_thread, + ui_dbg_select_thread, + ui_dbg_request_select_thread, + ui_dbg_step_into, + ui_dbg_request_step_into, + ui_dbg_step_over, + ui_dbg_request_step_over, + ui_dbg_run_to, + ui_dbg_request_run_to, + ui_dbg_step_until_ret, + ui_dbg_request_step_until_ret, + ui_dbg_get_bpt_qty, + ui_dbg_add_oldbpt, + ui_dbg_request_add_oldbpt, + ui_dbg_del_oldbpt, + ui_dbg_request_del_oldbpt, + ui_dbg_enable_oldbpt, + ui_dbg_request_enable_oldbpt, + ui_dbg_set_trace_size, + ui_dbg_clear_trace, + ui_dbg_request_clear_trace, + ui_dbg_is_step_trace_enabled, + ui_dbg_enable_step_trace, + ui_dbg_request_enable_step_trace, + ui_dbg_get_step_trace_options, + ui_dbg_set_step_trace_options, + ui_dbg_request_set_step_trace_options, + ui_dbg_is_insn_trace_enabled, + ui_dbg_enable_insn_trace, + ui_dbg_request_enable_insn_trace, + ui_dbg_get_insn_trace_options, + ui_dbg_set_insn_trace_options, + ui_dbg_request_set_insn_trace_options, + ui_dbg_is_func_trace_enabled, + ui_dbg_enable_func_trace, + ui_dbg_request_enable_func_trace, + ui_dbg_get_func_trace_options, + ui_dbg_set_func_trace_options, + ui_dbg_request_set_func_trace_options, + ui_dbg_get_tev_qty, + ui_dbg_get_tev_info, + ui_dbg_get_call_tev_callee, + ui_dbg_get_ret_tev_return, + ui_dbg_get_bpt_tev_ea, + ui_dbg_get_reg_value_type, + ui_dbg_get_processes, + ui_dbg_attach_process, + ui_dbg_request_attach_process, + ui_dbg_detach_process, + ui_dbg_request_detach_process, + ui_dbg_get_first_module, + ui_dbg_get_next_module, + ui_dbg_bring_to_front, + ui_dbg_get_current_thread, + ui_dbg_wait_for_next_event, + ui_dbg_get_debug_event, + ui_dbg_set_debugger_options, + ui_dbg_set_remote_debugger, + ui_dbg_load_debugger, + ui_dbg_retrieve_exceptions, + ui_dbg_store_exceptions, + ui_dbg_define_exception, + ui_dbg_suspend_thread, + ui_dbg_request_suspend_thread, + ui_dbg_resume_thread, + ui_dbg_request_resume_thread, + ui_dbg_get_process_options, + ui_dbg_check_bpt, + ui_dbg_set_process_state, + ui_dbg_get_manual_regions, + ui_dbg_set_manual_regions, + ui_dbg_enable_manual_regions, + ui_dbg_set_process_options, + ui_dbg_is_busy, + ui_dbg_hide_all_bpts, + ui_dbg_edit_manual_regions, + ui_dbg_get_sp_val, + ui_dbg_get_ip_val, + ui_dbg_get_reg_val, + ui_dbg_set_reg_val, + ui_dbg_request_set_reg_val, + ui_dbg_get_insn_tev_reg_val, + ui_dbg_get_insn_tev_reg_result, + ui_dbg_register_provider, + ui_dbg_unregister_provider, + ui_dbg_handle_debug_event, + ui_dbg_add_vmod, + ui_dbg_del_vmod, + ui_dbg_compare_bpt_locs, + ui_obsolete_dbg_save_bpts, + ui_dbg_set_bptloc_string, + ui_dbg_get_bptloc_string, + ui_dbg_internal_appcall, + ui_dbg_internal_cleanup_appcall, + ui_dbg_internal_get_sreg_base, + ui_dbg_internal_ioctl, + ui_dbg_read_memory, + ui_dbg_write_memory, + ui_dbg_read_registers, + ui_dbg_write_register, + ui_dbg_get_memory_info, + ui_dbg_get_event_cond, + ui_dbg_set_event_cond, + ui_dbg_enable_bpt, + ui_dbg_request_enable_bpt, + ui_dbg_del_bpt, + ui_dbg_request_del_bpt, + ui_dbg_map_source_path, + ui_dbg_map_source_file_path, + ui_dbg_modify_source_paths, + ui_dbg_is_bblk_trace_enabled, + ui_dbg_enable_bblk_trace, + ui_dbg_request_enable_bblk_trace, + ui_dbg_get_bblk_trace_options, + ui_dbg_set_bblk_trace_options, + ui_dbg_request_set_bblk_trace_options, + // trace management + ui_dbg_load_trace_file, + ui_dbg_save_trace_file, + ui_dbg_is_valid_trace_file, + ui_dbg_set_trace_file_desc, + ui_dbg_get_trace_file_desc, + ui_dbg_choose_trace_file, + ui_dbg_diff_trace_file, + ui_dbg_graph_trace, + ui_dbg_get_tev_memory_info, + ui_dbg_get_tev_event, + ui_dbg_get_insn_tev_reg_mem, + // breakpoint management (new codes were introduced in v6.3) + ui_dbg_getn_bpt, + ui_dbg_get_bpt, + ui_dbg_find_bpt, + ui_dbg_add_bpt, + ui_dbg_request_add_bpt, + ui_dbg_update_bpt, + ui_dbg_for_all_bpts, + ui_dbg_get_tev_ea, + ui_dbg_get_tev_type, + ui_dbg_get_tev_tid, + ui_dbg_get_trace_base_address, + // calluis for creating traces from scratch (added in 6.4) + ui_dbg_set_trace_base_address, + ui_dbg_add_tev, + ui_dbg_add_insn_tev, + ui_dbg_add_call_tev, + ui_dbg_add_ret_tev, + ui_dbg_add_bpt_tev, + ui_dbg_add_debug_event, + ui_dbg_add_thread, + ui_dbg_del_thread, + ui_dbg_add_many_tevs, + ui_dbg_set_bpt_group, + ui_dbg_set_highlight_trace_options, + ui_dbg_set_trace_platform, + ui_dbg_get_trace_platform, + // added in 6.6 + ui_dbg_internal_get_elang, + ui_dbg_internal_set_elang, + + // added in 6.7 + ui_dbg_load_dbg_dbginfo, + ui_dbg_set_resume_mode, + ui_dbg_request_set_resume_mode, + ui_dbg_set_bptloc_group, + ui_dbg_list_bptgrps, + ui_dbg_rename_bptgrp, + ui_dbg_del_bptgrp, + ui_dbg_get_grp_bpts, + ui_dbg_get_bpt_group, + ui_dbg_change_bptlocs, + + // added in 7.1 + ui_dbg_collect_stack_trace, + ui_dbg_get_module_info, + + // source-level debugging + ui_dbg_get_srcinfo_provider, + ui_dbg_get_global_var, + ui_dbg_get_local_var, + ui_dbg_get_local_vars, + ui_dbg_add_path_mapping, + ui_dbg_get_current_source_file, + ui_dbg_get_current_source_line, + + ui_dbg_srcdbg_step_into, + ui_dbg_srcdbg_request_step_into, + ui_dbg_srcdbg_step_over, + ui_dbg_srcdbg_request_step_over, + ui_dbg_srcdbg_step_until_ret, + ui_dbg_srcdbg_request_step_until_ret, + + ui_dbg_getn_thread_name, + ui_dbg_bin_search, + + ui_dbg_get_insn_tev_reg_val_i, + ui_dbg_get_insn_tev_reg_result_i, + ui_dbg_get_reg_val_i, + ui_dbg_set_reg_val_i, + + ui_dbg_get_reg_info, + + ui_dbg_set_trace_dynamic_register_set, + ui_dbg_get_trace_dynamic_register_set, + + // added in 7.7 + ui_dbg_enable_bptgrp, + + // added in 9.1 + ui_dbg_continue_backwards, + ui_dbg_request_continue_backwards, + ui_dbg_step_into_backwards, + ui_dbg_request_step_into_backwards, + ui_dbg_step_over_backwards, + ui_dbg_request_step_over_backwards, + ui_dbg_run_to_backwards, + ui_dbg_request_run_to_backwards, + ui_dbg_set_suspend_on_event, + + ui_dbg_end, + + // Debugging notifications +#ifdef _DEBUG + debug_obsolete_assert_thread_waitready = ui_dbg_end +#endif +}; + + +//-------------------------------------------------------------------------- + + +/// Pointer to the user-interface dispatcher function. +/// This pointer is in the kernel + +idaman callui_t ida_export_data (idaapi*callui)(ui_notification_t what,...); + + +/// After calling init_kernel() the ui must call this function. +/// It will open the database specified in the command line. +/// If the database did not exist, a new database will be created and +/// the input file will be loaded. +/// \return 0-ok, otherwise an exit code + +idaman int ida_export init_database(int argc, const char *const *argv, int *newfile); + + +/// The database termination function. +/// This function should be called to close the database. + +idaman void ida_export term_database(); + + +/// See error() + +idaman NORETURN AS_PRINTF(1, 0) void ida_export verror(const char *format, va_list va); + + +/// See show_hex() + +idaman AS_PRINTF(3, 0) void ida_export vshow_hex( + const void *dataptr, + size_t len, + const char *format, + va_list va); + + +/// See show_hex_file() + +idaman AS_PRINTF(4, 0) void ida_export vshow_hex_file( + linput_t *li, + int64 pos, + size_t count, + const char *format, + va_list va); + + +#endif // SWIG + +/// Get IDA kernel version (in a string like "5.1"). + +inline ssize_t get_kernel_version(char *buf, size_t bufsize) +{ + return callui(ui_get_kernel_version, buf, bufsize).ssize; +} + +// Return true if this is ida running as library +const int64 IDALIB_API_MAGIC = 0x001DA11B00000000LL; +inline bool is_ida_library(char *path = nullptr, size_t pathsize = 0, void** handle = nullptr) +{ + return callui(ui_broadcast, IDALIB_API_MAGIC, path, pathsize, handle).ssize > 0; +} + +//-------------------------------------------------------------------------- +// K E R N E L S E R V I C E S F O R U I +//-------------------------------------------------------------------------- +// +// Generating text for the disassembly, enum, and structure windows. + +/*! \brief Denotes a displayed line. + + (location_t would be a better name but it is too late to rename it now) + + An object may be displayed on one or more lines. All lines of an object are + generated at once and kept in a linearray_t class. + + place_t is an abstract class, another class must be derived from it. \n + Currently the following classes are used in IDA: + + idaplace_t - disassembly view \n + + Example (idaplace_t): \verbatim + + 004015AC + 004015AC loc_4015AC: ; CODE XREF: sub_4014B8+C5j + 004015AC xor eax, eax \endverbatim + + The first line is denoted by idaplace_t with ea=4015AC, lnnum=0 \n + The second line is denoted by idaplace_t with ea=4015AC, lnnum=1 \n + The third line is denoted by idaplace_t with ea=4015AC, lnnum=2 + + NB: the place_t class may change in the future, do not rely on it +*/ +class place_t +{ +public: + int lnnum; ///< Number of line within the current object + place_t() {} ///< Constructor + place_t(int ln) : lnnum(ln) {} ///< Constructor + DEFINE_MEMORY_ALLOCATION_FUNCS() + + /// Generate a short description of the location. + /// This description is used on the status bar. + /// \param vout the output buffer + /// \param ud pointer to user-defined context data. Is supplied by ::linearray_t + virtual void idaapi print(qstring *vout, void *ud) const = 0; + + /// Map the location to a number. + /// This mapping is used to draw the vertical scrollbar. + /// \param ud pointer to user-defined context data. Is supplied by ::linearray_t + virtual uval_t idaapi touval(void *ud) const = 0; + + /// Clone the location. + /// \return a pointer to a copy of the current location in dynamic memory + virtual place_t *idaapi clone() const = 0; + + /// Copy the specified location object to the current object + virtual void idaapi copyfrom(const place_t *from) = 0; + + /// Map a number to a location. + /// When the user clicks on the scrollbar and drags it, we need to determine + /// the location corresponding to the new scrollbar position. This function + /// is used to determine it. It builds a location object for the specified 'x' + /// and returns a pointer to it. + /// \param ud pointer to user-defined context data. Is supplied by ::linearray_t + /// \param x number to map + /// \param lnnum line number to initialize 'lnnum' + /// \return a freshly allocated object. See also PCF_MAKEPLACE_ALLOCATES + virtual place_t *idaapi makeplace(void *ud, uval_t x, int lnnum) const= 0; + + /// Deprecated. Please consider compare2(const place_t *, void *) instead. + virtual int idaapi compare(const place_t *t2) const = 0; + + /// Adjust the current location to point to a displayable object. + /// This function validates the location and makes sure that it points to + /// an existing object. For example, if the location points to the middle + /// of an instruction, it will be adjusted to point to the beginning of the + /// instruction. + /// \param ud pointer to user-defined context data. Is supplied by ::linearray_t + virtual void idaapi adjust(void *ud) = 0; + + /// Move to the previous displayable location. + /// \param ud pointer to user-defined context data. Is supplied by ::linearray_t + /// \return success + virtual bool idaapi prev(void *ud) = 0; + + /// Move to the next displayable location. + /// \param ud pointer to user-defined context data. Is supplied by ::linearray_t + /// \return success + virtual bool idaapi next(void *ud) = 0; + + /// Are we at the first displayable object?. + /// \param ud pointer to user-defined context data. Is supplied by ::linearray_t + /// \return true if the current location points to the first displayable object + virtual bool idaapi beginning(void *ud) const = 0; + + /// Are we at the last displayable object?. + /// \param ud pointer to user-defined context data. Is supplied by ::linearray_t + /// \return true if the current location points to the last displayable object + virtual bool idaapi ending(void *ud) const = 0; + + /// Generate text lines for the current location. + /// \param out storage for the lines + /// \param out_deflnnum pointer to the cell that will contain the number of + /// the most 'interesting' generated line + /// \param out_pfx_color pointer to the cell that will contain the line prefix color + /// \param out_bgcolor pointer to the cell that will contain the background color + /// \param ud pointer to user-defined context data. Is supplied by linearray_t + /// \param maxsize the maximum number of lines to generate + /// \return number of generated lines + virtual int idaapi generate( + qstrvec_t *out, + int *out_deflnnum, + color_t *out_pfx_color, + bgcolor_t *out_bgcolor, + void *ud, + int maxsize) const = 0; + + /// Serialize this instance. + /// It is fundamental that all instances of a particular subclass + /// of of place_t occupy the same number of bytes when serialized. + /// \param out buffer to serialize into + virtual void idaapi serialize(bytevec_t *out) const = 0; + + /// De-serialize into this instance. + /// 'pptr' should be incremented by as many bytes as + /// de-serialization consumed. + /// \param pptr pointer to a serialized representation of a place_t of this type. + /// \param end pointer to end of buffer. + /// \return whether de-serialization was successful + virtual bool idaapi deserialize(const uchar **pptr, const uchar *end) = 0; + + /// Get the place's ID (i.e., the value returned by register_place_class()) + /// \return the id + virtual int idaapi id() const = 0; + + /// Get this place type name. + /// All instances of a given class must return the same string. + /// \return the place type name. Please try and pick something that is + /// not too generic, as it might clash w/ other plugins. A good + /// practice is to prefix the class name with the name + /// of your plugin. E.g., "myplugin:srcplace_t". + virtual const char *idaapi name() const = 0; + + /// Map the location to an ea_t. + /// \return the corresponding ea_t, or BADADDR; + virtual ea_t idaapi toea() const { return BADADDR; } + + /// Rebase the place instance + /// \param infos the segments that were moved + /// \return true if place was rebased, false otherwise + virtual bool idaapi rebase(const segm_move_infos_t & /*infos*/ ) { return true; } + + /// Visit this place, possibly 'unhiding' a section of text. + /// If entering that place required some expanding, a place_t + /// should be returned that represents that section, plus some + /// flags for later use by 'leave()'. + /// \param out_flags flags to be used together with the place_t that is + /// returned, in order to restore the section to its + /// original state when leave() is called. + /// \return a place_t corresponding to the beginning of the section + /// of text that had to be expanded. That place_t's leave() will + /// be called with the flags contained in 'out_flags' when the user + /// navigates away from it. + virtual place_t *idaapi enter(uint32 * /*out_flags*/) const { return nullptr; } + + /// Leave this place, possibly 'hiding' a section of text that was + /// previously expanded (at enter()-time.) + virtual void idaapi leave(uint32 /*flags*/) const {} + + /// Compare two locations except line numbers (lnnum). + /// This function is used to organize loops. + /// For example, if the user has selected an range, its boundaries are remembered + /// as location objects. Any operation within the selection will have the following + /// look: for ( loc=starting_location; loc < ending_location; loc.next() ) + /// In this loop, the comparison function is used. + /// \param t2 the place to compare this one to. + /// \param ud pointer to user-defined context data. + /// \retval -1 if the current location is less than 't2' + /// \retval 0 if the current location is equal to than 't2' + /// \retval 1 if the current location is greater than 't2' + virtual int idaapi compare2(const place_t *t2, void * /*ud*/) const { return compare(t2); } + + /// Compare two places for equality, ignoring line numbers (lnnum). This is + /// semantically different than `compare2`, although by default it is + /// implemented in terms of it for backwards-compatibility. `compare2` + /// implements a three-way comparison to see if two places *sort* less than, + /// equal to, or grater than. This method actually looks for equality. Thus, + /// `t1->equals(t2, ud)` implies `t1->compare2(t2, ud) == 0`, but the reverse + /// is not always true. An example of this is for adjustable places that are + /// sensitive to the x-cursor position, and need to compare differently as a + /// result. + /// + /// \return true if the two places match / are equal. + virtual bool idaapi equals(const place_t *t2, void *ud) const + { + return compare2(t2, ud) == 0; + } +}; + +#define DEFAULT_PLACE_LNNUM -1 +#define MAX_PLACE_LNNUM INT_MAX + +/// compare places and their lnnums +idaman int ida_export l_compare(const place_t *t1, const place_t *t2); +idaman int ida_export l_compare2(const place_t *t1, const place_t *t2, void *ud); +idaman bool ida_export l_equals(const place_t *t1, const place_t *t2, void *ud); + +#ifndef SWIG + +//-------------------------------------------------------------------------- +/// Helper to define exported functions for ::place_t implementations +#define define_place_exported_functions(classname) \ +class classname; \ +idaman void ida_export classname ## __print(const classname *, qstring *, void*); \ +idaman uval_t ida_export classname ## __touval(const classname *, void*); \ +idaman place_t * ida_export classname ## __clone(const classname *); \ +idaman void ida_export classname ## __copyfrom(classname *, const place_t*); \ +idaman place_t * ida_export classname ## __makeplace(const classname *, void*, uval_t, int); \ +idaman int ida_export classname ## __compare(const classname *, const place_t*); \ +idaman int ida_export classname ## __compare2(const classname *, const place_t*, void*); \ +idaman bool ida_export classname ## __equals(const classname *, const place_t*, void*); \ +idaman void ida_export classname ## __adjust(classname *, void*); \ +idaman bool ida_export classname ## __prev(classname *, void*); \ +idaman bool ida_export classname ## __next(classname *, void*); \ +idaman bool ida_export classname ## __beginning(const classname *, void*); \ +idaman bool ida_export classname ## __ending(const classname *, void*); \ +idaman int ida_export classname ## __generate( \ + const classname *, \ + qstrvec_t*, \ + int*, \ + color_t*, \ + bgcolor_t*, \ + void*, \ + int); \ +idaman void ida_export classname ## __serialize(const classname *, bytevec_t *out); \ +idaman bool ida_export classname ## __deserialize(classname *, const uchar **, const uchar *); \ +idaman int ida_export classname ## __id(const classname *); \ +idaman const char *ida_export classname ## __name(const classname *); \ +idaman ea_t ida_export classname ## __toea(const classname *); \ +idaman place_t * ida_export classname ## __enter(const classname *, uint32 *); \ +idaman void ida_export classname ## __leave(const classname *, uint32); \ +idaman bool ida_export classname ## __rebase(classname *, const segm_move_infos_t &); + + +/// Helper to define virtual functions in ::place_t implementations +#define define_place_virtual_functions(class) \ + virtual void idaapi print(qstring *buf, void *ud) const override \ + { class ## __print(this, buf, ud); } \ + virtual uval_t idaapi touval(void *ud) const override \ + { return class ## __touval(this, ud); } \ + virtual place_t *idaapi clone() const override \ + { return class ## __clone(this); } \ + virtual void idaapi copyfrom(const place_t *from) override \ + { class ## __copyfrom(this, from); } \ + virtual place_t *idaapi makeplace( \ + void *ud, \ + uval_t x, \ + int _lnnum) const override \ + { return class ## __makeplace(this,ud,x,_lnnum); } \ + virtual int idaapi compare(const place_t *t2) const override \ + { return class ## __compare(this, t2); } \ + virtual void idaapi adjust(void *ud) override \ + { class ## __adjust(this,ud); } \ + virtual bool idaapi prev(void *ud) override \ + { return class ## __prev(this,ud); } \ + virtual bool idaapi next(void *ud) override \ + { return class ## __next(this,ud); } \ + virtual bool idaapi beginning(void *ud) const override \ + { return class ## __beginning(this,ud); } \ + virtual bool idaapi ending(void *ud) const override \ + { return class ## __ending(this,ud); } \ + virtual int idaapi generate( \ + qstrvec_t *_out, \ + int *_out_lnnum, \ + color_t *_out_pfx_color, \ + bgcolor_t *_out_bg_color, \ + void *_ud, \ + int _max) const override \ + { \ + return class ## __generate( \ + this, _out, _out_lnnum, _out_pfx_color, \ + _out_bg_color, _ud, _max); \ + } \ + virtual void idaapi serialize(bytevec_t *out) const override \ + { class ## __serialize(this, out); } \ + virtual bool idaapi deserialize( \ + const uchar **pptr, \ + const uchar *end) override \ + { return class ## __deserialize(this, pptr, end); } \ + virtual int idaapi id() const override \ + { return class ## __id(this); } \ + virtual const char * idaapi name() const override \ + { return class ## __name(this); } \ + virtual ea_t idaapi toea() const override \ + { return class ## __toea(this); } \ + virtual place_t *idaapi enter(uint32 *out_flags) const override \ + { return class ## __enter(this, out_flags); } \ + virtual void idaapi leave(uint32 flags) const override \ + { return class ## __leave(this, flags); } \ + virtual bool idaapi rebase(const segm_move_infos_t &infos) override \ + { return class ## __rebase(this, infos); } \ + virtual int idaapi compare2(const place_t *t2, void *ud) const override \ + { return class ## __compare2(this, t2, ud); } \ + virtual bool idaapi equals(const place_t *t2, void *ud) const override \ + { return class ## __equals(this, t2, ud); } + +define_place_exported_functions(simpleline_place_t) + + +#endif // SWIG + +//-------------------------------------------------------------------------- + +/*! \defgroup simpleline Simpleline interface + + \brief IDA custom viewer sample. + + It is enough to create an object of ::strvec_t class, put all lines + into it and create a custom ida viewer (::create_custom_viewer). + \code + strvec_t lines; + // fill it with lines... + simpleline_place_t s1; + simpleline_place_t s2(lines.size()-1); + static const custom_viewer_handlers_t handlers(...); + cv = create_custom_viewer("My title", &s1, &s2, &s1, nullptr, &lines, nullptr, nullptr); + \endcode + This will produce a nice colored text view. + Also see the SDK's 'custview' and 'hexview' plugins for more complete examples. +*/ +///@{ + +/// Maintain basic information for a line in a custom view +struct simpleline_t +{ + qstring line; ///< line text + color_t color = 1; ///< line prefix color + bgcolor_t bgcolor = DEFCOLOR; ///< line background color + simpleline_t() {} + simpleline_t(color_t c, const char *str) : line(str), color(c) {} + simpleline_t(const char *str) : line(str) {} + simpleline_t(const qstring &str) : line(str) {} + DEFINE_MEMORY_ALLOCATION_FUNCS() +}; + +/// A collection of simple lines to populate a custom view. +/// This is an example of what you would pass as the 'ud' argument to create_custom_viewer() +typedef qvector strvec_t; + +/// A location in a view populated by a ::strvec_t +class simpleline_place_t : public place_t +{ +public: + uint32 n; ///< line number + simpleline_place_t() { n = 0; lnnum = 0; } ///< Constructor + simpleline_place_t(int _n) { n = _n; lnnum = 0; } ///< Constructor + define_place_virtual_functions(simpleline_place_t); +}; +///@} + +//-------------------------------------------------------------------------- +// user defined data for linearray_t: use ptr to result of calc_default_idaplace_flags() +#ifndef SWIG +define_place_exported_functions(idaplace_t) +#endif // SWIG +/// A location in a disassembly view +class idaplace_t : public place_t +{ +public: + ea_t ea; ///< address + idaplace_t() {} ///< Constructor + idaplace_t(ea_t x, int ln) : place_t(ln), ea(x) {} ///< Constructor + define_place_virtual_functions(idaplace_t); +}; + +//------------------------------------------------------------------------- +/// A location in a hex view +#ifndef SWIG +define_place_exported_functions(hexplace_t) +struct outctx_base_t; +struct hexplace_gen_t; +class hexview_t; +idaman void ida_export hexplace_t__out_one_item( + const hexplace_t *_this, + outctx_base_t &ctx, + const hexplace_gen_t *hg, + int itemno, + color_t *color, + color_t patch_or_edit); +idaman size_t ida_export hexplace_t__ea2str( + char *buf, + size_t bufsize, + const hexplace_gen_t *hg, + ea_t ea); +#endif // SWIG + +#define HEXPLACE_COLOR_EDITED COLOR_SYMBOL +#define HEXPLACE_COLOR_PATCHED COLOR_VOIDOP +#define HEXPLACE_COLOR_SHOWSPACES COLOR_RESERVED1 + +// A helper, used as 'userdata' for generating lines in a hexplace_t +// None of the function pointers can be nullptr +struct hexplace_gen_t +{ + // data format to display + enum data_kind_t + { + dk_float, + dk_int, + dk_addr_names, + dk_addr_text, + }; + enum int_format_t + { + if_hex, + if_signed, + if_unsigned, + }; + // result of get_byte_value() + enum byte_kind_t + { + BK_VALID, // has a valid value + BK_INVALIDADDR, // address is invalid + BK_NOVALUE, // address is valid but contains no value + }; + + virtual bool is_editing() const = 0; + virtual bool is_editing_text() const = 0; + virtual bool is_curitem_changed() const = 0; + virtual bool is_edited_byte(ea_t ea, uint64 *out_value=nullptr) const = 0; + virtual byte_kind_t get_byte_value( + ea_t ea, + uint64 *out_value, + bool *out_edited) const = 0; + virtual void get_encoding(qstring *out) const = 0; + virtual ea_t get_cur_item_ea() const = 0; + virtual void get_cur_item_text(qstring *out) const = 0; + virtual int get_alignment() const = 0; + virtual int get_line_len(ea_t ea) const = 0; + virtual int get_items_per_line() const = 0; + virtual int get_bytes_per_item() const = 0; + virtual int get_item_width(ea_t ea) const = 0; + virtual data_kind_t get_data_kind() const = 0; + virtual int_format_t get_int_format() const = 0; + virtual bool has_central_separator() const = 0; + virtual bool show_text() const = 0; + virtual bool show_segaddr() const = 0; + virtual int get_bitness() const = 0; + + bool is_addr_kind() const + { + data_kind_t k = get_data_kind(); + return k == dk_addr_names || k == dk_addr_text; + } +}; + +//------------------------------------------------------------------------- +// class to represent lines in a hex dump window +// one line consists of hv->grid.items_per_line items +// each item is hv->grid.bytes_per_item bytes for 8-bit bytes or one "wide" byte +class hexplace_t : public idaplace_t +{ +protected: + ea_t sol; // EA at start-of-line +public: + hexplace_t(ea_t _ea, int ln) : idaplace_t(_ea, ln), sol(_ea) {} + define_place_virtual_functions(hexplace_t); + + void out_one_item( + outctx_base_t &ctx, + const hexplace_gen_t *hg, + int itemno, + color_t *color, + color_t patch_or_edit) const + { + hexplace_t__out_one_item(this, ctx, hg, itemno, color, patch_or_edit); + } + + // convert ea to text + // use seg:off if segment base is not zero + // otherwise print just the address + static size_t ea2str(char *buf, size_t bufsize, const hexplace_gen_t *hg, ea_t ea) + { + return hexplace_t__ea2str(buf, bufsize, hg, ea); + } + +}; + +//-------------------------------------------------------------------------- +#ifndef SWIG +define_place_exported_functions(tiplace_t) +#endif // SWIG +typedef uchar type_t; +typedef uval_t uval_t; + +//------------------------------------------------------------------------- +/// A location in a tinfo_t +typedef uint64 tif_cursor_t; +struct til_type_ref_t; +#define TIF_CURSOR_HEADER tif_cursor_t(-2) +#define TIF_CURSOR_FOOTER tif_cursor_t(-1) + +inline bool is_tif_cursor_header(tif_cursor_t c) { return c == TIF_CURSOR_HEADER; } +inline bool is_tif_cursor_footer(tif_cursor_t c) { return c == TIF_CURSOR_FOOTER; } +inline bool is_tif_cursor_index(tif_cursor_t c) { return c != TIF_CURSOR_HEADER && c != TIF_CURSOR_FOOTER; } + +//------------------------------------------------------------------------- +class tiplace_t : public place_t +{ +public: + uint32 ordinal = 0; + tif_cursor_t cursor = TIF_CURSOR_HEADER; + + tiplace_t() {} + tiplace_t(uint32 ord, tif_cursor_t c, int _lnnum) : ordinal(ord), cursor(c) + { + lnnum = _lnnum; + } + bool valid_ord() const { return ordinal != 0; } + void reset() { ordinal = 0; cursor = TIF_CURSOR_HEADER; lnnum = 0; } + + bool is_header() const { return cursor == TIF_CURSOR_HEADER; } + bool is_footer() const { return cursor == TIF_CURSOR_FOOTER; } + bool is_index() const { return cursor < TIF_CURSOR_HEADER; } + + void set_header() { cursor = TIF_CURSOR_HEADER; } + void set_footer() { cursor = TIF_CURSOR_FOOTER; } + + define_place_virtual_functions(tiplace_t); + + /// for structs: calculate the offset that corresponds to the tiplace. + /// \param ud pointer to user-defined context data. Is supplied by ::linearray_t + /// \param p_udmidx place to return the index of the current udt member, if any. + /// if there is no member at the current offset, return -1 + /// \param p_bitoff place to return the bit offset of the item from the beginning of the bit bucket + /// if there no bitfields, return -1 + /// \return the current offset or uint64(-1) + virtual uint64 calc_udm_offset(const void *ud, ssize_t *p_udmidx=nullptr, int *p_bitoff=nullptr) const newapi; + + /// for structs: calculate the index that corresponds to the offset and set it. + /// \param ud pointer to user-defined context data. Is supplied by ::linearray_t + /// \param offset offset of udt member + virtual void set_index_by_offset(void *ud, uint64 offset) newapi; + + /// get the kind of type this place represents + /// \param ud pointer to user-defined context data. Is supplied by ::linearray_t + /// \return one of BTF_TYPEDEF, BTF_STRUCT, BTF_UNION, BTF_ENUM or BT_UNK + virtual type_t get_kind(const void *ud) const newapi; + + /// fill the type information for use in actions + /// \param out pointer to storage + /// \param ud pointer to user-defined context data. Is supplied by ::linearray_t + /// \return success + virtual bool fill_type_ref(til_type_ref_t *out, const void *ud) const newapi; +}; + +//------------------------------------------------------------------------- +#define PCF_EA_CAPABLE 0x00000001 ///< toea() implementation returns meaningful data +#define PCF_MAKEPLACE_ALLOCATES 0x00000002 ///< makeplace() returns a freshly allocated (i.e., non-static) + ///< instance. All new code should pass that flag to + ///< register_place_class(), and the corresponding + ///< makeplace() class implementation should + ///< return new instances. + +//------------------------------------------------------------------------- +idaman int ida_export internal_register_place_class( + const place_t *tmplate, + int flags, + const plugin_t *owner, + int sdk_version); + + +//------------------------------------------------------------------------- +/// Register information about a place_t class. +/// +/// The kernel will not take ownership, nor delete the 'tmplate' instance. +/// Therefore, it's up to the plugin to handle it (the recommended way +/// of doing it is to pass address of a const static instance.) +/// In addition, the place_t will be automatically unregistered when the owner +/// plugin is unloaded from memory. +/// \param tmplate the place_t template +/// \param flags or'ed combination of PCF_* flags. You should always +/// pass at least PCF_MAKEPLACE_ALLOCATES, and have the +/// place_t::makeplace() implementation create new instances. +/// \param owner the owner plugin of the place_t type. Cannot be nullptr. +/// \return the place_t ID, or -1 if an error occurred. +inline int register_place_class( + const place_t *tmplate, + int flags, + const plugin_t *owner) +{ + return internal_register_place_class(tmplate, flags, owner, IDA_SDK_VERSION); +} + +//------------------------------------------------------------------------- +/// Get information about a previously-registered place_t class. +/// See also register_place_class(). +/// \param out_flags output flags (can be nullptr) +/// \param out_sdk_version sdk version the place was created with (can be nullptr) +/// \param id place class ID +/// \return the place_t template, or nullptr if not found +idaman const place_t *ida_export get_place_class( + int *out_flags, + int *out_sdk_version, + int id); + +//------------------------------------------------------------------------- +/// See get_place_class() +inline const place_t *get_place_class_template(int id) +{ + return get_place_class(nullptr, nullptr, id); +} + +//------------------------------------------------------------------------- +/// See get_place_class() +inline bool is_place_class_ea_capable(int id) +{ + int flags; + if ( get_place_class(&flags, nullptr, id) == nullptr ) + return false; + return (flags & PCF_EA_CAPABLE) != 0; +} + +//------------------------------------------------------------------------- +/// Get the place class ID for the place that has been registered as 'name'. +/// \param name the class name +/// \return the place class ID, or -1 if not found +idaman int ida_export get_place_class_id(const char *name); + +#ifndef __UI__ + // A TWidget represents any user-facing widget present in IDA. + // E.g., "IDA View-*", "Hex View-*", "Imports", "General registers", ... + class TWidget; +#else + #ifdef __QT__ + namespace QT + { + class QWidget; + }; + typedef QT::QWidget TWidget; + #else + class TView; + typedef TView TWidget; + #endif +#endif + +//------------------------------------------------------------------------- +class sync_source_t +{ + uchar storage[16]; + + const TWidget **get_widget_ptr_storage() const + { + return (const TWidget **) &storage[sizeof(storage) - sizeof(TWidget *)]; + } + +public: + sync_source_t(); + sync_source_t(const TWidget *_view) + { + memset(storage, 0, sizeof(storage)); + *get_widget_ptr_storage() = _view; + storage[0] = '\0'; + } + sync_source_t(const char *_regname) + { + QASSERT(1716, _regname[0] != '\0'); + memset(storage, 0, sizeof(storage)); + ::qstrncpy((char *) storage, _regname, sizeof(storage)); + } + + bool operator==(const sync_source_t &_o) const + { + return memcmp(storage, _o.storage, sizeof(storage)) == 0; + } + bool operator!=(const sync_source_t &_o) const + { + return !((*this) == _o); + } + + bool is_register() const { return storage[0] != '\0'; } + bool is_widget() const { return !is_register(); } + const TWidget *get_widget() const + { + QASSERT(1717, is_widget()); + return *get_widget_ptr_storage(); + } + const char *get_register() const + { + QASSERT(1718, is_register()); + return (const char *) storage; + } +}; +DECLARE_TYPE_AS_MOVABLE(sync_source_t); +CASSERT(sizeof(sync_source_t) == 16); +typedef qvector sync_source_vec_t; + +struct synced_group_t : public sync_source_vec_t +{ + bool has_widget(const TWidget *v) const { return has(sync_source_t((TWidget *) v)); } + bool has_register(const char *r) const { return has(sync_source_t(r)); } + bool has(const sync_source_t &ss) const { return find(ss) != end(); } +}; + +//------------------------------------------------------------------------- +/// Converts from an entry with a given place type, to another entry, +/// with another place type, to be used with the view 'view'. Typically +/// used when views are synchronized. +/// The 'renderer_info_t' part of 'dst' will be pre-filled with +/// the current renderer_info_t of 'view', while the 'place_t' instance +/// will always be nullptr. +enum lecvt_code_t +{ + LECVT_CANCELED = -1, + LECVT_ERROR = 0, + LECVT_OK = 1, +}; + +#define LECVT_WITHIN_LISTING 0x1 // only perform conversion if the location is contained in the current listing (e.g., in the decompiler, don't decompile other functions) +typedef lecvt_code_t idaapi lochist_entry_cvt2_t( + lochist_entry_t *dst, + const lochist_entry_t &src, + TWidget *view, + uint32 flags); + +//------------------------------------------------------------------------- +/// Register a converter, that will be used for the following reasons: +/// - determine what view can be synchronized with what other view +/// - when views are synchronized, convert the location from one view, +/// into an appropriate location in the other view +/// - if one of p1 or p2 is "idaplace_t", and the other is PCF_EA_CAPABLE, +/// then the converter will also be called when the user wants to jump to +/// an address (e.g., by pressing "g"). In that case, from's place_t's lnnum +/// will be set to -1 (i.e., can be used to descriminate between proper +/// synchronizations, and jump to's if needed.) +/// +/// Note: the converter can be used to convert in both directions, and can be +/// called with its 'from' being of the class of 'p1', or 'p2'. +/// If you want your converter to work in only one direction (e.g., from +/// 'my_dictionary_place_t' -> 'my_definition_place_t'), you can have it +/// return false when it is called with a lochist_entry_t's whose place is +/// of type 'my_definition_place_t'. +/// +/// Note: Whenever one of the 'p1' or 'p2' places is unregistered, +/// corresponding converters will be automatically unregistered as well. +/// +/// \param p1 the name of the first place_t class this converter can convert from/to +/// \param p2 the name of the second place_t class this converter can convert from/to +/// \param cvt the converter +idaman void ida_export register_loc_converter2( + const char *p1, + const char *p2, + lochist_entry_cvt2_t *cvt); + +//------------------------------------------------------------------------- +/// Search for a place converter from lochist_entry_t's with places of type +/// 'p1' to lochist_entry_t's with places of type 'p2'. +/// \param p1 the name of the place_t class to convert from +/// \param p2 the name of the place_t class to convert to +/// \return a converter, or nullptr if none found +idaman lochist_entry_cvt2_t *ida_export lookup_loc_converter2( + const char *p1, + const char *p2); + + + +//---------------------------------------------------------------------- +/// A position in a text window +class twinpos_t +{ +public: + place_t *at; ///< location in view + int x; ///< cursor x + twinpos_t(place_t *t=nullptr, int x0=0) { at=t; x=x0; } + DEFINE_MEMORY_ALLOCATION_FUNCS() +}; + +/// A line in a text window +class twinline_t +{ +public: + place_t *at; ///< location in view + qstring line; ///< line contents + color_t prefix_color; ///< line prefix color + bgcolor_t bg_color; ///< line background color + bool is_default = false; ///< is this the default line of the current location? + twinline_t(place_t *t=nullptr, color_t pc=1, bgcolor_t bc=DEFCOLOR) + : at(t), prefix_color(pc), bg_color(bc) {} + DEFINE_MEMORY_ALLOCATION_FUNCS() +}; +DECLARE_TYPE_AS_MOVABLE(twinline_t); + +/// A group of lines in a text window +typedef qvector text_t; + +#ifndef SWIG + +/// Helper for declaring member functions of the ::linearray_t class +#define DECLARE_LINEARRAY_HELPERS(decl) \ +decl void ida_export linearray_t_ctr(linearray_t *, void *ud); \ +decl void ida_export linearray_t_dtr(linearray_t *); \ +decl int ida_export linearray_t_set_place(linearray_t *, const place_t *new_at); \ +decl void ida_export linearray_t_copy_from(linearray_t *, const linearray_t *src); \ +decl bool ida_export linearray_t_beginning(const linearray_t *); \ +decl bool ida_export linearray_t_ending(const linearray_t *); \ +decl const qstring *ida_export linearray_t_down(linearray_t *); \ +decl const qstring *ida_export linearray_t_up(linearray_t *); + +class linearray_t; +DECLARE_LINEARRAY_HELPERS(idaman) +#else +# define DECLARE_LINEARRAY_HELPERS(decl) +#endif // SWIG + +/// The group of lines corresponding to a single place within a view +class linearray_t +{ + DECLARE_LINEARRAY_HELPERS(friend) + + qstrvec_t lines; // lines corresponding to the current place_t + place_t *at; + void *ud; // user defined data (UD) + // its meaning depends on the place_t used + color_t prefix_color; // prefix color + bgcolor_t bg_color; // background color + qstring extra; // the last line of the previous location after moving down + int dlnnum; // default line number (if unknown, -1) + int max_lines; // max number of lines to generate + + int getlines(); + void cleanup(); + int _set_place(const place_t *new_at); + const qstring *_down(); + const qstring *_up(); + void _copy_from(const linearray_t *src); + +public: + + linearray_t(void *_ud) { linearray_t_ctr(this, _ud); } + ~linearray_t() { linearray_t_dtr(this); } + DEFINE_MEMORY_ALLOCATION_FUNCS() + + /// Position the array. + /// This function must be called before calling any other member functions. + /// \param new_at new position of the array. linearray will make a clone + /// of this object. + /// \return the delta of lines that the linearray_t had to adjust the place by. \n + /// For example, if the place_t has a lnnum of 5, but it turns out, upon generating lines, \n + /// that the number of lines for that particular place is only 2, then 3 will be returned. + int set_place(const place_t *new_at) { return linearray_t_set_place(this, new_at); } + + /// Copy information from one linearray to another. + /// Only linearray objects with compatible 'ud' fields can be copied. + void copy_from(const linearray_t *src) { linearray_t_copy_from(this, src); } + + /// Get the current place. + /// If called before down(), then returns place of line which will be returned by down(). + /// If called after up(), then returns place if line returned by up(). + place_t *get_place() const { return at; } + + /// Get current background color. + /// (the same behavior as with get_place(): good before down() and after up()) + bgcolor_t get_bg_color() const { return bg_color; } + + /// Get current prefix color. + /// (the same behavior as with get_place(): good before down() and after up()) + bgcolor_t get_pfx_color() const { return prefix_color; } + + /// Get default line number. + /// (the same behavior as with get_place(): good before down() and after up()) + int get_dlnnum() const { return dlnnum; } + + /// Get number of lines for the current place. + /// (the same behavior as with get_place(): good before down() and after up()) + int get_linecnt() const { return int(lines.size()); } + + /// Get pointer to user data + void *userdata() const { return ud; } + + /// Change the user data + void set_userdata(void *userd) { ud = userd; } + + /// Are we at the beginning? + bool beginning() const { return linearray_t_beginning(this); } + + // Are we at the end? + bool ending() const { return linearray_t_ending(this); } + + /// Get the next line going downwards. + /// 'at' for the retrieved line is correct BEFORE calling this function. + const qstring *down() { return linearray_t_down(this); } + + /// Get the next line going upwards. + /// 'at' for the retrieved line is correct AFTER calling this function. + const qstring *up() { return linearray_t_up(this); } + + /// Set max number of lines to generate for a place. + /// 0 means to use the default value, MAX_ITEM_LINES from ida.cfg + void set_max_lines(int n) { max_lines = n; } +}; + +//------------------------------------------------------------------------- +typedef qvector section_lines_refs_t; +typedef qvector sections_lines_refs_t; + +//------------------------------------------------------------------------- +/// Contains information necessary for plugins to compute extra +/// information needed for rendering. +struct lines_rendering_input_t +{ + int cb = sizeof(*this); + sections_lines_refs_t sections_lines; ///< references to the lines that + ///< are used for rendering + const synced_group_t *sync_group = nullptr; ///< the 'synced' group 'widget' + ///< (see ui_get_lines_rendering_info) + ///< belongs to, or nullptr +}; + +//------------------------------------------------------------------------- +/// \defgroup CK_ keys +/// passed as 'bg_color' of a line_rendering_output_entry_t, to use +/// a CSS property of the widget ('qproperty-line-bgovl-extra-N') +/// instead of a direct color +///@{ +// +#define CK_TRACE 80 ///< traced address +#define CK_TRACE_OVL 81 ///< overlay trace address +#define CK_EXTRA1 82 ///< extra background overlay #1 +#define CK_EXTRA2 83 ///< extra background overlay #2 +#define CK_EXTRA3 84 ///< extra background overlay #3 +#define CK_EXTRA4 85 ///< extra background overlay #4 +#define CK_EXTRA5 86 ///< extra background overlay #5 +#define CK_EXTRA6 87 ///< extra background overlay #6 +#define CK_EXTRA7 88 ///< extra background overlay #7 +#define CK_EXTRA8 89 ///< extra background overlay #8 +#define CK_EXTRA9 90 ///< extra background overlay #9 +#define CK_EXTRA10 91 ///< extra background overlay #10 +#define CK_EXTRA11 92 ///< extra background overlay #11 +#define CK_EXTRA12 93 ///< extra background overlay #12 +#define CK_EXTRA13 94 ///< extra background overlay #13 +#define CK_EXTRA14 95 ///< extra background overlay #14 +#define CK_EXTRA15 96 ///< extra background overlay #15 +#define CK_EXTRA16 97 ///< extra background overlay #16 + +///@} + +/// \defgroup LROEF_ line_rendering_output_entry_t flags +/// used by 'flags' of a line_rendering_output_entry_t +///@{ +#define LROEF_MASK 0x00FFFFFF +#define LROEF_FULL_LINE 0x00000000 ///< full line background +#define LROEF_CPS_RANGE 0x00000001 ///< background for range of chars +///@} + +struct line_rendering_output_entry_t +{ + const twinline_t *line; + uint32 flags; ///< \ref LROEF_ + + // 0x00000000: nothing + // 0xAABBGGRR: where AA is 0: BBGGRR contains a key (CK_*) to a color property + // 0xAABBGGRR: where AA is !0: 0xAABBGGRR is the background color, with alpha value, to be applied to that line + // + // The 'bg_color' specified here, will be applied on top of the background + // color that was computed for 'line', which itself can be: + // - none (i.e., the default background color), + // - a possible value stored in the IDB (see 'set_item_color()'), + // - a value provided by processor_t::ev_get_bg_color + // The value provided here should typically be partly transparent + // so that it doesn't obstruct the computed background color + // (the best, by far, is to stick to CK_* keys: their corresponding + // colors typically have partial translucency, and each theme can + // customize them.) + bgcolor_t bg_color; + + int cpx; ///< number of char to start from, valid if LROEF_CPS_RANGE + int nchars; ///< chars count, valid if LROEF_CPS_RANGE + + line_rendering_output_entry_t(const twinline_t *_line, uint32 _flags=0, bgcolor_t _bg_color=0) + : line(_line), flags(_flags), bg_color(_bg_color), cpx(-1), nchars(-1) {} + + line_rendering_output_entry_t(const twinline_t *_line, int _cpx, int _nchars, uint32 _flags, bgcolor_t _bg_color) + : line(_line), flags(_flags|LROEF_CPS_RANGE), bg_color(_bg_color), cpx(_cpx), nchars(_nchars) {} + + bool is_bg_color_empty() const { return bg_color == 0; } + bool is_bg_color_key() const { return (bg_color & 0xFF000000) == 0 && !is_bg_color_empty(); } + bool is_bg_color_direct() const { return (bg_color & 0xFF000000) != 0; } + + bool operator==(const line_rendering_output_entry_t &r) const + { + bool ok = (flags & LROEF_MASK) == (r.flags & LROEF_MASK) + && line == r.line + && bg_color == r.bg_color; + if ( ok && (flags & LROEF_CPS_RANGE) != 0 ) + ok = cpx == r.cpx && nchars == r.nchars; + return ok; + } + + bool operator!=(const line_rendering_output_entry_t &r) const + { return !(*this == r); } +}; +DECLARE_TYPE_AS_MOVABLE(line_rendering_output_entry_t); +typedef qvector line_rendering_output_entries_refs_t; + +//------------------------------------------------------------------------- +struct lines_rendering_output_t +{ + line_rendering_output_entries_refs_t entries; + uint32 flags = 0; + + ~lines_rendering_output_t() + { + clear(); + } + + void clear() + { + for ( size_t i = 0, n = entries.size(); i < n; ++i ) + delete entries[i]; + } + + bool operator==(const lines_rendering_output_t &r) const + { + if ( flags != r.flags ) + return false; + const size_t n = entries.size(); + if ( n != r.entries.size() ) + return false; + for ( size_t i = 0; i < n; ++i ) + if ( *entries[i] != *r.entries[i] ) + return false; + return true; + } + + bool operator!=(const lines_rendering_output_t &r) const + { return !(*this == r); } + + void swap(lines_rendering_output_t &r) + { + qswap(flags, r.flags); + entries.swap(r.entries); + } +}; + +//------------------------------------------------------------------------- +// Represents a range in a line to be displayed in a listing (or graph). +// All coordinates are in codepoints (i.e., not UTF-8 bytes); color +// tags are ignored. +// In other words, coordinates are in "real, visible" glyphs. +typedef int cpidx_t; // codepoint index +typedef int cplen_t; // codepoint length +typedef qvector cpidxvec_t; +struct line_section_t +{ + cpidx_t start = -1; + cplen_t length = -1; + + + bool contains(cpidx_t x) const + { + return x >= start && x < start + length; + } + + bool is_open() const { return length < 0; } + bool is_closed() const { return !is_open(); } + bool valid() const { return is_closed(); } + + bool operator==(const line_section_t &r) const { return start == r.start && length == r.length; } + bool operator!=(const line_section_t &r) const { return !(*this == r); } +}; +DECLARE_TYPE_AS_MOVABLE(line_section_t); +typedef qvector line_sections_t; + +//------------------------------------------------------------------------- +// A tagged section in a line. +// Note: multiple sections with different tags can overlap. +// For example, the portion of text `[ebp+char_in_main]`: +// [ebp+char_in_main] +// ^________________________________________________________________________________________________________________________________________________________________^ // a `COLOR_OPND1` section +// ^_________________________^ // a `COLOR_SYMBOL` section +// ^____________________^ // a `COLOR_REG` section +// etc... +struct tagged_line_section_t : public line_section_t +{ + struct + { + int text_start = -1; // points right after + int text_end = -1; // points right before + } byte_offsets; + color_t tag = 0; + + tagged_line_section_t() {} + + bool operator==(const tagged_line_section_t &r) const + { + return this->line_section_t::operator==(r) + && tag == r.tag + && byte_offsets.text_start == r.byte_offsets.text_start + && byte_offsets.text_end == r.byte_offsets.text_end; + } + bool operator!=(const tagged_line_section_t &r) const { return !(*this == r); } + + bool valid() const + { + return line_section_t::valid() + && tag > 0 + && byte_offsets.text_start > -1 + && byte_offsets.text_end >= byte_offsets.text_start; + } + + bool valid_in(const qstring &in) const + { + return valid() + && byte_offsets.text_start <= in.length() + && byte_offsets.text_end <= in.length(); + } + + bool substr( + qstring *out, + const qstring &in, + const tagged_line_section_t *end = nullptr) const + { + bool ok = valid_in(in); + if ( ok ) + { + out->qclear(); + int e = byte_offsets.text_end; + if ( end != nullptr && end->byte_offsets.text_start < e ) + e = end->byte_offsets.text_start; + out->append(in.c_str() + byte_offsets.text_start, + e - byte_offsets.text_start); + } + return ok; + } + +}; +DECLARE_TYPE_AS_MOVABLE(tagged_line_section_t); +typedef qvector tagged_line_section_vec_t; + +//------------------------------------------------------------------------- +// The list of tagged sections corresponding to a line of text in a listing +struct tagged_line_sections_t : public tagged_line_section_vec_t +{ + const tagged_line_section_t *first(color_t tag) const + { + for ( const auto &one : *this ) + if ( one.tag == tag ) + return &one; + return nullptr; + } + + void sections_at( + tagged_line_sections_t *out, + cpidx_t x, + color_t tag=0) const + { + for ( const auto &one : *this ) + if ( one.contains(x) && (tag == 0 || one.tag == tag) ) + out->push_back(one); + } + + // For example, '// XREF: __loff_t/r _IO_FILE/r' + // // XREF: __loff_t/r _IO_FILE/r + // ^___ r1 ________________________________________________________________________________^ + // ^ r2 ^ r3 + // nearest_at() --> r1 (because r2 and r3 are zero-length) + const tagged_line_section_t *nearest_at( + cpidx_t x, + color_t tag=0) const + { + const tagged_line_section_t *nearest = nullptr; + for ( const auto &one : *this ) + { + if ( one.contains(x) + && (tag == 0 || one.tag == tag) + && (nearest == nullptr || one.start > nearest->start) ) + { + nearest = &one; + } + } + return nearest; + } + + // For example, '// XREF: __loff_t/r _IO_FILE/r' + // // XREF: __loff_t/r _IO_FILE/r + // ^___ r1 ________________________________________________________________________________^ + // ^ p1 ^ r2 ^ p2 ^ r3 ^ p3 + // nearest_before(r1, p1, COLOR_ADDR) --> nullptr + // nearest_before(r1, p2, COLOR_ADDR) --> r2 (zero-length) + // nearest_before(r1, p3, COLOR_ADDR) --> r3 (zero-length) + // nearest_after (r1, p1, COLOR_ADDR) --> r2 (zero-length) + // nearest_after (r1, p2, COLOR_ADDR) --> r3 (zero-length) + // nearest_after (r1, p3, COLOR_ADDR) --> nullptr + const tagged_line_section_t *nearest_before( + const tagged_line_section_t &range, + cpidx_t start, + color_t tag=0) const + { + const tagged_line_section_t *nearest = nullptr; + for ( const auto &one : *this ) + { + if ( range.contains(one.start) + && one.start <= start + && (tag == 0 || one.tag == tag) + && (nearest == nullptr || one.start > nearest->start) ) + { + nearest = &one; + } + } + return nearest; + } + const tagged_line_section_t *nearest_after( + const tagged_line_section_t &range, + cpidx_t start, + color_t tag=0) const + { + const tagged_line_section_t *nearest = nullptr; + for ( const auto &one : *this ) + { + if ( range.contains(one.start) + && one.start > start + && (tag == 0 || one.tag == tag) + && (nearest == nullptr || one.start < nearest->start) ) + { + nearest = &one; + } + } + return nearest; + } +}; + +/// Holds (live) data about a location being displayed in a listing +/// +/// Note that members of this type, are mere references to live data, +/// and if you intend on consulting them for extended periods of time +/// should should make a copy of them. +struct listing_location_t +{ + int cb = sizeof(*this); + + const lochist_entry_t *loc = nullptr; + const qstring *text = nullptr; + const tagged_line_sections_t *tagged_sections = nullptr; +}; + +#ifndef SWIG +/// Bitmask of builtin window types to be refreshed: +idaman uint64 ida_export get_dirty_infos(); +#endif // SWIG + + +/// Request a refresh of a builtin window. +/// \param mask \ref IWID_ +/// \param cnd set if true or clear flag otherwise + +idaman void ida_export request_refresh(uint64 mask, bool cnd=true); +inline void clear_refresh_request(uint64 mask) { request_refresh(mask, false); } + + +/// Get a refresh request state +/// \param mask \ref IWID_ +/// \return the state (set or cleared) + +idaman bool ida_export is_refresh_requested(uint64 mask); + + +//------------------------------------------------------------------------- +typedef int twidget_type_t; ///< \ref BWN_ + +/// \defgroup BWN_ Window types +/// also see \ref ui_open_builtin_funcs +///@{ +#define BWN_UNKNOWN -1 ///< unknown window +#define BWN_EXPORTS 0 ///< exports +#define BWN_IMPORTS 1 ///< imports +#define BWN_NAMES 2 ///< names +#define BWN_FUNCS 3 ///< functions +#define BWN_STRINGS 4 ///< strings +#define BWN_SEGS 5 ///< segments +#define BWN_SEGREGS 6 ///< segment registers +#define BWN_SELS 7 ///< selectors +#define BWN_SIGNS 8 ///< signatures +#define BWN_TILS 9 ///< type libraries +#define BWN_TICSR 10 ///< type library widget's (e.g., "Local types") chooser +#define BWN_CALLS 11 ///< function calls +#define BWN_PROBS 12 ///< problems +#define BWN_BPTS 13 ///< breakpoints +#define BWN_THREADS 14 ///< threads +#define BWN_MODULES 15 ///< modules +#define BWN_TRACE 16 ///< tracing view +#define BWN_CALL_STACK 17 ///< call stack +#define BWN_XREFS 18 ///< xrefs +#define BWN_SEARCH 19 ///< search results +#define BWN_FRAME 25 ///< function frame +#define BWN_NAVBAND 26 ///< navigation band +#define BWN_DISASM 27 ///< disassembly views +#define BWN_HEXVIEW 28 ///< hex view +#define BWN_NOTEPAD 29 ///< notepad +#define BWN_OUTPUT 30 ///< the text area, in the output window +#define BWN_CLI 31 ///< the command-line, in the output window +#define BWN_WATCH 32 ///< the 'watches' debugger window +#define BWN_LOCALS 33 ///< the 'locals' debugger window +#define BWN_STKVIEW 34 ///< the 'Stack view' debugger window +#define BWN_CHOOSER 35 ///< a non-builtin chooser +#define BWN_SHORTCUTCSR 36 ///< the shortcuts chooser (Qt version only) +#define BWN_SHORTCUTWIN 37 ///< the shortcuts window (Qt version only) +#define BWN_CPUREGS 38 ///< one of the 'General registers', 'FPU register', ... debugger windows +#define BWN_SO_STRUCTS 39 ///< the 'Structure offsets' dialog's 'Structures and Unions' panel +#define BWN_SO_OFFSETS 40 ///< the 'Structure offsets' dialog's offset panel +#define BWN_CMDPALCSR 41 ///< the command palette chooser (Qt version only) +#define BWN_CMDPALWIN 42 ///< the command palette window (Qt version only) +#define BWN_SNIPPETS 43 ///< the 'Execute script' window +#define BWN_CUSTVIEW 44 ///< custom viewers +#define BWN_ADDRWATCH 45 ///< the 'Watch List' window +#define BWN_PSEUDOCODE 46 ///< hexrays decompiler views +#define BWN_CALLS_CALLERS 47 ///< function calls, callers +#define BWN_CALLS_CALLEES 48 ///< function calls, callees +#define BWN_MDVIEWCSR 49 ///< lumina metadata view chooser +#define BWN_DISASM_ARROWS 50 ///< disassembly arrows widget +#define BWN_CV_LINE_INFOS 51 ///< custom viewers' lineinfo widget +#define BWN_SRCPTHMAP_CSR 52 ///< "Source paths..."'s path mappings chooser +#define BWN_SRCPTHUND_CSR 53 ///< "Source paths..."'s undesired paths chooser +#define BWN_UNDOHIST 54 ///< Undo history +#define BWN_SNIPPETS_CSR 55 ///< the list of snippets in the 'Execute script' window +#define BWN_SCRIPTS_CSR 56 ///< the "Recent scripts" chooser +#define BWN_BOOKMARKS 57 ///< a persistent 'Bookmarks' widget +#define BWN_TILIST 58 ///< a type listing widget +#define BWN_TIL_VIEW 59 ///< a type library's toplevel widget +#define BWN_TYPE_EDITOR 60 ///< a type editor +#define BWN_MICROCODE 61 ///< microcode view (part of hexrays decompiler) +#define BWN_XREF_TREE 62 ///< xref tree widget + +///@} + +/// \defgroup IWID_ Window refresh flags +/// passed as 'mask' parameter to request_refresh() +///@{ +#define IWID_EXPORTS (1ULL << BWN_EXPORTS ) ///< exports +#define IWID_IMPORTS (1ULL << BWN_IMPORTS ) ///< imports +#define IWID_NAMES (1ULL << BWN_NAMES ) ///< names +#define IWID_FUNCS (1ULL << BWN_FUNCS ) ///< functions +#define IWID_STRINGS (1ULL << BWN_STRINGS ) ///< strings +#define IWID_SEGS (1ULL << BWN_SEGS ) ///< segments +#define IWID_SEGREGS (1ULL << BWN_SEGREGS ) ///< segment registers +#define IWID_SELS (1ULL << BWN_SELS ) ///< selectors +#define IWID_SIGNS (1ULL << BWN_SIGNS ) ///< signatures +#define IWID_TILS (1ULL << BWN_TILS ) ///< type libraries +#define IWID_TICSR (1ULL << BWN_TICSR ) ///< type library widget's (e.g., "Local types") chooser +#define IWID_CALLS (1ULL << BWN_CALLS ) ///< function calls +#define IWID_PROBS (1ULL << BWN_PROBS ) ///< problems +#define IWID_BPTS (1ULL << BWN_BPTS ) ///< breakpoints +#define IWID_THREADS (1ULL << BWN_THREADS ) ///< threads +#define IWID_MODULES (1ULL << BWN_MODULES ) ///< modules +#define IWID_TRACE (1ULL << BWN_TRACE ) ///< tracing view +#define IWID_CALL_STACK (1ULL << BWN_CALL_STACK ) ///< call stack +#define IWID_XREFS (1ULL << BWN_XREFS ) ///< xrefs +#define IWID_SEARCH (1ULL << BWN_SEARCH ) ///< search results +#define IWID_FRAME (1ULL << BWN_FRAME ) ///< function frame +#define IWID_NAVBAND (1ULL << BWN_NAVBAND ) ///< navigation band +#define IWID_DISASM (1ULL << BWN_DISASM ) ///< disassembly views +#define IWID_HEXVIEW (1ULL << BWN_HEXVIEW ) ///< hex views +#define IWID_NOTEPAD (1ULL << BWN_NOTEPAD ) ///< notepad +#define IWID_OUTPUT (1ULL << BWN_OUTPUT ) ///< output +#define IWID_CLI (1ULL << BWN_CLI ) ///< input line +#define IWID_WATCH (1ULL << BWN_WATCH ) ///< watches +#define IWID_LOCALS (1ULL << BWN_LOCALS ) ///< locals +#define IWID_STKVIEW (1ULL << BWN_STKVIEW ) ///< stack view +#define IWID_CHOOSER (1ULL << BWN_CHOOSER ) ///< chooser +#define IWID_SHORTCUTCSR (1ULL << BWN_SHORTCUTCSR ) ///< shortcuts chooser +#define IWID_SHORTCUTWIN (1ULL << BWN_SHORTCUTWIN ) ///< shortcuts window +#define IWID_CPUREGS (1ULL << BWN_CPUREGS ) ///< registers +#define IWID_SO_STRUCTS (1ULL << BWN_SO_STRUCTS ) ///< stroff +#define IWID_SO_OFFSETS (1ULL << BWN_SO_OFFSETS ) ///< stroff +#define IWID_CMDPALCSR (1ULL << BWN_CMDPALCSR ) ///< command palette +#define IWID_CMDPALWIN (1ULL << BWN_CMDPALWIN ) ///< command palette +#define IWID_SNIPPETS (1ULL << BWN_SNIPPETS ) ///< snippets +#define IWID_CUSTVIEW (1ULL << BWN_CUSTVIEW ) ///< custom viewers +#define IWID_ADDRWATCH (1ULL << BWN_ADDRWATCH ) ///< address watches +#define IWID_PSEUDOCODE (1ULL << BWN_PSEUDOCODE ) ///< decompiler +#define IWID_CALLS_CALLERS (1ULL << BWN_CALLS_CALLERS) ///< funcalls, callers +#define IWID_CALLS_CALLEES (1ULL << BWN_CALLS_CALLEES) ///< funcalls, callees +#define IWID_MDVIEWCSR (1ULL << BWN_MDVIEWCSR ) ///< lumina md view +#define IWID_DISASM_ARROWS (1ULL << BWN_DISASM_ARROWS) ///< arrows widget +#define IWID_CV_LINE_INFOS (1ULL << BWN_CV_LINE_INFOS) ///< lineinfo widget +#define IWID_SRCPTHMAP_CSR (1ULL << BWN_SRCPTHMAP_CSR) ///< mappings chooser +#define IWID_SRCPTHUND_CSR (1ULL << BWN_SRCPTHUND_CSR) ///< undesired chooser +#define IWID_UNDOHIST (1ULL << BWN_UNDOHIST ) ///< Undo history +#define IWID_SNIPPETS_CSR (1ULL << BWN_SNIPPETS_CSR ) ///< snippets chooser +#define IWID_SCRIPTS_CSR (1ULL << BWN_SCRIPTS_CSR ) ///< recent scripts +#define IWID_BOOKMARKS (1ULL << BWN_BOOKMARKS ) ///< bookmarks list +#define IWID_TILIST (1ULL << BWN_TILIST ) ///< type listing +#define IWID_TIL_VIEW (1ULL << BWN_TIL_VIEW ) ///< type library's toplevel widget +#define IWID_TYPE_EDITOR (1ULL << BWN_TYPE_EDITOR ) ///< a type editor +#define IWID_XREF_TREE (1ULL << BWN_XREF_TREE ) ///< xref tree widget + +#define IWID_ANY_LISTING (IWID_DISASM|IWID_HEXVIEW|IWID_TILIST|IWID_FRAME|IWID_PSEUDOCODE|IWID_CUSTVIEW) ///< anything that uses a listing widget +#define IWID_EA_LISTING (IWID_DISASM|IWID_HEXVIEW|IWID_PSEUDOCODE) ///< anything that can be used to represent data/code at an address +#define IWID_ALL 0xFFFFFFFFFFFFFFFFULL ///< mask + +///@} + +/// Does the given widget type specify a chooser widget? + +inline bool is_chooser_widget(twidget_type_t t) +{ + return t == BWN_CHOOSER + || (t >= BWN_EXPORTS && t <= BWN_SEARCH && t != BWN_CALLS) + || t == BWN_SHORTCUTCSR + || t == BWN_CMDPALCSR + || t == BWN_CALLS_CALLERS + || t == BWN_CALLS_CALLEES + || t == BWN_MDVIEWCSR + || t == BWN_SRCPTHMAP_CSR + || t == BWN_SRCPTHUND_CSR + || t == BWN_UNDOHIST + || t == BWN_SNIPPETS_CSR + || t == BWN_SCRIPTS_CSR + || t == BWN_BOOKMARKS; +} + + +//--------------------------------------------------------------------------- +// D E B U G G I N G F U N C T I O N S +//--------------------------------------------------------------------------- + +/// Controls debug messages - combination of \ref IDA_DEBUG_ +idaman uint32 ida_export_data debug; + +/// \defgroup IDA_DEBUG_ IDA debug bits +/// used by ::debug +///@{ +#define IDA_DEBUG_DREFS 0x00000001 ///< drefs +#define IDA_DEBUG_OFFSET 0x00000002 ///< offsets +#define IDA_DEBUG_FLIRT 0x00000004 ///< flirt +#define IDA_DEBUG_IDP 0x00000008 ///< idp module +#define IDA_DEBUG_LDR 0x00000010 ///< ldr module +#define IDA_DEBUG_PLUGIN 0x00000020 ///< plugin module +#define IDA_DEBUG_IDS 0x00000040 ///< ids files +#define IDA_DEBUG_CONFIG 0x00000080 ///< config file +#define IDA_DEBUG_CHECKMEM 0x00000100 ///< check heap consistency +#define IDA_DEBUG_LICENSE 0x00000200 ///< licensing +#define IDA_DEBUG_DEMANGLE 0x00000400 ///< demangler +#define IDA_DEBUG_QUEUE 0x00000800 ///< queue +#define IDA_DEBUG_ROLLBACK 0x00001000 ///< rollback +#define IDA_DEBUG_ALREADY 0x00002000 ///< already data or code +#define IDA_DEBUG_TIL 0x00004000 ///< type system +#define IDA_DEBUG_NOTIFY 0x00008000 ///< show all notifications +#define IDA_DEBUG_DEBUGGER 0x00010000 ///< debugger +#define IDA_DEBUG_APPCALL 0x00020000 ///< appcall +#define IDA_DEBUG_SRCDBG 0x00040000 ///< source debugging +#define IDA_DEBUG_ACCESSIBILITY 0x00080000 ///< accessibility +#define IDA_DEBUG_NETWORK 0x00100000 ///< network +#define IDA_DEBUG_INTERNET IDA_DEBUG_NETWORK ///< internet connection (for API backward compatibility) +#define IDA_DEBUG_SIMPLEX 0x00200000 ///< full stack analysis +#define IDA_DEBUG_DBGINFO 0x00400000 ///< handling of debug info (e.g. pdb, dwarf) +#define IDA_DEBUG_LUMINA 0x00800000 ///< lumina related +#define IDA_DEBUG_THEMES 0x01000000 ///< themes +#define IDA_DEBUG_REGEX 0x02000000 ///< regular expression +#define IDA_DEBUG_SUBPROC 0x04000000 ///< sub process +#define IDA_DEBUG_RANGECB 0x08000000 ///< range-based entities like segments, functions and so on +#define IDA_DEBUG_ALWAYS 0xFFFFFFFF ///< everything +///@} + + +#ifndef SWIG + +/// Display debug message. +/// \param ida_debug_bits \ref IDA_DEBUG_, also see ::debug +/// \param format printf()-style format +/// \return number of bytes output +/// Note: use deb() macro + +AS_PRINTF(1, 2) inline int ida_deb(const char *format, ...) +{ + va_list va; + va_start(va, format); + int nbytes = callui(ui_msg, format, va).i; + va_end(va); + return nbytes; +} + +#define deb(ida_debug_bits, ...) \ + do \ + { \ + if ( (debug & (ida_debug_bits)) != 0 ) \ + ida_deb(__VA_ARGS__); \ + } while ( false ) + +/// Display hex dump in the messages window + +AS_PRINTF(3, 4) inline void show_hex( + const void *dataptr, + size_t len, + const char *format, + ...) +{ + va_list va; + va_start(va,format); + vshow_hex(dataptr, len, format, va); + va_end(va); +} + + +/// Display hex dump of a file in the messages window + +AS_PRINTF(4, 5) inline void show_hex_file( + linput_t *li, + int64 pos, + size_t count, + const char *format, + ...) +{ + va_list va; + va_start(va,format); + vshow_hex_file(li, pos, count, format, va); + va_end(va); +} +#endif // SWIG + +//------------------------------------------------------------------------- +// U I S E R V I C E F U N C T I O N S +//------------------------------------------------------------------------- + +/// Action states - returned by action_handler_t::update() +enum action_state_t +{ + AST_ENABLE_ALWAYS, ///< enable action and do not call action_handler_t::update() anymore + + AST_ENABLE_FOR_IDB, ///< enable action for the current idb. + ///< call action_handler_t::update() when a database is opened/closed + + AST_ENABLE_FOR_WIDGET, ///< enable action for the current widget. + ///< call action_handler_t::update() when a widget gets/loses focus + + AST_ENABLE, ///< enable action - call action_handler_t::update() when anything changes + + AST_DISABLE_ALWAYS, ///< disable action and do not call action_handler_t::action() anymore + AST_DISABLE_FOR_IDB, ///< analog of ::AST_ENABLE_FOR_IDB + AST_DISABLE_FOR_WIDGET, ///< analog of ::AST_ENABLE_FOR_WIDGET + AST_DISABLE, ///< analog of ::AST_ENABLE +}; + + +/// Check if the given action state is one of AST_ENABLE* + +inline bool is_action_enabled(action_state_t s) +{ + return s <= AST_ENABLE; +} + +//------------------------------------------------------------------------- +/// \defgroup CH_ Generic chooser flags +/// used as 'chooser_base_t::flags' +///@{ + +/// Modal chooser +#define CH_MODAL 0x00000001 +/// The chooser instance's lifecycle is not tied to the lifecycle of the +/// widget showing its contents. Closing the widget will not destroy the +/// chooser structure. This allows for, e.g., static global chooser instances +/// that don't need to be allocated on the heap. Also stack-allocated chooser +/// instances must set this bit. +#define CH_KEEP 0x00000002 +/// The chooser will allow multi-selection (only for GUI choosers). This bit +/// is set when using the chooser_multi_t structure. +#define CH_MULTI 0x00000004 +/// Obsolete +#define CH_MULTI_EDIT 0x00000008 +/// do not display ok/cancel/help/search buttons. +/// Meaningful only for gui modal windows because non-modal windows do not +/// have any buttons anyway. Text mode does not have them neither. +#define CH_NOBTNS 0x00000010 +/// generate ui_get_chooser_item_attrs (gui only) +#define CH_ATTRS 0x00000020 +#define CH_UNUSED 0x00000040 +/// if a non-modal chooser was already open, change selection to the default +/// one +#define CH_FORCE_DEFAULT 0x00000080 +/// allow to insert new items +#define CH_CAN_INS 0x00000100 +/// allow to delete existing item(s) +#define CH_CAN_DEL 0x00000200 +/// allow to edit existing item(s) +#define CH_CAN_EDIT 0x00000400 +/// allow to refresh chooser +#define CH_CAN_REFRESH 0x00000800 + +/// open with quick filter enabled and focused +#define CH_QFLT 0x00001000 +#define CH_QFTYP_SHIFT 13 +#define CH_QFTYP_DEFAULT 0 ///< set quick filtering type to the possible existing default for this chooser +#define CH_QFTYP_NORMAL (1 << CH_QFTYP_SHIFT) ///< normal (i.e., lexicographical) quick filter type +#define CH_QFTYP_WHOLE_WORDS (2 << CH_QFTYP_SHIFT) ///< whole words quick filter type +#define CH_QFTYP_REGEX (3 << CH_QFTYP_SHIFT) ///< regex quick filter type +#define CH_QFTYP_FUZZY (4 << CH_QFTYP_SHIFT) ///< fuzzy search quick filter type +#define CH_QFTYP_MASK (0x7 << CH_QFTYP_SHIFT) + +/// don't show a status bar +#define CH_NO_STATUS_BAR 0x00010000 + +/// restore floating position if present (equivalent of WOPN_RESTORE) (GUI version only) +#define CH_RESTORE 0x00020000 + +/// triggering a 'edit/rename' (i.e., F2 shortcut) on a cell, +/// should call the edit() callback for the corresponding row. +#define CH_RENAME_IS_EDIT 0x00040000 + +#define CH_BUILTIN_SHIFT 19 +#define CH_BUILTIN(id) ((id+1) << CH_BUILTIN_SHIFT) +/// Mask for builtin chooser numbers. Plugins should not use them. +#define CH_BUILTIN_MASK (0x3F << CH_BUILTIN_SHIFT) + +/// The chooser can provide a dirtree_t, meaning a tree-like structure +/// can be provided to the user (instead of a flat table) +#define CH_HAS_DIRTREE 0x02000000 + +#define CH_TM_NO_TREE 0x00000000 ///< chooser will show in no-tree mode +#define CH_TM_FOLDERS_ONLY 0x04000000 ///< chooser will show in folders-only mode +#define CH_TM_FULL_TREE 0x08000000 ///< chooser will show in full-tree mode +#define CH_TM_SHIFT 26 +#define CH_TM_MASK (0x3 << CH_TM_SHIFT) + +/// The chooser can be used in a diffing/merging workflow +#define CH_HAS_DIFF 0x10000000 + +/// The chooser will not have sorting abilities +#define CH_NO_SORT 0x20000000 + +/// The chooser will not have filtering abilities +#define CH_NO_FILTER 0x40000000 + +/// the chooser tree is not persisted (it is not loaded on startup and is +/// not saved on exit) +#define CH_NON_PERSISTED_TREE 0x80000000 +///@} + +/// \defgroup CH2_ Extended chooser flags +/// used as 'chooser_base_t::flags2' +///@{ + +/// The chooser is lazy-loaded; it receives the callback do_lazy_load_dir() +/// (only meaningful when CH_HAS_DIRTREE is set) +#define CH2_LAZY_LOADED 0x0001 +///@} +#define CH2_HAS_INODE2INDEX 0x0002 + +/// \defgroup CHCOL_ Chooser column flags +/// used by 'widths' parameter for \ref choosers +///@{ +#define CHCOL_PLAIN 0x00000000 ///< plain string +#define CHCOL_PATH 0x00010000 ///< file path. TUI IDA will truncate + ///< excessive cell lengths starting at + ///< their beginning, and prepending the + ///< resulting text with "..." order to + ///< leave the filename visible +#define CHCOL_HEX 0x00020000 ///< hexadecimal number +#define CHCOL_DEC 0x00030000 ///< decimal number +#define CHCOL_EA 0x00040000 ///< address +#define CHCOL_FNAME 0x00050000 ///< function name. If a chooser column has + ///< this flag set and implements + ///< chooser_base_t::get_ea(), rows background + ///< colors will be automatically set to + ///< match the navigator's "Library function", + ///< "Lumina function" and + ///< "External symbol" colors +#define CHCOL_FORMAT 0x00070000 ///< column format mask + +#define CHCOL_DEFHIDDEN 0x00100000 ///< column should be hidden by default +#define CHCOL_DRAGHINT 0x00200000 ///< the column number that will be used + ///< to build hints for the dragging undo + ///< label. This should be provided for at + ///< most one column for any given chooser. +#define CHCOL_INODENAME 0x00400000 ///< if CH_HAS_DIRTREE has been specified, + ///< this instructs the chooser that this + ///< column shows the inode name. This + ///< should be provided for at most one + ///< column for any given chooser. +///@} + + +/// \defgroup CHITEM_ Chooser item property bits +/// used by chooser_item_attrs_t::flags +///@{ +#define CHITEM_BOLD 0x0001 ///< display the item in bold +#define CHITEM_ITALIC 0x0002 ///< display the item in italic +#define CHITEM_UNDER 0x0004 ///< underline the item +#define CHITEM_STRIKE 0x0008 ///< strikeout the item +#define CHITEM_GRAY 0x0010 ///< gray out the item +///@} + +/// \name Chooser title +/// prefixes to be used in the chooser title +///@{ +#define CHOOSER_NOMAINMENU "NOMAINMENU\n" ///< do not display main menu +#define CHOOSER_NOSTATUSBAR "NOSTATUSBAR\n" ///< do not display status bar (obsolete. Use CH_NO_STATUS_BAR instead) +///@} + +class dirtree_t; + +/// Chooser item attributes +struct chooser_item_attrs_t +{ + int cb; ///< size of this structure. + ///< the callback must check this field and fill only + ///< the existing fields. the first 2 fields always exist: + int flags; ///< \ref CHITEM_ + bgcolor_t color; ///< item color + chooser_item_attrs_t() + : cb(sizeof(chooser_item_attrs_t)), + flags(0), + color(DEFCOLOR) {} + void reset() ///< restore to defaults + { + cb = sizeof(chooser_item_attrs_t); + flags = 0; + color = DEFCOLOR; + } + bool operator==(const chooser_item_attrs_t &other) const + { + return cb == other.cb + && flags == other.flags + && color == other.color; + } +}; +DECLARE_TYPE_AS_MOVABLE(chooser_item_attrs_t); + +/// Row data returned by get_chooser_rows(). +struct chooser_row_info_t +{ + qstrvec_t texts; ///< texts, one per chooser column + chooser_item_attrs_t attrs; ///< styling attributes + int icon = -1; ///< icon number + + // needed by pywraps + bool operator==(const chooser_row_info_t &other) const + { + return texts == other.texts + && attrs == other.attrs + && icon == other.icon; + } + bool operator!=(const chooser_row_info_t &other) const + { + return !(*this == other); + } +}; +DECLARE_TYPE_AS_MOVABLE(chooser_row_info_t); +typedef qvector chooser_row_info_vec_t; + +/// \defgroup GCRF_ Flags for get_chooser_rows(). +///@{ +#define GCRF_HIGH_BIT (0x8000000000000000ULL) // used to be: + // (size_t(1) << (8 * sizeof(size_t) - 1)) + // but SWIG won't swallow the expression +#define GCRF_HEADER (GCRF_HIGH_BIT | 0) ///< Return header texts +#define GCRF_SELECTION (GCRF_HIGH_BIT | 1) ///< Return selected rows +#define GCRF_CURRENT (GCRF_HIGH_BIT | 2) ///< Return the current row +#define GCRF_ALL (GCRF_HIGH_BIT | 3) ///< Return all rows +///@} + +/// the standard action description +/// The chooser has 4 standard actions: Insert, Delete, Edit, Refresh. +/// We used the term "popup actions" before, but now we prefer "standard +/// actions", so there is some confusion in the naming. +struct action_ctx_base_t; +typedef action_ctx_base_t action_update_ctx_t; +struct chooser_stdact_desc_t +{ + int version = 1; ///< to support the backward compatibility + const char *label; ///< see action_desc_t + const char *tooltip; + int icon; + + chooser_stdact_desc_t( + const char *_label = nullptr, + const char *_tooltip = nullptr, + int _icon = -1) + : label(_label), tooltip(_tooltip), icon(_icon) {} + virtual ~chooser_stdact_desc_t() {} + + /// the update callback, see action_handler_t::update() + /// When the update callback is called from the chooser UI engine, it can + /// be sure that ctx.source.chooser is a valid pointer to chooser_base_t + /// and that there are selected items for the Delete and Edit actions. + virtual action_state_t idaapi ucb(action_update_ctx_t * /*ctx*/) + { + return AST_ENABLE_FOR_WIDGET; + } +}; + +/// Chooser object. #chooser +struct chooser_base_t +{ +#ifdef SWIG + %immutable; +#endif +protected: + uint8 version = 3; ///< version of the class + uint8 reserved = 0; + uint16 flags2; ///< \ref CH2_ + uint32 flags; ///< \ref CH_ + +public: + // TODO reduce to 4 values + // embedded chooser: width, height. Other values are ignored. + // qt: y1 == -2 => minimal height (and centered) + // Other values are ignored. + int x0 = -1; ///< screen position, \ref choosers + int y0 = -1; + int x1 = -1; + int y1 = -1; + int width = 0; ///< (in chars) + int height = 0; ///< (in chars) + + const char *title; ///< menu title (includes ptr to help). + ///< May have chooser title prefixes (see "Chooser + ///< title" above). + int columns; ///< number of columns + const int *widths; ///< column widths + ///< - low 16 bits of each value hold the column width + ///< - high 16 bits are flags (see \ref CHCOL_) + const char *const *header; + ///< header line; contains the tooltips, and column name + ///< for each of 'columns' columns. + ///< When tooltips need to be provided, the syntax + ///< should be: "#tooltip#column-name". (Otherwise, + ///< the syntax is simply "column-name".) + int icon = -1; ///< default icon + + /// \defgroup chooser_index Special values of the chooser index + /// Used in the following contexts: + /// 1. as the return value of the choose() function + /// 2. as the `idx` field of the return value of the get_item_index(), + /// ins(), del(), edit(), enter(), refresh() callbacks of the + /// `chooser_t` structure + /// 3. as the parameter `n` of the chooser_t::refresh() callback + /// Usage matrix + // Context | 1 | 2 | 3 + // --------------------------- + // NO_SELECTION | X | X | X + // EMPTY_CHOOSER | X | | + // ALREADY_EXISTS | X | | + ///@{ + static constexpr ssize_t NO_SELECTION = -1; ///< there is no selected item + static constexpr ssize_t EMPTY_CHOOSER = -2; ///< the chooser has no data + ///< and cannot be displayed + static constexpr ssize_t ALREADY_EXISTS = -3; ///< the non-modal chooser + ///< with the same data is + ///< already open + static constexpr ssize_t NO_ATTR = -4; ///< reserved for IDAPython + ///@} + + enum { POPUP_INS, POPUP_DEL, POPUP_EDIT, POPUP_REFRESH, NSTDPOPUPS }; + /// array of custom labels of the standard actions. + /// Used to replace labels for these actions. \n + /// An empty name means that the default name will be used. + /// \note Availability of these actions is determined by the CH_CAN_... + /// flags. + /// The label, icon and other action attributes can be overwritten in the + /// action description returned by get_stdact_descs() + qstring popup_names[NSTDPOPUPS]; + + int deflt_col = 0; ///< Column that will have focus. + + chooser_base_t( + uint32 flags_ = 0, + int columns_ = 0, + const int *widths_ = nullptr, + const char *const *header_ = nullptr, + const char *title_ = nullptr, + uint16 flags2_ = 0) + : flags2(flags2_), + flags(flags_), + title(title_), + columns(columns_), + widths(widths_), + header(header_) {} + virtual ~chooser_base_t() {} + DEFINE_MEMORY_ALLOCATION_FUNCS() + + // called when the corresponding widget is destroyed. + // + // In some rare cases where multiple chooser_base_t instance can + // be backed by the same data (\sa get_obj_id), and a second instance + // is created while the first is already displayed (therefore making + // that second instance a duplicate), the second instance will be + // destroyed through this method (and chooser_base_t::ALREADY_EXISTS + // will be returned from choose().) + void call_destructor() + { + if ( has_widget_lifecycle() ) + delete this; + } + + /// get pointer to some custom data. + /// \note These data are also called "the underlying object". + /// Now this method is used only in the ActionsInspector class and + /// ida_kernwin.Choose IDAPython's class. + virtual void *get_chooser_obj() { return this; } + + /// get the id of the chooser data. + /// The choosers are the same if they have the same data ids. + /// \param[out] len length of the id. If it is 0 then it is considered + /// that the method returned an unique id. + /// \return address of the id or nullptr in the case len == 0 + virtual const void *get_obj_id(size_t *len) const + { + // return the unique id + *len = 0; + return nullptr; + } + + /// do the current and the given objects hold the same data? + bool is_same(const chooser_base_t *other) const + { + size_t len1; + const void *id1 = get_obj_id(&len1); + size_t len2; + const void *id2 = other->get_obj_id(&len2); + return len1 == len2 && len1 != 0 && memcmp(id1, id2, len1) == 0; + } + + /// is an operation allowed? + bool can_ins() const { return (flags & CH_CAN_INS ) != 0; } + bool can_del() const { return (flags & CH_CAN_DEL ) != 0; } + bool can_edit() const { return (flags & CH_CAN_EDIT ) != 0; } + bool can_refresh() const { return (flags & CH_CAN_REFRESH) != 0; } + + /// is a standard action allowed? + bool popup_allowed(int stdact_idx) const + { + switch ( stdact_idx ) + { + case POPUP_INS: return can_ins(); + case POPUP_DEL: return can_del(); + case POPUP_EDIT: return can_edit(); + case POPUP_REFRESH: return can_refresh(); + default: return false; + } + } + bool is_status_bar_hidden() const { return (flags & CH_NO_STATUS_BAR) != 0; } + bool should_restore_geometry() const { return (flags & CH_RESTORE) != 0; } + /// is choose modal? + bool is_modal() const { return (flags & CH_MODAL) != 0; } + /// should chooser object be deleted when the widget gets destroyed? + bool has_widget_lifecycle() const { return (flags & CH_KEEP) == 0; } + /// is multi-selection allowed? + bool is_multi() const { return (flags & CH_MULTI) != 0; } + /// should chooser generate ui_get_chooser_item_attrs events? + bool ask_item_attrs() const { return (flags & CH_ATTRS) != 0; } + /// should selection of the already opened non-modal chooser be changed? + bool is_force_default() const { return (flags & CH_FORCE_DEFAULT) != 0; } + /// get number of the built-in chooser + uint get_builtin_number() const + { + return ((flags & CH_BUILTIN_MASK) >> CH_BUILTIN_SHIFT) - 1; + } + /// enable or disable generation of ui_get_chooser_item_attrs events + void set_ask_item_attrs(bool enable) + { + if ( enable ) + flags |= CH_ATTRS; + else + flags &= ~CH_ATTRS; + } + // check chooser version + void check_version(uint32 ver) const { QASSERT(40217, version >= ver); } + // should the quick filter be visible at startup? + bool is_quick_filter_visible_initially() const { return (flags & CH_QFLT) != 0; } + // what mode should the quick filter initially be put in? + int get_quick_filter_initial_mode() const { return flags & CH_QFTYP_MASK; } + // does the chooser have the ability to show a tree view? + bool has_dirtree() const { return (flags & CH_HAS_DIRTREE) != 0; } + // does the chooser have the ability to participate in a diff/merge workflow? + bool has_diff_capability() const { return (flags & CH_HAS_DIFF) != 0; } + // does chooser have sorting abilities? + bool can_sort() const { return (flags & CH_NO_SORT) == 0; } + // does chooser have filtering abilities? + bool can_filter() const { return (flags & CH_NO_FILTER) == 0; } + // should renaming trigger the 'edit' callback? + bool should_rename_trigger_edit() const { return (flags & CH_RENAME_IS_EDIT) != 0; } + // is the chooser dirtree persisted? + bool is_dirtree_persisted() const { return (flags & CH_NON_PERSISTED_TREE) == 0; } + // is the chooser lazy-loaded? + bool is_lazy_loaded() const { return version >= 3 && (flags2 & CH2_LAZY_LOADED) != 0; } + // chooser_implement inode2index + bool has_inode_to_index() const { return version >= 3 && (flags2 & CH2_HAS_INODE2INDEX) != 0; } + + /// initialize the chooser and populate it. + /// \retval false the chooser is empty, do not display it + virtual bool idaapi init() { return true; } + + /// get the number of elements in the chooser + virtual size_t idaapi get_count() const = 0; + + /// get a description of an element. + /// \param[out] out vector of strings. \n + /// will receive the contents of each column + /// \param[out] out_icon element's icon id, -1 - no icon + /// \param[out] out_attrs element attributes + /// \param n element number (0..get_count()-1) + virtual void idaapi get_row( + qstrvec_t *out, + int *out_icon, + chooser_item_attrs_t *out_attrs, + size_t n) const = 0; + + + /// get the address of an element. + /// When this function returns valid addresses: + /// * If any column has the `CHCOL_FNAME` flag, rows will + /// be colored according to the attributes of the functions + /// who own those addresses (extern, library function, + /// Lumina, ... - similar to what the "Functions" widget does) + /// * When a selection is present and the user presses `` + /// (`` if the chooser is modal), IDA will jump + /// to that address (through jumpto()) + /// \param n element number (0-based) + /// \return the effective address, BADADDR if the element has no address + virtual ea_t idaapi get_ea(size_t /*n*/) const { return BADADDR; } + + /// return value of ins(), del(), edit(), enter(), refresh() callbacks + /// + /// If the chooser implements get_dirtree(), and has ins() and/or del() + /// capabilities, the meaning of the returned index(es) combined with + /// ALL_CHANGED and SELECTION_CHANGED must be as follows: + /// - for ins(): the index (in case of a chooser_t, or first index + /// in case of a chooser_multi_t), will be the index of the row + /// that was inserted. + /// - for del(): the index (or indexes in case of a chooser_multi_t), + /// will be the index(es) of the row(s) that was(were) deleted. + enum cbres_t + { + NOTHING_CHANGED, + ALL_CHANGED, + SELECTION_CHANGED, + }; + + /// The chooser window is closed. + virtual void idaapi closed() {} + +protected: + // the default labels of the standard actions are different for the qt- + // and txt-versions of the chooser + void init_popup_names(const char *const default_popup_names[NSTDPOPUPS]) + { + for ( int i = 0; i < NSTDPOPUPS; ++i ) + if ( popup_names[i].empty() ) + popup_names[i] = default_popup_names[i]; + } +#ifdef SWIG + %mutable; +#endif +}; + +#ifndef SWIG + +/// The chooser object without multi-selection. +struct chooser_t : public chooser_base_t +{ + /// Return value of ins(), del(), edit(), enter(), refresh() callbacks + struct cbret_t + { + ssize_t idx; + cbres_t changed; + cbret_t() : idx(NO_SELECTION), changed(NOTHING_CHANGED) {} + cbret_t(ssize_t idx_, cbres_t changed_ = ALL_CHANGED) + : idx(idx_), changed(changed_) {} + }; + + chooser_t(uint32 flags_ = 0, + int columns_ = 0, + const int *widths_ = nullptr, + const char *const *header_ = nullptr, + const char *title_ = nullptr, + uint16 flags2_ = 0) + : chooser_base_t( + (flags_ & ~CH_MULTI), + columns_, widths_, header_, + title_, + flags2_) {} + + /// Display a generic list chooser and allow the user to select an item. + /// May be overridden in derived choosers. + /// \param deflt default selection or NO_SELECTION + /// see the choose() function below + //lint -sem(chooser_t::choose,custodial(t)) + inline ssize_t choose(ssize_t deflt = 0); + + /// Get the position (index) of the item. + /// A simple chooser considers `item_data` as an index. + /// \param item_data pointer to some data that identifies the item + /// \return idx item index, + /// NO_SELECTION - there is no item with such data + virtual ssize_t idaapi get_item_index(const void *item_data) const newapi + { + // no calculation when `item_data` already is an index + return *(const ssize_t *)item_data; + } + + /// Type of ins(), del(), edit(), enter(), refresh() callbacks + typedef cbret_t (idaapi chooser_t::*cb_t)(size_t n); + + /// User asked to insert an element. + virtual cbret_t idaapi ins(ssize_t /*n*/) newapi { return cbret_t(); } + + /// User deleted an element. + /// \param n index of the element to delete + /// \return idx index of the selected item (cursor) + /// changed what is changed + virtual cbret_t idaapi del(size_t /*n*/) newapi { return cbret_t(); } + + /// User asked to edit an element. + /// \param n index of the element to edit + /// \return idx index of the selected item (cursor) + /// changed what is changed + virtual cbret_t idaapi edit(size_t /*n*/) newapi { return cbret_t(); } + + /// User pressed the enter key. + /// \param n index of the element where was pressed + /// \retval false nothing changed + /// \return idx index of the selected item (cursor) + /// changed what is changed + virtual cbret_t idaapi enter(size_t n) newapi + { + cbres_t changed = cbres_t(callui(ui_chooser_default_enter, this, &n).i); + return cbret_t(n, changed); + } + + /// The chooser needs to be refreshed. + /// \param n index of the selected (current) item + /// \return idx new index of the current item + /// (as it may change during refresh) + /// changed what is changed + virtual cbret_t idaapi refresh(ssize_t n) newapi + { + return cbret_t(n, ALL_CHANGED); + } + + /// Selection changed (cursor moved). + /// \note This callback is not supported in the txt-version. + /// \param n index of the new selected item + virtual void idaapi select(ssize_t /*n*/) const newapi {} + + /// get the dirtree_t that will be used to present a tree-like + /// structure to the user (see CH_HAS_DIRTREE) + /// \return the dirtree_t, or nullptr + virtual dirtree_t *idaapi get_dirtree() newapi { return nullptr; } + + /// Map an item index to a dirtree_t inode + /// This is necessary only if CH_HAS_DIRTREE is specified + /// \param n index of the item + /// \return the inode number + virtual inode_t idaapi index_to_inode(size_t /*n*/) const newapi { return inode_t(BADADDR); } + + /// Map an item index to a diffpos_t + /// This is necessary only if CH_HAS_DIFF is specified + /// \param n index of the item + /// \return the diffpos + virtual diffpos_t idaapi index_to_diffpos(size_t /*n*/) const newapi { return diffpos_t(-1); } + + /// Get the description of the standard chooser actions. + /// This method is called when creating the chooser widget. + /// It should fill the array of pointers to the action description. + /// 'nullptr' means that the default action attributes will be used. + /// \note Availability of the standard actions is determined by the + /// CH_CAN_... flags. + /// \param[out] ucbs the array of pointers to the description structure + /// \retval true UCBS is filled + /// \retval false no custom standard actions + virtual bool idaapi get_stdact_descs( + chooser_stdact_desc_t * /*descs*/[NSTDPOPUPS]) newapi + { + return false; + } + + /// Callback for lazy-loaded, dirtree-based choosers; + /// the function will be called when a folder is expanded and it has + /// not been loaded before. The implementation should use the + /// given dirtree's link() or mkdir() methods to add the folder contents. + /// \note The dirtree is chdir()-positioned to the directory being loaded, + /// so relative paths (like simple filenames) may be useful. + /// \param dt dirtree used to fill the directory in + /// \param dir_path an absolute dirtree path to this directory + /// \return success + virtual bool idaapi do_lazy_load_dir( + dirtree_t * /*dt*/, + const qstring & /*dir_path*/) newapi + { + return false; + } + + /// Map an a dirtree_t inode to the index + /// This will be used only if CH_HAS_DIRTREE and CH2_HAS_INODE2INDEX are specified + /// Implementing this function will result in increase overall performance + /// especially for big choosers. + /// \param inode inode of the item + /// \return the index + virtual size_t idaapi inode_to_index(inode_t /*inode*/) const newapi { return size_t(-1); } + +protected: + ssize_t new_sel_after_del(size_t n) const + { + size_t cnt = get_count(); + // assert: n < cnt + return n + 1 < cnt + ? n + 1 + : n - 1; // the last item deleted => no selection + } + ssize_t adjust_last_item(size_t n) const + { + size_t cnt = get_count(); + if ( cnt == 0 ) + return NO_SELECTION; + // take in account deleting of the last item(s) + return n < cnt ? n : cnt - 1; + } +}; + +/// The chooser object with multi-selection. +struct chooser_multi_t : public chooser_base_t +{ + chooser_multi_t( + uint32 flags_ = 0, + int columns_ = 0, + const int *widths_ = nullptr, + const char *const *header_ = nullptr, + const char *title_ = nullptr, + uint16 flags2_ = 0) + : chooser_base_t( + flags_ | CH_MULTI, + columns_, widths_, header_, + title_, + flags2_) {} + + /// Display a generic list chooser and allow the user to select an item. + /// May be overridden in derived choosers. + /// \param deflt default selection (may be empty) + /// see the choose() function below + //lint -sem(chooser_multi_t::choose,custodial(t)) + inline ssize_t choose(const sizevec_t &deflt = sizevec_t()); + + /// Get the positions of the items. + /// A simple chooser considers `item_data` as a list of indexes. + /// \param[in,out] sel items indexes + /// \param item_data pointer to some data that identifies the items + virtual void idaapi get_item_index( + sizevec_t *sel, + const void *item_data) const newapi + { + // no calculation when `item_data` already is a vector + *sel = *(const sizevec_t *)item_data; + } + + /// Type of ins(), del(), edit(), enter(), refresh() callbacks + typedef cbres_t (idaapi chooser_multi_t::*cb_t)(sizevec_t *sel); + + /// User asked to insert an element. + virtual cbres_t idaapi ins(sizevec_t * /*sel*/) newapi + { + return NOTHING_CHANGED; + } + + /// User deleted elements. + /// \param[in,out] sel selected items + /// \return what is changed + virtual cbres_t idaapi del(sizevec_t * /*sel*/) newapi + { + return NOTHING_CHANGED; + } + + /// User asked to edit an element. + /// \param[in,out] sel selected items + /// \return what is changed + virtual cbres_t idaapi edit(sizevec_t * /*sel*/) newapi + { + return NOTHING_CHANGED; + } + + /// User pressed the enter key. + /// \param[in,out] sel selected items + /// \return what is changed + virtual cbres_t idaapi enter(sizevec_t *sel) newapi + { + return cbres_t(callui(ui_chooser_default_enter, this, sel).i); + } + + /// The chooser needs to be refreshed. + /// It returns the new positions of the selected items. + /// \param[in,out] sel selected items + /// \return what is changed + virtual cbres_t idaapi refresh(sizevec_t * /*sel*/) newapi + { + return ALL_CHANGED; + } + + /// Selection changed + /// \note This callback is not supported in the txt-version. + /// \param sel new selected items + virtual void idaapi select(const sizevec_t &/*sel*/) const newapi {} + + /// get the dirtree_t that will be used to present a tree-like + /// structure to the user (see CH_HAS_DIRTREE) + /// \return the dirtree_t, or nullptr + virtual dirtree_t *idaapi get_dirtree() newapi { return nullptr; } + + /// Map an item index to a dirtree_t inode + /// This is necessary only if CH_HAS_DIRTREE is specified + /// \param n index of the item + /// \return the inode number + virtual inode_t idaapi index_to_inode(size_t /*n*/) const newapi { return inode_t(BADADDR); } + + /// Map an item index to a diffpos_t + /// This is necessary only if CH_HAS_DIFF is specified + /// \param n index of the item + /// \return the diffpos + virtual diffpos_t idaapi index_to_diffpos(size_t /*n*/) const newapi { return diffpos_t(-1); } + + /// Get the description of the standard chooser actions. + /// This method is called when creating the chooser widget. + /// It should fill the array of pointers to the action description. + /// 'nullptr' means that the default action attributes will be used. + /// \note Availability of the standard actions is determined by the + /// CH_CAN_... flags. + /// \param[out] ucbs the array of pointers to the description structure + /// \retval true UCBS is filled + /// \retval false no custom standard actions + virtual bool idaapi get_stdact_descs( + chooser_stdact_desc_t * /*descs*/[NSTDPOPUPS]) newapi + { + return false; + } + + /// Callback for lazy-loaded, dirtree-based choosers; + /// the function will be called when a folder is expanded and it has + /// not been loaded before. The implementation should use the + /// given dirtree's link() or mkdir() methods to add the folder contents. + /// \note The dirtree is chdir()-positioned to the directory being loaded, + /// so relative paths (like simple filenames) may be useful. + /// \param dt dirtree used to fill the directory in + /// \param dir_path an absolute dirtree path to this directory + /// \return success + virtual bool idaapi do_lazy_load_dir( + dirtree_t * /*dt*/, + const qstring & /*dir_path*/) newapi + { + return false; + } + + /// Map an a dirtree_t inode to the index + /// This will be used only if CH_HAS_DIRTREE and CH2_HAS_INODE2INDEX are specified + /// Implementing this function will result in increase overall performance + /// especially for big choosers. + /// \param inode inode of the item + /// \return the index + virtual size_t idaapi inode_to_index(inode_t /*inode*/) const newapi { return size_t(-1); } + +protected: + // used in the del() callback to iterate + static bool next_item_to_del(sizevec_t *sel); + ssize_t new_sel_after_del(const sizevec_t &sel) const; + void adjust_last_item(sizevec_t *sel, size_t n) const; +}; + + +/// Multi line text control, used to embed a text control in a form +struct textctrl_info_t +{ + size_t cb; ///< size of this structure + qstring text; ///< in, out: text control value + uint16 flags; ///< \ref TXTF_ +/// \defgroup TXTF_ Text control property bits +/// used by textctrl_info_t::flags +///@{ +#define TXTF_AUTOINDENT 0x0001 ///< auto-indent on new line +#define TXTF_ACCEPTTABS 0x0002 ///< Tab key inserts 'tabsize' spaces +#define TXTF_READONLY 0x0004 ///< text cannot be edited (but can be selected and copied) +#define TXTF_SELECTED 0x0008 ///< shows the field with its text selected +#define TXTF_MODIFIED 0x0010 ///< gets/sets the modified status +#define TXTF_FIXEDFONT 0x0020 ///< the control uses IDA's fixed font +#define TXTF_LINENUMBERS 0x0040 ///< the text editor widget should display line numbers +#define TXTF_HTML 0x0080 ///< Text will be rendered as html + ///< (only enabled if TXTF_READONLY, gui-only) +///@} + uint16 tabsize; ///< how many spaces a single tab will indent + textctrl_info_t(): cb(sizeof(textctrl_info_t)), flags(0), tabsize(0) {} ///< Constructor +}; + +/// \defgroup choosers Functions: generic list choosers +/// These functions display a window that allows the user to select items +///@{ + + +/// Display a generic list chooser (n-column) and allow the user to select +/// an item. +/// The closed() callback will be called when the window is closed. +/// In addition, after the window is closed, the chooser instance +/// will be delete()d unless CH_KEEP is specified (useful for global, or +/// stack-allocated chooser instances, that must not be deleted.) +/// \param ch pointer to the chooser object +/// \param def_item pointer to some data that identifies the default item +/// For modal choosers: +/// \return the index of the selected item (0-based) +/// \retval chooser_base_t::NO_SELECTION the user refused to choose +/// anything (pressed Esc). +/// \retval chooser_base_t::EMPTY_CHOOSER the chooser was not created +/// because the init() callback returned 'false' +/// For non-modal choosers: +/// \retval 0 the chooser was created +/// successfully +/// \retval chooser_base_t::ALREADY_EXISTS did not open a new chooser +/// because a chooser with the same object is already open. If +/// CH_FORCE_DEFAULT was set, the cursor of the chooser will be +/// positioned to the new item. +/// \retval chooser_base_t::EMPTY_CHOOSER the chooser was not created +/// because CH_FORCE_DEFAULT was set and the init() callback +/// returned 'false' + +//lint -sem(choose,custodial(1)) +ssize_t choose(chooser_base_t *ch, const void *def_item); + +inline ssize_t chooser_t::choose(ssize_t deflt) +{ + // chooser uses the default implementation of the get_item_index() + // callback + return ::choose(this, &deflt); +} + +inline ssize_t chooser_multi_t::choose(const sizevec_t &deflt) +{ + // chooser uses the default implementation of the get_item_index() + // callback + return ::choose(this, &deflt); +} + +///@} + +#endif // SWIG + +//------------------------------------------------------------------------- +enum navaddr_type_t +{ + nat_lib = 0, + nat_fun, + nat_cod, + nat_dat, + nat_und, + nat_ext, + nat_err, + nat_gap, + nat_cur, + nat_auto, // auto-analysis cursor color + nat_lum, // related to lumina + nat_hlo, // highlight outline + nat_last +}; + +/// Navigation band colorizer function. +/// +/// If ea==BADADDR, then 'nbytes' is a navaddr_type_t, and the colorizer +/// is in charge of returning the color associated to that type of address. +/// This is used for maintaining the legend in-sync with the colors used to +/// display the addresses in the navigation bar. +/// +/// \param ea address to calculate the color of, or BADADDR (see above) +/// \param nbytes number of bytes, this can be ignored for quick&dirty approach +/// \param ud user data +/// \return color of the specified address in RGB + +typedef uint32 idaapi nav_colorizer_t(ea_t ea, asize_t nbytes, void *ud); + + +/// Install new navigation band colorizer (::ui_set_nav_colorizer). + +inline void set_nav_colorizer( + nav_colorizer_t **out_was_func, + void **out_was_ud, + nav_colorizer_t *func, + void *ud) +{ + callui(ui_set_nav_colorizer, out_was_func, out_was_ud, func, ud); +} + +/// Custom viewer & code viewer handler types +enum custom_viewer_handler_id_t +{ + CVH_USERDATA, + CVH_KEYDOWN, ///< see ::custom_viewer_keydown_t + CVH_POPUP, ///< see ::custom_viewer_popup_t + CVH_DBLCLICK, ///< see ::custom_viewer_dblclick_t + CVH_CURPOS, ///< see ::custom_viewer_curpos_t + CVH_CLOSE, ///< see ::custom_viewer_close_t + CVH_CLICK, ///< see ::custom_viewer_click_t + CVH_QT_AWARE, ///< see set_custom_viewer_qt_aware() + CVH_HELP, ///< see ::custom_viewer_help_t + CVH_MOUSEMOVE, ///< see ::custom_viewer_mouse_moved_t + + CDVH_USERDATA = 1000, ///< see set_code_viewer_user_data() + CDVH_SRCVIEW, ///< see set_code_viewer_is_source() + CDVH_LINES_CLICK, ///< see ::code_viewer_lines_click_t + CDVH_LINES_DBLCLICK, ///< see ::code_viewer_lines_click_t + CDVH_LINES_POPUP, ///< see ::code_viewer_lines_click_t + CDVH_LINES_DRAWICON, ///< see ::code_viewer_lines_icon_t + CDVH_LINES_LINENUM, ///< see ::code_viewer_lines_linenum_t + CDVH_LINES_ICONMARGIN, ///< see set_code_viewer_lines_icon_margin() + CDVH_LINES_RADIX, ///< see set_code_viewer_lines_radix() + CDVH_LINES_ALIGNMENT ///< see set_code_viewer_lines_alignment() +}; + +//------------------------------------------------------------------------- +/// state & 1 => Shift is pressed \n +/// state & 2 => Alt is pressed \n +/// state & 4 => Ctrl is pressed \n +/// state & 8 => Mouse left button is pressed \n +/// state & 16 => Mouse right button is pressed \n +/// state & 32 => Mouse middle button is pressed \n +/// state & 128 => Meta is pressed (OSX only) +#define VES_SHIFT (1 << 0) +#define VES_ALT (1 << 1) +#define VES_CTRL (1 << 2) +#define VES_MOUSE_LEFT (1 << 3) +#define VES_MOUSE_RIGHT (1 << 4) +#define VES_MOUSE_MIDDLE (1 << 5) +#define VES_META (1 << 7) +typedef int input_event_modifiers_t; +typedef input_event_modifiers_t view_event_state_t; + +//------------------------------------------------------------------------- +/// Notification codes for events in the message window +enum msg_notification_t +{ + msg_activated, ///< The message window is activated. + ///< \param none + ///< \return void + + msg_deactivated, ///< The message window is deactivated. + ///< \param none + ///< \return void + + msg_click, ///< Click event. + ///< \param x (int) x-coordinate + ///< \param y (int) y-coordinate + ///< \param state (::view_event_state_t) + ///< \retval 1 handled + ///< \retval 0 not handled (invoke default handler) + + msg_dblclick, ///< Double click event. + ///< \param x (int) x-coordinate + ///< \param y (int) y-coordinate + ///< \param state (::view_event_state_t) + ///< \retval 1 handled + ///< \retval 0 not handled (invoke default handler) + + msg_closed, ///< View closed. + ///< \param none + ///< \return void + + msg_keydown, ///< Key down event. + ///< \param key (int) + ///< \param state (::view_event_state_t) + ///< \retval 1 handled + ///< \retval 0 not handled (invoke default handler) +}; + +//------------------------------------------------------------------------- +/// Information about a position relative to the renderer +struct renderer_pos_info_t +{ + /// Constructor + renderer_pos_info_t() : node(-1), cx(-1), cy(-1), sx(-1) {} + + int node; ///< the node, or -1 if the current renderer + ///< is not a graph renderer. + + short cx; ///< the X coords of the character in the current line. + ///< When in graph mode: X coords of the character in 'node'. \n + ///< When in flat mode: X coords of the character in the line, w/o \n + ///< taking scrolling into consideration. + + short cy; ///< the Y coords of the character. + ///< When in graph mode: Y coords of the character in 'node'. \n + ///< When in flat mode: Line number, starting from the top. + + short sx; ///< the number of chars that are scrolled (flat mode only) + + bool operator == (const renderer_pos_info_t &r) const + { return node == r.node && cx == r.cx && cy == r.cy && sx == r.sx; } + bool operator != (const renderer_pos_info_t &r) const + { return !(*this == r); } +}; + +//------------------------------------------------------------------------- +struct selection_item_t; + +//------------------------------------------------------------------------- +/// Abstraction of location in flat view/graph views +/// (out of 'view_mouse_event_t' to make it easy for SWiG to wrap) +union view_mouse_event_location_t +{ + ea_t ea; ///< flat view (rtype == ::TCCRT_FLAT) + const selection_item_t *item; ///< graph views (rtype != ::TCCRT_FLAT). + ///< nullptr if mouse is not currently over an item. +}; + + +/// Information about a mouse action within a view +struct view_mouse_event_t +{ + tcc_renderer_type_t rtype; ///< type of renderer that received the event + + uint32 x; ///< screen x coordinate + uint32 y; ///< screen y coordinate + + typedef view_mouse_event_location_t location_t; + location_t location; ///< location where event was generated + + view_event_state_t state; ///< contains information about what buttons are CURRENTLY pressed + ///< on the keyboard and mouse. view_mouse_event_t instances created + ///< in functions like mouseReleaseEvent() won't contain any information + ///< about the mouse, because it has been released. + + vme_button_t button; ///< represents which mouse button was responsible for generating the event. + ///< This field does not care about the current state of the mouse. + + renderer_pos_info_t renderer_pos; ///< position where event was generated, relative to the renderer +}; + +//------------------------------------------------------------------------- +/// Notification codes sent by the UI for IDAView or custom viewer events. +/// These notification codes should be used together with ::HT_VIEW hook type. +enum view_notification_t +{ + view_activated, ///< A view is activated + ///< \param view (TWidget *) + + view_deactivated, ///< A view is deactivated + ///< \param view (TWidget *) + + view_keydown, ///< Key down event + ///< \param view (TWidget *) + ///< \param key (int) + ///< \param state (::view_event_state_t) + + view_click, ///< Click event + ///< \param view (TWidget *) + ///< \param event (const ::view_mouse_event_t *) + + view_dblclick, ///< Double click event + ///< \param view (TWidget *) + ///< \param event (const ::view_mouse_event_t *) + + view_curpos, ///< Cursor position changed + ///< \param view (TWidget *) + + view_created, ///< A view is being created. + ///< \param view (TWidget *) + + view_close, ///< View closed + ///< \param view (TWidget *) + + view_switched, ///< A view's renderer has changed. + ///< \param view (TWidget *) + ///< \param rt (::tcc_renderer_type_t) + + view_mouse_over, ///< The user moved the mouse over (or out of) a node or an edge. + ///< This is only relevant in a graph view. + ///< \param view (TWidget *) + ///< \param event (const ::view_mouse_event_t *) + + view_loc_changed, ///< The location for the view has changed (can be either + ///< the place_t, the renderer_info_t, or both.) + ///< \param view (TWidget *) + ///< \param now (const lochist_entry_t *) + ///< \param was (const lochist_entry_t *) + + view_mouse_moved, ///< The mouse moved on the view + ///< \param view (TWidget *) + ///< \param event (const ::view_mouse_event_t *) +}; + +#ifndef SWIG + +/// The user has pressed a key + +typedef bool idaapi custom_viewer_keydown_t(TWidget *cv, int vk_key, int shift, void *ud); + + +/// The user right clicked. See ::ui_populating_widget_popup, too. + +typedef void idaapi custom_viewer_popup_t(TWidget *cv, void *ud); + + +/// The user moved the mouse. + +typedef void idaapi custom_viewer_mouse_moved_t(TWidget *cv, int shift, view_mouse_event_t *e, void *ud); + + +/// The user clicked + +typedef bool idaapi custom_viewer_click_t(TWidget *cv, int shift, void *ud); + + +/// The user double clicked + +typedef bool idaapi custom_viewer_dblclick_t(TWidget *cv, int shift, void *ud); + + +/// Deprecated. +/// +/// See custom_viewer_location_changed_t for a more +/// competent, and general solution. + +typedef void idaapi custom_viewer_curpos_t(TWidget *cv, void *ud); + + +/// Custom viewer is being destroyed + +typedef void idaapi custom_viewer_close_t(TWidget *cv, void *ud); + + +// obsolete + +typedef int idaapi obsolete_custom_viewer_help_t(TWidget *cv, void *ud); + + +/// Custom viewer: the user pressed F1 +/// If `true` and `out` is filled, it will be considered either an absolute URL, or +/// a path relative to the user guide's root + +typedef bool idaapi custom_viewer_help_t(qstring *out, TWidget *cv, void *ud); + + +/// Fine-tune 'loc->place()' according to the X position (i.e., 'loc->renderer_info().pos.cx') +/// +/// You can consider that the place_t object is a 'row cursor' in the +/// list of lines that fill the screen. But, it is only a 'vertical' +/// cursor: e.g., the simpleline_place_t has the 'n' mumber, which +/// specifies what line the place_t corresponds to, in the backing +/// strvec_t instance. +//// +/// However, some views have a place that can be sensitive to the X +/// coordinates of the view's cursor. Think of the "Hex View-1", or +/// the "Pseudocode-A" views: when moving the cursor on the X axis, +/// the 'row cursor' will not change (since we are moving on the same +/// line), but the corresponding 'ea_t' might. +/// +/// For such tricky situations, we provide the following callback, that +/// will provide the ability to update the 'loc->place()'s internal state +/// according to 'loc->renderer_info().pos.cx' so +/// that it really reflects the current cursor position. +/// Most custom viewers will not need to implement this, but if some data +/// in your place_t instances is dependent upon the X coordinate of the +/// cursor, you'll probably want to. +/// +/// Called whenever the user moves the cursor around (mouse, keyboard) +/// +/// Note: this callback should only ever read 'loc->renderer_info()', +/// not modify it. Doing so will result in undefined behavior. + +typedef void idaapi custom_viewer_adjust_place_t(TWidget *v, lochist_entry_t *loc, void *ud); + + +/// Does the line pointed to by pline include pitem, and if so at what X coordinate? +/// +/// place_t instances can be considered as a 'cursor' in a set of lines (see +/// custom_viewer_adjust_place_t), but they can be 'tuned' to +/// correctly represent the current position (e.g., hexrays decompiler plugins +/// tune its place_t instances so they contain the real, current 'ea_t', that +/// corresponds to the C-like expression that's shown at the X coordinate +/// within that line.) +/// +/// But then, when the viewer has to determine whether a certain twinline_t +/// in fact displays the current place, the sublcass's implementation of +/// place_t::compare() might lead it to think that the current twinline_t's +/// place_t is not correct (e.g., because the 'ea_t' has been fine-tuned +/// according to the caret's X coordinates.) +/// +/// Thus, if your plugin implements custom_viewer_adjust_place_t, +/// you probably want to implement this as well, or refreshes might be +/// unnecessarily frequent, leading to a worse user experience. +/// +/// This is typically called when the user moves the cursor around. +/// return +/// -1 if pitem is not included in pline +/// -2 pitem points to the entire line +/// >= 0 for the X coordinate within the pline, where pitem points + +typedef int idaapi custom_viewer_get_place_xcoord_t(TWidget *v, const place_t *pline, const place_t *pitem, void *ud); + + +enum locchange_reason_t +{ + lcr_unknown, + lcr_goto, + lcr_user_switch, // user pressed + lcr_auto_switch, // automatic switch + lcr_jump, + lcr_navigate, // navigate back & forward + lcr_scroll, // user used scrollbars + lcr_internal, // misc. other reasons +}; + +#define LCMD_SYNC (1 << 0) +class locchange_md_t // location change metadata +{ +protected: + uchar cb; + uchar r; + uchar f; + uchar reserved; + +public: + locchange_md_t(locchange_reason_t _reason, bool _sync) + : cb(sizeof(*this)), r(uchar(_reason)), f(_sync ? LCMD_SYNC : 0), reserved(0) {} + locchange_reason_t reason() const { return locchange_reason_t(r); } + bool is_sync() const { return (f & LCMD_SYNC) != 0; } +}; +CASSERT(sizeof(locchange_md_t) == sizeof(uint32)); +DECLARE_TYPE_AS_MOVABLE(locchange_md_t); + +/// The user asked to navigate to the given location. +/// +/// This gives the view the possibility of declining the move. +/// Reasons for this can be: +/// - the location cannot be displayed, +/// - going there requires a long-running operation, that can be +/// canceled by the user (e.g., in case of the hexrays plugins: +/// during decompilation of the target function.) +/// - ... +/// +/// This is called before the new location is committed to the view's history. +/// +/// return +/// 0 if the move is accepted +/// != 0 otherwise + +typedef int idaapi custom_viewer_can_navigate_t( + TWidget *v, + const lochist_entry_t *now, + const locchange_md_t &md, + void *ud); + + +/// The viewer's location (i.e., place, or cursor) changed. + +typedef void idaapi custom_viewer_location_changed_t( + TWidget *v, + const lochist_entry_t *was, + const lochist_entry_t *now, + const locchange_md_t &md, + void *ud); + + +// Code viewer handlers for the lineinfo widget located to the left of the text. + +/// The user clicked, right clicked or double clicked. +/// pos: the clicked icon number. -1 means the click occurred on space not reserved to icons. + +typedef void idaapi code_viewer_lines_click_t(TWidget *c, const place_t *p, int pos, int shift, void *ud); + + +/// Retrieve an icon for a code viewer line. +/// Icons are drawn on the gutter to the left of the code viewer text. +/// Multiple icons can be drawn for a line. Each icon has its position (the leftmost +/// icon has the position 0, the next one has the position 1, etc). +/// \param cv pointer to the code viewer +/// \param p the line position in the code viewer for which retrieve the icon +/// \param pos the icon number, will be 0,1,2,3... \n +/// can be modified to skip positions and draw at the specified one +/// \param ud user data of the code viewer +/// \return the id of the icon to draw. If bitwise or'ed with 0x80000000, +/// IDA calls this function once more with pos+1 to retrieve one more icon. + +typedef int idaapi code_viewer_lines_icon_t(TWidget *cv, const place_t *p, int *pos, void *ud); + + +/// Calculate the line number. Return false to not print any number. + +typedef bool idaapi code_viewer_lines_linenum_t(TWidget *cv, const place_t *p, uval_t *num, void *ud); + +#endif // SWIG + +//------------------------------------------------------------------------- +enum input_event_kind_t +{ + iek_unknown = 0, + iek_shortcut, + iek_key_press, + iek_key_release, + iek_mouse_button_press, + iek_mouse_button_release, + iek_mouse_wheel, +}; + +//------------------------------------------------------------------------- + +/// A representation of a user input + +struct input_event_t +{ + int cb; ///< size marker + input_event_kind_t kind; ///< the kind of event + input_event_modifiers_t modifiers; ///< current keyboard (and mouse) modifiers + TWidget *target; ///< the target widget + void *source; ///< the source event, should it be required for detailed inform (e.g., a QEvent in the GUI version of IDA) + + struct input_event_shortcut_data_t + { + const char *action_name; ///< the action that will be triggered + }; + struct input_event_keyboard_data_t + { + int key; ///< the key that was pressed to generate the event + char text[8]; ///< textual representation of the key + }; + struct input_event_mouse_data_t + { + int x; ///< the X position on the widget + int y; ///< the Y position on the widget + vme_button_t button; ///< the button that was pressed to generate the event + }; + + union + { + input_event_shortcut_data_t shortcut; + input_event_keyboard_data_t keyboard; + input_event_mouse_data_t mouse; + }; + + input_event_t() + { + memset(this, 0, sizeof(*this)); + cb = sizeof(*this); + } +}; + +//------------------------------------------------------------------------ + +/// Command line interpreter. +/// Provides functionality for the command line (located at the bottom of the main window). +/// Only GUI version of IDA supports CLIs. +struct cli_t +{ + size_t size; ///< size of this structure + int32 flags; ///< \ref CLIF_ +/// \defgroup CLIF_ CLI attributes +/// used by cli_t::flags +///@{ +#define CLIF_QT_AWARE 1 ///< keydown event will use Qt key codes +///@} + const char *sname; ///< short name (displayed on the button) + const char *lname; ///< long name (displayed in the menu) + const char *hint; ///< hint for the input line + + /// Callback: the user pressed Enter. + /// CLI is free to execute the line immediately or ask for more lines. + /// \param line command to execute (utf-8-encoded) + /// \retval true executed line + /// \retval false ask for more lines + bool (idaapi *execute_line)(const char *line); + /// Callback: a keyboard key has been pressed. + /// This callback is optional. + /// It is a generic callback and the CLI is free to do whatever it wants. + /// \param line current input line (in/out argument) + /// \param p_x pointer to current x coordinate of the cursor (in/out) + /// \param p_sellen pointer to current selection length (usually 0) + /// \param p_vk_key pointer to virtual key code (in/out). + /// if the key has been handled, it should be reset to 0 by CLI + /// \param shift shift state + /// \retval true modified input line or x coordinate or selection length + /// \retval false otherwise + bool (idaapi *keydown)( + qstring *line, + int *p_x, + int *p_sellen, + int *p_vk_key, + int shift); + + /// Callback: the user pressed Tab/Shift+Tab. + /// This callback is optional. + /// \param[out] out_completions results of completion + /// \param[out] out_hints one entry per completion result giving hints about the + /// candidate (function protype, variable type, etc ...) + /// Either has to be exactly the length of out_completions + /// or zero length to display no hints. + /// \param[out] out_docs one entry per completion result giving the docstring + /// of the corresponding item in out_completions. + /// Either has to be exactly the length of out_completions + /// or zero length to display no docstrings. + /// \param[out] out_match_start the codepoint index in the line, where match starts + /// \param[out] out_match_end the codepoint index in the line, where ends ends + /// \param line command line + /// \param x codepoint index of the cursor in line + /// \retval true got results + /// \retval false otherwise + bool (idaapi *find_completions)( + qstrvec_t *out_completions, + qstrvec_t *out_hints, + qstrvec_t *out_docs, + int *out_match_start, + int *out_match_end, + const char *line, + int x); +}; + +//--------------------------------------------------------------------------- +/// \defgroup MFF_ Exec request flags +/// passed as 'reqf' parameter to execute_sync() +///@{ +#define MFF_FAST 0x0000 ///< Execute code as soon as possible. + ///< this mode is ok for calling ui related functions + ///< that do not query the database. + +#define MFF_READ 0x0001 ///< Execute code only when ida is idle and it is safe + ///< to query the database. + ///< This mode is recommended only + ///< for code that does not modify the database. + ///< (nb: ida may be in the middle of executing + ///< another user request, for example it may be waiting + ///< for him to enter values into a modal dialog box) + +#define MFF_WRITE 0x0002 ///< Execute code only when ida is idle and it is safe + ///< to modify the database. in particular, + ///< this flag will suspend execution if there is + ///< a modal dialog box on the screen. + ///< this mode can be used to call any ida api function. + ///< #MFF_WRITE implies #MFF_READ + +#define MFF_NOWAIT 0x0004 ///< Do not wait for the request to be executed. + ///< the caller should ensure that the request is not + ///< destroyed until the execution completes. + ///< if not, the request will be ignored. + ///< the request must be created using the 'new' operator + ///< to use it with this flag. + ///< it can be used in cancel_exec_request(). + ///< This flag can be used to delay the code execution + ///< until the next UI loop run even from the main thread. +///@} + + +/// Execute code in the main thread - to be used with execute_sync(). +struct exec_request_t +{ + /// Internal magic + enum { MFF_MAGIC = 0x12345678 }; + + /// Can this request be executed? + bool valid() const + { + return (code & ~7) == MFF_MAGIC && (sem != nullptr || (code & MFF_NOWAIT) != 0); + } + + ssize_t code = 0; ///< temporary location, used internally + qsemaphore_t sem = nullptr; + ///< semaphore to communicate with the main thread. + ///< If nullptr, will be initialized by execute_sync(). + + /// Callback to be executed. + /// If this function raises an exception, execute_sync() never returns. + virtual ssize_t idaapi execute() = 0; + + /// Destructor + virtual ~exec_request_t() { qsem_free(sem); sem = nullptr; code = 0; } +}; + +//--------------------------------------------------------------------------- +/// Base class for defining UI requests. +/// Override the run() method and insert your code. +class ui_request_t +{ +public: + /// Run the UI request + /// \retval false remove the request from the queue + /// \retval true reschedule the request and run it again + virtual bool idaapi run() = 0; + virtual ~ui_request_t() {} +}; + +/// List of UI requests. The ui_request_t is allocated by the caller +/// but its ownership is transferred to the execute_ui_requests(). +/// The ui_request_t instance will be deleted as soon as it is executed and +/// was not rescheduled for another run. +class ui_requests_t : public qlist +{ + DECLARE_UNCOPYABLE(ui_requests_t) +public: + ui_requests_t() {} ///< Constructor + ~ui_requests_t() ///< Destructor + { + for ( iterator p=begin(); p != end(); ++p ) + delete *p; + } +}; + +/// Snapshot restoration completion callback. see restore_database_snapshot() +typedef void idaapi ss_restore_cb_t(const char *errmsg, void *ud); + +/// \defgroup UIJMP_ Jump flags +/// passed as 'uijmp_flags' parameter to jumpto() +///@{ +#define UIJMP_ACTIVATE 0x0001 ///< activate the new window +#define UIJMP_DONTPUSH 0x0002 ///< do not remember the current address + ///< in the navigation history +#define UIJMP_VIEWMASK 0x000C +#define UIJMP_ANYVIEW 0x0000 ///< jump in any ea_t-capable view +#define UIJMP_IDAVIEW 0x0004 ///< jump in idaview +#define UIJMP_IDAVIEW_NEW 0x0008 ///< jump in new idaview +///@} + +class interactive_graph_t; +struct screen_graph_selection_t; +struct dirtree_selection_t; + +//------------------------------------------------------------------------- +// Current selection +struct action_ctx_base_cur_sel_t +{ + twinpos_t from; ///< start of selection + twinpos_t to; ///< end of selection + + action_ctx_base_cur_sel_t() { reset(); } + void reset() + { + from.at = nullptr; + from.x = -1; + to.at = nullptr; + to.x = -1; + } +}; + +//------------------------------------------------------------------------- +/// Maintain information about the current state of the UI. +/// This allows actions to behave appropriately (see ::action_handler_t) +struct action_ctx_base_t +{ + /// Constructor + action_ctx_base_t() + { + reset(); + } + +/// \defgroup ACF_ Action context property bits +/// used by action_ctx_base_t::cur_flags +///@{ +#define ACF_HAS_SELECTION (1 << 0) ///< there is currently a valid selection +#define ACF_XTRN_EA (1 << 1) ///< cur_ea is in 'externs' segment +#define ACF_HAS_FIELD_DIRTREE_SELECTION (1 << 2) ///< 'dirtree_selection' field is present +#define ACF_HAS_SOURCE (1 << 3) ///< 'source' field is present +#define ACF_HAS_TYPE_REF (1 << 4) ///< 'type_ref' field is present +///@} + + /// Invalidate all context info + void reset() + { + widget = nullptr; + widget_type = BWN_UNKNOWN; + widget_title.clear(); + chooser = nullptr; + chooser_selection.clear(); + action = nullptr; + + // + cur_flags = ACF_HAS_FIELD_DIRTREE_SELECTION | ACF_HAS_SOURCE | ACF_HAS_TYPE_REF; + cur_ea = BADADDR; + cur_value = BADADDR; + cur_func = cur_fchunk = nullptr; + cur_seg = nullptr; + cur_sel.reset(); + regname = nullptr; + focus = nullptr; + graph = nullptr; + graph_selection = nullptr; + dirtree_selection = nullptr; + type_ref = nullptr; + } + TWidget *widget; + twidget_type_t widget_type; ///< type of current widget + qstring widget_title; ///< title of current widget + chooser_base_t *chooser; ///< the underlying chooser_base_t (if 'widget' is a chooser widget) + sizevec_t chooser_selection; ///< current chooser selection (0-based) + + const char *action; ///< action name + + uint32 cur_flags; ///< Current address information. see \ref ACF_ + + /// Check if the given flag is set + bool has_flag(uint32 flag) const { return (cur_flags & flag) == flag; } + + ea_t cur_ea; ///< the current EA of the position in the view + uval_t cur_value; ///< the possible address, or value the cursor is positioned on + + func_t *cur_func; ///< the current function + func_t *cur_fchunk; ///< the current function chunk + + segment_t *cur_seg; ///< the current segment + + action_ctx_base_cur_sel_t cur_sel; ///< the currently selected range. also see #ACF_HAS_SELECTION + + const char *regname; ///< register name (if widget_type == BWN_CPUREGS and context menu opened on register) + + TWidget *focus; ///< The focused widget in case it is not the 'form' itself (e.g., the 'quick filter' input in choosers.) + + interactive_graph_t *graph; ///< the current graph (if in a graph view) + screen_graph_selection_t *graph_selection; ///< the current graph selection (if in a graph view) + const selection_item_t *hovered; ///< the current item being hovered (if in a graph view) + + dirtree_selection_t *dirtree_selection; ///< the current dirtree_t selection (if applicable) + + til_type_ref_t *type_ref; ///< a reference to the current type (if 'widget' is a type listing widget; nullptr otherwise) +}; + +//------------------------------------------------------------------------- +/// Instances of this class will be filled with information that is +/// commonly used by actions when they need to +/// be activated. This is so they don't have to perform (possibly) +/// costly operations more than once. +typedef action_ctx_base_t action_activation_ctx_t; + +//------------------------------------------------------------------------- +/// Instances of this class will be filled with information that is +/// commonly used by actions when they need to +/// update. This is so they don't have to perform (possibly) +/// costly operations more than once. +typedef action_ctx_base_t action_update_ctx_t; + +#define AHF_VERSION 1 ///< action handler version (used by action_handler_t::flags) +#define AHF_VERSION_MASK 0xFF ///< mask for action_handler_t::flags + +//------------------------------------------------------------------------- +/// Manages the behavior of a registered action +struct action_handler_t +{ + int flags; ///< internal - for version management + + /// Constructor + action_handler_t(int _f = 0) : flags(_f) { flags |= AHF_VERSION; } + + /// Activate an action. + /// This function implements the core behavior of an action. + /// It is called when the action is triggered, from a menu, from + /// a popup menu, from the toolbar, or programmatically. + /// \return non-zero: all IDA windows will be refreshed + virtual int idaapi activate(action_activation_ctx_t *ctx) = 0; + + /// Update an action. + /// This is called when the context of the UI changed, and we need to let the + /// action update some of its properties if needed (label, icon, ...) + /// + /// In addition, this lets IDA know whether the action is enabled, + /// and when it should be queried for availability again. + /// + /// Note: This callback is not meant to change anything in the + /// application's state, except by calling one (or many) of + /// the "update_action_*()" functions on this very action. + virtual action_state_t idaapi update(action_update_ctx_t *ctx) = 0; + + /// Destructor + virtual ~action_handler_t() {} + + // Action handles may be allocated by a plugin and deleted by the kernel. + // Therefore it is a good idea to unify the memory allocation methods. + DEFINE_MEMORY_ALLOCATION_FUNCS(); +}; + +/// Describe an action to be registered (see register_action()) +struct action_desc_t +{ + int cb; ///< size of this structure + const char *name; ///< the internal name of the action; must be unique. + ///< a way to reduce possible conflicts is to prefix it + ///< with some specific prefix. E.g., "myplugin:doSthg". + + const char *label; ///< the label of the action, possibly with an accelerator + ///< key definition (e.g., "~J~ump to operand") + + action_handler_t *handler; ///< the action handler, for activating/updating. + ///< please read the comments at register_action(). + + const void *owner; ///< either the plugin_t, or plugmod_t responsible for + ///< registering the action. Can be nullptr + ///< Please see \ref ACTION_DESC_LITERAL_PLUGMOD + + const char *shortcut; ///< an optional shortcut definition. E.g., "Ctrl+Enter" + const char *tooltip; ///< an optional tooltip for the action + int icon; ///< an optional icon ID to use + +/// \defgroup ADF_ Action flags +/// used by register_action(). The upper 16 bits are reserved. +///@{ +#define ADF_OWN_HANDLER 0x01 ///< handler is owned by the action; it'll be + ///< destroyed when the action is unregistered. + ///< Use DYNACTION_DESC_LITERAL to set this bit. +#define ADF_NO_UNDO 0x02 ///< the action does not create an undo point. + ///< useful for actions that do not modify the database. +#define ADF_OT_MASK 0x0C ///< Owner type mask +#define ADF_OT_PLUGIN 0x00 ///< Owner is a plugin_t +#define ADF_OT_PLUGMOD 0x04 ///< Owner is a plugmod_t +#define ADF_OT_PROCMOD 0x08 ///< Owner is a procmod_t +#define ADF_GLOBAL 0x10 ///< Register the action globally, so that it's + ///< available even if no IDB is present +#define ADF_NO_HIGHLIGHT 0x20 ///< After activating, do not update the highlight + ///< according to what's under the cursor (listings only.) +#define ADF_CHECKABLE 0x40 ///< action is checkable +#define ADF_CHECKED 0x80 ///< starts in a checked state (requires ADF_CHECKABLE) +///@} + int flags; ///< See \ref ADF_ +}; + +/// Get an ::action_desc_t instance with the provided plugmod_t as the owner +/// This is meant for plugins +#define ACTION_DESC_LITERAL_PLUGMOD(name, label, handler, plgmod, shortcut, tooltip, icon) \ + { sizeof(action_desc_t), name, label, handler, plgmod, shortcut, tooltip, icon, ADF_OT_PLUGMOD } + +/// Get an ::action_desc_t instance with the provided procmod_t as the owner +/// This is meant for processor modules implementing processor_t::ev_get_procmod +#define ACTION_DESC_LITERAL_PROCMOD(name, label, handler, prcmod, shortcut, tooltip, icon) \ + { sizeof(action_desc_t), name, label, handler, prcmod, shortcut, tooltip, icon, ADF_OT_PROCMOD } + +/// Get an ::action_desc_t instance with a given owner and flags +#define ACTION_DESC_LITERAL_OWNER(name, label, handler, owner, shortcut, tooltip, icon, flags) \ + { sizeof(action_desc_t), name, label, handler, owner, shortcut, tooltip, icon, flags } + +/// For attach_dynamic_action_to_popup() only +#define DYNACTION_DESC_LITERAL(label, handler, shortcut, tooltip, icon) \ + { sizeof(action_desc_t), nullptr, label, handler, nullptr, shortcut, tooltip, icon, ADF_OWN_HANDLER } + +/// Codes for getting/setting action attributes +enum action_attr_t +{ + AA_NONE, ///< no effect + AA_LABEL, ///< see update_action_label() + AA_SHORTCUT, ///< see update_action_shortcut() + AA_TOOLTIP, ///< see update_action_tooltip() + AA_ICON, ///< see update_action_icon() + AA_STATE, ///< see update_action_state() + AA_CHECKABLE, ///< see update_action_checkable() + AA_CHECKED, ///< see update_action_checked() + AA_VISIBILITY, ///< see update_action_visibility() +}; + +#ifndef SWIG +// Handlers to be used with create_custom_viewer() +class custom_viewer_handlers_t +{ + int cb; +public: + inline constexpr custom_viewer_handlers_t( + custom_viewer_keydown_t *_keyboard = nullptr, + custom_viewer_popup_t *_popup = nullptr, + custom_viewer_mouse_moved_t *_mouse_moved = nullptr, + custom_viewer_click_t *_click = nullptr, + custom_viewer_dblclick_t *_dblclick = nullptr, + custom_viewer_curpos_t *_curpos = nullptr, + custom_viewer_close_t *_close = nullptr, + custom_viewer_help_t *_help = nullptr, + custom_viewer_adjust_place_t *_adjust_place = nullptr, + custom_viewer_get_place_xcoord_t *_get_place_xcoord = nullptr, + custom_viewer_location_changed_t *_location_changed = nullptr, + custom_viewer_can_navigate_t *_can_navigate = nullptr) + : cb(sizeof(*this)), + keyboard(_keyboard), + popup(_popup), + mouse_moved(_mouse_moved), + click(_click), + dblclick(_dblclick), + curpos(_curpos), + close(_close), + obsolete_help(nullptr), + adjust_place(_adjust_place), + get_place_xcoord(_get_place_xcoord), + location_changed(_location_changed), + can_navigate(_can_navigate), + help(_help) + {} + custom_viewer_keydown_t *keyboard; + custom_viewer_popup_t *popup; + custom_viewer_mouse_moved_t *mouse_moved; + custom_viewer_click_t *click; + custom_viewer_dblclick_t *dblclick; + custom_viewer_curpos_t *curpos; + custom_viewer_close_t *close; + obsolete_custom_viewer_help_t *obsolete_help; + custom_viewer_adjust_place_t *adjust_place; + custom_viewer_get_place_xcoord_t *get_place_xcoord; + custom_viewer_location_changed_t *location_changed; + custom_viewer_can_navigate_t *can_navigate; + custom_viewer_help_t *help; + + int size() const { return cb; } +}; +#endif // SWIG + + +#ifndef __UI__ // Not for the UI + +// Convenience functions offered by the user interface + +/// Execute a list of UI requests (::ui_execute_ui_requests_list). +/// \return a request id: a unique number that can be used to cancel the request + +THREAD_SAFE inline int execute_ui_requests(ui_requests_t *reqs) +{ + return callui(ui_execute_ui_requests_list, reqs).i; +} + + +/// Execute a variable number of UI requests (::ui_execute_ui_requests). +/// The UI requests will be dispatched in the context of the main thread. +/// \param req pointer to the first request ,use nullptr to terminate the var arg request list +/// \return a request id: a unique number that can be used to cancel the request + +THREAD_SAFE inline int execute_ui_requests(ui_request_t *req, ...) +{ + va_list va; + va_start(va, req); + int req_id = callui(ui_execute_ui_requests, req, va).i; + va_end(va); + return req_id; +} + + +/// Try to cancel an asynchronous exec request (::ui_cancel_exec_request). +/// \param req_id request id +/// \retval true successfully canceled +/// \retval false request has already been processed. + +THREAD_SAFE inline bool cancel_exec_request(int req_id) +{ + return callui(ui_execute_sync_ctl, esctl_cancel_request, req_id).cnd; +} + + +/// Try to cancel asynchronous exec requests created by the specified thread. +/// \param tid thread id +/// \return number of the canceled requests. + +THREAD_SAFE inline int cancel_thread_exec_requests(qthread_t tid) +{ + return callui(ui_execute_sync_ctl, esctl_cancel_thread_requests, tid).i; +} + + +/// Set the availability of the execute_sync functionality for the +/// given thread +/// +/// Setting it to `esa_unavailable` will cause the existing requests for +/// this thread to be cancelled. +/// Setting it to `esa_release` will clear the status for this thread, and +/// should be issued right before a call to `qthread_free` is issued. +/// +/// \param tid thread id +/// \param availability the availability +/// \return the previous availability + +THREAD_SAFE inline execute_sync_availability_t set_execute_sync_availability( + qthread_t tid, + execute_sync_availability_t availability) +{ + auto i = callui(ui_execute_sync_ctl, esctl_set_availability, tid, availability).i; + return execute_sync_availability_t(i); +} + + +/// Get the group of widgets/registers +/// this view is synchronized with +/// \param w the widget +/// \return the group of widgets/registers, or nullptr + +inline const synced_group_t *get_synced_group(const TWidget *w) +{ + return (synced_group_t *) callui(ui_get_synced_group, w).vptr; +} + +/// Jump to the specified address (::ui_jumpto). +/// \param ea destination +/// \param opnum -1: don't change x coord +/// \param uijmp_flags \ref UIJMP_ +/// \return success + +inline bool jumpto(ea_t ea, int opnum=-1, int uijmp_flags=UIJMP_ACTIVATE) +{ + return callui(ui_jumpto, ea, opnum, uijmp_flags).cnd; +} + + +/// Show a banner dialog box (::ui_banner). +/// \param wait time to wait before closing +/// \retval 1 ok +/// \retval 0 esc was pressed + +inline bool banner(int wait) { return callui(ui_banner, wait).cnd; } + + +/// Can we use msg() functions? + +THREAD_SAFE inline bool is_msg_inited() { return callui(ui_is_msg_inited).cnd; } + + +/// Refresh marked windows (::ui_refreshmarked) + +inline void refresh_idaview() { callui(ui_refreshmarked); } + + +/// Refresh all disassembly views (::ui_refresh), forces an immediate refresh. +/// Please consider request_refresh() instead + +inline void refresh_idaview_anyway() { callui(ui_refresh); } + + +/// Allow the user to set analyzer options. (show a dialog box) (::ui_analyzer_options) + +inline void analyzer_options() { callui(ui_analyzer_options); } + + +/// Get the address at the screen cursor (::ui_screenea) + +inline ea_t get_screen_ea() { ea_t ea; callui(ui_screenea, &ea); return ea; } + + +/// Get current operand number, -1 means no operand (::ui_get_opnum) + +inline int get_opnum() { return callui(ui_get_opnum).i; } + + +/// Get the cursor position on the screen (::ui_get_cursor). +/// \note coordinates are 0-based +/// \param[out] x x-coordinate +/// \param[out] y y-coordinate +/// \retval true pointers are filled +/// \retval false no disassembly window open + +inline bool get_cursor(int *x, int *y) { return callui(ui_get_cursor, x, y).cnd; } + + +/// Get coordinates of the output window's cursor (::ui_get_output_cursor). +/// \note coordinates are 0-based +/// \note this function will succeed even if the output window is not visible +/// \param[out] x column +/// \param[out] y line number (global, from the start of output) +/// \retval false the output window has been destroyed. +/// \retval true pointers are filled + +inline bool get_output_cursor(int *x, int *y) { return callui(ui_get_output_cursor, x, y).cnd; } + + +/// Get current line from the disassemble window (::ui_get_curline). +/// \return cptr current line with the color codes +/// (use tag_remove() to remove the color codes) + +inline const char *get_curline() { return callui(ui_get_curline).cptr; } + + +/// Open the given url (::ui_open_url) + +inline void open_url(const char *url) { callui(ui_open_url, url); } + + +/// Get the current address in a hex view. +/// \param hexdump_num number of hexview window + +inline ea_t get_hexdump_ea(int hexdump_num) { ea_t ea; callui(ui_hexdumpea, &ea, hexdump_num); return ea; } + + +/// Get keyboard key code by its name (::ui_get_key_code) + +inline ushort get_key_code(const char *keyname) { return callui(ui_get_key_code, keyname).i16; } + + +/// Get shortcut code previously created by ::ui_get_key_code. +/// \param key key constant +/// \param shift modifiers +/// \param is_qt are we using gui version? + +inline ushort lookup_key_code(int key, int shift, bool is_qt) { return callui(ui_lookup_key_code, key, shift, is_qt).i16; } + + +/// Refresh navigation band if changed (::ui_refresh_navband). +/// \param force refresh regardless + +inline void refresh_navband(bool force) { callui(ui_refresh_navband, force); } + + +/// Mark a non-modal custom chooser for a refresh (::ui_refresh_chooser). +/// \param title title of chooser +/// \return success + +inline bool refresh_chooser(const char *title) { return callui(ui_refresh_chooser, title).cnd; } + + +/// Close a non-modal chooser (::ui_close_chooser). +/// \param title window title of chooser to close +/// \return success + +inline bool close_chooser(const char *title) { return callui(ui_close_chooser, title).cnd; } + + +/// Install command line interpreter (::ui_install_cli) + +inline void install_command_interpreter(const cli_t *cp) { callui(ui_install_cli, cp, true); } + + +/// Remove command line interpreter (::ui_install_cli) + +inline void remove_command_interpreter(const cli_t *cp) { callui(ui_install_cli, cp, false); } + + +/// Generate disassembly text for a range. +/// \param[out] text result +/// \param ea1 start address +/// \param ea2 end address +/// \param truncate_lines (on idainfo::margin) + +inline void gen_disasm_text(text_t &text, ea_t ea1, ea_t ea2, bool truncate_lines) { callui(ui_gen_disasm_text, &text, ea1, ea2, truncate_lines); } + + +/// Execute code in the main thread. +/// \param req request specifying the code to execute +/// \param reqf \ref MFF_ +/// \return if \ref #MFF_NOWAIT is specified, return the request id. +/// otherwise return the value returned by exec_request_t::execute(). + +THREAD_SAFE inline ssize_t execute_sync(exec_request_t &req, int reqf) { return callui(ui_execute_sync, &req, reqf).ssize; } + + +/// Set the docking position of a widget (::ui_set_dock_pos). +/// \param src_ctrl title of widget to dock +/// \param dest_ctrl where to dock: if nullptr or invalid then create +/// a new tab relative to current active tab +/// \param orient \ref DP_ +/// \param left,top,right,bottom dimensions of dock, if not specified or invalid then +/// create the widget in the center of the screen with the +/// default size +/// \return success + +inline bool set_dock_pos(const char *src_ctrl, const char *dest_ctrl, int orient, int left = 0, int top = 0, int right = 0, int bottom = 0) +{ + return callui(ui_set_dock_pos, src_ctrl, dest_ctrl, orient, left, top, right, bottom).cnd; +} + +/// Retrieve the id of the icon by name (::ui_get_icon_id_by_name). +/// \param icon_name full name of the icon +/// \return icon id + +inline int get_icon_id_by_name(const char *icon_name) { return callui(ui_get_icon_id_by_name, icon_name).i; } + +/// Load an icon from a file (::ui_load_custom_icon_file). +/// Also see load_custom_icon(const void *, unsigned int, const char *) +/// \param file_name path to file +/// \return icon id + +inline int load_custom_icon(const char *file_name) { return callui(ui_load_custom_icon_file, file_name).i; } + + +/// Load an icon and return its id (::ui_load_custom_icon). +/// \param ptr pointer to raw image data +/// \param len image data length +/// \param format image format +/// \return icon id + +inline int load_custom_icon(const void *ptr, unsigned int len, const char *format) { return callui(ui_load_custom_icon, ptr, len, format).i; } + + +/// Free an icon loaded with load_custom_icon() (::ui_free_custom_icon). + +inline void free_custom_icon(int icon_id) { callui(ui_free_custom_icon, icon_id); } + + +/// Processes a UI action by name. +/// \param name action name +/// \param flags reserved/not used +/// \param param reserved/not used + +inline bool process_ui_action(const char *name, int flags=0, void *param=nullptr) +{ + return callui(ui_process_action, name, flags, param).cnd; +} + + +/// Take a database snapshot (::ui_take_database_snapshot). +/// \param ss in/out parameter. +/// - in: description, flags +/// - out: filename, id +/// \param err_msg optional error msg buffer +/// \return success + +inline bool take_database_snapshot( + snapshot_t *ss, + qstring *err_msg) +{ + return callui(ui_take_database_snapshot, ss, err_msg).cnd; +} + + +/// Restore a database snapshot. +/// Note: This call is asynchronous. When it is completed, the callback will be triggered. +/// \param ss snapshot instance (see build_snapshot_tree()) +/// \param cb A callback that will be triggered with a nullptr string. +/// on success and an actual error message on failure. +/// \param ud user data passed to be passed to the callback +/// \return false if restoration could not be started (snapshot file was not found). \n +/// If the returned value is True then check if the operation succeeded from the callback. + +inline bool restore_database_snapshot( + const snapshot_t *ss, + ss_restore_cb_t *cb, + void *ud) +{ + return callui(ui_restore_database_snapshot, ss, cb, ud).cnd; +} + +/// Timer opaque handle +typedef struct __qtimer_t {} *qtimer_t; + + +/// Register a timer (::ui_register_timer). +/// Timer functions are thread-safe and the callback is executed +/// in the context of the main thread. +/// \param interval_ms interval in milliseconds +/// \param callback the callback can return -1 to unregister the timer; +/// any other value >= 0 defines the new interval for the timer +/// \param ud callback params +/// \return handle to registered timer (use this handle to unregister it), or nullptr + +THREAD_SAFE inline qtimer_t register_timer( + int interval_ms, + int (idaapi *callback)(void *ud), + void *ud) +{ + return (qtimer_t)(callui(ui_register_timer, interval_ms, callback, ud).vptr); +} + + +/// Unregister a timer (::ui_unregister_timer). +/// \param t handle to a registered timer +/// \return success + +THREAD_SAFE inline bool unregister_timer(qtimer_t t) +{ + return callui(ui_unregister_timer, t).cnd; +} + +//------------------------------------------------------------------------- + +/// Create a new action (::ui_register_action). +/// After an action has been created, it is possible to attach it +/// to menu items (attach_action_to_menu()), or to popup menus +/// (attach_action_to_popup()). +/// +/// Because the actions will need to call the handler's activate() and +/// update() methods at any time, you shouldn't build your action handler +/// on the stack. +/// +/// Please see the SDK's "ht_view" plugin for an example how +/// to register actions. +/// \param desc action to register +/// \return success + +inline bool register_action(const action_desc_t &desc) +{ + return callui(ui_register_action, &desc).cnd; +} + + +/// Delete a previously-registered action (::ui_unregister_action). +/// \param name name of action +/// \return success + +inline bool unregister_action(const char *name) +{ + return callui(ui_unregister_action, name).cnd; +} + + +/// Get a list of all currently-registered actions +/// \param out the list of actions to be filled +inline void get_registered_actions(qstrvec_t *out) +{ + callui(ui_get_registered_actions, out); +} + + +/// Create a toolbar with the given name, label and optional position +/// \param name name of toolbar (must be unique) +/// \param label label of toolbar +/// \param before if non-nullptr, the toolbar before which the new toolbar will be inserted +/// \param flags a combination of \ref CREATETB_, to determine toolbar position +/// \return success +inline bool create_toolbar( + const char *name, + const char *label, + const char *before = nullptr, + int flags = 0) +{ + return callui(ui_create_toolbar, name, label, before, flags).cnd; +} + + +/// Delete an existing toolbar +/// \param name name of toolbar +/// \return success +inline bool delete_toolbar(const char *name) +{ + return callui(ui_delete_toolbar, name).cnd; +} + + +/// Create a menu with the given name, label and optional position, +/// either in the menubar, or as a submenu. +/// If 'menupath' is non-nullptr, it provides information about where +/// the menu should be positioned. +/// First, IDA will try and resolve the corresponding menu by its name. +/// If such an existing menu is found and is present in the menubar, +/// then the new menu will be inserted in the menubar before it. +/// Otherwise, IDA will try to resolve 'menupath' as it would for +/// attach_action_to_menu() and, if found, add the new menu like so: +/// \code +/// // The new 'My menu' submenu will appear in the 'Comments' submenu +/// // before the 'Enter comment..." command +/// create_menu("(...)", "My menu", "Edit/Comments/Enter comment..."); +/// \endcode +/// or +/// \code +/// // The new 'My menu' submenu will appear at the end of the +/// // 'Comments' submenu. +/// create_menu("(...)", "My menu", "Edit/Comments/"); +/// \endcode +/// If the above fails, the new menu will be appended to the menubar. +/// \param name name of menu (must be unique) +/// \param label label of menu +/// \param menupath where should the menu be inserted +/// \return success +inline bool create_menu( + const char *name, + const char *label, + const char *menupath=nullptr) +{ + return callui(ui_create_menu, name, label, menupath).cnd; +} + + +/// Delete an existing menu +/// \param name name of menu +/// \return success +inline bool delete_menu(const char *name) +{ + return callui(ui_delete_menu, name).cnd; +} + + +/// Attach a previously-registered action to the menu (::ui_attach_action_to_menu). +/// \note You should not change top level menu, or the Edit,Plugins submenus +/// If you want to modify the debugger menu, do it at the ui_debugger_menu_change +/// event (ida might destroy your menu item if you do it elsewhere). +/// \param menupath path to the menu item after or before which the insertion will take place. \n +/// - Example: Debug/StartProcess +/// - Whitespace, punctuation are ignored. +/// - It is allowed to specify only the prefix of the menu item. +/// - Comparison is case insensitive. +/// - menupath may start with the following prefixes: +/// - [S] - modify the main menu of the structure window +/// - [E] - modify the main menu of the enum window +/// \param name the action name +/// \param flags a combination of \ref SETMENU_, to determine menu item position +/// \return success + +inline bool attach_action_to_menu( + const char *menupath, + const char *name, + int flags=0) +{ + return callui(ui_attach_action_to_menu, menupath, name, flags).cnd; +} + + +/// Detach an action from the menu (::ui_detach_action_from_menu). +/// \param menupath path to the menu item +/// \param name the action name +/// \return success + +inline bool detach_action_from_menu( + const char *menupath, + const char *name) +{ + return callui(ui_detach_action_from_menu, menupath, name).cnd; +} + + +/// Attach an action to an existing toolbar (::ui_attach_action_to_toolbar). +/// \param toolbar_name the name of the toolbar +/// \param name the action name +/// \return success + +inline bool attach_action_to_toolbar( + const char *toolbar_name, + const char *name) +{ + return callui(ui_attach_action_to_toolbar, toolbar_name, name).cnd; +} + + +/// Detach an action from the toolbar (::ui_detach_action_from_toolbar). +/// \param toolbar_name the name of the toolbar +/// \param name the action name +/// \return success + +inline bool detach_action_from_toolbar( + const char *toolbar_name, + const char *name) +{ + return callui(ui_detach_action_from_toolbar, toolbar_name, name).cnd; +} + + +/// Helper. +/// +/// You are not encouraged to use this, as it mixes flags for +/// both register_action(), and attach_action_to_menu(). +/// +/// The only reason for its existence is to make it simpler +/// to port existing plugins to the new actions API. + +inline bool register_and_attach_to_menu( + const char *menupath, + const char *name, + const char *label, + const char *shortcut, + int flags, + action_handler_t *handler, + void *owner, + int action_desc_t_flags) +{ + action_desc_t desc = ACTION_DESC_LITERAL_OWNER(name, label, handler, owner, shortcut, nullptr, -1, action_desc_t_flags); + if ( !register_action(desc) ) + return false; + if ( !attach_action_to_menu(menupath, name, (flags & SETMENU_POSMASK)) ) + { + unregister_action(name); + return false; + } + return true; +} + +//------------------------------------------------------------------------ +// Get VCL global variables +class TPopupMenu; + +/// Display a widget, dock it if not done before +/// \param widget widget to display +/// \param options \ref WIDGET_OPEN +/// \param dest_ctrl where to dock: if nullptr or invalid then +/// use the active docker if there is not +/// create a new tab relative to current active tab + +inline void display_widget(TWidget *widget, uint32 options, const char *dest_ctrl=nullptr) +{ + callui(ui_display_widget, widget, options, dest_ctrl); +} + + +/// Close widget (::ui_close_widget, only gui version). +/// \param widget pointer to the widget to close +/// \param options \ref WIDGET_CLOSE + +inline void close_widget(TWidget *widget, int options) +{ + callui(ui_close_widget, widget, options); +} + + +/// Activate widget (only gui version) (::ui_activate_widget). +/// \param widget existing widget to display +/// \param take_focus give focus to given widget + +inline void activate_widget(TWidget *widget, bool take_focus) +{ + callui(ui_activate_widget, widget, take_focus); +} + + +/// Find widget with the specified caption (only gui version) (::ui_find_widget). +/// NB: this callback works only with the tabbed widgets! +/// \param caption title of tab, or window title if widget is not tabbed +/// \return pointer to the TWidget, nullptr if none is found + +inline TWidget *find_widget(const char *caption) +{ + return (TWidget *) callui(ui_find_widget, caption).vptr; +} + + +/// Get a pointer to the current widget (::ui_get_current_widget). + +inline TWidget *get_current_widget() +{ + return (TWidget *) callui(ui_get_current_widget).vptr; +} + + +/// Get the type of the TWidget * (::ui_get_widget_type). + +inline twidget_type_t get_widget_type(TWidget *widget) +{ + return twidget_type_t(callui(ui_get_widget_type, widget).i); +} + + +/// Get the TWidget's title (::ui_get_widget_title). + +inline bool get_widget_title(qstring *buf, TWidget *widget) +{ + return callui(ui_get_widget_title, buf, widget).cnd; +} + +/// Create new ida viewer based on ::place_t (::ui_create_custom_viewer). +/// \param title name of viewer +/// \param minplace first location of the viewer +/// \param maxplace last location of the viewer +/// \param curplace set current location +/// \param rinfo renderer information (can be nullptr) +/// \param ud user data for the viewer +/// \param cvhandlers handlers for the viewer (can be nullptr) +/// \param cvhandlers_ud pointer to arbitrary user data; it will be passed to cvhandlers +/// \param parent widget to hold viewer +/// \return pointer to resulting viewer + +inline TWidget *create_custom_viewer( + const char *title, + const place_t *minplace, + const place_t *maxplace, + const place_t *curplace, + const renderer_info_t *rinfo, + void *ud, + const custom_viewer_handlers_t *cvhandlers, + void *cvhandlers_ud, + TWidget *parent = nullptr) +{ + return (TWidget*) callui( + ui_create_custom_viewer, title, minplace, + maxplace, curplace, rinfo, ud, cvhandlers, cvhandlers_ud, parent).vptr; +} + + +/// Append 'loc' to the viewer's history, and cause the viewer +/// to display it. +///< \param v (TWidget *) +///< \param loc (const lochist_entry_t &) +///< \param flags (uint32) or'ed combination of CVNF_* values +///< \return success + +inline bool custom_viewer_jump( + TWidget *v, + const lochist_entry_t &loc, + uint32 flags=0) +{ + return callui(ui_custom_viewer_jump, v, &loc, flags).cnd; +} + + +/// Push current location in the history and jump to the given location (::ui_ea_viewer_history_push_and_jump). +/// This will jump in the given ea viewer and also in other synchronized views. +/// \param v ea viewer +/// \param ea jump destination +/// \param x,y coords on screen +/// \param lnnum desired line number of given address + +inline bool ea_viewer_history_push_and_jump(TWidget *v, ea_t ea, int x, int y, int lnnum) +{ + return callui(ui_ea_viewer_history_push_and_jump, v, ea, x, y, lnnum).cnd; +} + + +/// Get information about what's in the history (::ui_ea_viewer_history_info). +/// \param[out] nback number of available back steps +/// \param[out] nfwd number of available forward steps +/// \param v ea viewer +/// \retval false if the given ea viewer does not exist +/// \retval true otherwise + +inline bool get_ea_viewer_history_info(int *nback, int *nfwd, TWidget *v) +{ + return callui(ui_ea_viewer_history_info, nback, nfwd, v).cnd; +} + + +/// Refresh custom ida viewer (::ui_refresh_custom_viewer) + +inline void refresh_custom_viewer(TWidget *custom_viewer) +{ + callui(ui_refresh_custom_viewer, custom_viewer); +} + + +/// Repaint the given widget immediately (::ui_repaint_qwidget) + +inline void repaint_custom_viewer(TWidget *custom_viewer) +{ + callui(ui_repaint_qwidget, custom_viewer); +} + + +/// Destroy custom ida viewer + +inline void destroy_custom_viewer(TWidget *custom_viewer) +{ + callui(ui_destroy_custom_viewer, custom_viewer); +} + + +/// Set cursor position in custom ida viewer. +/// \param custom_viewer view +/// \param place target position +/// \param x desired cursor position (column) +/// \param y desired cursor position (line) +/// \return success + +inline bool jumpto(TWidget *custom_viewer, place_t *place, int x, int y) +{ + return callui(ui_jump_in_custom_viewer, custom_viewer, place, x, y).cnd; +} + + +/// Get current place in a custom viewer (::ui_get_curplace). +/// +/// See also the more complete get_custom_viewer_location() +/// +/// \param custom_viewer view +/// \param mouse mouse position (otherwise cursor position) +/// \param[out] x x coordinate +/// \param[out] y y coordinate + +inline place_t *get_custom_viewer_place( + TWidget *custom_viewer, + bool mouse, + int *x, + int *y) +{ + return (place_t *)callui(ui_get_curplace, custom_viewer, mouse, x, y).vptr; +} + +inline bool get_custom_viewer_location( + lochist_entry_t *out, + TWidget *custom_viewer, + bool mouse=false) +{ + return callui(ui_obsolete_get_custom_viewer_location, out, custom_viewer, mouse).cnd; +} + +//------------------------------------------------------------------------- +/// Get the current location in a custom viewer (::ui_get_custom_viewer_location_2). +/// +/// \param out output structure to fill +/// \param custom_viewer the listing widget +/// \param flags or'ed combination of CVLF_* values +inline bool get_custom_viewer_location( + listing_location_t *out, + TWidget *custom_viewer, + uint32 flags=0) +{ + return callui(ui_get_custom_viewer_location_2, out, custom_viewer, flags).cnd; +} + + +/// Are we running inside IDA Qt? + +inline bool is_idaq() +{ + return callui(ui_is_idaq).cnd; +} + + +/// Insert a previously-registered action into the widget's popup menu (::ui_attach_action_to_popup). +/// This function has two "modes": 'single-shot', and 'permanent'. +/// \param widget target widget +/// \param popup_handle target popup menu +/// - if non-nullptr, the action is added to this popup +/// menu invocation (i.e., 'single-shot') +/// - if nullptr, the action is added to a list of actions +/// that should always be present in context menus for this widget +/// (i.e., 'permanent'.) +/// \param name action name +/// \param popuppath can be nullptr +/// \param flags a combination of SETMENU_ flags (see \ref SETMENU_) +/// \return success + +inline bool attach_action_to_popup( + TWidget *widget, + TPopupMenu *popup_handle, + const char *name, + const char *popuppath = nullptr, + int flags=0) +{ + return callui(ui_attach_action_to_popup, widget, popup_handle, name, popuppath, flags).cnd; +} + + +/// Remove a previously-registered action, from the list of 'permanent' +/// context menu actions for this widget (::ui_detach_action_from_popup). +/// This only makes sense if the action has been added to 'widget's list +/// of permanent popup actions by calling attach_action_to_popup +/// in 'permanent' mode. +/// \param widget target widget +/// \param name action name + +inline bool detach_action_from_popup(TWidget *widget, const char *name) +{ + return callui(ui_detach_action_from_popup, widget, name).cnd; +} + + +/// Create & insert an action into the widget's popup menu (::ui_attach_dynamic_action_to_popup). +/// \note action_desc_t::handler for 'desc' must be instantiated using 'new', as it +/// will be 'delete'd when the action is unregistered. +/// \param unused deprecated; should be nullptr +/// \param popup_handle target popup +/// \param desc created with #DYNACTION_DESC_LITERAL +/// \param popuppath can be nullptr +/// \param flags a combination of SETMENU_ constants (see \ref SETMENU_) +/// \param buf a buffer, to retrieve the generated action name - can be nullptr +/// \return success + +inline bool attach_dynamic_action_to_popup( + TWidget *unused, + TPopupMenu *popup_handle, + const action_desc_t &desc, + const char *popuppath = nullptr, + int flags = 0, + qstring *buf = nullptr) +{ + return callui(ui_attach_dynamic_action_to_popup, unused, + popup_handle, &desc, popuppath, flags, buf).cnd; +} + +/// \defgroup ui_uaa_funcs Functions: update actions +/// Convenience functions for ::ui_update_action_attr +///@{ + +/// Update an action's label (::ui_update_action_attr). +/// \param name action name +/// \param label new label +/// \return success + +inline bool update_action_label(const char *name, const char *label) +{ + return callui(ui_update_action_attr, name, AA_LABEL, label).cnd; +} + + +/// Update an action's shortcut (::ui_update_action_attr). +/// \param name action name +/// \param shortcut new shortcut +/// \return success + +inline bool update_action_shortcut(const char *name, const char *shortcut) +{ + return callui(ui_update_action_attr, name, AA_SHORTCUT, shortcut).cnd; +} + + +/// Update an action's tooltip (::ui_update_action_attr). +/// \param name action name +/// \param tooltip new tooltip +/// \return success + +inline bool update_action_tooltip(const char *name, const char *tooltip) +{ + return callui(ui_update_action_attr, name, AA_TOOLTIP, tooltip).cnd; +} + + +/// Update an action's icon (::ui_update_action_attr). +/// \param name action name +/// \param icon new icon id +/// \return success + +inline bool update_action_icon(const char *name, int icon) +{ + return callui(ui_update_action_attr, name, AA_ICON, &icon).cnd; +} + + +/// Update an action's state (::ui_update_action_attr). +/// \param name action name +/// \param state new state +/// \return success + +inline bool update_action_state(const char *name, action_state_t state) +{ + return callui(ui_update_action_attr, name, AA_STATE, &state).cnd; +} + + +/// Update an action's checkability (::ui_update_action_attr). +/// \param name action name +/// \param checkable new checkability +/// \return success + +inline bool update_action_checkable(const char *name, bool checkable) +{ + return callui(ui_update_action_attr, name, AA_CHECKABLE, &checkable).cnd; +} + + +/// Update an action's checked state (::ui_update_action_attr). +/// \param name action name +/// \param checked new checked state +/// \return success + +inline bool update_action_checked(const char *name, bool checked) +{ + return callui(ui_update_action_attr, name, AA_CHECKED, &checked).cnd; +} + + +/// Update an action's visibility (::ui_update_action_attr). +/// \param name action name +/// \param visible new visibility +/// \return success + +inline bool update_action_visibility(const char *name, bool visible) +{ + return callui(ui_update_action_attr, name, AA_VISIBILITY, &visible).cnd; +} + +///@} + +/// \defgroup ui_gaa_funcs Functions: get action attributes +/// Convenience functions for ::ui_get_action_attr +//{ + +/// Get an action's label (::ui_get_action_attr). +/// \param[out] label the action label +/// \param name the action name +/// \return success + +inline bool get_action_label(qstring *label, const char *name) +{ + return callui(ui_get_action_attr, name, AA_LABEL, label).cnd; +} + + +/// Get an action's shortcut (::ui_get_action_attr). +/// \param[out] shortcut the action shortcut +/// \param name the action name +/// \return success + +inline bool get_action_shortcut(qstring *shortcut, const char *name) +{ + return callui(ui_get_action_attr, name, AA_SHORTCUT, shortcut).cnd; +} + + +/// Get an action's tooltip (::ui_get_action_attr). +/// \param[out] tooltip the action tooltip +/// \param name the action name +/// \return success + +inline bool get_action_tooltip(qstring *tooltip, const char *name) +{ + return callui(ui_get_action_attr, name, AA_TOOLTIP, tooltip).cnd; +} + + +/// Get an action's icon (::ui_get_action_attr). +/// \param name the action name +/// \param[out] icon the icon id +/// \return success + +inline bool get_action_icon(const char *name, int *icon) +{ + return callui(ui_get_action_attr, name, AA_ICON, icon).cnd; +} + + +/// Get an action's state (::ui_get_action_attr). +/// \param name the action name +/// \param[out] state the action's state +/// \return success + +inline bool get_action_state(const char *name, action_state_t *state) +{ + return callui(ui_get_action_attr, name, AA_STATE, state).cnd; +} + + +/// Get an action's checkability (::ui_get_action_attr). +/// \param name the action name +/// \param[out] checkable the action's checkability +/// \return success + +inline bool get_action_checkable(const char *name, bool *checkable) +{ + return callui(ui_get_action_attr, name, AA_CHECKABLE, checkable).cnd; +} + + +/// Get an action's checked state (::ui_get_action_attr). +/// \param name the action name +/// \param[out] checked the action's checked state +/// \return success + +inline bool get_action_checked(const char *name, bool *checked) +{ + return callui(ui_get_action_attr, name, AA_CHECKED, checked).cnd; +} + + +/// Get an action's visibility (::ui_get_action_attr). +/// \param name the action name +/// \param[out] visibility the action's visibility +/// \return success + +inline bool get_action_visibility(const char *name, bool *visibility) +{ + return callui(ui_get_action_attr, name, AA_VISIBILITY, visibility).cnd; +} + +///@} + +/// \defgroup ui_scvh_funcs Functions: custom viewer handlers +/// Convenience functions for ::ui_set_custom_viewer_handler +///@{ + +/// Set handlers for custom viewer events +/// Any of these handlers may be nullptr + +inline void set_custom_viewer_handlers( + TWidget *custom_viewer, + const custom_viewer_handlers_t *cvh, + void *cvh_ud) +{ + callui(ui_set_custom_viewer_handlers, custom_viewer, cvh, cvh_ud); +} + + +/// Set a handler for a custom viewer event (::ui_set_custom_viewer_handler). +/// see also ::ui_set_custom_viewer_handlers +/// \param custom_viewer the custom viewer +/// \param handler_id one of CVH_ in ::custom_viewer_handler_id_t +/// \param handler_or_data can be a handler or data. see examples in \ref ui_scvh_funcs +/// \return old value of the handler or data + +inline void *set_custom_viewer_handler( + TWidget *custom_viewer, + custom_viewer_handler_id_t handler_id, + void *handler_or_data) +{ + return callui(ui_set_custom_viewer_handler, custom_viewer, handler_id, + handler_or_data).vptr; +} + + +/// Allow the given viewer to interpret Qt events (::ui_set_custom_viewer_handler) + +inline bool set_custom_viewer_qt_aware(TWidget *custom_viewer) +{ + return callui(ui_set_custom_viewer_handler, custom_viewer, CVH_QT_AWARE).cnd; +} + +///@} + + +/// Get current line of custom viewer (::ui_get_custom_viewer_curline). +/// The returned line contains color codes +/// +/// See also the more powerful get_custom_viewer_location() +/// +/// \param custom_viewer view +/// \param mouse mouse position (otherwise cursor position) +/// \return pointer to contents of current line + +inline const char *get_custom_viewer_curline(TWidget *custom_viewer, bool mouse) +{ + return callui(ui_get_custom_viewer_curline, custom_viewer, mouse).cptr; +} + + +/// Get the X position of the item, in the line +/// \param custom_viewer the widget +/// \param pline a place corresponding to the line +/// \param pitem a place corresponding to the item +/// \retval -1 if 'pitem' is not included in the line +/// \retval -2 if 'pitem' points at the entire line +/// \retval >= 0 for the X coordinate within the pline, where pitem points + +inline int get_custom_viewer_place_xcoord( + TWidget *custom_viewer, + const place_t *pline, + const place_t *pitem) +{ + return callui(ui_get_custom_viewer_place_xcoord, custom_viewer, pline, pitem).i; +} + + +/// Get the current user input event (mouse button press, +/// key press, ...) +/// It is sometimes desirable to be able to tell when a certain +/// situation happens (e.g., 'view_curpos' gets triggered); this +/// function exists to provide that context (GUI version only) +/// \param out the input event data +/// \return false if we are not currently processing a user input event + +inline bool get_user_input_event( + input_event_t *out) +{ + return callui(ui_get_user_input_event, out).cnd; +} + +/// Get current line of output window (::ui_get_output_curline). +/// \param buf output buffer +/// \param mouse current for mouse pointer? +/// \return false if output contains no text + +inline bool get_output_curline(qstring *buf, bool mouse) +{ + return callui(ui_get_output_curline, buf, mouse).cnd; +} + + +/// Returns selected text from output window (::ui_get_output_selected_text). +/// \param buf output buffer +/// \return true if there is a selection + +inline bool get_output_selected_text(qstring *buf) +{ + return callui(ui_get_output_selected_text, buf).cnd; +} + + +/// Get current ida viewer (idaview or custom viewer) (::ui_get_current_viewer) + +inline TWidget *get_current_viewer() +{ + return (TWidget *)callui(ui_get_current_viewer).vptr; +} + + +/// Get last ida viewer (idaview or custom viewer) (::ui_get_last_widget) +/// \param mask an OR'ed set of IWID_* to limit the search to +/// \return the viewer, if found + +inline TWidget *get_last_widget(uint64 mask=uint64(-1)) +{ + return (TWidget *)callui(ui_get_last_widget, mask).vptr; +} + +/// Open function prototype editor to edit function type and create new type. +/// Allows to change the function prototype either in the "old" one-liner mode +/// or in the new multi-line editor, which supports shortcuts, etc. +/// Note: changes will not apply! It is the caller's job to apply the resulting out_tif. +/// Parameters: +/// \param[out] errbuf - (::qstring *) the output string for errors +/// \param[out] out_tif - (tinfo_t *) tif for created type +/// \param pfn - (func_t *) editing function +/// \param tif - (tinfo_t *) current function type +/// \param name - (const char *) function name +/// \return true if new type created successfully + +inline bool prompt_function_prototype(qstring *errbuf, tinfo_t *out_tif, func_t *pfn, tinfo_t *tif, const char *name) +{ + return callui(ui_prompt_function_prototype, errbuf, out_tif, pfn, tif, name).cnd; +} + +/// Collect tagged sections in a color-tagged line +/// (produced by `place_t::generate`) +/// +/// \param out sections storage +/// \param line input line +/// \return success +inline bool parse_tagged_line_sections(tagged_line_sections_t *out, const char *line) +{ + return callui(ui_parse_tagged_line_sections, out, line).cnd; +} + + + +/// Get the type of renderer currently in use in the given view (::ui_get_renderer_type) + +inline tcc_renderer_type_t get_view_renderer_type(TWidget *v) +{ + return tcc_renderer_type_t(callui(ui_get_renderer_type, v).i); +} + + +/// Set the type of renderer to use in a view (::ui_set_renderer_type) + +inline void set_view_renderer_type(TWidget *v, tcc_renderer_type_t rt) +{ + callui(ui_set_renderer_type, v, rt); +} + + +/// Set position range for custom viewer (::ui_set_custom_viewer_range) + +inline void set_custom_viewer_range( + TWidget *custom_viewer, + const place_t *minplace, + const place_t *maxplace) +{ + callui(ui_set_custom_viewer_range, custom_viewer, minplace, maxplace); +} + + +/// Create an empty widget, serving as a container for custom +/// user widgets + +inline TWidget *create_empty_widget(const char *title, int icon = -1) +{ + return (TWidget *) callui(ui_create_empty_widget, title, icon).vptr; +} + + +/// Clear the "Output" window + +inline void msg_clear() +{ + callui(ui_msg_clear); +} + + +/// Save the "Output" window contents into a file +/// \param path The path of the file to save the contents into. +/// An empty path means that the user will be prompted for +/// the destination and, if the file already exists, the user +/// will be asked to confirm before overriding its contents. +/// Upon return, 'path' will contain the path that the user +/// chose. +/// \return success + +inline bool msg_save(qstring &path) +{ + return callui(ui_msg_save, &path).cnd; +} + + +/// Retrieve the last 'count' lines from the output window, in reverse +/// order (from most recent, to least recent) +/// \param out Output storage +/// \param count The number of lines to retrieve. -1 means: all + +inline void msg_get_lines(qstrvec_t *out, int count=-1) +{ + callui(ui_msg_get_lines, out, count); +} + + +/// Get the current, active modal TWidget instance. +/// Note that in this context, the "wait dialog" is not considered: +/// this function will return nullptr even if it is currently shown. +/// \return TWidget * the active modal widget, or nullptr + +inline TWidget *get_active_modal_widget() +{ + return (TWidget *) callui(ui_get_active_modal_widget).vptr; +} + + +/// Translate the provided ea_t, into its pixel position (plus pixel ranges) +/// on the navigation band. + +inline int get_navband_pixel(bool *out_is_vertical, ea_t ea) +{ + return callui(ui_navband_pixel, out_is_vertical, ea).i; +} + + +/// Translate the pixel position on the navigation band, into an address + +inline ea_t get_navband_ea(int pixel) +{ + ea_t ea = BADADDR; + callui(ui_navband_ea, &ea, pixel); + return ea; +} + + +/// Get the system-specific window ID (GUI version only) +/// \param name name of the window (nullptr means the main IDA window) +/// \return the low-level window ID + +inline void *get_window_id(const char *name=nullptr) +{ + return callui(ui_get_window_id, name).vptr; +} + + +/// Is the given custom view an idaview? (::ui_is_idaview) + +inline bool is_idaview(TWidget *v) +{ + return callui(ui_is_idaview, v).cnd; +} + + +/// Get the selected range boundaries (::ui_read_selection). +/// \param v view +/// \param[out] p1 start of selection +/// \param[out] p2 end of selection +/// \retval false no range is selected +/// \retval true ok, start and end are filled + +inline bool read_selection(TWidget *v, twinpos_t *p1, twinpos_t *p2) +{ + return callui(ui_read_selection, v, p1, p2).cnd; +} + + +/// Get the address range for the selected range boundaries, +/// this is the convenient function for read_selection() +/// \param v view, nullptr means the last active window +/// containing addresses +/// \param[out] ea1 start ea +/// \param[out] ea2 end ea +/// \retval 0 no range is selected \n +/// \retval 1 ok, start ea and end ea are filled + +inline bool read_range_selection(TWidget *v, ea_t *ea1, ea_t *ea2) +{ + return callui(ui_read_range_selection, v, ea1, ea2).cnd; +} + + +/// Unmark selection (::ui_unmarksel) + +inline void unmark_selection() { callui(ui_unmarksel); } + + +/// Create a code viewer (::ui_create_code_viewer). +/// A code viewer contains on the left side a widget representing the +/// line numbers, and on the right side, the child widget passed as +/// parameter. +/// It will inherit its title from the child widget. +/// +/// \param custview the custom view to be added +/// \param flags \ref CDVF_ +/// \param parent widget to contain the new code viewer + +inline TWidget *create_code_viewer( + TWidget *custview, + int flags = 0, + TWidget *parent = nullptr) +{ + return (TWidget*)callui(ui_create_code_viewer, custview, flags, parent).vptr; +} + + +/// Set a handler for a code viewer event (::ui_set_custom_viewer_handler). \ingroup ui_scvh_funcs +/// \param code_viewer the code viewer +/// \param handler_id one of CDVH_ in ::custom_viewer_handler_id_t +/// \param handler_or_data can be a handler or data. see examples in \ref ui_scvh_funcs +/// \return old value of the handler or data + +inline void *set_code_viewer_handler( + TWidget *code_viewer, + custom_viewer_handler_id_t handler_id, + void *handler_or_data) +{ + return callui(ui_set_custom_viewer_handler, code_viewer, handler_id, + handler_or_data).vptr; +} + + +/// Set the user data on a code viewer (::ui_set_custom_viewer_handler). \ingroup ui_scvh_funcs + +inline bool set_code_viewer_user_data(TWidget *code_viewer, void *ud) +{ + return callui(ui_set_custom_viewer_handler, code_viewer, CDVH_USERDATA, ud).cnd; +} + + +/// Get the user data from a custom viewer (::ui_get_viewer_user_data) + +inline void *get_viewer_user_data(TWidget *viewer) +{ + return callui(ui_get_viewer_user_data, viewer).vptr; +} + + +/// Get the type of ::place_t instances a viewer uses & creates (::ui_get_viewer_place_type). + +inline tcc_place_type_t get_viewer_place_type(TWidget *viewer) +{ + return tcc_place_type_t(callui(ui_get_viewer_place_type, viewer).i); +} + + +/// Set handlers for code viewer line events. +/// Any of these handlers may be nullptr + +inline void set_code_viewer_line_handlers( + TWidget *code_viewer, + code_viewer_lines_click_t *click_handler, + code_viewer_lines_click_t *popup_handler, + code_viewer_lines_click_t *dblclick_handler, + code_viewer_lines_icon_t *drawicon_handler, + code_viewer_lines_linenum_t *linenum_handler) +{ + callui(ui_set_code_viewer_line_handlers, code_viewer, click_handler, + popup_handler, dblclick_handler, drawicon_handler, linenum_handler); +} + + +/// Set space allowed for icons in the margin of a code viewer (::ui_set_custom_viewer_handler). \ingroup ui_scvh_funcs + +inline bool set_code_viewer_lines_icon_margin(TWidget *code_viewer, int margin) +{ + return callui(ui_set_custom_viewer_handler, code_viewer, CDVH_LINES_ICONMARGIN, margin).cnd; +} + + +/// Set alignment for lines in a code viewer (::ui_set_custom_viewer_handler). \ingroup ui_scvh_funcs + +inline bool set_code_viewer_lines_alignment(TWidget *code_viewer, int align) +{ + return callui(ui_set_custom_viewer_handler, code_viewer, CDVH_LINES_ALIGNMENT, align).cnd; +} + + +/// Set radix for values displayed in a code viewer (::ui_set_custom_viewer_handler). \ingroup ui_scvh_funcs + +inline bool set_code_viewer_lines_radix(TWidget *code_viewer, int radix) +{ + return callui(ui_set_custom_viewer_handler, code_viewer, CDVH_LINES_RADIX, radix).cnd; +} + + +/// Specify that the given code viewer is used to display source code (::ui_set_custom_viewer_handler). \ingroup ui_scvh_funcs + +inline bool set_code_viewer_is_source(TWidget *code_viewer) +{ + return callui(ui_set_custom_viewer_handler, code_viewer, CDVH_SRCVIEW).cnd; +} + + +/// Get the size of a tab in spaces (::ui_get_tab_size). +/// \param path the path of the source view for which the tab size is requested. +/// - if nullptr, the default size is returned. + +inline int get_tab_size(const char *path) +{ + return callui(ui_get_tab_size, path).i; +} + + +/// Clear "Cancelled" flag (::ui_clr_cancelled) + +THREAD_SAFE inline void clr_cancelled() { callui(ui_clr_cancelled); } + + +/// Set "Cancelled" flag (::ui_set_cancelled) + +THREAD_SAFE inline void set_cancelled() { callui(ui_set_cancelled); } + + +/// Test the cancellation flag (::ui_test_cancelled). +/// \retval true Cancelled, a message is displayed +/// \retval false Not cancelled + +THREAD_SAFE inline bool user_cancelled() { return callui(ui_test_cancelled).cnd; } + + +/// Display a load file dialog and load file (::ui_load_file). +/// \param[out] temp_file name of the file with the extracted archive member. +/// \param[in,out] filename the name of input file as is, +/// library or archive name +/// \param[in,out] pli loader input source, +/// may be changed to point to temp_file +/// \param neflags combination of NEF_... bits (see \ref NEF_) +/// \param[in,out] ploaders list of loaders which accept file, +/// may be changed for loaders of temp_file +/// \retval true file was successfully loaded +/// \retval false otherwise + +inline bool ui_load_new_file( + qstring *temp_file, + qstring *filename, + linput_t **pli, + ushort neflags, + load_info_t **ploaders) +{ + return callui(ui_load_file, temp_file, filename, pli, neflags, ploaders).cnd; +} + + +/// Load a debugger plugin and run the specified program (::ui_run_dbg). +/// \param dbgopts value of the -r command line switch +/// \param exename name of the file to run +/// \param argc number of arguments for the executable +/// \param argv argument vector +/// \return success + +inline bool ui_run_debugger( + const char *dbgopts, + const char *exename, + int argc, + const char *const *argv) +{ + return callui(ui_run_dbg, dbgopts, exename, argc, argv).cnd; +} + + +/// Load debugging information from a file. +/// \param path path to file +/// \param li loader input. if nullptr, check DBG_NAME_KEY +/// \param base loading address +/// \param verbose dump status to message window + +inline bool load_dbg_dbginfo( + const char *path, + linput_t *li=nullptr, + ea_t base=BADADDR, + bool verbose=false) +{ + return callui(ui_dbg_load_dbg_dbginfo, path, li, base, verbose).cnd; +} + + +/// Add hotkey for IDC function (::ui_add_idckey). +/// \param hotkey hotkey name +/// \param idcfunc IDC function name +/// \return \ref IDCHK_ + +inline int add_idc_hotkey(const char *hotkey, const char *idcfunc) +{ + return callui(ui_add_idckey, hotkey, idcfunc).i; +} + + +/// Get the highlighted identifier in the viewer (::ui_get_highlight_2). +/// \param out_str buffer to copy identifier to +/// \param viewer the viewer +/// \param out_flags storage for the flags (see \ref HIF_) +/// \param flags input flags; optionally specify a highlight slot (0-7) +/// \return false if no identifier is highlighted + +inline bool get_highlight(qstring *out_str, TWidget *viewer, uint32 *out_flags, uint32 flags=0) +{ + return callui(ui_get_highlight_2, out_str, viewer, out_flags, flags).cnd; +} + + +/// Set the highlighted identifier in the viewer (::ui_set_highlight). +/// \param viewer the viewer +/// \param str the text to match, or nullptr to remove current +/// \param flags combination of HIF_... bits (see \ref HIF_) +/// \return false if an error occurred + +inline bool set_highlight(TWidget *viewer, const char *str, int flags) +{ + return callui(ui_set_highlight, viewer, str, flags).cnd; +} + + +#ifndef SWIG +/// Pointer to range marker function (for idaviews and hexviews) +/// This pointer is initialized by setup_range_marker() + +extern void (idaapi*range_marker)(ea_t ea, asize_t size); + + +/// Initialize pointer to idaview marker + +inline void setup_range_marker() +{ + void *ptr = callui(ui_get_range_marker).vptr; + if ( ptr != nullptr ) + range_marker = reinterpret_cast(ptr); +} + +/// Inform the UI about any modifications of [ea, ea+size) + +inline void mark_range_for_refresh(ea_t ea, asize_t size) +{ + if ( range_marker != nullptr ) + range_marker(ea, size); +} + + +/// Tell UI to refresh all idaviews and hexviews + +inline void mark_all_eaviews_for_refresh() +{ + if ( range_marker != nullptr ) + range_marker(0, BADADDR); +} + +/// Ignores range_marker during the lifetime of the object. +/// Refreshes all idaviews and hexviews at the end. +struct range_marker_suspender_t +{ + void (idaapi *backup)(ea_t ea, asize_t size); + range_marker_suspender_t() + { + backup = range_marker; + range_marker = nullptr; + } + ~range_marker_suspender_t() + { + range_marker = backup; + mark_all_eaviews_for_refresh(); + } +}; +#endif // SWIG + + +/// \defgroup ui_open_builtin_funcs Functions: open built-in windows +/// Convenience functions for ::ui_open_builtin +///@{ + +/// Open the exports window (::ui_open_builtin). +/// \param ea index of entry to select by default +/// \return pointer to resulting window + +inline TWidget *open_exports_window(ea_t ea) +{ + return (TWidget *) callui(ui_open_builtin, BWN_EXPORTS, ea).vptr; +} + + +/// Open the exports window (::ui_open_builtin). +/// \param ea index of entry to select by default +/// \return pointer to resulting window + +inline TWidget *open_imports_window(ea_t ea) +{ + return (TWidget *) callui(ui_open_builtin, BWN_IMPORTS, ea).vptr; +} + + +/// Open the names window (::ui_open_builtin). +/// \param ea index of entry to select by default +/// \return pointer to resulting window + +inline TWidget *open_names_window(ea_t ea) +{ + return (TWidget *) callui(ui_open_builtin, BWN_NAMES, ea).vptr; +} + + +/// Open the 'Functions' window (::ui_open_builtin). +/// \param ea index of entry to select by default +/// \return pointer to resulting window + +inline TWidget *open_funcs_window(ea_t ea) +{ + return (TWidget *) callui(ui_open_builtin, BWN_FUNCS, ea).vptr; +} + + +/// Open the 'Strings' window (::ui_open_builtin). +/// \param ea index of entry to select by default +/// \param selstart,selend only display strings that occur within this range +/// \return pointer to resulting window + +inline TWidget *open_strings_window(ea_t ea, ea_t selstart=BADADDR, ea_t selend=BADADDR) +{ + return (TWidget *) callui(ui_open_builtin, BWN_STRINGS, ea, selstart, selend).vptr; +} + + +/// Open the segments window (::ui_open_builtin). +/// \param ea index of entry to select by default +/// \return pointer to resulting window + +inline TWidget *open_segments_window(ea_t ea) +{ + return (TWidget *) callui(ui_open_builtin, BWN_SEGS, ea).vptr; +} + + +/// Open the segment registers window (::ui_open_builtin). +/// \param ea index of entry to select by default +/// \return pointer to resulting window + +inline TWidget *open_segregs_window(ea_t ea) +{ + return (TWidget *) callui(ui_open_builtin, BWN_SEGREGS, ea).vptr; +} + + +/// Open the selectors window (::ui_open_builtin). +/// \return pointer to resulting window + +inline TWidget *open_selectors_window() +{ + return (TWidget *) callui(ui_open_builtin, BWN_SELS, 0).vptr; +} + + +/// Open the signatures window (::ui_open_builtin). +/// \return pointer to resulting window + +inline TWidget *open_signatures_window() +{ + return (TWidget *) callui(ui_open_builtin, BWN_SIGNS, 0).vptr; +} + + +/// Open the type libraries window (::ui_open_builtin). +/// \return pointer to resulting window + +inline TWidget *open_tils_window() +{ + return (TWidget *) callui(ui_open_builtin, BWN_TILS, 0).vptr; +} + + +/// Open the local types window (::ui_open_builtin2). +/// \param ordinal ordinal of type to select by default +/// \param cursor cursor to the type member +/// \return pointer to resulting window + +inline TWidget *open_loctypes_window(int ordinal, const tif_cursor_t *cursor=nullptr) +{ + return (TWidget *) callui(ui_open_builtin2, BWN_TICSR, ordinal, cursor).vptr; +} + +/// Open the sub-til window (::ui_open_builtin2). +/// \param tif tif to open +/// \param cursor cursor to the type member +/// \return pointer to resulting window + +inline TWidget *open_til_view_window(tinfo_t *tif, const tif_cursor_t *cursor=nullptr) +{ + return (TWidget *) callui(ui_open_builtin2, BWN_TIL_VIEW, tif, cursor).vptr; +} + +/// Open the function calls window (::ui_open_builtin). +/// \return pointer to resulting window + +inline TWidget *open_calls_window(ea_t ea) +{ + return (TWidget *) callui(ui_open_builtin, BWN_CALLS, ea).vptr; +} + +/// Open the problems window (::ui_open_builtin). +/// \param ea index of entry to select by default +/// \return pointer to resulting window + +inline TWidget *open_problems_window(ea_t ea) +{ + return (TWidget *) callui(ui_open_builtin, BWN_PROBS, ea).vptr; +} + + +/// Open the breakpoints window (::ui_open_builtin). +/// \param ea index of entry to select by default +/// \return pointer to resulting window + +inline TWidget *open_bpts_window(ea_t ea) +{ + return (TWidget *) callui(ui_open_builtin, BWN_BPTS, ea).vptr; +} + + +/// Open the threads window (::ui_open_builtin). +/// \return pointer to resulting window + +inline TWidget *open_threads_window() +{ + return (TWidget *) callui(ui_open_builtin, BWN_THREADS, 0).vptr; +} + + +/// Open the modules window (::ui_open_builtin). +/// \return pointer to resulting window + +inline TWidget *open_modules_window() +{ + return (TWidget *) callui(ui_open_builtin, BWN_MODULES, 0).vptr; +} + + +/// Open the tracing window (::ui_open_builtin). +/// \return pointer to resulting window + +inline TWidget *open_trace_window() +{ + return (TWidget *) callui(ui_open_builtin, BWN_TRACE, 0).vptr; +} + + +/// Open the call stack window (::ui_open_builtin). +/// \return pointer to resulting window + +inline TWidget *open_stack_window() +{ + return (TWidget *) callui(ui_open_builtin, BWN_CALL_STACK, 0).vptr; +} + + +/// Open the cross references window (::ui_open_builtin). +/// \param ea index of entry to select by default +/// \return pointer to resulting window + +inline TWidget *open_xrefs_window(ea_t ea) +{ + return (TWidget *) callui(ui_open_builtin, BWN_XREFS, ea).vptr; +} + + +/// Open the frame window for the given function (::ui_open_builtin). +/// \param pfn function to analyze +/// \param offset offset where the cursor is placed +/// \return pointer to resulting window if 'pfn' is a valid function and the window was displayed, \n +/// nullptr otherwise + +inline TWidget *open_frame_window(func_t *pfn, uval_t offset) +{ + return (TWidget *) callui(ui_open_builtin, BWN_FRAME, pfn, offset).vptr; +} + + +/// Open the navigation band window (::ui_open_builtin). +/// \param ea sets the address of the navband arrow +/// \param zoom sets the navband zoom level +/// \return pointer to resulting window + +inline TWidget *open_navband_window(ea_t ea, int zoom) +{ + return (TWidget *) callui(ui_open_builtin, BWN_NAVBAND, ea, zoom).vptr; +} + + +/// Open a disassembly view (::ui_open_builtin). +/// \param window_title title of view to open +/// \param ranges if != nullptr, then display a flow chart with the specified ranges +/// \return pointer to resulting window + +inline TWidget *open_disasm_window(const char *window_title, const rangevec_t *ranges=nullptr) +{ + return (TWidget *) callui(ui_open_builtin, BWN_DISASM, window_title, BADADDR, ranges, 0).vptr; +} + + +/// Open a hexdump view (::ui_open_builtin). +/// \param window_title title of view to open +/// \return pointer to resulting window + +inline TWidget *open_hexdump_window(const char *window_title) +{ + return (TWidget *) callui(ui_open_builtin, BWN_HEXVIEW, window_title, BADADDR, 0).vptr; +} + + +/// Open the notepad window (::ui_open_builtin). +/// \return pointer to resulting window + +inline TWidget *open_notepad_window() +{ + return (TWidget *) callui(ui_open_builtin, BWN_NOTEPAD, 0).vptr; +} + + +/// Open the bookmarks window (::ui_open_builtin). +/// \param w The widget for which the bookmarks will open. For example, +/// this can be an IDAView, or Enums view, etc. +/// \return pointer to resulting window + +inline TWidget *open_bookmarks_window(TWidget *w) +{ + return (TWidget *) callui(ui_open_builtin, BWN_BOOKMARKS, w, 0).vptr; +} + + +///@} + +/// [Un]synchronize sources +/// \param what +/// \param with +/// \param sync +/// \return success +inline bool sync_sources( + const sync_source_t &what, + const sync_source_t &with, + bool sync) +{ + return callui(ui_sync_sources, &what, &with, sync).cnd; +} + + +/// \defgroup ui_choose_funcs Functions: built-in choosers +/// Convenience functions for ::ui_choose and ::choose_type_t +///@{ + + +/// Choose a signature (::ui_choose, ::chtype_idasgn). +/// \return name of selected signature, nullptr if none selected + +inline char *choose_idasgn() +{ + return callui(ui_choose, chtype_idasgn).cptr; +} + + +/// Choose a type library (::ui_choose, ::chtype_idatil). +/// \param buf output buffer to store the library name +/// \retval true 'buf' was filled with the name of the selected til +/// \retval false otherwise + +inline bool choose_til(qstring *buf) +{ + return callui(ui_choose, chtype_idatil, buf).cnd; +} + + +/// Choose an entry point (::ui_choose, ::chtype_entry). +/// \param title chooser title +/// \return ea of selected entry point, #BADADDR if none selected + +inline ea_t choose_entry(const char *title) +{ + ea_t ea; + callui(ui_choose, chtype_entry, &ea, title); + return ea; +} + + +/// Choose a name (::ui_choose, ::chtype_name). +/// \param title chooser title +/// \return ea of selected name, #BADADDR if none selected + +inline ea_t choose_name(const char *title) +{ + ea_t ea; + callui(ui_choose, chtype_name, &ea, title); + return ea; +} + + +/// Choose an xref to a stack variable (::ui_choose, ::chtype_name). +/// \param pfn function +/// \param srkvar_tid frame variable TID +/// \return ea of the selected xref, BADADDR if none selected + +inline ea_t choose_stkvar_xref(func_t *pfn, tid_t srkvar_tid) +{ + ea_t ea; + callui(ui_choose, chtype_stkvar_xref, &ea, pfn, srkvar_tid); + return ea; +} + + +/// Choose an xref to an address (::ui_choose, ::chtype_xref). +/// \param to referenced address +/// \return ea of selected xref, BADADDR if none selected + +inline ea_t choose_xref(ea_t to) +{ + ea_t ea; + callui(ui_choose, chtype_xref, &ea, to); + return ea; +} + + +/// Choose an enum (::ui_choose, ::chtype_enum). +/// \param[out] out the selected enum type +/// \param title chooser title +/// \param default_ord ordinal of enum to select by default +/// \retval true the selected type is in OUT +/// \retval false nothing was selected + +inline bool choose_enum(tinfo_t *out, const char *title, uint32 default_ord) +{ + return callui(ui_choose, chtype_enum, out, title, default_ord).cnd; +} + + +/// Choose an enum, restricted by value & size (::ui_choose, ::chtype_enum_by_value_and_size). +/// If the given value cannot be found initially, this function will +/// ask if the user would like to import a standard enum. +/// \param[out] out the selected enum type +/// \param title chooser title +/// \param default_ord ordinal of enum to select by default +/// \param value value to search for +/// \param nbytes size of value +/// \param[out] serial serial number of imported enum member, if one was found +/// \retval true the selected type is in OUT +/// \retval false nothing was selected + +inline bool choose_enum_by_value( + tinfo_t *out, + const char *title, + uint32 default_ord, + uint64 value, + int nbytes, + uchar *serial) +{ + return callui(ui_choose, chtype_enum_by_value_and_size, out, default_ord, title, value, nbytes, serial).cnd; +} + +/// Choose a function (::ui_choose, ::chtype_func). +/// \param title chooser title +/// \param default_ea ea of function to select by default +/// \return pointer to function that was selected, nullptr if none selected + +inline func_t *choose_func(const char *title, ea_t default_ea) +{ + return callui(ui_choose, chtype_func, title, default_ea).fptr; +} + + +/// Choose a segment (::ui_choose, ::chtype_segm). +/// \param title chooser title +/// \param default_ea ea of segment to select by default +/// \return pointer to segment that was selected, nullptr if none selected + +inline segment_t *choose_segm(const char *title, ea_t default_ea) +{ + return callui(ui_choose, chtype_segm, title, default_ea).segptr; +} + + +/// Choose a structure (::ui_choose, ::chtype_struct). +/// \param[out] out the selected structure type +/// \param[in] title chooser title +/// \retval true the selected type is in OUT +/// \retval false nothing was selected + +inline bool choose_struct(tinfo_t *out, const char *title) +{ + return callui(ui_choose, chtype_struct, out, title).cnd; +} + + +/// Choose a segment register change point (::ui_choose, ::chtype_srcp). +/// \param title chooser title +/// \return pointer to segment register range of selected change point, nullptr if none selected + +inline sreg_range_t *choose_srcp(const char *title) +{ + return callui(ui_choose, chtype_srcp, title).sraptr; +} + +///@} + +#ifndef SWIG + +/// Get path to a structure offset (for nested structures/enums) (::ui_choose, ::chtype_strpath). + +inline int choose_struc_path( + const char *title, + tid_t strid, + uval_t offset, + adiff_t delta, + bool appzero, + tid_t *path) +{ + return callui(ui_choose, chtype_strpath, title, strid, + offset, delta, appzero, path).i; +} + + + + +/// Invoke the chooser with a chooser object (::ui_choose, ::chtype_generic). +/// see the choose() function above + +//lint -sem(choose,custodial(1)) +inline ssize_t choose(chooser_base_t *ch, const void *def_item) +{ + return callui(ui_choose, chtype_generic, ch, def_item).ssize; +} + +#endif // SWIG + + +/// Get the underlying object of the specified chooser (::ui_get_chooser_obj). +/// +/// This attemps to find the choser by its title and, if found, returns +/// the result of calling its chooser_base_t::get_chooser_obj() method. +/// +/// \note This is object is chooser-specific. +/// \return the object that was used to create the chooser + +inline void *get_chooser_obj(const char *chooser_caption) +{ + return callui(ui_get_chooser_obj, chooser_caption).vptr; +} + +/// Get the text corresponding to the index N in the chooser data. +/// Use -1 to get the header. +/// +/// See also get_chooser_rows(). + +inline bool get_chooser_data( + qstrvec_t *out, + const char *chooser_caption, + int n) +{ + return callui(ui_get_chooser_data, out, chooser_caption, n).cnd; +} + +/// Get the chooser contents corresponding to the rows indicated by "what". +/// \param out A vector of chooser_row_info_t, one entry per returned row. +/// \param chooser_caption The caption that identifies the desired chooser. +/// \param what Either one of the GCRF_ flags, or a row index. +/// \return Success. + +inline bool get_chooser_rows( + chooser_row_info_vec_t *out, + const char *chooser_caption, + size_t what) +{ + return callui(ui_get_chooser_rows, out, chooser_caption, what).cnd; +} + + +/// Enable item-specific attributes for chooser items (::ui_enable_chooser_item_attrs). +/// For example: color list items differently depending on a criterium. \n +/// If enabled, the chooser will generate ui_get_chooser_item_attrs \n +/// events that can be intercepted by a plugin to modify the item attributes. \n +/// This event is generated only in the GUI version of IDA. \n +/// Specifying #CH_ATTRS bit at the chooser creation time has the same effect. +/// \return success + +inline bool idaapi enable_chooser_item_attrs(const char *chooser_caption, bool enable) +{ + return callui(ui_enable_chooser_item_attrs, chooser_caption, enable).cnd; +} + + +/// See show_wait_box() + +THREAD_SAFE AS_PRINTF(1, 0) inline void show_wait_box_v(const char *format, va_list va) +{ + callui(ui_mbox, mbox_wait, format, va); +} + + +/// Display a dialog box with "Please wait...". +/// The behavior of the dialog box can be configured with well-known \n +/// tokens, that should be placed at the start of the format string: \n +/// "NODELAY\n": the dialog will show immediately, instead of \n +/// appearing after usual grace threshold \n +/// "HIDECANCEL\n": the cancel button won't be added to the dialog box \n +/// and user_cancelled() will always return false (but \n +/// can be called to refresh UI) \n +/// Using "HIDECANCEL" implies "NODELAY" \n +/// Plugins must call hide_wait_box() to close the dialog box, otherwise \n +/// the user interface will remain disabled. \n +/// +/// Note that, if the wait dialog is already visible, show_wait_box() will \n +/// 1) push the currently-displayed text on a stack \n +/// 2) display the new text \n +/// Then, when hide_wait_box() is called, if that stack isn't empty its top \n +/// label will be popped and restored in the wait dialog. \n +/// This implies that a plugin should call hide_wait_box() exactly as many \n +/// times as it called show_wait_box(), or the wait dialog might remain \n +/// visible and block the UI. \n +/// Also, in case the plugin knows the wait dialog is currently displayed, \n +/// alternatively it can call replace_wait_box(), to replace the text of the\n +/// dialog without pushing the currently-displayed text on the stack. +THREAD_SAFE AS_PRINTF(1, 2) inline void show_wait_box(const char *format, ...) +{ + va_list va; + va_start(va, format); + show_wait_box_v(format, va); + va_end(va); +} + + +/// Hide the "Please wait dialog box" + +THREAD_SAFE inline void hide_wait_box() +{ + // stupid watcom requires va_list should not be nullptr + callui(ui_mbox, mbox_hide, nullptr, &callui); +} + + +/// Replace the label of "Please wait dialog box" + +THREAD_SAFE AS_PRINTF(1, 2) inline void replace_wait_box(const char *format, ...) +{ + va_list va; + va_start(va, format); + callui(ui_mbox, mbox_replace, format, va); + va_end(va); +} + + +/// Issue a beeping sound (::ui_beep). +/// \param beep_type ::beep_t + +inline void beep(beep_t beep_type=beep_default) +{ + callui(ui_beep, beep_type); +} + + +/// Display copyright warning (::ui_copywarn). +/// \return yes/no + +inline bool display_copyright_warning() +{ + return callui(ui_copywarn).cnd; +} + +#endif // __UI__ END OF UI SERVICE FUNCTIONS + + +/// Show a message box asking to send the input file to support@hex-rays.com. +/// \param format the reason why the input file is bad + +THREAD_SAFE AS_PRINTF(1, 2) inline void ask_for_feedback(const char *format, ...) +{ + va_list va; + va_start(va, format); + callui(ui_mbox, mbox_feedback, format, va); + va_end(va); +} + + +/// Display a dialog box and wait for the user to input an address (::ui_ask_addr). +/// \param addr in/out parameter. contains pointer to the address. +/// \param format printf() style format string with the question +/// \retval 0 the user pressed Esc. +/// \retval 1 ok, the user entered an address + +AS_PRINTF(2, 3) inline bool ask_addr(ea_t *addr, const char *format, ...) +{ + va_list va; + va_start(va, format); + bool ok = callui(ui_ask_addr, addr, format, va).cnd; + va_end(va); + return ok; +} + + +/// Display a dialog box and wait for the user to input an segment name (::ui_ask_seg). +/// This function allows to enter segment register names, segment base +/// paragraphs, segment names to denote a segment. +/// \param sel in/out parameter. contains selector of the segment +/// \param format printf() style format string with the question +/// \retval 0 if the user pressed Esc. \n +/// \retval 1 ok, the user entered an segment name + +AS_PRINTF(2, 3) inline bool ask_seg(sel_t *sel, const char *format, ...) +{ + va_list va; + va_start(va, format); + bool ok = callui(ui_ask_seg, sel, format, va).cnd; + va_end(va); + return ok; +} + + +/// Display a dialog box and wait for the user to input an number (::ui_ask_long). +/// The number is represented in C-style. +/// This function allows to enter any IDC expression and +/// properly calculates it. +/// \param value in/out parameter. contains pointer to the number +/// \param format printf() style format string with the question +/// \retval 0 if the user pressed Esc. \n +/// \retval 1 ok, the user entered a valid number. + +AS_PRINTF(2, 3) inline bool ask_long(sval_t *value, const char *format, ...) +{ + va_list va; + va_start(va, format); + bool ok = callui(ui_ask_long, value, format, va).cnd; + va_end(va); + return ok; +} + + +//--------------------------------------------------------------------------- +// E R R O R / W A R N I N G / I N F O D I A L O G B O X E S +//--------------------------------------------------------------------------- + +/// If this variable is set, then dialog boxes will not appear on the screen. +/// Warning/info messages are shown in the messages window. \n +/// The default value of user input dialogs will be returned to the +/// caller immediately. \n +/// This variable is used to enable unattended work of ida. + +idaman bool ida_export_data batch; + + +/// Exiting because of a a fatal error? +/// Is non-zero if we are exiting with from the error() function. + +idaman int ida_export_data errorexit; + + +/// Display error dialog box and exit. +/// If you just want to display an error message and let IDA continue, +/// do NOT use this function! Use warning() or info() instead. +/// \param format printf() style message string. +/// It may have some prefixes, see 'Format of dialog box' for details. + +THREAD_SAFE AS_PRINTF(1, 2) NORETURN inline void error(const char *format, ...) +{ + va_list va; + va_start(va, format); + verror(format, va); + // NOTREACHED +} + + +/// Display warning dialog box and wait for the user to press Enter or Esc. +/// This messagebox will by default contain a "Don't display this message again" \n +/// checkbox if the message is repetitively displayed. If checked, the message \n +/// won't be displayed anymore during the current IDA session. \n +/// \param format printf() style format string. +/// It may have some prefixes, see 'Format of dialog box' for details. +/// \param va pointer to variadic arguments. +/// \return < 0 in case the warning was inhibited, >= 0 otherwise + +THREAD_SAFE AS_PRINTF(1, 0) inline ssize_t vwarning(const char *format, va_list va) +{ + return callui(ui_mbox, mbox_warning, format, va).ssize; +} + +THREAD_SAFE AS_PRINTF(1, 2) inline ssize_t warning(const char *format, ...) +{ + va_list va; + va_start(va, format); + ssize_t rc = vwarning(format, va); + va_end(va); + return rc; +} + + +/// Display info dialog box and wait for the user to press Enter or Esc. +/// This messagebox will by default contain a "Don't display this message again" \n +/// checkbox. If checked, the message will never be displayed anymore (state saved \n +/// in the Windows registry or the idareg.cfg file for a non-Windows version). +/// \param format printf() style format string. +/// It may have some prefixes, see 'Format of dialog box' for details. +/// \param va pointer to variadic arguments. +/// \return < 0 in case the message was inhibited, >= 0 otherwise + +THREAD_SAFE AS_PRINTF(1, 0) inline ssize_t vinfo(const char *format, va_list va) +{ + return callui(ui_mbox, mbox_info, format, va).ssize; +} + +THREAD_SAFE AS_PRINTF(1, 2) inline ssize_t info(const char *format, ...) +{ + va_list va; + va_start(va, format); + size_t rc = vinfo(format, va); + va_end(va); + return rc; +} + + +/// Display "no memory for module ..." dialog box and exit. +/// \param format printf() style message string. +/// \param va pointer to variadic arguments. + +THREAD_SAFE AS_PRINTF(1, 0) NORETURN inline void vnomem(const char *format, va_list va) +{ + callui(ui_mbox, mbox_nomem, format, va); + // NOTREACHED + abort(); // to suppress compiler warning or error +} + +THREAD_SAFE AS_PRINTF(1, 2) NORETURN inline void nomem(const char *format, ...) +{ + va_list va; + va_start(va, format); + vnomem(format, va); + // NOTREACHED +} + + +/// Output a formatted string to the output window [analog of printf()]. +/// Everything appearing on the output window may be written +/// to a text file. For this the user should define the following environment +/// variable: \n +/// set IDALOG=idalog.txt +/// +/// \param format printf() style message string. +/// \param va pointer to variadic arguments. +/// \return number of bytes output + +THREAD_SAFE AS_PRINTF(1, 0) inline int vmsg(const char *format, va_list va) +{ + return callui(ui_msg, format, va).i; +} + +THREAD_SAFE AS_PRINTF(1, 2) inline int msg(const char *format, ...) +{ + va_list va; + va_start(va, format); + int nbytes = vmsg(format, va); + va_end(va); + return nbytes; +} + +#ifndef SWIG + +/*! \defgroup FORM_C ask_form()/open_form() + + \brief This module describes how to generate a custom form. + +
+
+  The following keywords might appear at the beginning of the 'form' argument
+  (case insensitive):
+
+  STARTITEM number
+
+    where number is a number of input field the cursor will stand on.
+    By default the cursor is in the first field of the dialog box.
+    The input fields are numbered from 0 (the first field is field 0).
+
+  AUTOSYNC
+
+    This will cause all fields to be updated with their current
+    input value at the time CB_YES is called. This lets the callback
+    (if provided) possibly perform validation of fields directly from
+    their storage location, without having to query for their values
+    individually.
+
+  BUTTON name caption
+
+    Alternative caption for a button. It may contain the character
+    to highlight in this form:  ~Y~es
+    Valid button names are: YES, NO, CANCEL
+    For example:
+        BUTTON YES Please do
+        BUTTON NO Nope
+        BUTTON CANCEL NONE
+
+    By default the NO button is not displayed. If it is displayed, then
+    the return value of the function will be different!
+    (see the function description)
+
+    Empty text means that there won't be any corresponding button.
+    (you may also use NONE as the caption to hide it)
+
+    A * after the button name means that this button will be the default:
+
+      BUTTON CANCEL* Cancel
+
+  Next, if the dialog box is kept in IDA.HLP, the following may appear:
+  (this defines help context for the whole dialog box)
+
+  @hlpMessageName[]
+
+  If the form is not in IDA.HLP file, then it can have a built-in
+  help message. In this case the help screen should be enclosed in the
+  following keywords:
+
+  HELP
+  ....
+  ....
+  ....
+  ENDHELP
+
+  Each keyword should be on a separate line.
+
+  Next there must be the title line and two empty lines.
+  Most of the text in the dialog box text string is copied to the dialog
+  without modification. There are three special cases:
+
+        - dynamic labels (format parameters)
+        - callback arguments
+        - input fields
+
+  For example, this dialog box:
+
+  ------ format:
+        Sample dialog box
+
+
+        This is sample dialog box for %A
+        using address %$
+
+        <~E~nter value:N::18::>
+
+  ------
+
+  Contains two dynamic labels (text %A and address %$) and one input field
+  (numerical input box with the label "Enter value").
+
+  Parameters for the dynamic labels and input fields are taken from the
+  function's input arguments (va_list). The corresponding argument should
+  contain a pointer (sic, pointer) to the value to be displayed.
+
+  The dialog box above should be called as
+
+                \code
+                char *string = "something";
+                ea_t addr = someaddr;
+                uval_t answer = 0;
+                int ok = ask_form(format, string, &addr, &answer);
+                \endcode
+
+
+  Dynamic labels are used to specify variant parts of the dialog box text.
+  They use the following syntax:
+
+        %nT
+
+  where
+        n  - optional decimal field ID, which may be used in the
+             ::form_actions_t calls to get/set label value at runtime
+        T  - a character specifying type of input field. All input field
+             types (except B and K) are valid format specifiers. See below
+             for the list.
+
+
+  There are two special specifiers for callbacks:
+
+  The combination '%/' corresponds to a callback function that will be
+  called when any of the fields is modified. The callback type is ::formchgcb_t.
+  There can be only one such callback. It corresponds to the first variadic
+  argument regardless of its exact position in the format string.
+
+  The combination '%*' is used to store user data (void *) in the form.
+  This data can be later retrieved from the ::formchgcb_t callback via the
+  form action method get_ud().
+
+  Input fields use the following syntax:
+
+  
+
+  where
+        label - any text string serving as label for the input field
+                the label may contain an accelerator key like this: "~O~pen"
+                (O is the accelerator key; Alt-O will work too)
+        type  - a character specifying type of input field.
+                The form() function will perform initial validation of
+                value specified by the user and convert it appropriately.
+                See table of input field types below. The type can be followed
+                by a decimal number, an input field ID.
+        width - decimal number specifying the maximum possible number of
+                characters that can be entered into the input field
+                for X: decimal number specifying size of the buffer
+                  of characters that can be entered into the input field
+                  passed for text input fields (including terminating 0).
+                  if omitted or <0, assumed to be at least MAXSTR
+                for B, k: the code generated when the user presses the
+                  button (passed to the button callback)
+                for f (path to file) this attribute specifies the dialog type:
+                  0-'open file' dialog box
+                  1-'save file' dialog box
+                for F (folder) it is ignored
+                for f, F: buffer is assumed to be at least QMAXPATH long
+                for b (combobox) this attribute specifies the readonly attribute:
+                  0   - read-only combobox
+                  > 0 - editable combobox
+                for n, N, D, O, Y, H, M, m: the width can have a '+' prefix.
+                  in this case, if the entered value starts with '+' or '-'
+                  sign, it will be added to or subtracted from the initial
+                  value. the caller will receive the result of this operation.
+                  the relative offset is always decimal.
+        swidth -decimal number specifying width of visible part of input field
+                this number may be omitted.
+                for E, t: decimal number specifying the width of the input area.
+                  for these types the number cannot be omitted.
+                  note that the height is calculated automatically
+        @hlp[]- help context for the input field. you may replace the
+                help context with '::' (two colons) if you don't want to
+                specify help context. The help context is a number of help
+                page from IDA.HLP file.
+
+
+  Input field types                               va_list parameter
+  -----------------                               -----------------
+
+  q - UTF-8 string                                ::qstring*
+  h - HTML text                                   char * (only for GUI version; only for dynamic labels; no input)
+  S - segment                                     ::sel_t*
+  N - hex number, "0x" prefix                     ::uval_t*
+  n - signed hex number, "0x" prefix              ::sval_t*
+  L - C notation number                           ::uint64*
+      (prefix 0x - hex, 0 - octal, otherwise decimal)
+  l - same as L but with optional sign            ::int64*
+  M - hex number, no "0x" prefix                  ::uval_t*
+  m - same as M but for uint64                    ::uint64*
+  u - unsigned decimal number                     ::uval_t*
+  D - decimal number                              ::sval_t*
+  O - octal number, "O" prefix                    ::sval_t*
+  o - signed octal number, "O" prefix             ::sval_t*
+  Y - binary number, "0b" prefix                  ::sval_t*
+  s - signed binary number, "0b" prefix           ::sval_t*
+  H - char value, C notation                      ::sval_t*
+  $ - address                                     ::ea_t*
+  i - identifier (register names are forbidden)   ::qstring*
+  I - identifier (register names are permitted)   ::qstring*
+  B - button                                      ::buttoncb_t*
+  k - txt: button (same as B)/gui: hyperlink      ::buttoncb_t*
+  K - color button                                ::bgcolor_t*
+  F - path to folder                              char* at least #QMAXPATH size
+  f - path to file                                char* at least #QMAXPATH size
+  y - type declaration                            ::qstring*
+  X - command                                     char* at least #MAXSTR size
+  E - chooser                                     ::chooser_base_t * - embedded chooser
+                                                  ::sizevec_t * - in/out: selected lines (0-based)
+                                                    selected rows are saved to this array
+                                                    for modal forms only
+                                                    (NB: this field takes two args)
+  t - multi line text control                     ::textctrl_info_t *
+  b - combobox (dropdown list)                    ::qstrvec_t * - the list of items
+                                                  int* or ::qstring* - the preselected item
+                                                    (::qstring* when the combo is editable, i.e. width field is >0)
+  p - UTF-8 string                                ::qstring* - echoed as a password field (i.e., with '*' masks)
+
+  The M, n, N, u, D, O, o, Y, s, H, $ fields try to parse the input as an IDC expression
+  and convert the result into the required value type
+
+  If the buffer for 'F' field contains filemasks and descriptions like this:
+    *.exe|Executable files,*.dll|Dll files
+  they will be used in the dialog box filter.
+
+  The hint message can be specified before the label enclosed in '#':
+
+  <#hint message#label:...>
+
+  Radiobuttons and checkboxes are represented by:
+
+  
+  >         - end of block
+
+  where valid types are C and R
+  (you may use lowercase 'c' and 'r' if you need to create two radiobutton
+  or checkbox groups on the same lines). The field ID of the whole group
+  can be specified between the brackets: ID>
+
+  field types           va_list parameter
+  -----------           -----------------
+
+  C - checkbox          ushort*                 bit mask of checkboxes
+  R - radiobutton       ushort*                 number of radiobutton
+
+  The group box title and hint messages can be specified like this:
+
+  <#item hint[#group box title[#group box hint]]#label:type>
+
+  The group box title and the group box hint can be specified only in the
+  first item of the box. If the item hint doesn't exist, it should be
+  specified as an empty hint (##title##).
+  The subsequent items can have an item hint only:
+
+  <#item hint#label:type>
+
+  Initial values of input fields are specified in the corresponding
+  input/output parameters (taken from va_list array).
+
+  OK, Cancel and (possibly) Help buttons are displayed at the bottom of
+  the dialog box automatically. Their captions can be changed by the BUTTON
+  keywords described at the beginning of this page.
+
+  Input field definition examples:
+
+   
+   <~A~nalysis enabled:C>
+   <~I~ndicator enabled:C>>
+   
+   <~O~utput file:f:1:64::>
+   <~O~utput directory:F::64::>
+
+  Resizable fields can be separated by splitters (GUI only).
+
+  A vertical splitter is represented by <|>. E.g.,:
+    <~Chooser~:E1:0:40:::><|><~E~ditor:t2:0:40:::>
+  whereas a horizontal splitter is represented by <->. E.g.,:
+    <~Chooser~:E1:0:40:::>
+    <->
+    <~E~ditor:t2:0:40:::>
+
+  It's also possible to organize fields by tabs (GUI only),
+  by adding a: <=:tab_label> after a series of fields. E.g.,:
+
+    >
+    <=:Tab with a checkbox>
+    
+    <=:Tab with numeric input>100>
+
+  (where the field ID for the tabbed widget is: 100)
+
+  
+*/ +///@{ +//---------------------------------------------------------------------- +// F O R M S - C O M P L E X D I A L O G B O X E S +//---------------------------------------------------------------------- + +/// See ask_form() + +inline int vask_form(const char *format, va_list va) +{ + return callui(ui_ask_form, format, va).i; +} + +/// Display a dialog box and wait for the user. +/// If the form contains the "BUTTON NO " keyword, then the return values +/// are the same as in the ask_yn() function (\ref ASKBTN_) +/// \param form dialog box as a string. see \ref FORM_C +/// \retval 0 no memory to display or form syntax error +/// (a warning is displayed in this case). +/// the user pressed the 'No' button (if the form has it) or +/// the user cancelled the dialog otherwise. +/// all variables retain their original values. +/// \retval 1 ok, all input fields are filled and validated. +/// \retval -1 the form has the 'No' button and the user cancelled the dialog + +inline int ask_form(const char *form, ...) +{ + va_list va; + va_start(va, form); + int code = vask_form(form, va); + va_end(va); + return code; +} + + +/// Create and/or activate dockable modeless form (::ui_open_form). +/// \param format string +/// \param flags \ref WIDGET_OPEN +/// \param va args +/// \return pointer to resulting TWidget + +inline TWidget *vopen_form(const char *format, uint32 flags, va_list va) +{ + return (TWidget *)callui(ui_open_form, format, flags, va).vptr; +} + + +/// Display a dockable modeless dialog box and return a handle to it. +/// The modeless form can be closed in the following ways: +/// - by pressing the small 'x' in the window title +/// - by calling form_actions_t::close() from the form callback +/// (\ref form_actions_t) +/// +/// \note pressing the 'Yes/No/Cancel' buttons does not close the modeless +/// form, except if the form callback explicitly calls close(). +/// \param form dialog box as a string. see \ref FORM_C +/// \param flags \ref WIDGET_OPEN +/// \return handle to the form or nullptr. +/// the handle can be used with TWidget functions: close_widget()/activate_widget()/etc + +inline TWidget *open_form(const char *form, uint32 flags, ...) +{ + va_list va; + va_start(va, flags); + TWidget *widget = vopen_form(form, flags, va); + va_end(va); + return widget; +} + +///@} FORM_C + + +/// Functions available from ::formchgcb_t. +/// For getters/setters for specific field values, see #DEF_SET_METHOD. +struct form_actions_t +{ + /// Get value of an input field. + /// \return false if no such field id or invalid field type (B) + virtual bool idaapi _get_field_value(int field_id, void *buf) = 0; + + /// Set value of an input field. + /// \return false if no such field id or invalid field type (B) + virtual bool idaapi _set_field_value(int field_id, const void *buf) = 0; + + /// Enable or disable an input field. + /// \return false if no such field id + virtual bool idaapi enable_field(int field_id, bool enable) = 0; + + /// Show or hide an input field. + /// \return false if no such field id + virtual bool idaapi show_field(int field_id, bool display) = 0; + + /// Move/Resize an input field. + /// Parameters specified as -1 are not modified. + /// \return false no such field id + virtual bool idaapi move_field(int field_id, int x, int y, int w, int h) = 0; + + /// Get currently focused input field. + /// \return -1 if no such field + virtual int idaapi get_focused_field() = 0; + + /// Set currently focused input field. + /// \return false if no such field id + virtual bool idaapi set_focused_field(int field_id) = 0; + + /// Refresh a field + virtual void idaapi refresh_field(int field_id) = 0; + + /// Close the form + virtual void idaapi close(int close_normally) = 0; + + /// Retrieve the user data specified through %* + virtual void *idaapi get_ud() = 0; + + /// Get value of an UTF-8 string input field. + /// \return false if no such field id or invalid field type (B) + virtual bool idaapi _get_str_field_value(int field_id, char *buf, const size_t bufsize) = 0; + +/// Helper to define functions in ::form_actions_t that get/set field values of different types. +/// Please see this file's source code for specific uses. +#define DEF_SET_METHOD(NAME, TYPE) \ + bool idaapi set_ ## NAME ## _value(int field_id, const TYPE *val) \ + { \ + return _set_field_value(field_id, val); \ + } +/// \copydoc DEF_SET_METHOD +#define DEF_FIELD_METHOD(NAME, TYPE) \ + bool idaapi get_ ## NAME ## _value(int field_id, TYPE *val) \ + { \ + return _get_field_value(field_id, val); \ + } \ + DEF_SET_METHOD(NAME, TYPE) +/// \copydoc DEF_SET_METHOD +#define DEF_STR_FIELD_METHOD(NAME ) \ + bool idaapi get_ ## NAME ## _value(int field_id, char *buf, const size_t bufsize) \ + { \ + return _get_str_field_value(field_id, buf, bufsize); \ + } \ + DEF_SET_METHOD(NAME, char) + + // get/set value of radio button (r, R) + DEF_FIELD_METHOD(radiobutton, ushort) + // get/set value of radio button group + DEF_FIELD_METHOD(rbgroup, ushort) + // get/set value of check box (c, C) + DEF_FIELD_METHOD(checkbox, ushort) + // get/set value of check box group + DEF_FIELD_METHOD(cbgroup, ushort) + // get/set value of color control (K) + DEF_FIELD_METHOD(color, bgcolor_t) + // get/set embedded chooser selected items (E) + DEF_FIELD_METHOD(chooser, sizevec_t) + // get/set value of editable combo box (b when field 'width' >0) + DEF_FIELD_METHOD(combobox, qstring) + // get/set selected item of read-only combo box (b when field 'width' ==0) + DEF_FIELD_METHOD(combobox, int) + // get/set value of multiline text input control (t) + DEF_FIELD_METHOD(text, textctrl_info_t) + // get/set text of buttons + DEF_FIELD_METHOD(button, qstring) + // get/set value of dynamic label (%) + DEF_STR_FIELD_METHOD(label) + // get/set string value (X, F, f) + DEF_STR_FIELD_METHOD(string) + // get/set path value (F, f) + DEF_STR_FIELD_METHOD(path) + // get/set string value (q, I, y, p) + DEF_FIELD_METHOD(string, qstring) + // get/set identifier value (I) + DEF_FIELD_METHOD(ident, qstring) + // get/set value of segment (S) + DEF_FIELD_METHOD(segment, sel_t) + // get/set signed value (n, D, O, Y, H) + DEF_FIELD_METHOD(signed, sval_t) + // get/set unsigned value (N, M) + DEF_FIELD_METHOD(unsigned, uval_t) + // get/set value of default base (usually hex) number (l) + DEF_FIELD_METHOD(int64, int64) + // get/set value of default base (usually hex) number (L) + DEF_FIELD_METHOD(uint64, uint64) + // get/set address value ($) + DEF_FIELD_METHOD(ea, ea_t) + // get/set tabbed widget current tab + DEF_FIELD_METHOD(current_tab, int) + +#undef DEF_FIELD_METHOD +#undef DEF_SET_METHOD +#undef DEF_STR_FIELD_METHOD + + enum dlgbtn_t + { + dbt_yes, + dbt_cancel, + dbt_no + }; + + // Enable or disable a standard dialog button (Yes, Cancel, None) + virtual bool enable_dialog_button(dlgbtn_t btn, bool enabled) = 0; + + // Get state of a standard button + virtual bool is_dialog_button_enabled(dlgbtn_t btn) = 0; + + // Update the list of available options for a combobox (b) + // + // Note: the contents of the `qstrvec_t` that was passed to ask_form/open_form will be modified + virtual void swap_combobox_choices(int fid, qstrvec_t &items) = 0; + + /// Set the label text of an input field. + /// If the text is empty, the label will be deleted. + /// Currently supports 'multi line text control' and 'chooser' field types. + /// GUI version only. + /// \return success + virtual bool idaapi set_field_label(int fid, const qstring &text) = 0; + + // Update tab attribute + enum tab_attr_t + { + ta_unknown = 0, + ta_visible, + ta_enabled, + }; + virtual bool idaapi set_tab_attribute(int fid, int tab, tab_attr_t attr, const void *in) = 0; + virtual bool idaapi get_tab_attribute(void *out, int fid, int tab, tab_attr_t attr) = 0; + inline bool set_tab_visible(int fid, int tab, bool visible) + { + return set_tab_attribute(fid, tab, ta_visible, &visible); + } + inline bool is_tab_visible(bool *out, int fid, int tab) + { + return get_tab_attribute(out, fid, tab, ta_visible); + } + inline bool set_tab_enabled(int fid, int tab, bool enabled) + { + return set_tab_attribute(fid, tab, ta_enabled, &enabled); + } + inline bool is_tab_enabled(bool *out, int fid, int tab) + { + return get_tab_attribute(out, fid, tab, ta_enabled); + } +}; + + +/// Callback. Called when an input field is modified. +/// The callback will be also called before displaying the form and as soon +/// as the user presses the 'Yes/No/Cancel' buttons or closes the window. +/// The callback will be called for both modal and modeless window. +/// \param field_id id of the modified field or a special field id \ref CB_ +/// \param fa helper class with useful virtual functions +/// \retval 0 continue editing +/// \retval >0 form will be closed +/// \note the return value is used by IDA only for modal forms, and only for +/// the following special field ids: CB_CLOSE, CB_YES, CB_NO, CB_CANCEL +/// (for modeless forms CB_CLOSE is also used). + +typedef int idaapi formchgcb_t(int field_id, form_actions_t &fa); + + +/// Callback. Called when a button is clicked. +/// \param button_code button code as specified in the form +/// \param fa helper class with useful virtual functions +/// \retval 0 currently ignored + +typedef int idaapi buttoncb_t(int button_code, form_actions_t &fa); + + +#endif // SWIG + +//--------------------------------------------------------------------------- +// Y E S / N O D I A L O G B O X +//--------------------------------------------------------------------------- + +/// \defgroup ASKBTN_ Button IDs +/// used by ask_yn() and ask_buttons() +///@{ +#define ASKBTN_YES 1 ///< Yes button +#define ASKBTN_NO 0 ///< No button +#define ASKBTN_CANCEL -1 ///< Cancel button +#define ASKBTN_BTN1 1 ///< First (Yes) button +#define ASKBTN_BTN2 0 ///< Second (No) button +#define ASKBTN_BTN3 -1 ///< Third (Cancel) button +///@} + + +THREAD_SAFE AS_PRINTF(5, 0) inline int vask_buttons( + const char *Yes, + const char *No, + const char *Cancel, + int deflt, + const char *format, + va_list va) +{ + return callui(ui_ask_buttons, Yes, No, Cancel, deflt, format, va).i; +} + + +AS_PRINTF(2, 0) inline int vask_yn(int deflt, const char *format, va_list va) +{ + return vask_buttons(nullptr, nullptr, nullptr, deflt, format, va); +} + + +/// Display a dialog box and get choice from "Yes", "No", "Cancel". +/// \param deflt default choice: one of \ref ASKBTN_ +/// \param format The question in printf() style format +/// \return the selected button (one of \ref ASKBTN_). Esc key returns #ASKBTN_CANCEL. + +AS_PRINTF(2, 3) inline int ask_yn(int deflt, const char *format, ...) +{ + va_list va; + va_start(va, format); + int code = vask_yn(deflt, format, va); + va_end(va); + return code; +} + + +/// Display a dialog box and get choice from maximum three possibilities (::ui_ask_buttons). +/// \note for all buttons: +/// - use "" or nullptr to take the default name for the button. +/// - prepend "HIDECANCEL\n" in 'format' to hide the Cancel button +/// +/// \param Yes text for the first button +/// \param No text for the second button +/// \param Cancel text for the third button +/// \param deflt default choice: one of \ref ASKBTN_ +/// \param format printf-style format string for question. It may have some prefixes, see below. +/// \param va parameters for the format string +/// \return one of \ref ASKBTN_ specifying the selected button (Esc key returns Cancel/3rd button value) + +AS_PRINTF(5, 6) inline int ask_buttons( + const char *Yes, + const char *No, + const char *Cancel, + int deflt, + const char *format, + ...) +{ + va_list va; + va_start(va, format); + int code = vask_buttons(Yes, No, Cancel, deflt, format, va); + va_end(va); + return code; +} + +//------------------------------------------------------------------------ +/* Format of dialog box (actually they are mutliline strings + delimited by newline characters) + + The very first line of dialog box can specify a dialog box + title if the following line appears: + + TITLE title string + + + Then, the next line may contain an icon to display + in the GUI version (ignored by the text version): + + ICON NONE (no icon) + INFO (information icon) + QUESTION (question icon) + WARNING (warning icon) + ERROR (error icon) + + + Then, the next line may contain a 'Don't display this message again' + checkbox. If this checkbox is selected and the user didn't select cancel, + the button he selected is saved and automatically returned. + + AUTOHIDE NONE (no checkbox) + DATABASE (return value is saved to database) + REGISTRY (return value is saved to Windows registry or idareg.cfg + if non-Windows version) + SESSION (return value is saved for the current IDA session) + It is possible to append "*" to the AUTOHIDE keywords to have this checkbox + initially checked. For example: "AUTOHIDE REGISTRY*" + + To hide the cancel button the following keyword can be used: + + HIDECANCEL + + To enable rich text (i.e., HTML) in the Qt version of IDA, + the following keyword can be used: + + RICHTEXT + + Please note that the user still can cancel the dialog box by pressing Esc + or clicking on the 'close window' button. + + Finally, if the dialog box is kept in IDA.HLP, the following may appear + to add a Help button (this defines help context for the whole dialog box): + + @hlpMessageName[] + + + Each keyword should be alone on a line. + + Next, a format string must be specified. + To center message lines in the text version, start them with '\3' character + (currently ignored in the GUI version). +*/ + +//--------------------------------------------------------------------------- +// A S K S T R I N G O F T E X T +//--------------------------------------------------------------------------- + +/// Display a dialog box and wait for the user to input a text string (::ui_ask_str). +/// Use this function to ask one-line text. For multiline input, use ask_text(). +/// This function will trim the trailing spaces. +/// \param str qstring to fill. Can contain the default value. Cannot be nullptr. +/// \param hist category of history lines. an arbitrary number. \n +/// this number determines lines accessible in the history \n +/// of the user input (when he presses down arrow) \n +/// One of \ref HIST_ should be used here +/// \param format printf() style format string with the question +/// \param va pointer to variadic arguments. +/// \return false if the user cancelled the dialog, otherwise returns true. + +AS_PRINTF(3, 0) inline bool vask_str( + qstring *str, + int hist, + const char *format, + va_list va) +{ + return callui(ui_ask_str, str, hist, format, va).cnd; +} + +AS_PRINTF(3, 4) inline bool ask_str(qstring *str, int hist, const char *format, ...) +{ + va_list va; + va_start(va, format); + bool result = vask_str(str, hist, format, va); + va_end(va); + return result; +} + +/// \defgroup HIST_ Input line history constants +/// passed as 'hist' parameter to ask_str() +///@{ +#define HIST_SEG 1 ///< segment names +#define HIST_CMT 2 ///< comments +#define HIST_SRCH 3 ///< search substrings +#define HIST_IDENT 4 ///< identifiers. usually CPU register names are forbidden +#define HIST_FILE 5 ///< file names +#define HIST_TYPE 6 ///< type declarations +#define HIST_CMD 7 ///< commands +#define HIST_DIR 8 ///< directory names (text version only) +#define HIST_IDENT2 9 ///< identifiers, including CPU register names +///@} + + +/// Display a dialog box and wait for the user to input an identifier. +/// If the user enters a non-valid identifier, this function displays a warning +/// and allows the user to correct it. CPU register names are usually forbidden. +/// \param str qstring to fill. Can contain the default value. Cannot be nullptr. +/// \param format printf() style format string with the question +/// \return false if the user cancelled the dialog, otherwise returns true. + +AS_PRINTF(2, 3) inline bool ask_ident(qstring *str, const char *format, ...) +{ + va_list va; + va_start(va, format); + bool result = vask_str(str, HIST_IDENT, format, va); + va_end(va); + return result; +} + +/// Display a dialog box and wait for the user to input an identifier. +/// If the user enters a non-valid identifier, this function displays a warning +/// and allows the user to correct it. CPU register names are permitted. +/// \param str qstring to fill. Can contain the default value. Cannot be nullptr. +/// \param format printf() style format string with the question +/// \return false if the user cancelled the dialog, otherwise returns true. + +AS_PRINTF(2, 3) inline bool ask_ident2(qstring *str, const char *format, ...) +{ + va_list va; + va_start(va, format); + bool result = vask_str(str, HIST_IDENT2, format, va); + va_end(va); + return result; +} + + + +/// Display a dialog box and wait for the user to input multiline text (::ui_ask_text). +/// \param answer output buffer +/// \param max_size maximum size of text in bytes including terminating zero (0 for unlimited) +/// \param defval default value. will be displayed initially in the input line. +/// may be nullptr. +/// \param format printf() style format string with the question. +/// the following options are accepted at its beginning: +/// "ACCEPT TABS\n": accept tabulations in the input +/// "NORMAL FONT\n": use regular font (otherwise the notepad font) +/// \param va pointer to variadic arguments. +/// \return false-if the user pressed Esc, otherwise returns true. + +AS_PRINTF(4, 0) inline bool vask_text( + qstring *answer, + size_t max_size, + const char *defval, + const char *format, + va_list va) +{ + return callui(ui_ask_text, answer, max_size, defval, format, va).cnd; +} + +AS_PRINTF(4, 5) inline bool ask_text( + qstring *answer, + size_t max_size, + const char *defval, + const char *format, + ...) +{ + va_list va; + va_start(va, format); + bool result = vask_text(answer, max_size, defval, format, va); + va_end(va); + return result; +} + + +//--------------------------------------------------------------------------- +// A S K A D D R E S S E S , N A M E S , N U M B E R S , E T C . +//--------------------------------------------------------------------------- + +/// Display a dialog box and wait for the user to input a file name (::ui_ask_file). +/// This function displays a window with file names present in the directory +/// pointed to by 'defval'. +/// +/// The 'format' parameter can contain a 'FILTER' description, of the +/// form 'description1|{wildcard1}+|...|descriptionN|{wildcardsN}+', +/// where each file type description has a corresponding set of one +/// (or more) ';'-separated mask(s). E.g., +/// \code +/// Text files|*.txt|Executable files|*.exe;*.bin +/// \endcode +/// +/// \param for_saving will the filename be used to save a file? +/// \param defval default value. will be displayed initially in the input line. +/// may be nullptr may be or a wildcard file name. +/// \param format printf-style format string with the dialog title, possibly including a filter. +/// \param va pointer to variadic arguments. +/// \return nullptr the user cancelled the dialog. +/// Otherwise the user entered a valid file name. + +AS_PRINTF(3, 0) inline char *vask_file( + bool for_saving, + const char *defval, + const char *format, + va_list va) +{ + return callui(ui_ask_file, for_saving, defval, format, va).cptr; +} + + +AS_PRINTF(3, 4) inline char *ask_file( + bool for_saving, + const char *defval, + const char *format, + ...) +{ + va_list va; + va_start(va, format); + char *answer = vask_file(for_saving, defval, format, va); + va_end(va); + return answer; +} + + +//--------------------------------------------------------------------------- +// A D D - O N S +//--------------------------------------------------------------------------- + +/// Information about an installed add-on (e.g. a plugin) +struct addon_info_t +{ + size_t cb; //< size of this structure + const char *id; //< product code, e.g. "com.hexrays.hexx86w". Must be unique + const char *name; //< descriptive name, e.g. "Hex-Rays x86 Decompiler (Windows)" + const char *producer; //< e.g. "Hex-Rays SA" + const char *version; //< version string, e.g. 1.5.110408 + const char *url; //< URL of the product http://www.hex-rays.com/decompiler.shtml + const char *freeform; //< any string, e.g. "Copyright (c) 2007-2025 Hex-Rays" + const void *custom_data; //< custom data (license ID etc). Can be nullptr. Not displayed in UI. + size_t custom_size; + + /// Constructor + addon_info_t() + : cb(sizeof(addon_info_t)), + id(nullptr), + name(nullptr), + producer(nullptr), + version(nullptr), + url(nullptr), + freeform(nullptr), + custom_data(nullptr), + custom_size(0) + {} +}; + +#ifndef __UI__ + +/// \defgroup ui_addons_funcs Functions: add-ons +/// Convenience functions for ::ui_addons +///@{ + +/// Register an add-on. Show its info in the About box. +/// For plugins, should be called from init() function +/// (repeated calls with the same product code overwrite previous entries) +/// returns: index of the add-on in the list, or -1 on error + +inline int register_addon(const addon_info_t *info) +{ + return callui(ui_addons, 0, info).i; +} + + +/// Get number of installed addons + +inline int addon_count() +{ + return callui(ui_addons, 1).i; +} + + +/// Get info about a registered addon with a given product code. +/// info->cb must be valid! +/// NB: all pointers are invalidated by next call to register_addon or get_addon_info +/// \return false if not found + +inline bool get_addon_info(const char *id, addon_info_t *info) +{ + return callui(ui_addons, 2, id, info).cnd; +} + + +/// Get info about a registered addon with specific index. +/// info->cb must be valid! +/// NB: all pointers are invalidated by next call to register_addon or get_addon_info +/// \return false if index is out of range + +inline bool get_addon_info_idx(int index, addon_info_t *info) +{ + return callui(ui_addons, 3, index, info).cnd; +} + +///@} ui_addons_funcs + +#endif + +//--------------------------------------------------------------------------- +// S T R I N G F U N C T I O N S +//--------------------------------------------------------------------------- +/// \defgroup str_funcs Functions: strings +/// functions that manipulate strings +///@{ + +/// Add space characters to the colored string so that its length will be at least +/// 'len' characters. Don't trim the string if it is longer than 'len'. +/// \param str pointer to colored string to modify (may not be nullptr) +/// \param bufsize size of the buffer with the string +/// \param len the desired length of the string +/// \return pointer to the end of input string + +idaman THREAD_SAFE char *ida_export add_spaces(char *str, size_t bufsize, ssize_t len); + + +/// Remove trailing space characters from a string. +/// \param str pointer to string to modify (may be nullptr) +/// \return pointer to input string + +idaman THREAD_SAFE char *ida_export trim(char *str); + + +/// Skip whitespaces in the string. +/// \return pointer to first non-whitespace char in given string + +idaman THREAD_SAFE const char *ida_export skip_spaces(const char *ptr); +inline char *skip_spaces(char *ptr) ///< \copydoc skip_spaces() + { return CONST_CAST(char*)(skip_spaces((const char *)ptr)); } + +/// Map strings to integer values - see strarray() +struct strarray_t +{ + int code; + const char *text; +}; + + +/// \defgroup CLNL_ line cleanup flags +/// Passed as 'flags' parameter to qcleanline() +///@{ +#define CLNL_RTRIM (1 << 0) ///< Remove trailing space characters. +#define CLNL_LTRIM (1 << 1) ///< Remove leading space characters. +#define CLNL_FINDCMT (1 << 2) ///< Search for the comment symbol everywhere in the line, not only at the beginning + +#define CLNL_TRIM (CLNL_RTRIM|CLNL_LTRIM) +///@} + +/// Performs some cleanup operations to a line. +/// \param buf string to modify +/// \param cmt_char character that denotes the start of a comment: +/// - the entire text is removed if the line begins with +/// this character (ignoring leading spaces) +/// - all text after (and including) this character is removed +/// if flag CLNL_FINDCMT is set +/// +/// \param flags a combination of \ref CLNL_. defaults to CLNL_TRIM +/// \return length of line + +idaman THREAD_SAFE ssize_t ida_export qcleanline( + qstring *buf, + char cmt_char='\0', + uint32 flags=CLNL_TRIM|CLNL_FINDCMT); + + +/// Find a line with the specified code in the ::strarray_t array. +/// If the last element of the array has code==0 then it is considered as the default entry. \n +/// If no default entry exists and the code is not found, strarray() returns "". + +idaman THREAD_SAFE const char *ida_export strarray(const strarray_t *array, size_t array_size, int code); + + +/// Convert linear address to UTF-8 string + +idaman size_t ida_export ea2str(char *buf, size_t bufsize, ea_t ea); + +//--------------------------------------------------------------------------- +// C O N V E R S I O N S +//--------------------------------------------------------------------------- +/// \defgroup conv Functions: string conversion +/// functions that convert between string encodings +///@{ + +/// Convert linear address to UTF-8 string +inline bool ea2str(qstring *out, ea_t ea) +{ + char tmp[MAXSTR]; + if ( ea2str(tmp, sizeof(tmp), ea) <= 0 ) + return false; + *out = tmp; + return true; +} + + +/// Convert string to linear address. +/// Tries to interpret the string as: \n +/// 1) "current IP" keyword if supported by assembler (e.g. "$" in x86) \n +/// 2) segment:offset expression, where "segment" may be a name or a fixed segment register (e.g. cs, ds) \n +/// 3) just segment name/register (translated to segment's start address) \n +/// 4) a name in the database (or debug name during debugging) \n +/// 5) hexadecimal value without prefix or suffix \n +/// 6) +delta or -delta, where numerical 'delta' is added to or subtracted from 'screen_ea' \n +/// 7) register name (only during debugging) \n +/// 8) if all else fails, try to evaluate 'str' as an IDC expression +/// +/// \param out[out] the buffer to put the result +/// \param str string to parse +/// \param screen_ea the current address in the disassembly/pseudocode view +/// \return success + +idaman bool ida_export str2ea(ea_t *out, const char *str, ea_t screen_ea=BADADDR); + + +/// Same as str2ea() but possibly with some steps skipped. +/// \param[out] out the buffer to put the result +/// \param str string to parse +/// \param screen_ea the current address in the disassembly/pseudocode view +/// \param flags see \ref S2EAOPT_ +/// \return success + +idaman bool ida_export str2ea_ex(ea_t *out, const char *str, ea_t screen_ea=BADADDR, int flags=0); + +/// \defgroup S2EAOPT_ String to address conversion flags +/// passed as 'flags' parameter to str2ea_ex() +///@{ +#define S2EAOPT_NOCALC 0x00000001 ///< don't try to interpret string as IDC (or current extlang) expression +///@} + +/// Convert a number in C notation to an address. +/// decimal: 1234 \n +/// octal: 0123 \n +/// hexadecimal: 0xabcd \n +/// binary: 0b00101010 +/// \param[out] out the buffer to put the result +/// \param str the string to parse + +idaman bool ida_export atoea(ea_t *out, const char *str); + +#ifndef SWIG + +/// Convert segment selector to UTF-8 string + +idaman size_t ida_export stoa(qstring *out, ea_t from, sel_t seg); + +/// Convert UTF-8 string to segment selector. +/// \retval 0 fail +/// \retval 1 ok (hex) +/// \retval 2 ok (segment name or reg) + +idaman int ida_export atos(sel_t *seg, const char *str); + + +#define MAX_NUMBUF (128+8) ///< 16-byte value in binary base (0b00101010...) + + +/// Get the number of UTF-8 characters required to represent +/// a number with the specified number of bytes and radix. +/// \param nbytes if 0, use default number of bytes, usually 4 or 8 depending on __EA64__ +/// \param radix if 0, use default radix, usually 16 + +idaman size_t ida_export b2a_width(int nbytes, int radix); + + +/// Convert number to UTF-8 string (includes leading zeroes). +/// \param x value to convert +/// \param buf output buffer +/// \param bufsize size of output buffer +/// \param nbytes 1, 2, 3, or 4 +/// \param radix 2, 8, 10, or 16 +/// \return size of resulting string + +idaman size_t ida_export b2a32(char *buf, size_t bufsize, uint32 x, int nbytes, int radix); + + +/// Same as b2a32(), but can handle 'nbytes' = 8 + +idaman size_t ida_export b2a64(char *buf, size_t bufsize, uint64 x, int nbytes, int radix); + + + +/// Get max number of UTF-8 characters required to represent +/// a given type of value, with a given size (without leading zeroes). +/// \param nbytes size of number +/// \param flag should be one of FF_ for #MS_0TYPE +/// \param n if 1, shr 'flag' by 4 + +idaman size_t ida_export btoa_width(int nbytes, flags64_t flag, int n); + + +/// Same as b2a32(), but will generate a string without any leading zeroes. +/// Can be used to output some numbers in the instructions. + +idaman size_t ida_export btoa32(char *buf, size_t bufsize, uint32 x, int radix=0); + + +/// 64-bit equivalent of btoa32() + +idaman size_t ida_export btoa64(char *buf, size_t bufsize, uint64 x, int radix=0); + + +/// 128-bit equivalent of btoa32() + +idaman size_t ida_export btoa128(char *buf, size_t bufsize, uint128 x, int radix=0); + +#ifdef __EA64__ +#define b2a b2a64 +#define btoa btoa64 +#define atob atob64 +#else +#define b2a b2a32 ///< shortcut for number->string conversion, see b2a32() +#define btoa btoa32 ///< shortcut for number->string conversion, see btoa32() +#define atob atob32 ///< shortcut for string->number conversion, see atob32() +#endif + + +/// Convert instruction operand immediate number to UTF-8. +/// This is the main function to output numbers in the instruction operands. \n +/// It prints the number with or without the leading zeroes depending on the flags. \n +/// This function is called from out_value(). Please use out_value() if you can. + +idaman size_t ida_export numop2str( + char *buf, + size_t bufsize, + ea_t ea, + int n, + uint64 x, + int nbytes, + int radix=0); + + +/// Convert UTF-8 to a number using the current assembler formats. +/// e.g. for ibmpc, '12o' is octal, '12h' is hex, etc. +/// \return success + +idaman bool ida_export atob32(uint32 *x, const char *str); + + +/// 64-bit equivalent of atob32() + +idaman bool ida_export atob64(uint64 *x, const char *str); // returns 1-ok + + +/// Pretty-print a size. +/// The output is concise and easy to grasp, like 23k, 1.2G, or 8.56T. +/// Values >= 1000TB are printed as "infty". +/// \param buf the output buffer +/// \param bufsize size of the output buffer. 8 is enough. +/// \param value value to print +/// \return number of bytes stored into buf. +/// if the buffer is too small, the output will be truncated. + +idaman size_t ida_export pretty_print_size(char *buf, size_t bufsize, uint64 value); + +/// Parse a pretty-printed size. +/// This performs the reverse operation of pretty_print_size() +/// \param out the resulting value +/// \param in the formatted string +/// \return success + +idaman bool ida_export parse_pretty_size(uint64 *out, const char *in); + + +/// Auxiliary function. +/// Print displacement to a name (+disp or -disp) in the natural radix +/// \param buf output buffer to append to +/// \param disp displacement to output. 0 leads to no modifications +/// \param tag whether to output color tags + +idaman void ida_export append_disp(qstring *buf, adiff_t disp, bool tag=true); + + +/// Convert RADIX50 -> UTF-8. +/// \param p pointer to UTF-8 string +/// \param r pointer to radix50 string +/// \param k number of elements in the input string \n +/// (element of radix50 string is a word) \n +/// (element of UTF-8 string is a character) +/// \return number of elements left unprocessed in the input string, \n +/// because the input string contains unconvertible elements. \n +/// 0-ok, all elements are converted + +idaman THREAD_SAFE int ida_export r50_to_asc(char *p, const ushort *r, int k); + + +/// Convert UTF-8 -> RADIX50 (see r50_to_asc()) + +int THREAD_SAFE asc_to_r50(ushort *r, const char *p, int k); + + +///@} Conversion functions +///@} String functions + +//---------------------------------------------------------------------------- +/// \cond +#define IS_BUTTONCB_T(v) (std::is_same<decltype(v), buttoncb_t>::value) +#define IS_FORMCHGCB_T(v) (std::is_same<decltype(v), formchgcb_t>::value) +#define IS_TEXTCTRL_INFO_T(v) (std::is_base_of<textctrl_info_t, std::remove_reference<decltype(v)>::type>::value) +#define IS_CHOOSER_BASE_T(v) (std::is_base_of<chooser_base_t, std::remove_reference<decltype(v)>::type>::value) + +/// \endcond + +#endif // SWIG + +//---------------------------------------------------------------------------- +/// \defgroup winkeys Compatibility Windows virtual keys +/// compatibility windows virtual keys to use in plugins which are not Qt aware. (check the #CVH_QT_AWARE flag) +/// these keys are provided for compilation of older plugins that use windows virtual keys on all platforms. +/// those constants are currently passed to cli_t->keydown and customview/CVH_KEYDOWN handlers. +///@{ +#define IK_CANCEL 0x03 +#define IK_BACK 0x08 +#define IK_TAB 0x09 +#define IK_CLEAR 0x0C +#define IK_RETURN 0x0D +#define IK_SHIFT 0x10 +#define IK_CONTROL 0x11 +#define IK_MENU 0x12 +#define IK_PAUSE 0x13 +#define IK_CAPITAL 0x14 +#define IK_KANA 0x15 +#define IK_ESCAPE 0x1B +#define IK_MODECHANGE 0x1F +#define IK_SPACE 0x20 +#define IK_PRIOR 0x21 +#define IK_NEXT 0x22 +#define IK_END 0x23 +#define IK_HOME 0x24 +#define IK_LEFT 0x25 +#define IK_UP 0x26 +#define IK_RIGHT 0x27 +#define IK_DOWN 0x28 +#define IK_SELECT 0x29 +#define IK_PRINT 0x2A +#define IK_EXECUTE 0x2B +#define IK_SNAPSHOT 0x2C +#define IK_INSERT 0x2D +#define IK_DELETE 0x2E +#define IK_HELP 0x2F +#define IK_LWIN 0x5B +#define IK_RWIN 0x5C +#define IK_APPS 0x5D +#define IK_SLEEP 0x5F +#define IK_NUMPAD0 0x60 +#define IK_NUMPAD1 0x61 +#define IK_NUMPAD2 0x62 +#define IK_NUMPAD3 0x63 +#define IK_NUMPAD4 0x64 +#define IK_NUMPAD5 0x65 +#define IK_NUMPAD6 0x66 +#define IK_NUMPAD7 0x67 +#define IK_NUMPAD8 0x68 +#define IK_NUMPAD9 0x69 +#define IK_MULTIPLY 0x6A +#define IK_ADD 0x6B +#define IK_SEPARATOR 0x6C +#define IK_SUBTRACT 0x6D +#define IK_DECIMAL 0x6E +#define IK_DIVIDE 0x6F +#define IK_F1 0x70 +#define IK_F2 0x71 +#define IK_F3 0x72 +#define IK_F4 0x73 +#define IK_F5 0x74 +#define IK_F6 0x75 +#define IK_F7 0x76 +#define IK_F8 0x77 +#define IK_F9 0x78 +#define IK_F10 0x79 +#define IK_F11 0x7A +#define IK_F12 0x7B +#define IK_F13 0x7C +#define IK_F14 0x7D +#define IK_F15 0x7E +#define IK_F16 0x7F +#define IK_F17 0x80 +#define IK_F18 0x81 +#define IK_F19 0x82 +#define IK_F20 0x83 +#define IK_F21 0x84 +#define IK_F22 0x85 +#define IK_F23 0x86 +#define IK_F24 0x87 +#define IK_NUMLOCK 0x90 +#define IK_SCROLL 0x91 +#define IK_OEM_FJ_MASSHOU 0x93 +#define IK_OEM_FJ_TOUROKU 0x94 +#define IK_LSHIFT 0xA0 +#define IK_RSHIFT 0xA1 +#define IK_LCONTROL 0xA2 +#define IK_RCONTROL 0xA3 +#define IK_LMENU 0xA4 +#define IK_RMENU 0xA5 +#define IK_BROWSER_BACK 0xA6 +#define IK_BROWSER_FORWARD 0xA7 +#define IK_BROWSER_REFRESH 0xA8 +#define IK_BROWSER_STOP 0xA9 +#define IK_BROWSER_SEARCH 0xAA +#define IK_BROWSER_FAVORITES 0xAB +#define IK_BROWSER_HOME 0xAC +#define IK_VOLUME_MUTE 0xAD +#define IK_VOLUME_DOWN 0xAE +#define IK_VOLUME_UP 0xAF +#define IK_MEDIA_NEXT_TRACK 0xB0 +#define IK_MEDIA_PREV_TRACK 0xB1 +#define IK_MEDIA_STOP 0xB2 +#define IK_MEDIA_PLAY_PAUSE 0xB3 +#define IK_LAUNCH_MAIL 0xB4 +#define IK_LAUNCH_MEDIA_SELECT 0xB5 +#define IK_LAUNCH_APP1 0xB6 +#define IK_LAUNCH_APP2 0xB7 +#define IK_OEM_1 0xBA +#define IK_OEM_PLUS 0xBB +#define IK_OEM_COMMA 0xBC +#define IK_OEM_MINUS 0xBD +#define IK_OEM_PERIOD 0xBE +#define IK_OEM_2 0xBF +#define IK_OEM_3 0xC0 +#define IK_OEM_4 0xDB +#define IK_OEM_5 0xDC +#define IK_OEM_6 0xDD +#define IK_OEM_7 0xDE +#define IK_OEM_102 0xE2 +#define IK_PLAY 0xFA +#define IK_ZOOM 0xFB +#define IK_OEM_CLEAR 0xFE +///@} + +/// \defgroup CB_ Form callback special values +///@{ +enum cb_id +{ + CB_INIT = -1, + CB_YES = -2, // the user pressed 'Yes' button + CB_CLOSE = -3, // the form is closed by the window manager + // (usually by pressing the small 'x' at the title) + CB_INVISIBLE = -4, // corresponds to ui_widget_invisible + CB_DESTROYING = -5, // the actual widget tree is being destroyed + CB_NO = -6, // the user pressed 'No' button + CB_CANCEL = -7, // the user pressed 'Cancel' button or Esc +}; +///@} + +#ifndef SWIG +//------------------------------------------------------------------------- +inline void place_t__serialize(const place_t *_this, bytevec_t *out) +{ + out->pack_dd(_this->lnnum); +} + +//------------------------------------------------------------------------- +inline bool place_t__deserialize(place_t *_this, const uchar **pptr, const uchar *end) +{ + if ( *pptr >= end ) + return false; + _this->lnnum = unpack_dd(pptr, end); + return true; +} +#endif + +#ifndef NO_OBSOLETE_FUNCS +/// Deprecated. Please use ACTION_DESC_LITERAL_* instead. +#define ACTION_DESC_LITERAL(name, label, handler, shortcut, tooltip, icon)\ + { sizeof(action_desc_t), name, label, handler, &PLUGIN, shortcut, tooltip, icon, ADF_OT_PLUGIN } + +DEPRECATED inline void get_user_strlist_options(strwinsetup_t *out) +{ + callui(ui_obsolete_get_user_strlist_options, out); +} +DEPRECATED inline bool del_idc_hotkey(const char *hotkey) +{ + return callui(ui_obsolete_del_idckey, hotkey).cnd; +} +idaman DEPRECATED void ida_export ida_checkmem(const char *file, int line); +#endif + +#endif // __KERNWIN_HPP + +``` + +`IdaSDK/lex.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef LEX_HPP +#define LEX_HPP + +#include <ieee.h> + +/*! \file lex.hpp + + \brief Tools for parsing C-like input + + Functions in this file use objects of opaque type lexer_t. + To create a lexer_t instance, use create_lexer(). +*/ + +typedef ushort lxtype; ///< see \ref lx_ +typedef uint32 callcnv_t; + +/// \defgroup lx_ Parser token types +/// See token_t::type +/// \note All separators have their ASCII codes as lxtype +///@{ +const lxtype + lx_end = 1, ///< no more tokens + lx_ident = 2, ///< ident + lx_number = 3, ///< long constant + lx_string = 4, ///< string constant (token_t.chr != 0 => unicode string) + lx_char = 5, ///< char constant + lx_typename = 6, ///< user-defined type + lx_float = 7, ///< IEEE floating point constant + lx_int64 = 8, ///< int64 constant + lx_callcnv = 9, ///< custom calling convention + lx_key = 128; ///< keywords start. All keys are lx_key + keynum. \n + ///< Two-char separators are: (c1 + (c2 << 8)). \n + ///< Three-char separators: + ///< - "<<=" = ('<' + ('<'<<8)) + '=' + ///< - ">>=" = ('>' + ('>'<<8)) + '=' +///@} + + +/// Parser token +struct token_t +{ + qstring str; ///< idents & strings + lxtype type = 0; ///< see \ref lx_ + sval_t num = 0; ///< long & char constants + union + { + bool unicode = false; ///< (::lx_string: != 0 => unicode string) + bool is_unsigned; ///< (::lx_number, ::lx_int64: != 0 => unsigned value) + }; + union + { + fpvalue_t fnum; ///< floating point constant + int64 i64; ///< ::lx_int64 + callcnv_t callcnv; ///< ::lx_callcnv + }; + token_t() : fnum() {} +}; +DECLARE_TYPE_AS_MOVABLE(token_t); + +class lexer_t; // lexical analyzer, opaque structure + + +/// Preprocessor callback for unknown tokens. +/// Will be called when preprocessor calculates the value of #if expression. + +typedef error_t lx_resolver_t(lexer_t *lx, void *ud, token_t *curtok, sval_t *res); + +// Conversion from 'type1 ' to 'type_2' is sign-extended. This may cause unexpected runtime behavior. +// We want this sign-extension to happen, since it comes mostly from HANDLEs. +// (see https://msdn.microsoft.com/en-us/library/ms235307.aspx ) +MSC_DIAG_OFF(4826) + +/// Preprocessor cast +struct cast_t +{ + bool is_unsigned; + int size; + + cast_t() + { + reset(); + } + void reset(void) + { + is_unsigned = false; + size = 0; + } +}; + +struct lex_value_t +{ + bool is_unsigned; + union + { + int64 val; + uint64 uval; + }; + + lex_value_t() + { + reset(); + } + void set(const lex_value_t &v) + { + set_val(v.val, v.is_unsigned); + } + void reset(void) + { + set_val(0, true); + } + void set_val(int64 v, bool _is_unsigned) + { + is_unsigned = _is_unsigned; + val = v; + } + + uint64 get_uval(void) const + { + return val; + } + int64 get_val(void) const + { + return val; + } + + bool is_zero(void) const + { + return get_val() == 0; + } + + void perform_cast(const cast_t &cast); + + void unary_minus(const lex_value_t &v); + void unary_plus(const lex_value_t &v); + void unary_not(const lex_value_t &v); + void bitwise_not(const lex_value_t &v); + + void mul(const lex_value_t &v); + void div(const lex_value_t &v); + void mod(const lex_value_t &v); + void add(const lex_value_t &v); + void sub(const lex_value_t &v); + + void shift_right(const lex_value_t &v); + void shift_left(const lex_value_t &v); + void bitwise_and(const lex_value_t &v); + void bitwise_xor(const lex_value_t &v); + void bitwise_or(const lex_value_t &v); + void logical_and(const lex_value_t &v); + void logical_or(const lex_value_t &v); + + void cmpge(const lex_value_t &v); + void cmple(const lex_value_t &v); + void cmplt(const lex_value_t &v); + void cmpgt(const lex_value_t &v); + void cmpeq(const lex_value_t &v); + void cmpneq(const lex_value_t &v); +}; + +MSC_DIAG_ON(4826) + +/// Preprocessor callbacks for casts. +/// Will be called when preprocessor calculates the value of #if expression. + +typedef error_t lx_parse_cast_t(lexer_t *lx, cast_t *cast, token_t *ct); + +/// Preprocessor callback. +/// It will be called for each input line. +/// \return an error code (0-ok) + +typedef int idaapi lx_preprocessor_cb(void *ud, const char *fname, int nl, const char *line); + + +/// Callback for #pragma directives. +/// \return an error code (0-ok) + +typedef int idaapi lx_pragma_cb(void *ud, const char *line); + + +/// Callback for #warning directives. +/// \return an error code (0-ok) + +typedef int idaapi lx_warning_cb(void *ud, const char *line); + + +/// Callback for #define directives +/// \return an error code (0-ok) + +typedef int idaapi lx_macro_cb( + void *ud, + const char *name, + const char *body, + int nargs, + bool isfunc, + bool is_new_macro); + + +/// Callback for #undef directives +/// \return an error code (0-ok) + +typedef int idaapi lx_undef_cb(void *ud, const char *name); + +#define LEX_MACRO_SYSTEM 1 ///< define system macros such as `__LINUX__` + +/// Create new lexical analyzer and set its keyword table. +/// If keys==nullptr, then set the default C keyword table + +idaman lexer_t *ida_export create_lexer( + const char *const *keys, + size_t size, + void *ud=nullptr, + uint32 macro_flags=0); + + +/// Destroy a lexical analyzer + +idaman void ida_export destroy_lexer(lexer_t *lx); + + +/// Define a macro + +idaman error_t ida_export lex_define_macro( + lexer_t *lx, + const char *macro, + const char *body, + int nargs=0, + bool isfunc=false); + +/// Undefine a macro +/// \param lx the lexer object +/// \param macro macro to undefine + +idaman void ida_export lex_undefine_macro( + lexer_t *lx, + const char *macro); + +/// Set lexer options. +/// \param lx the lexer object +/// \param options \ref LXOPT_ +/// \return the old options + +idaman int ida_export lex_set_options(lexer_t *lx, int options); + +/// \defgroup LXOPT_ Lexer options +/// Passed as 'options' parameter to lex_set_options(). +/// By default all options are on. +///@{ +#define LXOPT_PARSE_FLOATS 0x0001 ///< enable floating point constants +#define LXOPT_REQ_SEPARATOR 0x0002 ///< require a separator between a number and an ident or a character/string constant or dot +#define LXOPT_NOCASE_FILES 0x0004 ///< case-insensitive file search +#define LXOPT_C99_CONSTANTS 0x0008 ///< the size and sign of constants depend on the value itself and the 'U', 'L', and 'LL' + ///< modifier suffixes. Otherwise the constant is always considered as signed and the size + ///< depends only on the number of bytes in the value +#define LXOPT_STR_INCLUDE_BACKSLASHES 0x0010 ///< do not escape backslash characters in string constants +///@} + + +/// Get next token +/// \param lx the lexer object +/// \param t the output buffer for the token +/// \param p_lnnum the line number where the token starts + +idaman error_t ida_export lex_get_token(lexer_t *lx, token_t *t, int32 *p_lnnum=nullptr); + + +/// Enumerate all macros. +/// Do so until 'cb' returns non-zero. + +idaman int ida_export lex_enum_macros( + const lexer_t *lx, + int idaapi cb(const char *name, const char *body, int nargs, bool isfunc, void *ud), + void *ud=nullptr); + + +/// Debug: get text representation of token + +idaman const char *ida_export lex_print_token(qstring *buf, const token_t *t); + + +//------------------------------------------------------------------------- +/// \name String oriented functions +///@{ + +/// Set the input line and the macro table. +/// if macros==nullptr, the macro table will not be changed. + +idaman error_t ida_export lex_init_string( + lexer_t *lx, + const char *line, + void *macros=nullptr); +///@} + +//------------------------------------------------------------------------- +/// \name File oriented functions +///@{ + +/// Initialization: file may be nullptr. +/// Also see lex_term_file(). + +idaman error_t ida_export lex_init_file(lexer_t *lx, const char *file); + + +/// Error handling. +/// if level > 0, then return information about the enclosing file which +/// included the current one. + +idaman const char *ida_export lex_get_file_line( + lexer_t *lx, + int32 *linenum, + const char **lineptr, + int level=0); + + +/// Termination: also see lex_init_file() + +idaman void ida_export lex_term_file(lexer_t *lx, bool del_macros); +///@} + +//------------------------------------------------------------------------- +/// \name Token stack +///@{ +typedef qstack<token_t> tokenstack_t; ///< see get_token(), unget_token() + + +/// Retrieve token from a stack or lexer. +/// If buf is not empty then get the token on top of the stack. +/// If buf is empty then gen the next token from the lexer. +/// \return success + +inline bool get_token(token_t *t, lexer_t *lx, tokenstack_t &buf) +{ + if ( !buf.empty() ) + *t = buf.pop(); + else if ( lex_get_token(lx, t) != eOk ) + return false; + return true; +} + + +/// Push a token back onto the token stack + +inline void unget_token(const token_t &t, tokenstack_t &buf) +{ + buf.push(t); +} +///@} + + +/// is NAME a valid C keyword (exclude 'this') + +idaman bool ida_export is_c_keyword(const char *name); + + + +#endif // LEX_HPP + +``` + +`IdaSDK/libfuncs.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 2005-2025 Hex-Rays SA <support@hex-rays.com> + * ALL RIGHTS RESERVED. + * + * Routines and data structures for working with library signatures (e.g. FLIRT). + */ +#ifndef __LIBFUNC_HPP +#define __LIBFUNC_HPP +#pragma pack(push, 1) +struct idasgn_header_t +{ + char magic[6]; // 'IDASGN' +#define SIGN_HEADER_MAGIC "IDASGN" + uchar version; // currently 7 (see fix_version() below) +#define SIGN_HEADER_VERSION 10 + uchar processor_id; // idp id + uint32 file_formats; // allowed file formats (filetype_t) + uint16 ostype; // operation system type (bit field) +#define OSTYPE_MSDOS 0x0001 +#define OSTYPE_WIN 0x0002 +#define OSTYPE_OS2 0x0004 +#define OSTYPE_NETW 0x0008 +#define OSTYPE_UNIX 0x0010 +#define OSTYPE_OTHER 0x0020 + uint16 apptype; // application type: +#define APPT_CONSOLE 0x0001 // console +#define APPT_GRAPHIC 0x0002 // graphics +#define APPT_PROGRAM 0x0004 // EXE +#define APPT_LIBRARY 0x0008 // DLL +#define APPT_DRIVER 0x0010 // DRIVER +#define APPT_1THREAD 0x0020 // Singlethread +#define APPT_MTHREAD 0x0040 // Multithread +#define APPT_16BIT 0x0080 // 16 bit application +#define APPT_32BIT 0x0100 // 32 bit application +#define APPT_64BIT 0x0200 // 64 bit application + // + // Idea: check library date and exe date + // + uint16 flags; // signature file flags +#define LS_STARTUP 0x0001 // has startup entry as first module +#define LS_CTYPE 0x0002 // has ctype +#define LS_CTYPE2 0x0004 // ctype element is 2 bytes +#define LS_CTYPE_ALT 0x0008 // alternative ctype checksum present +#define LS_ZIP 0x0010 // compressed signature +#define LS_CTYPE_3V 0x0020 // 3rd variant of ctype checksum present + uint16 number_of_modules_v5; + uint16 ctype_crc; + char ctype_name[12]; + uchar libname_length; + uint16 ctype_crc_alt; // added in version 5 + uint32 number_of_modules; // added in version 6 + uint16 pattern_length; + uint16 ctype_crc_3v; // added in version 10 + + void fix_version(FILE *infp); +}; +#pragma pack(pop) + +/// Get idasgn header by a short signature name. +/// \param out_header buffer for the signature file header +/// \param out_libname buffer for the name of the library +/// \param name short name of a signature +/// \return true in case of success + +idaman bool ida_export get_idasgn_header_by_short_name(idasgn_header_t *out_header, qstring *out_libname, const char *name); + +/// Get idasgn full path by a short signature name. +/// \param out_fullpath buffer for the signature file full path +/// \param name short name of a signature +/// \return true in case of success + +idaman bool ida_export get_idasgn_path_by_short_name(qstring *out_fullpath, const char *name); +#endif + +``` + +`IdaSDK/lines.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _LINES_HPP +#define _LINES_HPP + +#include <ida.hpp> + +/*! \file lines.hpp + + \brief High level functions that deal with the generation + of the disassembled text lines. + + This file also contains definitions for the syntax highlighting. + + Finally there are functions that deal with anterior/posterior + user-defined lines. +*/ + +struct range_t; + +//--------------------------------------------------------------------------- +// C O L O R D E F I N I T I O N S +//--------------------------------------------------------------------------- + +/// \defgroup color_def Color definitions +/// +/// Here we describe the structure of embedded escape sequences used to +/// implement syntax highlighting. +/// +/// The processor module should insert appropriate escape characters into the +/// output lines as necessary. +/// +/// A typical color sequence looks like this: +/// +/// #COLOR_ON COLOR_xxx text #COLOR_OFF COLOR_xxx +/// +/// The first 2 items turn color 'xxx' on, then the text follows, +/// and the color is turned off by two last items. +/// COLOR_INV and COLOR_ADDR does not have the closing COLOR_OFF part. +/// +/// All non-space symbols must have a color code. +/// +/// Color sequences may be nested. +/// +/// Each instruction operand must have the COLOR_OPNDx color. +/// For example: COLOR_OPND1. These color codes are used by IDA to determine +/// the operand boundaries in the listing. +/// +/// Example 1: +/// lwz r28, FlexCan_rec.MB_ID(r3) +/// With color codes (multiple lines just for the convenience of reading): +/// [ON INSN]lwz[OFF INSN] [ON OPND1][ON REG]r28[OFF REG][OFF OPND1] +/// [ON SYMBOL],[OFF SYMBOL] [ON OPND2][ON DNAME]FlexCan_rec.MB_ID[OFF DNAME] +/// [ON SYMBOL]([OFF SYMBOL][ON REG]r3[OFF REG][ON SYMBOL])[OFF SYMBOL][OFF OPND2] +/// +/// Example 2: +/// bne loc_A0DA8C +/// With color codes: +/// [ON INSN]bne[OFF INSN] [ON OPND2][ON CODNAME] +/// [ON ADDR]0000000000A0DA8Cloc_A0DA8C[OFF CODNAME][OFF OPND2] +/// +/// Normally you should not construct the escape sequences manually. +/// Use the helper functions like out_symbol(), out_long() or similar. +///@{ + +/// \defgroup color_esc Color escape characters +/// Initiate/Terminate a color tag +///@{ +#define COLOR_ON '\1' ///< Escape character (ON). + ///< Followed by a color code (::color_t). +#define COLOR_OFF '\2' ///< Escape character (OFF). + ///< Followed by a color code (::color_t). +#define COLOR_ESC '\3' ///< Escape character (Quote next character). + ///< This is needed to output '\1' and '\2' + ///< characters. +#define COLOR_INV '\4' ///< Escape character (Inverse foreground and background colors). + ///< This escape character has no corresponding #COLOR_OFF. + ///< Its action continues until the next #COLOR_INV or end of line. + +#define SCOLOR_ON "\1" ///< Escape character (ON) +#define SCOLOR_OFF "\2" ///< Escape character (OFF) +#define SCOLOR_ESC "\3" ///< Escape character (Quote next character) +#define SCOLOR_INV "\4" ///< Escape character (Inverse colors) + +/// Is the given char a color escape character? +inline THREAD_SAFE bool requires_color_esc(char c) { return c >= COLOR_ON && c <= COLOR_INV; } +///@} + +typedef uchar color_t; ///< color tag - see \ref COLOR_ +/// \defgroup COLOR_ Color tags +/// Specify a color for a syntax item +///@{ +const color_t + COLOR_DEFAULT = 0x01, ///< Default + COLOR_REGCMT = 0x02, ///< Regular comment + COLOR_RPTCMT = 0x03, ///< Repeatable comment (comment defined somewhere else) + COLOR_AUTOCMT = 0x04, ///< Automatic comment + COLOR_INSN = 0x05, ///< Instruction + COLOR_DATNAME = 0x06, ///< Dummy Data Name + COLOR_DNAME = 0x07, ///< Regular Data Name + COLOR_DEMNAME = 0x08, ///< Demangled Name + COLOR_SYMBOL = 0x09, ///< Punctuation + COLOR_CHAR = 0x0A, ///< Char constant in instruction + COLOR_STRING = 0x0B, ///< String constant in instruction + COLOR_NUMBER = 0x0C, ///< Numeric constant in instruction + COLOR_VOIDOP = 0x0D, ///< Void operand + COLOR_CREF = 0x0E, ///< Code reference + COLOR_DREF = 0x0F, ///< Data reference + COLOR_CREFTAIL = 0x10, ///< Code reference to tail byte + COLOR_DREFTAIL = 0x11, ///< Data reference to tail byte + COLOR_ERROR = 0x12, ///< Error or problem + COLOR_PREFIX = 0x13, ///< Line prefix + COLOR_BINPREF = 0x14, ///< Binary line prefix bytes + COLOR_EXTRA = 0x15, ///< Extra line + COLOR_ALTOP = 0x16, ///< Alternative operand + COLOR_HIDNAME = 0x17, ///< Hidden name + COLOR_LIBNAME = 0x18, ///< Library function name + COLOR_LOCNAME = 0x19, ///< Local variable name + COLOR_CODNAME = 0x1A, ///< Dummy code name + COLOR_ASMDIR = 0x1B, ///< Assembler directive + COLOR_MACRO = 0x1C, ///< Macro + COLOR_DSTR = 0x1D, ///< String constant in data directive + COLOR_DCHAR = 0x1E, ///< Char constant in data directive + COLOR_DNUM = 0x1F, ///< Numeric constant in data directive + COLOR_KEYWORD = 0x20, ///< Keywords + COLOR_REG = 0x21, ///< Register name + COLOR_IMPNAME = 0x22, ///< Imported name + COLOR_SEGNAME = 0x23, ///< Segment name + COLOR_UNKNAME = 0x24, ///< Dummy unknown name + COLOR_CNAME = 0x25, ///< Regular code name + COLOR_UNAME = 0x26, ///< Regular unknown name + COLOR_COLLAPSED= 0x27, ///< Collapsed line + COLOR_FG_MAX = 0x28, ///< Max color number + + // Fictive colors + + COLOR_ADDR = COLOR_FG_MAX, ///< Hidden address marks. + ///< the address is represented as 16-digit + ///< hex number: 01234567ABCDEF00. + ///< it doesn't have the #COLOR_OFF pair. + + COLOR_OPND1 = COLOR_ADDR+1, ///< Instruction operand 1 + COLOR_OPND2 = COLOR_ADDR+2, ///< Instruction operand 2 + COLOR_OPND3 = COLOR_ADDR+3, ///< Instruction operand 3 + COLOR_OPND4 = COLOR_ADDR+4, ///< Instruction operand 4 + COLOR_OPND5 = COLOR_ADDR+5, ///< Instruction operand 5 + COLOR_OPND6 = COLOR_ADDR+6, ///< Instruction operand 6 + COLOR_OPND7 = COLOR_ADDR+7, ///< Instruction operand 7 + COLOR_OPND8 = COLOR_ADDR+8, ///< Instruction operand 8 + + + COLOR_RESERVED1= COLOR_ADDR+11,///< This tag is reserved for internal IDA use + COLOR_LUMINA = COLOR_ADDR+12;///< Lumina-related, only for the navigation band +///@} + +/// Size of a tagged address (see ::COLOR_ADDR) +#define COLOR_ADDR_SIZE (sizeof(ea_t)*2) + +/// \defgroup SCOLOR_ Color string constants +/// These definitions are used with the #COLSTR macro +///@{ +#define SCOLOR_DEFAULT "\x01" ///< Default +#define SCOLOR_REGCMT "\x02" ///< Regular comment +#define SCOLOR_RPTCMT "\x03" ///< Repeatable comment (defined not here) +#define SCOLOR_AUTOCMT "\x04" ///< Automatic comment +#define SCOLOR_INSN "\x05" ///< Instruction +#define SCOLOR_DATNAME "\x06" ///< Dummy Data Name +#define SCOLOR_DNAME "\x07" ///< Regular Data Name +#define SCOLOR_DEMNAME "\x08" ///< Demangled Name +#define SCOLOR_SYMBOL "\x09" ///< Punctuation +#define SCOLOR_CHAR "\x0A" ///< Char constant in instruction +#define SCOLOR_STRING "\x0B" ///< String constant in instruction +#define SCOLOR_NUMBER "\x0C" ///< Numeric constant in instruction +#define SCOLOR_VOIDOP "\x0D" ///< Void operand +#define SCOLOR_CREF "\x0E" ///< Code reference +#define SCOLOR_DREF "\x0F" ///< Data reference +#define SCOLOR_CREFTAIL "\x10" ///< Code reference to tail byte +#define SCOLOR_DREFTAIL "\x11" ///< Data reference to tail byte +#define SCOLOR_ERROR "\x12" ///< Error or problem +#define SCOLOR_PREFIX "\x13" ///< Line prefix +#define SCOLOR_BINPREF "\x14" ///< Binary line prefix bytes +#define SCOLOR_EXTRA "\x15" ///< Extra line +#define SCOLOR_ALTOP "\x16" ///< Alternative operand +#define SCOLOR_HIDNAME "\x17" ///< Hidden name +#define SCOLOR_LIBNAME "\x18" ///< Library function name +#define SCOLOR_LOCNAME "\x19" ///< Local variable name +#define SCOLOR_CODNAME "\x1A" ///< Dummy code name +#define SCOLOR_ASMDIR "\x1B" ///< Assembler directive +#define SCOLOR_MACRO "\x1C" ///< Macro +#define SCOLOR_DSTR "\x1D" ///< String constant in data directive +#define SCOLOR_DCHAR "\x1E" ///< Char constant in data directive +#define SCOLOR_DNUM "\x1F" ///< Numeric constant in data directive +#define SCOLOR_KEYWORD "\x20" ///< Keywords +#define SCOLOR_REG "\x21" ///< Register name +#define SCOLOR_IMPNAME "\x22" ///< Imported name +#define SCOLOR_SEGNAME "\x23" ///< Segment name +#define SCOLOR_UNKNAME "\x24" ///< Dummy unknown name +#define SCOLOR_CNAME "\x25" ///< Regular code name +#define SCOLOR_UNAME "\x26" ///< Regular unknown name +#define SCOLOR_COLLAPSED "\x27" ///< Collapsed line +#define SCOLOR_ADDR "\x28" ///< Hidden address mark +///@} + +//----------------- Line prefix colors -------------------------------------- +/// \defgroup COLOR_PFX Line prefix colors +/// Note: line prefix colors are not used in processor modules +///@{ +#define COLOR_DEFAULT 0x01 ///< Default +#define COLOR_SELECTED 0x02 ///< Selected +#define COLOR_LIBFUNC 0x03 ///< Library function +#define COLOR_REGFUNC 0x04 ///< Regular function +#define COLOR_CODE 0x05 ///< Single instruction +#define COLOR_DATA 0x06 ///< Data bytes +#define COLOR_UNKNOWN 0x07 ///< Unexplored byte +#define COLOR_EXTERN 0x08 ///< External name definition segment +#define COLOR_CURITEM 0x09 ///< Current item +#define COLOR_CURLINE 0x0A ///< Current line +#define COLOR_HIDLINE 0x0B ///< Hidden line +#define COLOR_LUMFUNC 0x0C ///< Lumina function +#define COLOR_BG_MAX 0x0D ///< Max color number + +#define PALETTE_SIZE (COLOR_FG_MAX+COLOR_BG_MAX) +///@} + + +/// This macro is used to build colored string constants (e.g. for format strings) +/// \param str string literal to surround with color tags +/// \param tag one of SCOLOR_xxx constants +#define COLSTR(str,tag) SCOLOR_ON tag str SCOLOR_OFF tag + + +//------------------------------------------------------------------------ + +/// \defgroup color_conv Convenience functions +/// Higher level convenience functions are defined in ua.hpp. +/// Please use the following functions only if functions from ua.hpp +/// are not useful in your case. +///@{ + +/// Insert an address mark into a string. +/// \param buf pointer to the output buffer; the tag will be appended or inserted into it +/// \param ea address to include +/// \param ins if true, the tag will be inserted at the beginning of the buffer + +idaman THREAD_SAFE void ida_export tag_addr(qstring *buf, ea_t ea, bool ins=false); + + +/// Move pointer to a 'line' to 'cnt' positions right. +/// Take into account escape sequences. +/// \param line pointer to string +/// \param cnt number of positions to move right +/// \return moved pointer + +idaman THREAD_SAFE const char *ida_export tag_advance(const char *line, int cnt); + + +/// Move the pointer past all color codes. +/// \param line can't be nullptr +/// \return moved pointer, can't be nullptr + +idaman THREAD_SAFE const char *ida_export tag_skipcodes(const char *line); + + +/// Skip one color code. +/// This function should be used if you are interested in color codes +/// and want to analyze all of them. +/// Otherwise tag_skipcodes() function is better since it will skip all colors at once. +/// This function will skip the current color code if there is one. +/// If the current symbol is not a color code, it will return the input. +/// \return moved pointer + +idaman THREAD_SAFE const char *ida_export tag_skipcode(const char *line); + + +/// Calculate length of a colored string +/// This function computes the length in unicode codepoints of a line +/// \return the number of codepoints in the line, or -1 on error + +idaman THREAD_SAFE ssize_t ida_export tag_strlen(const char *line); + + +/// Remove color escape sequences from a string. +/// \param buf output buffer with the string, cannot be nullptr. +/// \param str input string, cannot be nullptr. +/// \param init_level used to verify that COLOR_ON and COLOR_OFF tags are balanced +/// \return length of resulting string, -1 if error + +idaman THREAD_SAFE ssize_t ida_export tag_remove(qstring *buf, const char *str, int init_level=0); + +inline THREAD_SAFE ssize_t idaapi tag_remove(qstring *buf, const qstring &str, int init_level=0) +{ + return tag_remove(buf, str.c_str(), init_level); +} + +inline THREAD_SAFE ssize_t idaapi tag_remove(qstring *buf, int init_level=0) +{ + if ( buf->empty() ) + return 0; + return tag_remove(buf, buf->begin(), init_level); +} + +///@} color_conv + +///@} color_def + + +/// Get prefix color for line at 'ea' +/// \return \ref COLOR_PFX +idaman color_t ida_export calc_prefix_color(ea_t ea); + +/// Get background color for line at 'ea' +/// \return RGB color +idaman bgcolor_t ida_export calc_bg_color(ea_t ea); + + +//------------------------------------------------------------------------ +// S O U R C E F I L E S +//------------------------------------------------------------------------ + +/// \name Source files +/// IDA can keep information about source files used to create the program. +/// Each source file is represented by a range of addresses. +/// A source file may contain several address ranges. +///@{ + +/// Mark a range of address as belonging to a source file. +/// An address range may belong only to one source file. +/// A source file may be represented by several address ranges. +/// \param ea1 linear address of start of the address range +/// \param ea2 linear address of end of the address range (excluded) +/// \param filename name of source file. +/// \return success + +idaman bool ida_export add_sourcefile(ea_t ea1, ea_t ea2, const char *filename); + + +/// Get name of source file occupying the given address. +/// \param ea linear address +/// \param bounds pointer to the output buffer with the address range +/// for the current file. May be nullptr. +/// \return nullptr if source file information is not found, +/// otherwise returns pointer to file name + +idaman const char *ida_export get_sourcefile(ea_t ea, range_t *bounds=nullptr); + + +/// Delete information about the source file. +/// \param ea linear address +/// \return success + +idaman bool ida_export del_sourcefile(ea_t ea); +///@} + +//------------------------------------------------------------------------ +// G E N E R A T I O N O F D I S A S S E M B L E D T E X T +//------------------------------------------------------------------------ + +/// \name Generation of disassembled text +///@{ + +/// User-defined line-prefixes are displayed just after the autogenerated +/// line prefixes in the disassembly listing. +/// There is no need to call this function explicitly. +/// Use the user_defined_prefix_t class. +/// \param prefix_len prefixed length. if 0, then uninstall UDP +/// \param udp object to generate user-defined prefix +/// \param owner pointer to the plugin_t that owns UDP +/// if non-nullptr, then the object will be uninstalled and destroyed +/// when the plugin gets unloaded +idaman bool ida_export install_user_defined_prefix( + size_t prefix_len, + struct user_defined_prefix_t *udp, + const void *owner); + +/// Class to generate user-defined prefixes in the disassembly listing. +struct user_defined_prefix_t +{ + /// Creating a user-defined prefix object installs it. + user_defined_prefix_t(size_t prefix_len, const void *owner) + { + install_user_defined_prefix(prefix_len, this, owner); + } + + /// Destroying a user-defined prefix object uninstalls it. + virtual idaapi ~user_defined_prefix_t() + { + install_user_defined_prefix(0, this, nullptr); + } + + // Get a user-defined prefix. + /// This callback must be overridden by the derived class. + /// \param vout the output buffer + /// \param ea the current address + /// \param insn the current instruction. if the current item is not + /// an instruction, then insn.itype is zero. + /// \param lnnum number of the current line (each address may have several + /// listing lines for it). 0 means the very first line for + /// the current address. + /// \param indent see explanations for \ref gen_printf() + /// \param line the line to be generated. + /// the line usually contains color tags. + /// this argument can be examined to decide + /// whether to generate the prefix. + virtual void idaapi get_user_defined_prefix( + qstring *vout, + ea_t ea, + const class insn_t &insn, + int lnnum, + int indent, + const char *line) = 0; +}; + +///@} + +//------------------------------------------------------------------------ +// A N T E R I O R / P O S T E R I O R L I N E S +//------------------------------------------------------------------------ + +/// \name Anterior/Posterior lines +///@{ + +/// See higher level functions below + +idaman AS_PRINTF(3, 0) bool ida_export vadd_extra_line( + ea_t ea, + int vel_flags, // see VEL_... + const char *format, + va_list va); + +#define VEL_POST 0x01 ///< append posterior line +#define VEL_CMT 0x02 ///< append comment line + + +/// Add anterior/posterior non-comment line(s). +/// \param ea linear address +/// \param isprev do we add anterior lines? (0-no, posterior) +/// \param format printf() style format string. may contain \\n to denote new lines. +/// \return true if success + +AS_PRINTF(3, 4) inline bool add_extra_line(ea_t ea, bool isprev, const char *format, ...) +{ + va_list va; + va_start(va,format); + int vel_flags = (isprev ? 0 : VEL_POST); + bool ok = vadd_extra_line(ea, vel_flags, format, va); + va_end(va); + return ok; +} + + +/// Add anterior/posterior comment line(s). +/// \param ea linear address +/// \param isprev do we add anterior lines? (0-no, posterior) +/// \param format printf() style format string. may contain \\n to denote +/// new lines. The resulting string should not contain comment +/// characters (;), the kernel will add them automatically. +/// \return true if success + +AS_PRINTF(3, 4) inline bool add_extra_cmt(ea_t ea, bool isprev, const char *format, ...) +{ + va_list va; + va_start(va,format); + int vel_flags = (isprev ? 0 : VEL_POST) | VEL_CMT; + bool ok = vadd_extra_line(ea, vel_flags, format, va); + va_end(va); + return ok; +} + + +/// Add anterior comment line(s) at the start of program. +/// \param format printf() style format string. may contain \\n to denote +/// new lines. The resulting string should not contain comment +/// characters (;), the kernel will add them automatically. +/// \return true if success + +AS_PRINTF(1, 2) inline bool add_pgm_cmt(const char *format, ...) +{ + va_list va; + va_start(va,format); + bool ok = vadd_extra_line(inf_get_min_ea(), VEL_CMT, format, va); + va_end(va); + return ok; +} + +///@} + +///---------------------------------------------------------------------\cond +/// The following functions are used in kernel only: + +// Generate disassembly (many lines) and put them into a buffer +// Returns number of generated lines +idaman int ida_export generate_disassembly( + qstrvec_t *out, // buffer to hold generated lines + int *lnnum, // number of "the most interesting" line + ea_t ea, // address to generate disassembly for + int maxsize, // maximum number of lines + int flags = 0); + +#define GDISMF_AS_STACK (1 << 0) ///< Display undefined items as 2/4/8 bytes +#define GDISMF_ADDR_TAG (1 << 1) ///< generate an hidden addr tag at the beginning of the line +#define GDISMF_REMOVE_TAGS (1 << 2) ///< remove color tags from the output +#define GDISMF_UNHIDE (1 << 3) ///< display hidden objects (segment, function, range) + +// Generate one line of disassembly +// This function discards all "non-interesting" lines +// It is designed to generate one-line descriptions +// of addresses for lists, etc. +idaman bool ida_export generate_disasm_line( + qstring *buf, // output buffer + ea_t ea, // address to generate disassembly for + int flags=0); +#define GENDSM_FORCE_CODE (1 << 0) ///< generate a disassembly line as if + ///< there is an instruction at 'ea' +#define GENDSM_MULTI_LINE (1 << 1) ///< if the instruction consists of several lines, + ///< produce all of them (useful for parallel instructions) +#define GENDSM_REMOVE_TAGS (1 << 2) ///< remove color tags from the output buffer +#define GENDSM_UNHIDE (1 << 3) ///< display hidden objects (segment, function, range) + +/// Get length of the line prefix that was used for the last generated line + +idaman int ida_export get_last_pfxlen(); + + +// Get pointer to the sequence of characters denoting 'close comment' +// empty string means no comment (the current assembler has no open-comment close-comment pairs) +// This function uses ash.cmnt2 + +idaman const char *ida_export closing_comment(); + + +// Every anterior/posterior line has its number. +// Anterior lines have numbers from E_PREV +// Posterior lines have numbers from E_NEXT + +const int E_PREV = 1000; ///< Anterior line starting number +const int E_NEXT = 2000; ///< Posterior line starting number + +idaman int ida_export get_first_free_extra_cmtidx(ea_t ea, int start); +idaman bool ida_export update_extra_cmt(ea_t ea, int what, const char *str); +idaman bool ida_export del_extra_cmt(ea_t ea, int what); +idaman ssize_t ida_export get_extra_cmt(qstring *buf, ea_t ea, int what); +idaman void ida_export delete_extra_cmts(ea_t ea, int what); + +idaman ea_t ida_export align_down_to_stack(ea_t newea); +idaman ea_t ida_export align_up_to_stack(ea_t ea1, ea_t ea2=BADADDR); + +// A helper class, to encode from UTF-8, -> into the target encoding. +// This is typically used when generating listings (or any kind of +// output file.) +struct encoder_t +{ + // whether or not a message should be printed, letting the + // user know that some text couldn't be recoded properly + enum notify_recerr_t + { + nr_none, + nr_once, + }; + + virtual ~encoder_t() {} + virtual bool idaapi get_bom(bytevec_t *out) const = 0; + // returns true if conversion was entirely successful, false otherwise. + // codepoints that couldn't be converted, will be output as C + // literal-escaped UTF-8 sequences (e.g., "\xC3\xD9"), and if + // 'nr_once' was passed at creation-time, a one-time notification + // well be output in the messages window. + virtual bool idaapi encode(qstring *s) const = 0; + // encode()s the UTF-8 string composed by format + args, and + // returns true if all the resulting bytes could be written to + // the output file. + AS_PRINTF(3, 4) virtual bool idaapi print(FILE *out, const char *format, ...) const = 0; + // should a file be opened as binary, or should it rather be opened + // in text mode? This will have an importance in how '\n' characters + // are possibly converted into '\x0A\x0D' on windows, which is most + // inappropriate when output'ing e.g., UTF-16, UTF-32.. + virtual bool idaapi requires_binary_mode() const = 0; +}; + +// Create the encoder with the given target encoding. If -1 is passed +// then the effective target encoding will be computed like so: +// if ( encidx < 0 ) +// { +// encidx = get_outfile_encoding_idx(); +// if ( encidx == STRENC_DEFAULT ) +// encidx = get_default_encoding_idx(BPU_1B); +// } +idaman encoder_t *ida_export create_encoding_helper( + int encidx=-1, + encoder_t::notify_recerr_t nr=encoder_t::nr_once); + +/// Callback functions to output lines: +///@{ +typedef int idaapi html_header_cb_t(FILE *fp); +typedef int idaapi html_footer_cb_t(FILE *fp); +typedef int idaapi html_line_cb_t( + FILE *fp, + const qstring &line, + bgcolor_t prefix_color, + bgcolor_t bg_color); +#define gen_outline_t html_line_cb_t +///@} + +///-------------------------------------------------------------------\endcond + + +#endif + +``` + +`IdaSDK/llong.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _LLONG_HPP +#define _LLONG_HPP + +//--------------------------------------------------------------------------- +#if defined(_MSC_VER) +typedef unsigned __int64 uint64; +typedef __int64 int64; +#elif defined(__GNUC__) +typedef unsigned long long uint64; +typedef long long int64; +#endif + +//--------------------------------------------------------------------------- +#ifdef __cplusplus +inline constexpr int64 make_int64(uint32 ll, int32 hh) { return ll | (int64(hh) << 32); } +inline constexpr uint64 make_uint64(uint32 ll, int32 hh) { return ll | (uint64(hh) << 32); } +inline constexpr uint32 low(const uint64 &x) { return uint32(x); } +inline constexpr uint32 high(const uint64 &x) { return uint32(x>>32); } +inline constexpr uint32 low(const int64 &x) { return uint32(x); } +inline constexpr int32 high(const int64 &x) { return uint32(x>>32); } +#else +#define make_int64(ll,hh) (ll | (int64(hh) << 32)) +#define make_uint64(ll,hh) (ll | (uint64(hh) << 32)) +#endif + +idaman THREAD_SAFE int64 ida_export llong_scan( + const char *buf, + int radix, + const char **end); +#ifndef swap64 + idaman THREAD_SAFE uint64 ida_export swap64(uint64); +# ifdef __cplusplus + inline int64 swap64(int64 x) + { + return int64(swap64(uint64(x))); + } +# endif +#endif + +//--------------------------------------------------------------------------- +// 128 BIT NUMBERS +//--------------------------------------------------------------------------- +#ifdef __HAS_INT128__ + +typedef unsigned __int128 uint128; +typedef __int128 int128; + +inline int128 make_int128(uint64 ll, int64 hh) { return ll | (int128(hh) << 64); } +inline uint128 make_uint128(uint64 ll, uint64 hh) { return ll | (uint128(hh) << 64); } +inline uint64 low(const uint128 &x) { return uint64(x); } +inline uint64 high(const uint128 &x) { return uint64(x>>64); } +inline uint64 low(const int128 &x) { return uint64(x); } +inline int64 high(const int128 &x) { return uint64(x>>64); } + +#else +#ifdef __cplusplus +class uint128 +{ + uint64 l; + uint64 h; + friend class int128; +public: + uint128(void) {} + uint128(uint x) { l = x; h = 0; } + uint128(int x) { l = x; h = (x < 0)? -1 : 0; } + uint128(uint64 x) { l = x; h = 0; } + uint128(int64 x) { l = x; h = (x < 0) ? -1 : 0; } + uint128(uint64 ll, uint64 hh) { l = ll; h = hh; } + friend uint64 low (const uint128 &x) { return x.l; } + friend uint64 high(const uint128 &x) { return x.h; } + friend uint128 operator+(const uint128 &x, const uint128 &y); + friend uint128 operator-(const uint128 &x, const uint128 &y); + friend uint128 operator/(const uint128 &x, const uint128 &y); + friend uint128 operator%(const uint128 &x, const uint128 &y); + friend uint128 operator*(const uint128 &x, const uint128 &y); + friend uint128 operator|(const uint128 &x, const uint128 &y); + friend uint128 operator&(const uint128 &x, const uint128 &y); + friend uint128 operator^(const uint128 &x, const uint128 &y); + friend uint128 operator>>(const uint128 &x, int cnt); + friend uint128 operator<<(const uint128 &x, int cnt); + uint128 &operator+=(const uint128 &y); + uint128 &operator-=(const uint128 &y); + uint128 &operator/=(const uint128 &y); + uint128 &operator%=(const uint128 &y); + uint128 &operator*=(const uint128 &y); + uint128 &operator|=(const uint128 &y); + uint128 &operator&=(const uint128 &y); + uint128 &operator^=(const uint128 &y); + uint128 &operator>>=(int cnt); + uint128 &operator<<=(int cnt); + uint128 &operator++(void); + uint128 &operator--(void); + friend uint128 operator+(const uint128 &x) { return x; } + friend uint128 operator-(const uint128 &x); + friend uint128 operator~(const uint128 &x) { return uint128(~x.l,~x.h); } + friend bool operator==(const uint128 &x, const uint128 &y) { return x.l == y.l && x.h == y.h; } + friend bool operator!=(const uint128 &x, const uint128 &y) { return x.l != y.l || x.h != y.h; } + friend bool operator> (const uint128 &x, const uint128 &y) { return x.h > y.h || (x.h == y.h && x.l > y.l); } + friend bool operator< (const uint128 &x, const uint128 &y) { return x.h < y.h || (x.h == y.h && x.l < y.l); } + friend bool operator>=(const uint128 &x, const uint128 &y) { return x.h > y.h || (x.h == y.h && x.l >= y.l); } + friend bool operator<=(const uint128 &x, const uint128 &y) { return x.h < y.h || (x.h == y.h && x.l <= y.l); } +}; + +class int128 +{ + uint64 l; + int64 h; + friend class uint128; +public: + int128(void) {} + int128(uint x) { l = x; h = 0; } + int128(int x) { l = x; h = (x < 0) ? -1 : 0; } + int128(uint64 x) { l = x; h = 0; } + int128(int64 x) { l = x; h = (x < 0) ? -1 : 0; } + int128(uint64 ll, uint64 hh) { l=ll; h=hh; } + int128(const uint128 &x) { l=x.l; h=x.h; } + friend uint64 low (const int128 &x) { return x.l; } + friend uint64 high(const int128 &x) { return x.h; } + friend int128 operator+(const int128 &x, const int128 &y); + friend int128 operator-(const int128 &x, const int128 &y); + friend int128 operator/(const int128 &x, const int128 &y); + friend int128 operator%(const int128 &x, const int128 &y); + friend int128 operator*(const int128 &x, const int128 &y); + friend int128 operator|(const int128 &x, const int128 &y); + friend int128 operator&(const int128 &x, const int128 &y); + friend int128 operator^(const int128 &x, const int128 &y); + friend int128 operator>>(const int128 &x, int cnt); + friend int128 operator<<(const int128 &x, int cnt); + int128 &operator+=(const int128 &y); + int128 &operator-=(const int128 &y); + int128 &operator/=(const int128 &y); + int128 &operator%=(const int128 &y); + int128 &operator*=(const int128 &y); + int128 &operator|=(const int128 &y); + int128 &operator&=(const int128 &y); + int128 &operator^=(const int128 &y); + int128 &operator>>=(int cnt); + int128 &operator<<=(int cnt); + int128 &operator++(void); + int128 &operator--(void); + friend int128 operator+(const int128 &x) { return x; } + friend int128 operator-(const int128 &x); + friend int128 operator~(const int128 &x) { return int128(~x.l,~x.h); } + friend bool operator==(const int128 &x, const int128 &y) { return x.l == y.l && x.h == y.h; } + friend bool operator!=(const int128 &x, const int128 &y) { return x.l != y.l || x.h != y.h; } + friend bool operator> (const int128 &x, const int128 &y) { return x.h > y.h || (x.h == y.h && x.l > y.l); } + friend bool operator< (const int128 &x, const int128 &y) { return x.h < y.h || (x.h == y.h && x.l < y.l); } + friend bool operator>=(const int128 &x, const int128 &y) { return x.h > y.h || (x.h == y.h && x.l >= y.l); } + friend bool operator<=(const int128 &x, const int128 &y) { return x.h < y.h || (x.h == y.h && x.l <= y.l); } +}; + +inline int128 make_int128(uint64 ll, int64 hh) { return int128(ll, hh); } +inline uint128 make_uint128(uint64 ll, int64 hh) { return uint128(ll, hh); } +idaman THREAD_SAFE void ida_export swap128(uint128 *x); + +//--------------------------------------------------------------------------- +inline uint128 operator+(const uint128 &x, const uint128 &y) +{ + uint64 h = x.h + y.h; + uint64 l = x.l + y.l; + if ( l < x.l ) + h = h + 1; + return uint128(l,h); +} + +//--------------------------------------------------------------------------- +inline uint128 operator-(const uint128 &x, const uint128 &y) +{ + uint64 h = x.h - y.h; + uint64 l = x.l - y.l; + if ( l > x.l ) + h = h - 1; + return uint128(l,h); +} + +//--------------------------------------------------------------------------- +inline uint128 operator|(const uint128 &x, const uint128 &y) +{ + return uint128(x.l | y.l, x.h | y.h); +} + +//--------------------------------------------------------------------------- +inline uint128 operator&(const uint128 &x, const uint128 &y) +{ + return uint128(x.l & y.l, x.h & y.h); +} + +//--------------------------------------------------------------------------- +inline uint128 operator^(const uint128 &x, const uint128 &y) +{ + return uint128(x.l ^ y.l, x.h ^ y.h); +} + +//--------------------------------------------------------------------------- +inline uint128 &uint128::operator+=(const uint128 &y) +{ + return *this = *this + y; +} + +//--------------------------------------------------------------------------- +inline uint128 &uint128::operator-=(const uint128 &y) +{ + return *this = *this - y; +} + +//--------------------------------------------------------------------------- +inline uint128 &uint128::operator|=(const uint128 &y) +{ + return *this = *this | y; +} + +//--------------------------------------------------------------------------- +inline uint128 &uint128::operator&=(const uint128 &y) +{ + return *this = *this & y; +} + +//--------------------------------------------------------------------------- +inline uint128 &uint128::operator^=(const uint128 &y) +{ + return *this = *this ^ y; +} + +//--------------------------------------------------------------------------- +inline uint128 &uint128::operator/=(const uint128 &y) +{ + return *this = *this / y; +} + +//--------------------------------------------------------------------------- +inline uint128 &uint128::operator%=(const uint128 &y) +{ + return *this = *this % y; +} + +//--------------------------------------------------------------------------- +inline uint128 &uint128::operator*=(const uint128 &y) +{ + return *this = *this * y; +} + +//--------------------------------------------------------------------------- +inline uint128 &uint128::operator<<=(int cnt) +{ + return *this = *this << cnt; +} + +//--------------------------------------------------------------------------- +inline uint128 &uint128::operator>>=(int cnt) +{ + return *this = *this >> cnt; +} + +//--------------------------------------------------------------------------- +inline uint128 &uint128::operator++(void) +{ + if ( ++l == 0 ) + ++h; + return *this; +} + +//--------------------------------------------------------------------------- +inline uint128 &uint128::operator--(void) +{ + if ( l == 0 ) + --h; + --l; + return *this; +} + +//--------------------------------------------------------------------------- +inline uint128 operator-(const uint128 &x) +{ + return ~x + 1; +} + +#ifndef NO_OBSOLETE_FUNCS +typedef uint64 ulonglong; +typedef int64 longlong; +#endif + +#endif // ifdef __cplusplus +#endif // ifdef __HAS_INT128__ + +#endif // define _LLONG_HPP + +``` + +`IdaSDK/loader.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _LOADER_HPP +#define _LOADER_HPP +#include <ida.hpp> + +/*! \file loader.hpp + + \brief Definitions of IDP, LDR, PLUGIN module interfaces + + This file also contains: + - functions to load files into the database + - functions to generate output files + - high level functions to work with the database (open, save, close) + + The LDR interface consists of one structure: loader_t \n + The IDP interface consists of one structure: processor_t \n + The PLUGIN interface consists of one structure: plugin_t + + Modules can't use standard FILE* functions. + They must use functions from <fpro.h> + + Modules can't use standard memory allocation functions. + They must use functions from <pro.h> + + The exported entry #1 in the module should point to the + the appropriate structure. (loader_t for LDR module, for example) +*/ + +//---------------------------------------------------------------------- +// DEFINITION OF LDR MODULES +//---------------------------------------------------------------------- + +class linput_t; // loader input source. see diskio.hpp for the functions +struct extlang_t; // check expr.hpp +struct plugmod_t; // idp.hpp + +/// Loader description block - must be exported from the loader module +struct loader_t +{ + uint32 version; ///< api version, should be #IDP_INTERFACE_VERSION + uint32 flags; ///< \ref LDRF_ +/// \defgroup LDRF_ Loader flags +/// Used by loader_t::flags +///@{ +#define LDRF_RELOAD 0x0001 ///< loader recognizes #NEF_RELOAD flag +#define LDRF_REQ_PROC 0x0002 ///< Requires a processor to be set. + ///< if this bit is not set, load_file() must + ///< call set_processor_type(..., SETPROC_LOADER) +///@} + + /// Check input file format. + /// This function will be called one or more times depending on the result value. + /// \param[out] fileformat name of file format + /// \param[out] processor desired processor (optional) + /// \param li input file + /// \param filename name of the input file, + /// if it is an archive member name then the actual file doesn't exist + /// \return + /// 1 if file format is recognized, and fills 'fileformatname', otherwise returns 0. + /// This function may return a unique file format number instead of 1. + /// To get this unique number, please contact the author. + /// If the return value is ORed with #ACCEPT_ARCHIVE, then + /// it is an archive loader. Its process_archive() will be called + /// instead of load_file(). + /// If the return value is ORed with #ACCEPT_CONTINUE, then + /// this function will be called another time. + /// If the return value is ORed with #ACCEPT_FIRST, then this format + /// should be placed first in the "load file" dialog box. + /// In the sorting order of file formats the archive formats have priority. + int (idaapi *accept_file)( + qstring *fileformatname, + qstring *processor, + linput_t *li, + const char *filename); + +/// Specify that a file format is served by archive loader +/// See loader_t::accept_file +#define ACCEPT_ARCHIVE 0x2000 +/// Specify that the function must be called another time +/// See loader_t::accept_file +#define ACCEPT_CONTINUE 0x4000 +/// Specify that a file format should be place first in "load file" dialog box. +/// See loader_t::accept_file +#define ACCEPT_FIRST 0x8000 + + /// Load file into the database. + /// \param li input file + /// \param neflags \ref NEF_ + /// \param fileformatname name of type of the file + /// (it was returned by #accept_file) + /// + /// If this function fails, loader_failure() should be called + void (idaapi *load_file)( + linput_t *li, + ushort neflags, + const char *fileformatname); +/// \defgroup NEF_ Load file flags +/// Passed as 'neflags' parameter to loader_t::load_file +///@{ +#define NEF_SEGS 0x0001 ///< Create segments +#define NEF_RSCS 0x0002 ///< Load resources +#define NEF_NAME 0x0004 ///< Rename entries +#define NEF_MAN 0x0008 ///< Manual load +#define NEF_FILL 0x0010 ///< Fill segment gaps +#define NEF_IMPS 0x0020 ///< Create import segment +#define NEF_FIRST 0x0080 ///< This is the first file loaded + ///< into the database. +#define NEF_CODE 0x0100 ///< for load_binary_file(): + ///< load as a code segment +#define NEF_RELOAD 0x0200 ///< reload the file at the same place: + ///< - don't create segments + ///< - don't create fixup info + ///< - don't import segments + ///< - etc. + ///< + ///< Load only the bytes into the base. + ///< A loader should have the #LDRF_RELOAD + ///< bit set. +#define NEF_FLAT 0x0400 ///< Autocreate FLAT group (PE) +#define NEF_MINI 0x0800 ///< Create mini database (do not copy + ///< segment bytes from the input file; + ///< use only the file header metadata) +#define NEF_LOPT 0x1000 ///< Display additional loader options dialog +#define NEF_LALL 0x2000 ///< Load all segments without questions +///@} + + /// Create output file from the database. + /// This function may be absent. + /// If fp == nullptr, then this function returns: + /// - 0: can't create file of this type + /// - 1: ok, can create file of this type + /// + /// If fp != nullptr, then this function should create the output file + int (idaapi *save_file)(FILE *fp, const char *fileformatname); + + /// Take care of a moved segment (fix up relocations, for example). + /// This function may be absent. + /// A special calling method \code move_segm(BADADDR, delta, 0, formatname); \endcode + /// means that the whole program has been moved in the memory (rebased) by delta bytes + /// \param from previous linear address of the segment + /// \param to current linear address of the segment + /// \param size size of the moved segment + /// \param fileformatname the file format + /// \retval 1 ok + /// \retval 0 failure + int (idaapi *move_segm)( + ea_t from, + ea_t to, + asize_t size, + const char *fileformatname); + + /// Display list of archive members and let the user select one. + /// Extract the selected archive member into a temporary file. + /// \param[out] temp_file name of the file with the extracted archive member. + /// \param li input file + /// \param[in,out] module_name in: name of archive + /// out: name of the extracted archive member + /// \param[in,out] neflags \ref NEF_ + /// \param fileformatname name of type of the file + /// (it was returned by #accept_file) + /// \param defmember extract the specified member, + /// for example "subdir/member.exe", + /// may be nullptr + /// \param errbuf error message if 0 is returned, + /// may be nullptr + /// \return -1-cancelled by the user, 1-ok, 0-error, see errbuf for details + int (idaapi *process_archive)( + qstring *temp_file, + linput_t *li, + qstring *module_name, + ushort *neflags, + const char *fileformatname, + const char *defmember, + qstring *errbuf); +}; + + +/// See loader_failure() + +idaman AS_PRINTF(1, 0) NORETURN void ida_export vloader_failure(const char *format, va_list va); + + +/// Display a message about a loader failure and stop the loading process. +/// The kernel will destroy the database. +/// If format == nullptr, no message will be displayed +/// This function does not return (it longjumps)! +/// It may be called only from loader_t::load_file + +AS_PRINTF(1, 2) NORETURN inline void loader_failure(const char *format=nullptr, ...) +{ + va_list va; + va_start(va, format); + vloader_failure(format, va); +} + +//------------------------------------------------------------------------- +#if defined(__NT__) +# define DLLEXT "dll" +#elif defined(__LINUX__) +# define DLLEXT "so" +#elif defined(__MAC__) +# define DLLEXT "dylib" +#else +# error Unknown loader ext +#endif + +//---------------------------------------------------------------------- +/// \def{LOADER_DLL, Pattern to find loader files} +#ifdef __EA64__ +# define LOADER_DLL "*." DLLEXT +#else +# define LOADER_DLL "*32." DLLEXT +#endif + +//---------------------------------------------------------------------- +// Functions for the UI to load files +//---------------------------------------------------------------------- +/// List of loaders +struct load_info_t +{ + load_info_t *next; + qstring dllname; + qstring ftypename; + qstring processor; ///< desired processor name + filetype_t ftype; + uint32 loader_flags; ///< copy of loader_t::flags + uint32 lflags; ///< \ref LIF_ + int pri; ///< 2-archldr, 1-place first, 0-normal priority + +/// \defgroup LIF_ loader info flags +/// Used by load_info_t::lflags +///@{ +#define LIF_ARCHLDR 0x0001 ///< archive loader +///@} + + bool is_archldr(void) { return (lflags & LIF_ARCHLDR) != 0; } +}; +DECLARE_TYPE_AS_MOVABLE(load_info_t); + +/// Build list of potential loaders + +idaman load_info_t *ida_export build_loaders_list(linput_t *li, const char *filename); + + +/// Free the list of loaders + +idaman void ida_export free_loaders_list(load_info_t *list); + + +/// Get name of loader from its DLL file +/// (for example, for PE files we will get "PE"). +/// This function modifies the original string and returns a pointer into it. +/// NB: if the file extension is a registered extlang extension (e.g. py or idc) +/// the extension is retained + +idaman char *ida_export get_loader_name_from_dll(char *dllname); + + +/// Get name of loader used to load the input file into the database. +/// If no external loader was used, returns -1. +/// Otherwise copies the loader file name without the extension in the buf +/// and returns its length +/// (for example, for PE files we will get "PE"). +/// For scripted loaders, the file extension is retained. + +idaman ssize_t ida_export get_loader_name(char *buf, size_t bufsize); + + +/// Load a binary file into the database. +/// This function usually is called from ui. +/// \param filename the name of input file as is +/// (if the input file is from library, then +/// this is the name from the library) +/// \param li loader input source +/// \param _neflags \ref NEF_. For the first file, +/// the flag #NEF_FIRST must be set. +/// \param fileoff Offset in the input file +/// \param basepara Load address in paragraphs +/// \param binoff Load offset (load_address=(basepara<<4)+binoff) +/// \param nbytes Number of bytes to load from the file. +/// - 0: up to the end of the file +/// +/// If nbytes is bigger than the number of +/// bytes rest, the kernel will load as much +/// as possible +/// \retval true ok +/// \retval false failed (couldn't open the file) + +idaman bool ida_export load_binary_file( + const char *filename, + linput_t *li, + ushort _neflags, + qoff64_t fileoff, + ea_t basepara, + ea_t binoff, + uint64 nbytes); + + +/// Load a non-binary file into the database. +/// This function usually is called from ui. +/// \param filename the name of input file as is +/// (if the input file is from library, then +/// this is the name from the library) +/// \param li loader input source +/// \param sysdlldir a directory with system dlls. Pass "." if unknown. +/// \param _neflags \ref NEF_. For the first file +/// the flag #NEF_FIRST must be set. +/// \param loader pointer to ::load_info_t structure. +/// If the current IDP module has \ph{loader} != nullptr +/// then this argument is ignored. +/// \return success + +idaman bool ida_export load_nonbinary_file( + const char *filename, + linput_t *li, + const char *sysdlldir, + ushort _neflags, + load_info_t *loader); + + +/// Calls loader_t::process_archive() +/// For parameters and return value description +/// look at loader_t::process_archive(). +/// Additional parameter 'loader' is a pointer to ::load_info_t structure. + +idaman int ida_export process_archive( + qstring *temp_file, + linput_t *li, + qstring *module_name, + ushort *neflags, + const char *defmember, + const load_info_t *loader, + qstring *errbuf=nullptr); + +//-------------------------------------------------------------------------- +/// Output file types +enum ofile_type_t +{ + OFILE_MAP = 0, ///< MAP file + OFILE_EXE = 1, ///< Executable file + OFILE_IDC = 2, ///< IDC file + OFILE_LST = 3, ///< Disassembly listing + OFILE_ASM = 4, ///< Assembly + OFILE_DIF = 5, ///< Difference +}; + +//------------------------------------------------------------------ +/// Generate an output file. +/// \param otype type of output file. +/// \param fp the output file handle +/// \param ea1 start address. For some file types this argument is ignored +/// \param ea2 end address. For some file types this argument is ignored +/// as usual in ida, the end address of the range is not included +/// \param flags \ref GENFLG_ +/// +/// For ::OFILE_EXE: +/// \retval 0 can't generate exe file +/// \retval 1 ok +/// +/// For other file types: +/// \return number of the generated lines. -1 if an error occurred + +idaman int ida_export gen_file(ofile_type_t otype, FILE *fp, ea_t ea1, ea_t ea2, int flags); + +/// \defgroup GENFLG_ Generate file flags +/// Passed as 'flags' parameter to gen_file() +///@{ +#define GENFLG_MAPSEG 0x0001 ///< ::OFILE_MAP: generate map of segments +#define GENFLG_MAPNAME 0x0002 ///< ::OFILE_MAP: include dummy names +#define GENFLG_MAPDMNG 0x0004 ///< ::OFILE_MAP: demangle names +#define GENFLG_MAPLOC 0x0008 ///< ::OFILE_MAP: include local names +#define GENFLG_IDCTYPE 0x0008 ///< ::OFILE_IDC: gen only information about types +#define GENFLG_ASMTYPE 0x0010 ///< ::OFILE_ASM,::OFILE_LST: gen information about types too +#define GENFLG_GENHTML 0x0020 ///< ::OFILE_ASM,::OFILE_LST: generate html (::ui_genfile_callback will be used) +#define GENFLG_ASMINC 0x0040 ///< ::OFILE_ASM,::OFILE_LST: gen information only about types +///@} + +//---------------------------------------------------------------------- +// Helper functions for the loaders & ui +//---------------------------------------------------------------------- + +/// Load portion of file into the database. +/// This function will include (ea1..ea2) into the addressing space of the +/// program (make it enabled). +/// \param li pointer of input source +/// \param pos position in the file +/// \param ea1,ea2 range of destination linear addresses +/// \param patchable should the kernel remember correspondence of +/// file offsets to linear addresses. +/// \retval 1 ok +/// \retval 0 read error, a warning is displayed +/// \note The storage type of the specified range will be changed to STT_VA. + +idaman int ida_export file2base( + linput_t *li, + qoff64_t pos, + ea_t ea1, + ea_t ea2, + int patchable); + +#define FILEREG_PATCHABLE 1 ///< means that the input file may be + ///< patched (i.e. no compression, + ///< no iterated data, etc) +#define FILEREG_NOTPATCHABLE 0 ///< the data is kept in some encoded + ///< form in the file. + + +/// Load database from the memory. +/// This function works for wide byte processors too. +/// \param memptr pointer to buffer with bytes +/// \param ea1,ea2 range of destination linear addresses +/// \param fpos position in the input file the data is taken from. +/// if == -1, then no file position correspond to the data. +/// \return 1 always +/// \note The storage type of the specified range will be changed to STT_VA. + +idaman int ida_export mem2base(const void *memptr, ea_t ea1, ea_t ea2, qoff64_t fpos); + + +/// Unload database to a binary file. +/// This function works for wide byte processors too. +/// \param fp pointer to file +/// \param pos position in the file +/// \param ea1,ea2 range of source linear addresses +/// \return 1-ok(always), write error leads to immediate exit + +idaman int ida_export base2file(FILE *fp, qoff64_t pos, ea_t ea1, ea_t ea2); + + +/// Extract a module for an archive file. +/// Parse an archive file, show the list of modules to the user, allow him to +/// select a module, extract the selected module to a file (if the extract module +/// is an archive, repeat the process). +/// This function can handle ZIP, AR, AIXAR, OMFLIB files. +/// The temporary file will be automatically deleted by IDA at the end. +/// \param[in,out] filename in: input file. +/// out: name of the selected module. +/// \param bufsize size of the buffer with 'filename' +/// \param[out] temp_file_ptr will point to the name of the file that +/// contains the extracted module +/// \param is_remote is the input file remote? +/// \retval true ok +/// \retval false something bad happened (error message has been displayed to the user) + +idaman bool ida_export extract_module_from_archive( + char *filename, + size_t bufsize, + char **temp_file_ptr, + bool is_remote); + + +/// Add long comment at \inf{min_ea}. +/// - Input file: .... +/// - File format: .... +/// +/// This function should be called only from the loader to describe the input file. + +idaman void ida_export create_filename_cmt(void); + + +/// Get the input file type. +/// This function can recognize libraries and zip files. + +idaman filetype_t ida_export get_basic_file_type(linput_t *li); + + +/// Get name of the current file type. +/// The current file type is kept in \inf{filetype}. +/// \param buf buffer for the file type name +/// \param bufsize its size +/// \return size of answer, this function always succeeds + +idaman size_t ida_export get_file_type_name(char *buf, size_t bufsize); + + +//---------------------------------------------------------------------- +// Work with IDS files: read and use information from them +// + +/// See ::importer_t +struct impinfo_t +{ + const char *dllname; + void (idaapi*func)(uval_t num, const char *name, uval_t node); + uval_t node; +}; + + +/// Callback for checking dll module - passed to import_module(). +/// \param li pointer to input file +/// \param ii import info. +/// If the function finds that ii.dllname does not match +/// the module name passed to import_module(), it returns 0. \n +/// Otherwise it calls ii.func for each exported entry. \n +/// If ii.dllname==nullptr then ii.func will be called +/// with num==0 and name==dllname. +/// \retval 0 dllname doesn't match, import_module() should continue +/// \retval 1 ok + +typedef int idaapi importer_t(linput_t *li, impinfo_t *ii); + + +/// Find and import a DLL module. +/// This function adds information to the database (renames functions, etc). +/// \param module name of DLL +/// \param windir system directory with dlls +/// \param modnode node with information about imported entries. +/// either altval or supval arrays may be absent. +/// the node should never be deleted. +/// - imports by ordinals: +/// altval(ord) contains linear address +/// - imports by name: +/// supval(ea) contains the imported name +/// please use set_import_ordinal()/set_import_name() +/// to work with MODNODE +/// \param importer callback function (may be nullptr) to check dll module +/// \param ostype type of operating system (subdir name). +/// nullptr means the IDS directory itself (not recommended) + +idaman void ida_export import_module( + const char *module, + const char *windir, + uval_t modnode, + importer_t *importer, + const char *ostype); + + +/// Set information about the ordinal import entry. +/// This function performs 'modnode.altset(ord, ea2node(ea));' +/// \param modnode node with information about imported entries +/// \param ea linear address of the entry +/// \param ord ordinal number of the entry + +idaman void ida_export set_import_ordinal( + uval_t modnode, + ea_t ea, + uval_t ord); + + +/// Set information about the named import entry. +/// This function performs 'modnode.supset_ea(ea, name);' +/// \param modnode node with information about imported entries +/// \param ea linear address of the entry +/// \param name name of the entry + +idaman void ida_export set_import_name( + uval_t modnode, + ea_t ea, + const char *name); + + +/// Load and apply IDS file. +/// This function loads the specified IDS file and applies it to the database. +/// If the program imports functions from a module with the same name +/// as the name of the ids file being loaded, then only functions from this +/// module will be affected. Otherwise (i.e. when the program does not import +/// a module with this name) any function in the program may be affected. +/// \param fname name of file to apply +/// \retval 1 ok +/// \retval 0 some error (a message is displayed). +/// if the ids file does not exist, no message is displayed + +idaman int ida_export load_ids_module(char *fname); + + +//---------------------------------------------------------------------- +// DEFINITION OF PLUGIN MODULES +//---------------------------------------------------------------------- +/// A plugin is a module in the plugins subdirectory that can perform +/// an action asked by the user. (usually via pressing a hotkey) +class plugin_t +{ +public: + int version; ///< Should be equal to #IDP_INTERFACE_VERSION + int flags; ///< \ref PLUGIN_ +/// \defgroup PLUGIN_ Plugin features +/// Used by plugin_t::flags +///@{ +#define PLUGIN_MOD 0x0001 ///< Plugin changes the database. + ///< IDA won't call the plugin if + ///< the processor module prohibited any changes. +#define PLUGIN_DRAW 0x0002 ///< IDA should redraw everything after calling the plugin. +#define PLUGIN_SEG 0x0004 ///< Plugin may be applied only if the current address belongs to a segment +#define PLUGIN_UNL 0x0008 ///< Unload the plugin immediately after calling 'run'. + ///< This flag may be set anytime. + ///< The kernel checks it after each call to 'run' + ///< The main purpose of this flag is to ease + ///< the debugging of new plugins. +#define PLUGIN_HIDE 0x0010 ///< Plugin should not appear in the Edit, Plugins menu. + ///< This flag is checked at the start. +#define PLUGIN_DBG 0x0020 ///< A debugger plugin. init() should put + ///< the address of ::debugger_t to dbg. +#define PLUGIN_PROC 0x0040 ///< Load plugin when a processor module is loaded. (and keep it + ///< until the processor module is unloaded) +#define PLUGIN_FIX 0x0080 ///< Load plugin when IDA starts and keep it in the memory until IDA stops +#define PLUGIN_MULTI 0x0100 ///< The plugin can work with multiple idbs in parallel. + ///< init() returns a pointer to a plugmod_t object + ///< run/term functions are not used. + ///< Virtual functions of plugmod_t are used instead. +#define PLUGIN_SCRIPTED 0x8000 ///< Scripted plugin. Should not be used by plugins, + ///< the kernel sets it automatically. +///@} + + plugmod_t *(idaapi *init)(void); ///< Initialize plugin - returns a pointer to plugmod_t +#if !defined(NO_OBSOLETE_FUNCS) || defined(__DEFINE_PLUGIN_RETURN_CODES__) +/// \defgroup PLUGIN_INIT Plugin initialization codes +/// Return values for plugin_t::init() +/// Deprecated, please update your plugins to use PLUGIN_MULTI. +///@{ +#define PLUGIN_SKIP nullptr ///< Plugin doesn't want to be loaded +#define PLUGIN_OK ((plugmod_t *)1) ///< Plugin agrees to work with the current database. + ///< It will be loaded as soon as the user presses the hotkey +#define PLUGIN_KEEP ((plugmod_t *)2) ///< Plugin agrees to work with the current database and wants to stay in the memory +///@} +#endif + + void (idaapi *term)(void); ///< Terminate plugin. This function will be called + ///< when the plugin is unloaded. May be nullptr. + ///< Must be nullptr for PLUGIN_MULTI plugins + bool (idaapi *run)(size_t arg); ///< Invoke plugin. + ///< Must be nullptr for PLUGIN_MULTI plugins + const char *comment; ///< Long comment about the plugin. + ///< it could appear in the status line + ///< or as a hint + const char *help; ///< Multiline help about the plugin + const char *wanted_name; ///< The preferred short name of the plugin + const char *wanted_hotkey; ///< The preferred hotkey to run the plugin +}; + +#ifndef __X86__ + CASSERT(sizeof(plugin_t) == 64); +#else + CASSERT(sizeof(plugin_t) == 36); +#endif + +#if !defined(__KERNEL__) && !defined(PLUGIN_SUBMODULE) +idaman ida_module_data plugin_t PLUGIN; // (declaration for plugins) +#endif + +/// Get plugin options from the command line. +/// If the user has specified the options in the -Oplugin_name:options +/// format, them this function will return the 'options' part of it +/// The 'plugin' parameter should denote the plugin name +/// Returns nullptr if there we no options specified + +idaman const char *ida_export get_plugin_options(const char *plugin); + + +//-------------------------------------------------------------------------- +/// Pattern to find plugin files +#ifdef __EA64__ +# define PLUGIN_DLL "*." DLLEXT +#else +# define PLUGIN_DLL "*32." DLLEXT +#endif + + +// LOW LEVEL DLL LOADING FUNCTIONS +// Only the kernel should use these functions! +/// \cond +#define LNE_MAXSEG 10 // Max number of segments + +#if 0 +extern char dlldata[4096]; // Reserved place for DLL data +#define DLLDATASTART 0xA0 // Absolute offset of dlldata +extern char ldrdata[64]; // Reserved place for LOADER data +#define LDRDATASTART (DLLDATASTART+sizeof(dlldata)) // Absolute offset of ldrdata +#endif + +struct idadll_t +{ + void *dllinfo[LNE_MAXSEG]; + void *entry; // first entry point of DLL + idadll_t(void) { dllinfo[0] = nullptr; entry = nullptr; } + bool is_loaded(void) const { return dllinfo[0] != nullptr; } +}; + +#define MODULE_ENTRY_LOADER "LDSC" ///< name of loader entry point +#define MODULE_ENTRY_PLUGIN "PLUGIN" ///< name of plugin entry point +#define MODULE_ENTRY_IDP "LPH" ///< name of processor module entry point + +int _load_core_module( + idadll_t *dllmem, + const char *file, + const char *entry); + // dllmem - allocated segments + // dos: segment 1 (data) isn't allocated + // Returns 0 - ok, else: +#define RE_NOFILE 1 /* No such file */ +#define RE_NOTIDP 2 /* Not IDP file */ +#define RE_NOPAGE 3 /* Can't load: bad segments */ +#define RE_NOLINK 4 /* No linkage info */ +#define RE_BADRTP 5 /* Bad relocation type */ +#define RE_BADORD 6 /* Bad imported ordinal */ +#define RE_BADATP 7 /* Bad relocation atype */ +#define RE_BADMAP 8 /* DLLDATA offset is invalid */ + +void load_core_module_or_die( + idadll_t *dllmem, + const char *file, + const char *entry); +idaman bool ida_export load_core_module( + idadll_t *dllmem, + const char *file, + const char *entry); + +idaman void ida_export free_dll(idadll_t *dllmem); +/// \endcond + +/// Processor name +struct idp_name_t +{ + qstring lname; ///< long processor name + qstring sname; ///< short processor name + bool hidden; ///< is hidden + idp_name_t() : hidden(false) {} +}; +DECLARE_TYPE_AS_MOVABLE(idp_name_t); +typedef qvector<idp_name_t> idp_names_t; ///< vector of processor names + +/// Processor module description +struct idp_desc_t +{ + qstring path; ///< module file name + time_t mtime; ///< time of last modification + qstring family; ///< processor's family + idp_names_t names; ///< processor names + bool is_script; ///< the processor module is a script + bool checked; ///< internal, for cache management + idp_desc_t(): mtime(time_t(-1)), is_script(false), checked(false) {} +}; +DECLARE_TYPE_AS_MOVABLE(idp_desc_t); +typedef qvector<idp_desc_t> idp_descs_t; ///< vector of processor module descriptions + + +/// Get IDA processor modules descriptions + +idaman const idp_descs_t *ida_export get_idp_descs(void); + + +//-------------------------------------------------------------------------- +/// \def{IDP_DLL, Pattern to find idp files} +#ifdef __EA64__ +# define IDP_DLL "*." DLLEXT +#else +# define IDP_DLL "*32." DLLEXT +#endif + + +//-------------------------------------------------------------------------- +/// Structure to store plugin information +struct plugin_info_t +{ + plugin_info_t *next; ///< next plugin information + char *path; ///< full path to the plugin + char *org_name; ///< original short name of the plugin + char *name; ///< short name of the plugin + ///< it will appear in the menu + ushort org_hotkey; ///< original hotkey to run the plugin + ushort hotkey; ///< current hotkey to run the plugin + size_t arg; ///< argument used to call the plugin + plugin_t *entry; ///< pointer to the plugin if it is already loaded + idadll_t dllmem; + int flags; ///< a copy of plugin_t::flags + char *comment; ///< a copy of plugin_t::comment + qstring idaplg_name; ///< "name" provided by ida-plugin.json or basename of path (without extension) + plugin_info_t() + { + memset(this, 0, sizeof(*this)); + } +}; + + +/// Get pointer to the list of plugins. (some plugins might be listed several times +/// in the list - once for each configured argument) + +idaman plugin_info_t *ida_export get_plugins(void); + + +/// Find a user-defined plugin and optionally load it. +/// \param name short plugin name without path and extension, +/// or absolute path to the file name +/// \param load_if_needed if the plugin is not present in the memory, try to load it +/// \return pointer to plugin description block + +idaman plugin_t *ida_export find_plugin(const char *name, bool load_if_needed=false); + +inline plugin_t *load_plugin(const char *name) +{ + return find_plugin(name, true); +} + + +/// Run a loaded plugin with the specified argument. +/// \param ptr pointer to plugin description block +/// \param arg argument to run with + +idaman bool ida_export run_plugin(const plugin_t *ptr, size_t arg); + + +/// Load & run a plugin + +inline bool idaapi load_and_run_plugin(const char *name, size_t arg) +{ + return run_plugin(load_plugin(name), arg); +} + + +/// Run a plugin as configured. +/// \param ptr pointer to plugin information block + +idaman bool ida_export invoke_plugin(plugin_info_t *ptr); + + +/// Information for the user interface about available debuggers +struct dbg_info_t +{ + plugin_info_t *pi; + struct debugger_t *dbg; + dbg_info_t(plugin_info_t *_pi, struct debugger_t *_dbg) : pi(_pi), dbg(_dbg) {} +}; +DECLARE_TYPE_AS_MOVABLE(dbg_info_t); + + +/// Get information about available debuggers + +idaman size_t ida_export get_debugger_plugins(const dbg_info_t **array); + + +/// Initialize plugins with the specified flag + +idaman void ida_export init_plugins(int flag); + + +/// Terminate plugins with the specified flag + +idaman void ida_export term_plugins(int flag); + + +//------------------------------------------------------------------------ + +/// Get offset in the input file which corresponds to the given ea. +/// If the specified ea can't be mapped into the input file offset, +/// return -1. + +idaman qoff64_t ida_export get_fileregion_offset(ea_t ea); + + +/// Get linear address which corresponds to the specified input file offset. +/// If can't be found, return #BADADDR + +idaman ea_t ida_export get_fileregion_ea(qoff64_t offset); + + +//------------------------------------------------------------------------ +/// Generate an exe file (unload the database in binary form). +/// \return fp the output file handle. if fp == nullptr then return: +/// - 1: can generate an executable file +/// - 0: can't generate an executable file +/// \retval 1 ok +/// \retval 0 failed + +idaman int ida_export gen_exe_file(FILE *fp); + + +//------------------------------------------------------------------------ +/// Reload the input file. +/// This function reloads the byte values from the input file. +/// It doesn't modify the segmentation, names, comments, etc. +/// \param file name of the input file. if file == nullptr then returns: +/// - 1: can reload the input file +/// - 0: can't reload the input file +/// \param is_remote is the file located on a remote computer with +/// the debugger server? +/// \return success + +idaman bool ida_export reload_file(const char *file, bool is_remote); + + +//--------------------------------------------------------------------------- +// S N A P S H O T F U N C T I O N S + +/// Maximum database snapshot description length +#define MAX_DATABASE_DESCRIPTION 128 + +class snapshot_t; +typedef qvector<snapshot_t *> snapshots_t; ///< vector of database snapshots + +/// Snapshot attributes +class snapshot_t +{ +private: + snapshot_t &operator=(const snapshot_t &); + snapshot_t(const snapshot_t &); + + int compare(const snapshot_t &r) const + { + return ::compare(id, r.id); + } + +public: + qtime64_t id = 0; ///< snapshot ID. This value is computed using qgettimeofday() + uint16 flags = 0; ///< \ref SSF_ +/// \defgroup SSF_ Snapshot flags +/// Used by snapshot_t::flags +///@{ +#define SSF_AUTOMATIC 0x0001 ///< automatic snapshot +///@} + char desc[MAX_DATABASE_DESCRIPTION]; ///< snapshot description + char filename[QMAXPATH]; ///< snapshot file name + snapshots_t children; ///< snapshot children + DEFINE_MEMORY_ALLOCATION_FUNCS() + DECLARE_COMPARISON_OPERATORS(snapshot_t) + void clear() + { + for ( snapshots_t::iterator p=children.begin(); + p != children.end(); + ++p ) + { + delete *p; + } + children.clear(); + } + + snapshot_t() + { + desc[0] = '\0'; + filename[0] = '\0'; + } + + ~snapshot_t() + { + clear(); + } +}; +DECLARE_TYPE_AS_MOVABLE(snapshot_t); + +//------------------------------------------------------------------------ +/// Build the snapshot tree. +/// \param root snapshot root that will contain the snapshot tree elements. +/// \return success + +idaman bool ida_export build_snapshot_tree(snapshot_t *root); + + +//------------------------------------------------------------------------ +/// Update the snapshot attributes. +/// \note only the snapshot description can be updated. +/// \param filename snapshot file name or nullptr for the current database +/// \param root snapshot root (returned from build_snapshot_tree()) +/// \param attr snapshot instance containing the updated attributes +/// \param uf \ref SSUF_ +/// \return success +idaman bool ida_export update_snapshot_attributes( + const char *filename, + const snapshot_t *root, + const snapshot_t *attr, + int uf); + +/// \defgroup SSUF_ Snapshot update flags +/// Passed as 'uf' parameter to update_snapshot_attributes() +///@{ +#define SSUF_DESC 0x00000001 ///< Update the description +#define SSUF_PATH 0x00000002 ///< Update the path +#define SSUF_FLAGS 0x00000004 ///< Update the flags +///@} + +//------------------------------------------------------------------------ +/// Visit the snapshot tree. +/// \param root snapshot root to start the enumeration from +/// \param callback callback called for each child. return 0 to continue enumeration +/// and non-zero to abort enumeration +/// \param ud user data. will be passed back to the callback +/// \return true-ok, false-failed + +idaman int ida_export visit_snapshot_tree( + snapshot_t *root, + int (idaapi *callback)(snapshot_t *ss, void *ud), + void *ud=nullptr); + + +/// Flush buffers to the disk + +idaman int ida_export flush_buffers(void); + + +/// Is the database considered as trusted? + +idaman bool ida_export is_trusted_idb(void); + +//------------------------------------------------------------------------ +/// Save current database using a new file name. +/// \param outfile output database file name; nullptr means the current path +/// \param flags \ref DBFL_; -1 means the current flags +/// \param root optional: snapshot tree root. +/// \param attr optional: snapshot attributes +/// \note when both root and attr are not nullptr then the snapshot +/// attributes will be updated, otherwise the snapshot attributes +/// will be inherited from the current database. +/// \return success + +idaman bool ida_export save_database( + const char *outfile=nullptr, + uint32 flags=-1, + const snapshot_t *root = nullptr, + const snapshot_t *attr = nullptr); + +/// \defgroup DBFL_ Database flags +/// Used to manage saving/closing of a database +///@{ +#define DBFL_KILL 0x01 ///< delete unpacked database +#define DBFL_COMP 0x02 ///< collect garbage +#define DBFL_BAK 0x04 ///< create backup file (if !DBFL_KILL) +#define DBFL_TEMP 0x08 ///< temporary database +///@} + +/// Get the current database flag +/// \param dbfl flag \ref DBFL_ +/// \returns the state of the flag (set or cleared) + +idaman bool ida_export is_database_flag(uint32 dbfl); + +/// Set or clear database flag +/// \param dbfl flag \ref DBFL_ +/// \param cnd set if true or clear flag otherwise + +idaman void ida_export set_database_flag(uint32 dbfl, bool cnd=true); +inline void clr_database_flag(uint32 dbfl) { set_database_flag(dbfl, false); } + +/// Is a temporary database? +inline bool is_temp_database(void) { return is_database_flag(DBFL_TEMP); } + + +//------------------------------------------------------------------------ +/// \defgroup PATH_TYPE_ Types of the file pathes +///@{ +enum path_type_t +{ + PATH_TYPE_CMD, ///< full path to the file specified in the command line + PATH_TYPE_IDB, ///< full path of IDB file + PATH_TYPE_ID0, ///< full path of ID0 file +}; +///@} + +/// Get the file path +/// \param pt file path type \ref PATH_TYPE_ +/// \returns file path, never returns nullptr +idaman const char *ida_export get_path(path_type_t pt); + +/// Set the file path +/// \param pt file path type \ref PATH_TYPE_ +/// \param path new file path, +/// use nullptr or empty string to clear the file path +idaman void ida_export set_path(path_type_t pt, const char *path); + + +/// Check the file extension +/// \returns true if it is the reserved extension +idaman bool ida_export is_database_ext(const char *ext); + + +/// Get the value of the ELF_DEBUG_FILE_DIRECTORY configuration +/// directive. +idaman const char *ida_export get_elf_debug_file_directory(); + +/// \cond + +#endif + +``` + +`IdaSDK/make_script_ns.hpp`: + +```hpp +#ifndef MAKE_NS_SCRIPT_H +#define MAKE_NS_SCRIPT_H + +#include <pro.h> + +//-------------------------------------------------------------------------- +idaman void ida_export plugin_name_from_path_or_name( + qstring *out_plg_name, + const char *path); + +//-------------------------------------------------------------------------- +idaman void ida_export make_script_ns( + qstring *out, + const char *module_type, + const char *name); + +#endif /* MAKE_NS_SCRIPT_H */ + +``` + +`IdaSDK/md5.h`: + +```h +#ifndef MD5_H +#define MD5_H + +struct MD5Context +{ + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; + + MD5Context() { memset(this, 0, sizeof(*this)); } +}; + +idaman THREAD_SAFE void ida_export MD5Init(MD5Context *context); +idaman THREAD_SAFE void ida_export MD5Update(MD5Context *context, const void *buf, size_t len); +idaman THREAD_SAFE void ida_export MD5Final(uchar digest[16], MD5Context *context); +idaman THREAD_SAFE void ida_export MD5Transform(uint32 buf[4], uint32 const in[16]); + +//--------------------------------------------------------------------------- +struct md5_t +{ + uchar hash[16]; + + md5_t() { clear(); } + md5_t(const md5_t &r) { assign(r); } + + const uchar &operator[](size_t i) const { return hash[i]; } + uchar &operator[](size_t i) { return hash[i]; } + + void clear() { memset(hash, 0, sizeof(hash)); } + void swap(md5_t &other) { std::swap(*this, other); } + + md5_t &operator=(const md5_t &r) + { + if ( this != &r ) + assign(r); + return *this; + } + + DECLARE_COMPARISONS(md5_t) + { + return memcmp(hash, r.hash, sizeof(hash)); + } + bool empty() const + { + for ( size_t i=0; i < sizeof(hash); i++ ) + if ( hash[i] != 0 ) + return false; + return true; + } + + +protected: + void assign(const md5_t &r) { memmove(hash, r.hash, sizeof(hash)); } +}; +DECLARE_TYPE_AS_MOVABLE(md5_t); +typedef qvector<md5_t> md5_vec_t; + +struct md5_size_t +{ + int64 size = 0; + md5_t md5; + bool empty() const { return md5.empty(); } + DECLARE_COMPARISONS(md5_size_t) + { + COMPARE_FIELDS(size); + COMPARE_FIELDS(md5); + return 0; + } + // there is no implementation for these methods in this directory + // (they are used by vault, and the implementation is autogenerated there) + void serialize(bytevec_t *out, int version) const; + bool deserialize(const uchar **ptr, size_t len, int version); +}; +DECLARE_TYPE_AS_MOVABLE(md5_size_t); +typedef qvector<md5_size_t> md5_sizes_t; + +#endif /* !MD5_H */ + +``` + +`IdaSDK/merge.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 2005-2025 Hex-Rays SA <support@hex-rays.com> + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _MERGE_HPP +#define _MERGE_HPP + +#include <functional> +#include <queue> +#include <nalt.hpp> +#include <diff3.hpp> + +/*! \file merge.hpp + + \brief Merge functionality. + + NOTE: this functionality is available in IDA Teams (not IDA Pro) + + There are 3 databases involved in merging: base_idb, local_db, and remote_idb. + - base_idb: the common base ancestor of 'local_db' and 'remote_db'. + in the UI this database is located in the middle. + - local_idb: local database that will contain the result of the merging. + in the UI this database is located on the left. + - remote_idb: remote database that will merge into local_idb. It may reside + locally on the current computer, despite its name. + in the UI this database is located on the right. + base_idb and remote_idb are opened for reading only. + base_idb may be absent, in this case a 2-way merging is performed. + + Conflicts can be resolved automatically or interactively. + The automatic resolving scores the conflicting blocks and takes the better one. + The interactive resolving displays the full rendered contents side by + side, and expects the user to select the better side for each conflict. + + Since IDB files contain various kinds of information, there are many + merging phases. The entire list can be found in merge.cpp. Below are + just some selected examples: + - merge global database settings (inf and other global vars) + - merge segmentation and changes to the database bytes + - merge various lists: exports, imports, loaded tils, etc + - merge names, functions, function frames + - merge debugger settings, breakpoints + - merge struct/enum views + - merge local type libraries + - merge the disassembly items (i.e. the segment contents) + this includes operand types, code/data separation, etc + - merge plugin specific info like decompiler types, dwarf mappings, etc + + To unify UI elements of each merge phase, we use merger views: + - A view that consists of 2 or 3 panes: left (local_idb) and right (remote_idb). + The common base is in the middle, if present. + - Rendering of the panes depends on the phase, different phases + show different contents. + - The conflicts are highlighted by a colored background. Also, + the detail pane can be consulted for additional info. + - The user can select a conflict (or a bunch of conflicts) and say + "use this block". + - The user can browse the panes as he wishes. He will not be forced + to handle conflicts in any particular order. However, once he + finishes working with a merge handler and proceeds to the next one, + he cannot go back. + - Scrolling the left pane will synchronously scroll the right pane + and vice versa. + - There are the navigation commands like "go to the prev/next conflict" + - The number of remaining conflicts to resolve is printed in the + "Progress" chooser. + - The user may manually modify local database inside the merger view. + For that he may use the regular hotkeys. However, editing the database + may lead to new conflicts, so we better restrict the available actions + to some reasonable minimum. Currently, this is not implemented. + + IDA works in a new "merge" mode during merging. In this mode most + events are not generated. We forbid them to reduce the risk that a rogue + third-party plugin that is not aware of the "merge" mode would spoil something. + + For example, normally renaming a function causes a cascade of events + and may lead to other database modifications. Some of them may be + desired, some - not. Since there are some undesired events, it is + better to stop generating them. However, some events are required to + render the disassembly listing. For example, ev_ana_insn, av_out_insn. + This is why some events are still generated in the "merge" mode. + + To let processor modules and plugins merge their data, we introduce + a new event: ev_create_merge_handlers. It is generated immediately after + opening all three idbs. The interested modules should react to this + event by creating new merge handlers, if they need them. + + While the kernel can create arbitrary merge handlers, modules + can create only the standard ones returned by: + + create_nodeval_merge_handler() + create_nodeval_merge_handlers() + create_std_modmerge_handlers() + + We do not document merge_handler_t because once a merge handler is + created, it is used exclusively by the kernel. + + See mergemod.hpp for more information about the merge mode for modules. +*/ + +#ifdef switch_dbctx + #define ui_switch_dbctx_guard switch_dbctx + #undef switch_dbctx +#endif + +//------------------------------------------------------------------------ +/// Kinds of merge handlers. +enum merge_kind_t ENUM_SIZE(uint32) +{ + MERGE_KIND_NETNODE, ///< netnode (no merging, to be used in idbunits) + MERGE_KIND_AUTOQ, ///< auto queues + MERGE_KIND_INF, ///< merge the inf variable (global settings) + MERGE_KIND_ENCODINGS, ///< merge encodings + MERGE_KIND_ENCODINGS2, ///< merge default encodings + MERGE_KIND_SCRIPTS2, ///< merge scripts common info + MERGE_KIND_SCRIPTS, ///< merge scripts + MERGE_KIND_CUSTDATA, ///< merge custom data type and formats + MERGE_KIND_CUSTCNV, ///< merge custom calling conventions + MERGE_KIND_ENUMS, ///< merge enums + MERGE_KIND_STRUCTS, ///< merge structs (globally: add/delete structs entirely) + MERGE_KIND_TILS, ///< merge type libraries + MERGE_KIND_TINFO, ///< merge tinfo + MERGE_KIND_STRMEM, ///< merge struct members + MERGE_KIND_UDTMEM, ///< merge UDT members (local types) + MERGE_KIND_GHSTRCMT, ///< merge ghost structure comment + MERGE_KIND_STRMEMCMT, ///< merge member comments for ghost struc + MERGE_KIND_SELECTORS, ///< merge selectors + MERGE_KIND_STT, ///< merge flag storage types + MERGE_KIND_SEGMENTS, ///< merge segments + MERGE_KIND_SEGGRPS, ///< merge segment groups + MERGE_KIND_SEGREGS, ///< merge segment registers + MERGE_KIND_ORPHANS, ///< merge orphan bytes + MERGE_KIND_BYTEVAL, ///< merge byte values + MERGE_KIND_FIXUPS, ///< merge fixups + MERGE_KIND_MAPPING, ///< merge manual memory mapping + MERGE_KIND_EXPORTS, ///< merge exports + MERGE_KIND_IMPORTS, ///< merge imports + MERGE_KIND_PATCHES, ///< merge patched bytes + MERGE_KIND_FLAGS, ///< merge flags64_t + MERGE_KIND_EXTRACMT, ///< merge extra next or prev lines + MERGE_KIND_AFLAGS_EA, ///< merge aflags for mapped EA + MERGE_KIND_IGNOREMICRO, ///< IM ("$ ignore micro") flags + MERGE_KIND_FILEREGIONS, ///< merge fileregions + MERGE_KIND_HIDDENRANGES, ///< merge hidden ranges + MERGE_KIND_SOURCEFILES, ///< merge source files ranges + MERGE_KIND_FUNC, ///< merge func info + MERGE_KIND_FRAMEMGR, ///< merge frames (globally: add/delete frames entirely) + MERGE_KIND_FRAME, ///< merge function frame info (frame members) + MERGE_KIND_STKPNTS, ///< merge SP change points + MERGE_KIND_FLOWS, ///< merge flows + MERGE_KIND_CREFS, ///< merge crefs + MERGE_KIND_DREFS, ///< merge drefs + MERGE_KIND_BPTS, ///< merge breakpoints + MERGE_KIND_WATCHPOINTS, ///< merge watchpoints + MERGE_KIND_BOOKMARKS, ///< merge bookmarks + MERGE_KIND_TRYBLKS, ///< merge try blocks + MERGE_KIND_DIRTREE, ///< merge std dirtrees + MERGE_KIND_VFTABLES, ///< merge vftables + MERGE_KIND_SIGNATURES, ///< signatures + MERGE_KIND_PROBLEMS, ///< problems + MERGE_KIND_UI, ///< UI + MERGE_KIND_DEKSTOPS, ///< dekstops + MERGE_KIND_NOTEPAD, ///< notepad + MERGE_KIND_LOADER, ///< loader data + MERGE_KIND_DEBUGGER, ///< debugger data + MERGE_KIND_DBG_MEMREGS, ///< manual memory regions (debugger) + MERGE_KIND_LUMINA, ///< lumina function metadata + MERGE_KIND_LAST, ///< last predefined merge handler type. + ///< please note that there can be more merge handler types, + ///< registered by plugins and processor modules. + MERGE_KIND_END = merge_kind_t(-2), + ///< insert to the end of handler list, + ///< valid for merge_handler_params_t::insert_after + MERGE_KIND_NONE = merge_kind_t(-1) +}; + +//------------------------------------------------------------------------ +class merge_handler_t; +class merge_data_t; +using merge_handlers_t = qvector<merge_handler_t *>; +//-------------------------------------------------------------------------- +/// Return TRUE if IDA is running in diff mode (MERGE_POLICY_MDIFF/MERGE_POLICY_VDIFF) +idaman bool ida_export is_diff_merge_mode(); + +//-------------------------------------------------------------------------- +/// class to contain public info about the merge process +class merge_data_t +{ +public: + /// several items can be grouped into a block. It is more natural to + /// consider all items in a block as a whole (e.g. IT-blocks in ARM). + /// This class can be used to detect a block containing an instruction + /// and to setup internal block-specific data before updating of an item + /// that is a parts of a block + struct item_block_locator_t + { + /// get block address (address of first item in the block) by address of item + /// this function returns item address if it does not belong to any block + virtual ea_t get_block_head(merge_data_t &md, diff_source_idx_t idx, ea_t item_head) = 0; + + /// setup block-specific info before region updating, return FALSE if + /// nothing was changed + virtual bool setup_blocks( + merge_data_t &md, + diff_source_idx_t from, + diff_source_idx_t to, + const diff_range_t ®ion) = 0; + + virtual ~item_block_locator_t() {} + }; + + class merge_mappers_t &mappers; + + int dbctx_ids[3] = { -1, -1, -1 }; ///< local, remote, base ids + int nbases = 0; ///< number of database participating in merge process, + ///< maybe 2 or 3 + merge_handlers_t ev_handlers; ///< event handlers + item_block_locator_t *item_block_locator = nullptr; + merge_handler_t *last_udt_related_merger = nullptr; + + merge_data_t(); + virtual ~merge_data_t(); + merge_data_t(const merge_data_t &) = delete; + void operator=(const merge_data_t &) = delete; + + void set_dbctx_ids(int local, int remote, int base) + { + dbctx_ids[LOCAL_IDX] = local; + dbctx_ids[REMOTE_IDX] = remote; + dbctx_ids[BASE_IDX] = base; + nbases = base != -1 ? 3 : 2; + } + + int local_id() const { return dbctx_ids[LOCAL_IDX]; } + int remote_id() const { return dbctx_ids[REMOTE_IDX]; } + int base_id() const { return dbctx_ids[BASE_IDX]; } + + void add_event_handler(merge_handler_t *handler) { ev_handlers.push_back(handler); } + void remove_event_handler(merge_handler_t *handler) { ev_handlers.del(handler); } + + ea_t get_block_head(diff_source_idx_t idx, ea_t item_head) + { + return item_block_locator == nullptr + ? item_head + : item_block_locator->get_block_head(*this, idx, item_head); + } + bool setup_blocks(diff_source_idx_t dst_idx, diff_source_idx_t src_idx, const diff_range_t ®ion) + { + return item_block_locator != nullptr + && item_block_locator->setup_blocks(*this, dst_idx, src_idx, region); + } + + // make these functions virtual to be available from plugins + + /// check that node exists in any of databases + virtual bool has_existing_node(const char *nodename) const; + + /// map IDs of structures, enumerations and their members + /// \param[out] tid item ID in TO database + /// \param ea item ID to find counterpart + /// \param from source database index, \ref diff_source_idx_t + /// \param to destination database index, \ref diff_source_idx_t + /// \param strict raise interr if could not map + /// \return success + virtual bool map_privrange_id( + tid_t *tid, + ea_t ea, + diff_source_idx_t from, + diff_source_idx_t to, + bool strict=true); + + /// migrate type, + /// replaces type references into FROM database to references into TO database + /// \param[inout] tif type to migrate, will be cleared in case of fail + /// \param from source database index, \ref diff_source_idx_t + /// \param to destination database index, \ref diff_source_idx_t + /// \param strict raise interr if could not map + /// \return success + virtual bool map_tinfo( + tinfo_t *tif, + diff_source_idx_t from, + diff_source_idx_t to, + bool strict=true); + + /// compare types from two databases + /// \param tif1 type + /// \param diffidx1 database index, \ref diff_source_idx_t + /// \param tif2 type + /// \param diffidx2 database index, \ref diff_source_idx_t + /// \return -1, 0, 1 + virtual int compare_merging_tifs( + const tinfo_t &tif1, + diff_source_idx_t diffidx1, + const tinfo_t &tif2, + diff_source_idx_t diffidx2) const; +}; + +//------------------------------------------------------------------------ +/// Merge handler parameters. +/// They describe the handler label, its kind (which is usually automatically +/// allocated by IDA), and various flags. +/// The MH_UI_... flags describe how the merge differences will be displayed to the user. +/// By default they are displayed as a list (using a chooser), and a detail pane +/// at the bottom. +/// Currently the chooser columns are automatically determined based on the diffpos +/// name. A diffpos name is a textual representation of an object that we compare +/// during merging. For example, if 2 IDBs have different imagebases, then the user +/// would see diffpos names like the following: +/// addresses.imagebase: 0x8048000 +/// addresses.imagebase: 0xA000000 +/// Specifying MH_UI_COLONNAME would cause IDA to create a 2-column chooser for +/// this merge handler. +struct merge_handler_params_t +{ + merge_data_t &md; + qstring label; + merge_kind_t kind; ///< merge handler kind \ref merge_kind_t + merge_kind_t insert_after; ///< desired position inside 'handlers' \ref merge_kind_t + uint32 mh_flags; +#define MH_LISTEN 0x00000001 ///< merge handler will receive merge events +#define MH_TERSE 0x00000002 ///< do not display equal lines in the merge results table +#define MH_UI_NODETAILS 0x00000100 ///< ida will not show the diffpos details +#define MH_UI_COMPLEX 0x00000200 ///< diffpos details won't be displayed in the diffpos chooser +#define MH_UI_DP_NOLINEDIFF 0x00000400 ///< Detail pane: do not show differences inside the line +#define MH_UI_DP_SHORTNAME 0x00000800 ///< Detail pane: use the first part of a complex diffpos name as the tree node name +#define MH_UI_INDENT 0x00001000 ///< preserve indent for diffpos name in diffpos chooser +#define MH_UI_SPLITNAME 0x00800000 ///< ida will split the diffpos name by 7-bit ASCII char + ///< to create chooser columns +#define MH_UI_CHAR_MASK 0x007F0000 ///< 7-bit ASCII split character +#define MH_UI_DEF_CHAR(v) ((((v) & 0x7F) << 16) | MH_UI_SPLITNAME) + ///< define split char +#ifndef SWIG +#define MH_UI_COMMANAME MH_UI_DEF_CHAR(',') +#else +#define MH_UI_COMMANAME 0x00AC0000 +#endif + ///< ida will split the diffpos name by ',' to create chooser columns +#ifndef SWIG +#define MH_UI_COLONNAME MH_UI_DEF_CHAR(':') +#else +#define MH_UI_COLONNAME 0x00BA0000 +#endif + ///< ida will split the diffpos name by ':' to create chooser columns +#define MH_DUMMY 0x80000000 ///< dummy entry - just to fill an array slot + + merge_handler_params_t( + merge_data_t &_md, + const qstring &_label, + merge_kind_t _kind, + merge_kind_t _insert_after, + uint32 _mh_flags) + : md(_md), + label(_label), + kind(_kind), + insert_after(_insert_after), + mh_flags(_mh_flags) + {} + + /// Should IDA display the diffpos detail pane? + static bool ui_has_details(uint32 _mh_flags) { return (_mh_flags & MH_UI_NODETAILS) == 0; } + bool ui_has_details() const { return ui_has_details(mh_flags); } + + /// Do not display the diffpos details in the chooser. + /// For example, the MERGE_KIND_SCRIPTS handler puts the script body as the + /// diffpos detail. It would not be great to show them as part of the chooser. + static bool ui_complex_details(uint32 _mh_flags) { return (_mh_flags & MH_UI_COMPLEX) != 0; } + bool ui_complex_details() const { return ui_complex_details(mh_flags); } + + /// It customary to create long diffpos names having many components that + /// are separated by any 7-bit ASCII character (besides of '\0'). + /// In this case it is possible to instruct IDA to use this separator + /// to create a multi-column chooser. + /// For example the MERGE_KIND_ENUMS handler has the following diffpos name: + /// enum_1,enum_2 + /// If MH_UI_COMMANAME is specified, IDA will create 2 columns for these names. + static bool ui_complex_name(uint32 _mh_flags) { return (_mh_flags & MH_UI_SPLITNAME) != 0; } + bool ui_complex_name() const { return ui_complex_name(mh_flags); } + static char ui_split_char(uint32 _mh_flags) { return (_mh_flags >> 16) & 0x7F; } + char ui_split_char() const { return ui_split_char(mh_flags); } + static qstring ui_split_str(uint32 _mh_flags) { return qstring(1, ui_split_char(_mh_flags)); } + qstring ui_split_str() const { return ui_split_str(mh_flags); } + + /// The detail pane shows the diffpos details for the current diffpos range + /// as a tree-like view. In this pane the diffpos names are used as tree node + /// names and the diffpos details as their children. + /// Sometimes, for complex diffpos names, the first part of the name + /// looks better than the entire name. + /// For example, the MERGE_KIND_SEGMENTS handler has the following diffpos name: + /// <range>,<segm1>,<segm2>,<segm3> + /// if MH_UI_DP_SHORTNAME is specified, IDA will use <range> as a tree node name + static bool ui_dp_shortname(uint32 _mh_flags) { return (_mh_flags & MH_UI_DP_SHORTNAME) != 0; } + bool ui_dp_shortname() const { return ui_dp_shortname(mh_flags); } + + /// In detail pane IDA shows difference between diffpos details. + /// IDA marks added or deleted detail by color. + /// In the modified detail the changes are marked. + /// Use this UI hint if you do not want to show the differences inside detail. + static bool ui_linediff(uint32 _mh_flags) { return (_mh_flags & MH_UI_DP_NOLINEDIFF) == 0; } + bool ui_linediff() const { return ui_linediff(mh_flags); } + + /// In the ordinary situation the spaces from the both sides of diffpos name are trimmed. + /// Use this UI hint to preserve the leading spaces. + static bool ui_indent(uint32 _mh_flags) { return (_mh_flags & MH_UI_INDENT) != 0; } + bool ui_indent() const { return ui_indent(mh_flags); } +}; + +//------------------------------------------------------------------------ + +/// helper class for module data diff source +struct moddata_diff_helper_t +{ + const char *module_name = nullptr; ///< will be used as a prefix for field desc + const char *netnode_name = nullptr; ///< name of netnode with module data attributes + const idbattr_info_t *fields = nullptr; ///< module data attribute descriptions + size_t nfields = 0; ///< number of descriptions + uint32 additional_mh_flags = MH_UI_NODETAILS; + ///< additional merge handler flags + + moddata_diff_helper_t( + const char *_module_name, + const char *_netnode_name, + const idbattr_info_t *_fields, + size_t _nfields) + : module_name(_module_name), + netnode_name(_netnode_name), + fields(_fields), + nfields(_nfields) + { + } + virtual ~moddata_diff_helper_t() {} + + virtual void merge_starting(diff_source_idx_t /*diffidx*/, void * /*module_data*/) {} + virtual void merge_ending(diff_source_idx_t /*diffidx*/, void * /*module_data*/) {} + virtual void *get_struc_ptr(merge_data_t &/*md*/, diff_source_idx_t /*diffidx*/, const idbattr_info_t &/*fi*/) { INTERR(2048); } + virtual void print_diffpos_details(qstrvec_t * /*out*/, const idbattr_info_t &/*fi*/) {} + virtual bool val2str(qstring * /*out*/, const idbattr_info_t &/*fi*/, uint64 /*value*/) { return false; } + virtual bool str2val(uint64 * /*out*/, const idbattr_info_t &/*fi*/, const char * /*strvals*/) { return false; } +}; +/// netnode value modificators (to be used in nodeval_diff_source, see below) +enum nds_flags_t +{ + NDS_IS_BOOL = 0x0001, ///< boolean value + NDS_IS_EA = 0x0002, ///< EA value + NDS_IS_RELATIVE = 0x0004, ///< value is relative to index (stored as delta) + NDS_IS_STR = 0x0008, ///< string value + NDS_SUPVAL = 0x0010, ///< stored as netnode supvals (not scalar) + NDS_BLOB = 0x0020, ///< stored as netnode blobs + NDS_EV_RANGE = 0x0040, ///< enable default handling of mev_modified_ranges, mev_deleting_segm + NDS_EV_FUNC = 0x0080, ///< enable default handling of mev_added_func/mev_deleting_func + NDS_MAP_IDX = 0x0100, ///< apply ea2node() to index (==NETMAP_IDX) + NDS_MAP_VAL = 0x0200, ///< apply ea2node() to value. Along with NDS_INC + ///< it gives effect of NETMAP_VAL, examples: + ///< altval_ea : NDS_MAP_IDX + ///< charval : NDS_VAL8 + ///< charval_ea: NDS_MAP_IDX|NDS_VAL8 + ///< eaget : NDS_MAP_IDX|NDS_MAP_VAL|NDS_INC + NDS_VAL8 = 0x1000, ///< use 8-bit values (==NETMAP_V8) + NDS_INC = 0x2000, ///< stored value is incremented (scalars only) + NDS_UI_ND = 0x4000, ///< UI: no need to show diffpos detail pane, \ref MH_UI_NODETAILS, + ///< make sense if \ref merge_node_helper_t is used +}; + +//-------------------------------------------------------------------------- +/// abstract adapter to provide access to non-standard netnode array entries +struct merge_node_helper_t +{ + virtual ~merge_node_helper_t() {} + + /// print the name of the specified entry + /// (to be used in print_diffpos_name) + virtual qstring print_entry_name(uchar /*tag*/, nodeidx_t /*ndx*/, void * /*module_data*/) const { return qstring(); } + + /// print the details of the specified entry + /// usually contains multiple lines, one for each attribute or detail. + /// (to be used in print_diffpos_details) + virtual void print_entry_details(qstrvec_t * /*out*/, uchar /*tag*/, nodeidx_t /*ndx*/, void * /*module_data*/) const {} + + /// get column headers for chooser + /// (to be used in linear_diff_source_t::get_column_headers) + virtual void get_column_headers(qstrvec_t * /*headers*/, uchar /*tag*/, void * /*module_data*/) const {} + + /// filter: check if we should perform merging for given record + virtual bool is_mergeable(uchar /*tag*/, nodeidx_t /*ndx*/) const { return true; } + + /// return netnode to be used as source. If this function returns BADNODE + /// netnode will be created using netnode name passed to create_nodeval_diff_source + virtual netnode get_netnode() const { return BADNODE; } + + /// map scalar/string/buffered value + virtual void map_scalar( + nodeidx_t * /*scalar_value*/, + void * /*module_data*/, + diff_source_idx_t /*from*/, + diff_source_idx_t /*to*/) const + { + } + virtual void map_string( + qstring * /*string_value*/, + void * /*module_data*/, + diff_source_idx_t /*from*/, + diff_source_idx_t /*to*/) const + { + } + virtual void map_value( + bytevec_t * /*value*/, + void * /*module_data*/, + diff_source_idx_t /*from*/, + diff_source_idx_t /*to*/) const + { + } + + /// notify helper that some data was changed in the database and internal + /// structures (e.g. caches) should be refreshed + virtual void refresh(uchar /*tag*/, void * /*module_data*/) {} + + /// return name of netnode to be used in logging. If this function returns nullptr + /// netnode name and tag passed to create_nodeval_diff_source will be used + virtual const char *get_logname() const { return nullptr; } + + /// can be used by derived classes + static void append_eavec(qstring *s, const char *prefix, const eavec_t &eas) + { + s->append(prefix); + s->append(" ["); + if ( !eas.empty() ) + { + for ( const auto ea : eas ) + s->cat_sprnt("%a,", ea); + s->remove_last(); + } + s->append(']'); + } +}; +typedef merge_node_helper_t *merge_node_helper_creator_t(merge_data_t &md, int dbctx_id); +using merge_node_hlpfunc_creator_t = std::function<merge_node_helper_creator_t>; + +//-------------------------------------------------------------------------- +/// field descriptor used to organize merging of a netnode array +struct merge_node_info_t +{ + const char *name; ///< name of the array (label) + uchar tag; ///< a tag used to access values in the netnode + uint32 nds_flags; ///< node value attributes (a combination of \ref nds_flags_t) + merge_node_helper_creator_t *nhc; + ///< a factory to create instances of \ref merge_node_helper_t +}; +DECLARE_TYPE_AS_MOVABLE(merge_node_info_t); +struct merge_node_info2_t +{ + const char *name; ///< name of the array (label) + uchar tag; ///< a tag used to access values in the netnode + uint32 nds_flags; ///< node value attributes (a combination of \ref nds_flags_t) + merge_node_helper_t *node_helper; + ///< merge handler creation helper, foreign owner +}; +DECLARE_TYPE_AS_MOVABLE(merge_node_info2_t); + +/// Create a merge handler for netnode scalar/string values +/// \param mhp merging parameters +/// \param label handler short name (to be be appended to mhp.label) +/// \param moddata_id module data ID (to be passed to get_module_data) +/// \param nodename netnode name +/// \param tag a tag used to access values in the netnode +/// \param nds_flags netnode value attributes (a combination of \ref nds_flags_t) +/// \param nhc a factory to create instances of \ref merge_node_helper_t +/// \param skip_empty_nodes do not create handler in case of empty netnode +/// \return diff source object (normally should be attahced to a merge handler) +idaman merge_handler_t *ida_export create_nodeval_merge_handler( + const merge_handler_params_t &mhp, + const char *label, + int moddata_id, + const char *nodename, + uchar tag, + uint32 nds_flags, + merge_node_hlpfunc_creator_t nhc = nullptr, + bool skip_empty_nodes = true); +idaman merge_handler_t *ida_export create_nodeval_merge_handler2( + const merge_handler_params_t &mhp, + const char *label, + int moddata_id, + const char *nodename, + uchar tag, + uint32 nds_flags, + merge_node_helper_t *node_helper = nullptr, // owned by the caller + bool skip_empty_nodes = true); + +/// Create a serie of merge handlers for netnode scalar/string values +/// (call create_nodeval_merge_handler() for each member of VALDESC) +/// \param out [out] created handlers will be placed here +/// \param mhp merging parameters +/// \param moddata_id module data ID (to be passed to get_module_data) +/// \param nodename netnode name +/// \param valdesc array of handler descriptions +/// \param nvals number of members in VALDESC +/// \param skip_empty_nodes do not create handlers for empty netnodes +/// \return diff source object (normally should be attahced to a merge handler) +idaman void ida_export create_nodeval_merge_handlers( + merge_handlers_t *out, + const merge_handler_params_t &mhp, + int moddata_id, + const char *nodename, + const merge_node_info_t *valdesc, + size_t nvals, + bool skip_empty_nodes = true); +idaman void ida_export create_nodeval_merge_handlers2( + merge_handlers_t *out, + const merge_handler_params_t &mhp, + int moddata_id, + const char *nodename, + const merge_node_info2_t *valdesc, + size_t nvals, + bool skip_empty_nodes = true); + +//------------------------------------------------------------------------ +// macros for convenience to be used in modules: + +/// idbattr_info_t entry for scalar structure field +#define IDI_FLDENTRY(struc, field, mask, valmap, name) \ + { name, /* field description */ \ + qoffsetof(struc, field), /* offset */ \ + sizeof(struc::field), /* width */ \ + mask, /* bitmask */ \ + 0, /* tag (for node values only) */ \ + valmap, /* vmap */ \ + nullptr, /* individual_node */ \ + IDI_STRUCFLD|IDI_SCALAR } /* flags */ + +/// idbattr_info_t entry for qstring structure field +#define IDI_FLDQSTR(struc, field, name) \ + { name, qoffsetof(struc, field), 0, 0, 0, nullptr, nullptr, IDI_STRUCFLD|IDI_QSTRING } + +/// idbattr_info_t entry for scalar node altval +#define IDI_ALTENTRY(altidx, tag, width, mask, valmap, name) \ + { name, /* field description */ \ + uintptr_t(altidx), /* altval index */ \ + width, /* width */ \ + mask, /* bitmask */ \ + tag, /* tag (for node values only) */ \ + valmap, /* vmap */ \ + nullptr, /* individual_node */ \ + IDI_ALTVAL|IDI_SCALAR } /* flags */ + +/// idbattr_info_t entry for named node hash entry +#define IDI_HASHENTRY(hashname, tag, width, mask, flag, valmap, name) \ + { name, /* field description */ \ + uintptr_t(hashname), /* hash name */ \ + width, /* width */ \ + mask, /* bitmask */ \ + tag, /* tag (for node values only) */ \ + valmap, /* vmap */ \ + nullptr, /* individual_node */ \ + IDI_HASH|(flag) } /* flags */ + +/// idbattr_info_t entry for node supstr +#define IDI_SUPSTR(altidx, tag, name) \ + { name, uintptr_t(altidx), 0, 0, tag, nullptr, nullptr, IDI_SUPVAL|IDI_CSTR } + +/// standard idbattr_info_t entry for 'device' (to be used in processor modules) +#define IDI_DEVICE_ENTRY IDI_SUPSTR(-1, stag, "device") + +/// merge_node_info_t entry for node value +#define MNI_ENTRY(tag, flags, name, helper) \ + { name, tag, NDS_MAP_IDX|NDS_EV_RANGE|(flags), helper } + +/// merge_node_info_t entry to be used inside functions only +#define MNI_FUNCENTRY(tag, flags, name) \ + MNI_ENTRY(tag, NDS_EV_FUNC|(flags), name, nullptr) + +/// merge_node_info_t entry for node with default representation (no helper) +#define MNI_STDENTRY(tag, flags, name) MNI_ENTRY(tag, flags, name, nullptr) +//------------------------------------------------------------------------ +idaman void ida_export destroy_moddata_merge_handlers(int data_id); + +#ifdef ui_switch_dbctx_guard + #define switch_dbctx ui_switch_dbctx_guard +#endif + +//------------------------------------------------------------------------ +/// Get nice name for EA diffpos +/// \param ea diffpos +/// \param[out] out nice name +/// \note \see get_nice_colored_name +idaman ssize_t ida_export get_ea_diffpos_name(qstring *out, ea_t ea); +#endif // _MERGE_HPP + +``` + +`IdaSDK/mergemod.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 2005-2025 Hex-Rays SA <support@hex-rays.com> + * ALL RIGHTS RESERVED. + */ + +#ifndef _MERGEMOD_HPP +#define _MERGEMOD_HPP + +#include <merge.hpp> + +/*! \file mergemod.hpp + + \brief Merge functionality for modules + +NOTE: this functionality is available in IDA Teams (not IDA Pro) + +This file contains helper classes and convenience functions for module +(plugin or processor module) merging. + +Each module is responsible for merging the data it owns (the module data). +At the very beginning, the merging engine generates the ev_create_merge_handlers event. +Modules should hook to this event to create merge handlers (mergers) that are +responsible for the module data. + +We assume that each module may have: + + - its data structure, derived from plugmod_t or procmod_t. + we call this structure moddata. + - a dedicated netnode (module node), modnode for short. + +Moddata is registered with the IDA kernel using the set_module_data() function, which +returns an integer, moddata_id. moddata_id is used to access the module data +structure during merging, so it is mandatory for all modules that support merging. + +The following sources of mergeable data are supported: + + 1. Data fields inside moddata + 2. Values (scalar or binary, including blobs) stored in the module node + 3. Values (scalar or binary, including blobs) stored in arbitrary netnodes + 4. Data fields inside an auxiliary structure (provided by a special helper) + 5. Indexed arrays of data stored in netnodes + +Usually the sources #1-4 are handled by a single merger, which can be +parameterized using the folowing information: + + - moddata_id + - module name + - module node name + - array of field descriptors (idbattr_info_t idpopts_info[], see ida.hpp) + +See plugins/mex1 for an example of such a merger. + +These parameters are stored in a helper class (moddata_diff_helper_t +or derived). The helper class can override the following virtual methods: + + merge_starting - prepare module data for merging (e.g. load data from idb) + merge_ending - opposite to merge_starting (e.g. save merged data to idb) + get_struc_ptr - get pointer to the auxiliary structure (to handle source #4); + this method will be called only if the fields with the + IDI_HLPSTRUC bit are present in the idpopts_info[] array + +For most plugins, the default implementation of moddata_diff_helper_t or +the std_moddata_diff_helper_t helper (presented below) is sufficient. +You can find examples of non-standard helpers in plugins/mex2. + +The source #5 is handled by a different set of mergers described by an array +of merge_node_info_t entries: a merger per entry. A non-trivial example can be +found in plugins/mex3 and plugins/ex_merge_ldrdata. + +A module can use the create_std_modmerge_handlers() function to create necessary +merge handlers. Please pay attention to the following arguments: + + helper - a helper class responsible for access to the internal + module data for the sources #1-4. It can be used to + prepare a pointer to the internal module structure + and load/save data before/after merging + (example: plugins/mex2). Im most cases the default helper + class moddata_diff_helper_t can be used. + merge_node_info - array of descriptions for the source #5. Note that the same + module node is used for all array elements. If you need + this kind of mergers for other netnodes, you should add + them manually using the create_nodeval_merge_handler() + function (example: plugins/mex3) + +See also module/mergecmn.cpp for procmod-specific functions and macros. + +Glossary: + + modmerger = module merger + moddata = module data + moddata_id = module data id + +*/ + +//-------------------------------------------------------------------------- +/// Convinient macros to create merge handler parameters +#define DEFINE_PLUGIN_MH_PARAMS(label,flags) \ + merge_handler_params_t mhp( \ + md, /* merge handler data */ \ + "Plugins/" label, /* default subdir for plugins */ \ + MERGE_KIND_NONE, /* allocate a merge kind */ \ + MERGE_KIND_END, /* insert to the end of handler list */ \ + flags) + +//-------------------------------------------------------------------------- +/// Prototype of the custom function to create merge handlers. +/// This function is defined by modules if necessary. +void create_merge_handlers(class merge_data_t &md); + +//-------------------------------------------------------------------------- +/// convinience function to create merge handlers for modules/plugins +idaman void ida_export create_std_modmerge_handlers( + merge_handler_params_t &mhp, + int moddata_id, + moddata_diff_helper_t &helper, + const merge_node_info_t *merge_node_info=nullptr, + size_t n_merge_node_info=0); +idaman void ida_export create_std_modmerge_handlers2( + merge_handler_params_t &mhp, + int moddata_id, + moddata_diff_helper_t &helper, + const merge_node_info2_t *merge_node_info=nullptr, + size_t n_merge_node_info=0); + +//-------------------------------------------------------------------------- +/// Module data diff helper with default implementation of merge_ending() +/// method: it calls load_from_idb() (should be provided by MOD_T) for module +/// data structure +template <class MOD_T> +struct std_moddata_diff_helper_t : public moddata_diff_helper_t +{ + std_moddata_diff_helper_t( + const char *mod_name, + const char *node_name, + const idbattr_info_t *_fields, + size_t _nfields) + : moddata_diff_helper_t(mod_name, node_name, _fields, _nfields) + { + } + virtual void merge_ending( + diff_source_idx_t, + void *module_data) override + { + MOD_T &pm = *(MOD_T*)module_data; + pm.load_from_idb(); + } +}; + +//-------------------------------------------------------------------------- +/// Create an instance of a helper class with specified name and parameters +/// The instance can be passed to create_std_modmerge_handlers() +#define MODDATA_DIFF_HELPER(class_name, name, label, node_name, idbattrs) \ + static class_name name(label, node_name, idbattrs, qnumber(idbattrs)) + +//-------------------------------------------------------------------------- +/// Create an instance of default helper class \ref moddata_diff_helper_t with specified parameters +/// The instance can be passed to create_std_modmerge_handlers() +#define SIMPLE_MODDATA_DIFF_HELPER(name, label, node_name, idbattrs) \ + MODDATA_DIFF_HELPER(moddata_diff_helper_t, name, \ + label, node_name, idbattrs) + +#endif // _MERGEMOD_HPP + +``` + +`IdaSDK/moves.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef __MOVES_HPP +#define __MOVES_HPP + +#include <dirtree.hpp> + +#include <unordered_map> + +//------------------------------------------------------------------------- +struct graph_location_info_t +{ + double zoom; // zoom level, 1.0 == 100%, 0 means auto position + double orgx; // graph origin, x coord + double orgy; // graph origin, y coord + graph_location_info_t(void) : zoom(0), orgx(0), orgy(0) {} + bool operator == (const graph_location_info_t &r) const + { return zoom == r.zoom && orgx == r.orgx && orgy == r.orgy; } + bool operator != (const graph_location_info_t &r) const + { return !(*this == r); } + void serialize(bytevec_t *out) const; + bool deserialize(memory_deserializer_t &mmdsr); +}; + +//------------------------------------------------------------------------- +inline void graph_location_info_t::serialize(bytevec_t *out) const +{ + CASSERT(sizeof(graph_location_info_t) == 3*8); + out->append(this, sizeof(graph_location_info_t)); +} + +//------------------------------------------------------------------------- +inline bool graph_location_info_t::deserialize(memory_deserializer_t &mmdsr) +{ + return mmdsr.unpack_obj(this, sizeof(graph_location_info_t)) != nullptr; +} + +//------------------------------------------------------------------------- +struct segm_move_info_t +{ + segm_move_info_t(ea_t _from = 0, ea_t _to = 0, size_t _sz = 0) + : from(_from), to(_to), size(_sz) {} + ea_t from, to; + size_t size; + + bool operator == (const segm_move_info_t &r) const + { return from == r.from && to == r.to && size == r.size; } + bool operator != (const segm_move_info_t &r) const + { return !(*this == r); } +}; +DECLARE_TYPE_AS_MOVABLE(segm_move_info_t); +typedef qvector<segm_move_info_t> segm_move_info_vec_t; + +struct segm_move_infos_t : public segm_move_info_vec_t +{ + const segm_move_info_t *find(ea_t ea) const + { + for ( size_t i = 0; i < size(); ++i ) + { + const segm_move_info_t &cur = at(i); + if ( ea >= cur.from && ea < cur.from + cur.size ) + return &cur; + } + return nullptr; + } +}; + +//------------------------------------------------------------------------- +class place_t; + +//------------------------------------------------------------------------- +struct renderer_info_pos_t // out of renderer_info_t, to enable SWiG parsing +{ + int node; + short cx; + short cy; + + renderer_info_pos_t() : node(-1), cx(-1), cy(-1) {} + bool operator == (const renderer_info_pos_t &r) const + { return node == r.node && cx == r.cx && cy == r.cy; } + bool operator != (const renderer_info_pos_t &r) const + { return !(*this == r); } + void serialize(bytevec_t *out) const; + bool deserialize(memory_deserializer_t &mmdsr); +}; + +//------------------------------------------------------------------------- +inline void renderer_info_pos_t::serialize(bytevec_t *out) const +{ + out->pack_dd(node); + out->pack_dw(cx); + out->pack_dw(cy); +} + +//------------------------------------------------------------------------- +inline bool renderer_info_pos_t::deserialize(memory_deserializer_t &mmdsr) +{ + node = mmdsr.unpack_dd(); + cx = mmdsr.unpack_dw(); + if ( mmdsr.empty() ) + return false; + cy = mmdsr.unpack_dw(); + return true; +} + +//------------------------------------------------------------------------- +struct renderer_info_t +{ + graph_location_info_t gli; + typedef renderer_info_pos_t pos_t; + pos_t pos; + tcc_renderer_type_t rtype = TCCRT_INVALID; + + renderer_info_t() {} + renderer_info_t(tcc_renderer_type_t _rtype, short cx, short cy) : rtype(_rtype) + { + pos.cx = cx; + pos.cy = cy; + } + bool operator == (const renderer_info_t &r) const + { return rtype == r.rtype && pos == r.pos && gli == r.gli; } + bool operator != (const renderer_info_t &r) const + { return !(*this == r); } +}; + + +//------------------------------------------------------------------------- +class navstack_t; +struct navstack_entry_t; +typedef qvector<navstack_entry_t> navstack_entry_vec_t; +struct expanded_area_t; + +#define LSEF_PLACE (1 << 0) +#define LSEF_RINFO (1 << 1) +#define LSEF_PTYPE (1 << 2) +#define LSEF_ALL (LSEF_PLACE|LSEF_RINFO|LSEF_PTYPE) + +#ifndef SWIG +# define DEFINE_NAVSTACK_T_HELPERS(decl) \ + decl void ida_export navstack_t_register_live(navstack_t &); \ + decl void ida_export navstack_t_deregister_live(navstack_t &); \ + decl bool ida_export navstack_t_init(navstack_t &, navstack_entry_t *, const char *, uint32); \ + decl bool ida_export navstack_t_perform_move(const char *, const char *, const char *, bool); \ + decl void ida_export navstack_t_set_current(navstack_t &, const navstack_entry_t &, bool); \ + decl bool ida_export navstack_t_get_current(const navstack_t &, navstack_entry_t *, const char *); \ + decl void ida_export navstack_t_get_all_current(const navstack_t &, navstack_entry_vec_t *); \ + decl void ida_export navstack_t_stack_jump(navstack_t &, bool try_to_unhide, const navstack_entry_t &e); \ + decl bool ida_export navstack_t_stack_nav(navstack_t &, navstack_entry_t *, bool, uint32 cnt, bool try_to_unhide); \ + decl bool ida_export navstack_t_stack_seek(navstack_t &, navstack_entry_t *, uint32 index, bool try_to_unhide, bool apply_cur); \ + decl uint32 ida_export navstack_t_stack_index(const navstack_t &); \ + decl void ida_export navstack_t_set_stack_entry(const navstack_t &, uint32, const navstack_entry_t &); \ + decl bool ida_export navstack_t_get_stack_entry(navstack_entry_t *, const navstack_t &, uint32); \ + decl uint32 ida_export navstack_t_stack_size(const navstack_t &); \ + decl void ida_export navstack_t_stack_clear(navstack_t &, const navstack_entry_t &); +# ifdef TESTABLE_BUILD +# define DEFINE_NAVSTACK_T_HELPERS_TB(decl) \ + decl void ida_export navstack_t_dump(const navstack_t &); +# else +# define DEFINE_NAVSTACK_T_HELPERS_TB(decl) +# endif +#else +# define DEFINE_NAVSTACK_T_HELPERS(decl) +# define DEFINE_NAVSTACK_T_HELPERS_TB(decl) +#endif // SWIG + +DEFINE_NAVSTACK_T_HELPERS(idaman) +DEFINE_NAVSTACK_T_HELPERS_TB(idaman) + +#ifndef SWIG +#define DEFINE_NAVSTACK_ENTRY_T_HELPERS(decl) \ + decl void ida_export navstack_entry_t_serialize(bytevec_t *, const navstack_entry_t &); \ + decl bool ida_export navstack_entry_t_deserialize(navstack_entry_t *, const uchar **, const uchar *const, const place_t *); +#else +#define DEFINE_NAVSTACK_ENTRY_T_HELPERS(decl) +#endif // SWIG +DEFINE_NAVSTACK_ENTRY_T_HELPERS(idaman) + +//------------------------------------------------------------------------- +struct lochist_entry_t +{ + renderer_info_t rinfo; + place_t *plce = nullptr; + + lochist_entry_t() {} + lochist_entry_t(const place_t *p, + const renderer_info_t &r) + : rinfo(r), plce((place_t *) p) + { + if ( plce != nullptr ) + plce = plce->clone(); + } + lochist_entry_t(const lochist_entry_t &other) { *this = other; } + ~lochist_entry_t() { clear(); } + const renderer_info_t &renderer_info() const { return rinfo; } + const place_t *place() const { return plce; } + + renderer_info_t &renderer_info() { return rinfo; } + place_t *place() { return plce; } + + void set_place(const place_t *p) { clear(); if ( p != nullptr ) plce = p->clone(); } + void set_place(const place_t &p) { set_place(&p); } + + bool is_valid() const { return plce != nullptr; } + + lochist_entry_t &operator=(const lochist_entry_t &r) + { + if ( this == &r ) + return *this; + clear(); + rinfo = r.rinfo; + if ( r.plce != nullptr ) + plce = r.plce->clone(); + return *this; + } + + void acquire_place(place_t *in_p) + { clear(); plce = in_p; } + +protected: + void clear() + { + if ( plce != nullptr ) + { + qfree(plce); + plce = nullptr; + } + } + + friend class lochist_t; + DEFINE_NAVSTACK_T_HELPERS(friend) + DEFINE_NAVSTACK_ENTRY_T_HELPERS(friend) +}; +DECLARE_TYPE_AS_MOVABLE(lochist_entry_t); + +//------------------------------------------------------------------------- +struct navstack_entry_t : public lochist_entry_t +{ + qstring widget_id; + qstring ud_str; + + navstack_entry_t() {} + navstack_entry_t( + const lochist_entry_t &e, + const qstring &w) + : lochist_entry_t(e), widget_id(w) {} + navstack_entry_t( + const place_t *p, + const renderer_info_t &r, + const qstring &w) + : lochist_entry_t(p, r), widget_id(w) {} + navstack_entry_t(const navstack_entry_t &r) + : lochist_entry_t(r), widget_id(r.widget_id), ud_str(r.ud_str) {} + ~navstack_entry_t() { clear(); } + + navstack_entry_t &operator=(const navstack_entry_t &r) + { + if ( this == &r ) + return *this; + clear(); + * (lochist_entry_t *) this = (const lochist_entry_t &) r; + widget_id = r.widget_id; + ud_str = r.ud_str; + return *this; + } + + void serialize(bytevec_t *out) const { navstack_entry_t_serialize(out, *this); } + bool deserialize(const uchar **ptr, const uchar *const end, const place_t *tmplate) + { return navstack_entry_t_deserialize(this, ptr, end, tmplate); } + +private: + void clear() + { + lochist_entry_t::clear(); + widget_id.clear(); + ud_str.clear(); + } +}; +DECLARE_TYPE_AS_MOVABLE(navstack_entry_t); + +#define UNHID_SEGM 0x0001 ///< unhid a segment at 'target' +#define UNHID_FUNC 0x0002 ///< unhid a function at 'target' +#define UNHID_RANGE 0x0004 ///< unhid an range at 'target' + +#define DEFAULT_CURSOR_Y 0xFFFF +#define DEFAULT_LNNUM -1 +#define CURLOC_LIST "$ curlocs" +#define MAX_MARK_SLOT 1024 // Max number of marked locations + +//------------------------------------------------------------------------- +class navstack_t +{ + DEFINE_NAVSTACK_T_HELPERS(friend) + DEFINE_NAVSTACK_T_HELPERS_TB(friend) + + netnode node = BADNODE; + +public: + +#define LHF_HISTORY_DISABLED (1 << 0) // enable history? + uint32 flags = 0; + + navstack_t() { navstack_t_register_live(*this); } + ~navstack_t() { navstack_t_deregister_live(*this); } + + bool is_history_enabled() const { return (flags & LHF_HISTORY_DISABLED) == 0; } + bool init(navstack_entry_t *defpos, const char *stream_name, uint32 _flags) + { + return navstack_t_init(*this, defpos, stream_name, _flags); + } + + static bool perform_move(const char *stream_name, const char *source_stream_name, const char *widget_id, bool move_stack) + { + return navstack_t_perform_move(stream_name, source_stream_name, widget_id, move_stack); + } + + nodeidx_t netcode() const { return node; } + + void set_current(const navstack_entry_t &e, bool in_charge) + { return navstack_t_set_current(*this, e, in_charge); } + + bool get_current(navstack_entry_t *out, const char *widget_id) const + { return navstack_t_get_current(*this, out, widget_id); } + + void get_all_current(navstack_entry_vec_t *out) const + { return navstack_t_get_all_current(*this, out); } + + void stack_jump(bool try_to_unhide, const navstack_entry_t &e) + { navstack_t_stack_jump(*this, try_to_unhide, e); } + + uint32 stack_index() const + { return navstack_t_stack_index(*this); } + + bool stack_seek(navstack_entry_t *out, uint32 index, bool try_to_unhide) + { return navstack_t_stack_seek(*this, out, index, try_to_unhide, true); } + + bool stack_forward(navstack_entry_t *out, uint32 cnt, bool try_to_unhide) + { return navstack_t_stack_nav(*this, out, true, cnt, try_to_unhide); } + + bool stack_back(navstack_entry_t *out, uint32 cnt, bool try_to_unhide) + { return navstack_t_stack_nav(*this, out, false, cnt, try_to_unhide); } + + bool stack_nav(navstack_entry_t *out, bool forward, uint32 cnt, bool try_to_unhide) + { return navstack_t_stack_nav(*this, out, forward, cnt, try_to_unhide); } + + void stack_clear(const navstack_entry_t &new_tip) + { navstack_t_stack_clear(*this, new_tip); } + + void set_stack_entry(uint32 index, const navstack_entry_t &e) + { navstack_t_set_stack_entry(*this, index, e); } + + bool get_stack_entry(navstack_entry_t *out, uint32 index) const + { return navstack_t_get_stack_entry(out, *this, index); } + + bool get_current_stack_entry(navstack_entry_t *out) const + { return get_stack_entry(out, stack_index()); } + + uint32 stack_size(void) const + { return navstack_t_stack_size(*this); } + +#ifdef TESTABLE_BUILD + void dump() const { return navstack_t_dump(*this); } +#endif +}; +DECLARE_TYPE_AS_MOVABLE(navstack_t); + +//------------------------------------------------------------------------- +#ifndef SWIG +idaman uint32 ida_export bookmarks_t_mark(const lochist_entry_t &, uint32, const char *, const char *, void *); +idaman bool ida_export bookmarks_t_get(lochist_entry_t *, qstring *, uint32 *, void *); +idaman bool ida_export bookmarks_t_get_desc(qstring *, const lochist_entry_t &, uint32, void *); +idaman bool ida_export bookmarks_t_set_desc(qstring, const lochist_entry_t &, uint32, void *); +idaman uint32 ida_export bookmarks_t_find_index(const lochist_entry_t &, void *); +idaman uint32 ida_export bookmarks_t_size(const lochist_entry_t &, void *); +idaman bool ida_export bookmarks_t_erase(const lochist_entry_t &, uint32, void *); +idaman dirtree_id_t ida_export bookmarks_t_get_dirtree_id(const lochist_entry_t &, void *); +#endif // SWIG + +//------------------------------------------------------------------------- +class bookmarks_t +{ + bookmarks_t(); // No. + ~bookmarks_t() {} +public: +#define BOOKMARKS_CHOOSE_INDEX (uint32(-1)) +#define BOOKMARKS_BAD_INDEX (uint32(-1)) +#define BOOKMARKS_PROMPT_WITH_HINT_PREFIX '\x01' + + // Mark/unmark position + // index - the marked position number (0..MAX_MARK_SLOT) + // if specified as BOOKMARKS_CHOOSE_INDEX: ask the user to select the mark slot. + // title - if index == BOOKMARKS_CHOOSE_INDEX, then the window caption of + // the dialog which will appear on the screen. title==nullptr will + // lead to the default caption: "please select a mark slot" + // desc - description of the marked position. If nullptr, IDA will show a + // dialog box asking the user to enter the description. + // If non-nullptr but starts with BOOKMARKS_PROMPT_WITH_HINT_PREFIX, + // IDA will also prompt the user, but with a pre-filled value + // starting at &desc[1]. + // returns used marker number (BOOKMARKS_BAD_INDEX - none) + static uint32 mark( + const lochist_entry_t &e, + uint32 index, + const char *title, + const char *desc, + void *ud) + { return bookmarks_t_mark(e, index, title, desc, ud); } + + // 'out_entry' MUST: + // - contain a valid place_t*; data will be deserialized into it + // - have a valid, corresponding tcc_place_type_t + static bool get( + lochist_entry_t *out_entry, + qstring *out_desc, + uint32 *index, // index==BOOKMARKS_CHOOSE_INDEX? let the user choose + void *ud) + { return bookmarks_t_get(out_entry, out_desc, index, ud); } + + static bool get_desc( + qstring *out, + const lochist_entry_t &e, + uint32 index, + void *ud) + { return bookmarks_t_get_desc(out, e, index, ud); } + + static uint32 find_index( + const lochist_entry_t &e, + void *ud) + { return bookmarks_t_find_index(e, ud); } + + static uint32 size( + const lochist_entry_t &e, + void *ud) + { return bookmarks_t_size(e, ud); } + + static bool erase( + const lochist_entry_t &e, + uint32 index, + void *ud) + { return bookmarks_t_erase(e, index, ud); } + + static dirtree_id_t get_dirtree_id( + const lochist_entry_t &e, + void *ud) + { return bookmarks_t_get_dirtree_id(e, ud); } +}; + + +#ifndef SWIG +/// Deprecated. Please use `navstack_t` instead +class lochist_t {}; +idaman DEPRECATED void ida_export lochist_t_register_live(lochist_t &); +idaman DEPRECATED void ida_export lochist_t_deregister_live(lochist_t &); +idaman DEPRECATED bool ida_export lochist_t_init(lochist_t &, const char *, const place_t &, void *, uint32); +idaman DEPRECATED void ida_export lochist_t_jump(lochist_t &, bool try_to_unhide, const lochist_entry_t &e); +idaman DEPRECATED bool ida_export lochist_t_fwd(lochist_t &, uint32 cnt, bool try_to_unhide); +idaman DEPRECATED bool ida_export lochist_t_back(lochist_t &, uint32 cnt, bool try_to_unhide); +idaman DEPRECATED bool ida_export lochist_t_seek(lochist_t &, uint32 index, bool try_to_unhide, bool apply_cur); +idaman DEPRECATED const lochist_entry_t *ida_export lochist_t_get_current(const lochist_t &); +idaman DEPRECATED uint32 ida_export lochist_t_current_index(const lochist_t &); +idaman DEPRECATED void ida_export lochist_t_set(lochist_t &, uint32, const lochist_entry_t &); +idaman DEPRECATED bool ida_export lochist_t_get(lochist_entry_t *, const lochist_t &, uint32); +idaman DEPRECATED uint32 ida_export lochist_t_size(const lochist_t &); +idaman DEPRECATED void ida_export lochist_t_save(const lochist_t &); +idaman DEPRECATED void ida_export lochist_t_clear(lochist_t &); +idaman DEPRECATED void ida_export lochist_entry_t_serialize(bytevec_t *, const lochist_entry_t &); +idaman DEPRECATED bool ida_export lochist_entry_t_deserialize(lochist_entry_t *, const uchar **, const uchar *const, const place_t *); +#endif + +#endif // __MOVES_HPP + +``` + +`IdaSDK/nalt.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef NALT_HPP +#define NALT_HPP + +#include <ida.hpp> +#include <netnode.hpp> + +/*! \file nalt.hpp + + \brief Definitions of various information kept in netnodes + + Each address in the program has a corresponding netnode: netnode(ea). + + If we have no information about an address, the corresponding + netnode is not created. + Otherwise we will create a netnode and save information in it. + All variable length information (names, comments, offset information, etc) + is stored in the netnode. + + Don't forget that some information is already stored in the flags (bytes.hpp) + + \warning + Many of the functions in this file are very low level (they are marked + as low level functions). Use them only if you can't find higher level + function to set/get/del information. + + You can create your own nodes in IDP module and store information + in them. See ::netnode. +*/ + +/// \defgroup NALT_ Structure of altvals array +/// Structure of altvals array of netnode(ea). +/// altvals is a virtual array of 32-bit longs attached to a netnode. +/// the size of this array is unlimited. Unused indexes are not kept in the +/// database. We use only first several indexes to this array. +///@{ +#define NALT_ENUM uval_t(-2) ///< reserved for enums +#define NALT_WIDE uval_t(-1) ///< 16-bit byte value +#define NALT_SWITCH 1 ///< switch idiom address (used at jump targets) +//#define NALT_OBASE1 2 // offset base 2 +#define NALT_STRUCT 3 ///< struct id +//#define NALT_SEENF 4 // 'seen' flag (used in structures) +//#define NALT_OOBASE0 5 // outer offset base 1 +//#define NALT_OOBASE1 6 // outer offset base 2 +//#define NALT_XREFPOS 7 // saved xref address in the xrefs window +#define NALT_AFLAGS 8 ///< additional flags for an item +#define NALT_LINNUM 9 ///< source line number +#define NALT_ABSBASE 10 ///< absolute segment location +#define NALT_ENUM0 11 ///< enum id for the first operand +#define NALT_ENUM1 12 ///< enum id for the second operand +//#define NALT_STROFF0 13 // struct offset, struct id for the first operand +//#define NALT_STROFF1 14 // struct offset, struct id for the second operand +#define NALT_PURGE 15 ///< number of bytes purged from the stack when a function is called indirectly +#define NALT_STRTYPE 16 ///< type of string item +#define NALT_ALIGN 17 ///< alignment value if the item is #FF_ALIGN + ///< (should by equal to power of 2) +//#define NALT_HIGH0 18 // linear address of byte referenced by +// // high 16 bits of an offset (FF_0HIGH) +//#define NALT_HIGH1 19 // linear address of byte referenced by +// // high 16 bits of an offset (FF_1HIGH) +#define NALT_COLOR 20 ///< instruction/data background color +///@} + +/// \defgroup NSUP_ Structure of supvals array +/// Structure of supvals array of netnode(ea). +/// Supvals is a virtual array of objects of arbitrary length attached +/// to a netnode (length of one element is limited by #MAXSPECSIZE, though) +/// We use first several indexes to this array: +///@{ +#define NSUP_CMT 0 ///< regular comment +#define NSUP_REPCMT 1 ///< repeatable comment +#define NSUP_FOP1 2 ///< forced operand 1 +#define NSUP_FOP2 3 ///< forced operand 2 +#define NSUP_JINFO 4 ///< jump table info +#define NSUP_ARRAY 5 ///< array parameters +#define NSUP_OMFGRP 6 ///< OMF: group of segments (not used anymore) +#define NSUP_FOP3 7 ///< forced operand 3 +#define NSUP_SWITCH 8 ///< switch information +#define NSUP_REF0 9 ///< complex reference information for operand 1 +#define NSUP_REF1 10 ///< complex reference information for operand 2 +#define NSUP_REF2 11 ///< complex reference information for operand 3 +#define NSUP_OREF0 12 ///< outer complex reference information for operand 1 +#define NSUP_OREF1 13 ///< outer complex reference information for operand 2 +#define NSUP_OREF2 14 ///< outer complex reference information for operand 3 +#define NSUP_STROFF0 15 ///< stroff: struct path for the first operand +#define NSUP_STROFF1 16 ///< stroff: struct path for the second operand +#define NSUP_SEGTRANS 17 ///< segment translations +#define NSUP_FOP4 18 ///< forced operand 4 +#define NSUP_FOP5 19 ///< forced operand 5 +#define NSUP_FOP6 20 ///< forced operand 6 +#define NSUP_REF3 21 ///< complex reference information for operand 4 +#define NSUP_REF4 22 ///< complex reference information for operand 5 +#define NSUP_REF5 23 ///< complex reference information for operand 6 +#define NSUP_OREF3 24 ///< outer complex reference information for operand 4 +#define NSUP_OREF4 25 ///< outer complex reference information for operand 5 +#define NSUP_OREF5 26 ///< outer complex reference information for operand 6 +#define NSUP_XREFPOS 27 ///< saved xref address and type in the xrefs window +#define NSUP_CUSTDT 28 ///< custom data type id +#define NSUP_GROUPS 29 ///< SEG_GRP: pack_dd encoded list of selectors +#define NSUP_ARGEAS 30 ///< instructions that initialize call arguments +#define NSUP_FOP7 31 ///< forced operand 7 +#define NSUP_FOP8 32 ///< forced operand 8 +#define NSUP_REF6 33 ///< complex reference information for operand 7 +#define NSUP_REF7 34 ///< complex reference information for operand 8 +#define NSUP_OREF6 35 ///< outer complex reference information for operand 7 +#define NSUP_OREF7 36 ///< outer complex reference information for operand 8 +#define NSUP_EX_FLAGS 37 ///< Extended flags + +// values E_PREV..E_NEXT+1000 are reserved (1000..2000..3000 decimal) + +/// SP change points blob (see funcs.cpp). +/// values NSUP_POINTS..NSUP_POINTS+0x1000 are reserved +#define NSUP_POINTS 0x1000 + +/// manual instruction. +/// values NSUP_MANUAL..NSUP_MANUAL+0x1000 are reserved +#define NSUP_MANUAL 0x2000 + +/// type information. +/// values NSUP_TYPEINFO..NSUP_TYPEINFO+0x1000 are reserved +#define NSUP_TYPEINFO 0x3000 + +/// register variables. +/// values NSUP_REGVAR..NSUP_REGVAR+0x1000 are reserved +#define NSUP_REGVAR 0x4000 + +/// local labels. +/// values NSUP_LLABEL..NSUP_LLABEL+0x1000 are reserved +#define NSUP_LLABEL 0x5000 + +/// register argument type/name descriptions +/// values NSUP_REGARG..NSUP_REGARG+0x1000 are reserved +#define NSUP_REGARG 0x6000 + +/// function tails or tail referers +/// values NSUP_FTAILS..NSUP_FTAILS+0x1000 are reserved +#define NSUP_FTAILS 0x7000 + +/// graph group information +/// values NSUP_GROUP..NSUP_GROUP+0x1000 are reserved +#define NSUP_GROUP 0x8000 + +/// operand type information. +/// values NSUP_OPTYPES..NSUP_OPTYPES+0x100000 are reserved +#define NSUP_OPTYPES 0x9000 + +/// function metadata before lumina information was applied +/// values NSUP_ORIGFMD..NSUP_ORIGFMD+0x1000 are reserved +#define NSUP_ORIGFMD 0x109000 + +/// function frame type +/// values NSUP_FRAME..NSUP_FRAME+0x10000 are reserved +#define NSUP_FRAME 0x10A000 + +///@} + +/// \defgroup NALT_X Netnode xref tags +/// Tag values to store xrefs (see cref.cpp) +///@{ +#define NALT_CREF_TO 'X' ///< code xref to, idx: target address +#define NALT_CREF_FROM 'x' ///< code xref from, idx: source address +#define NALT_DREF_TO 'D' ///< data xref to, idx: target address +#define NALT_DREF_FROM 'd' ///< data xref from, idx: source address +///@} + +/// \defgroup N_TAG Netnode graph tags +/// Tag values to store graph info +///@{ +#define NSUP_GR_INFO 'g' ///< group node info: color, ea, text +#define NALT_GR_LAYX 'p' ///< group layout ptrs, hash: md5 of 'belongs' +#define NSUP_GR_LAYT 'l' ///< group layouts, idx: layout pointer +///@} + +/// Patch netnode tag +#define PATCH_TAG 'P' + +/// \defgroup N_DESK UI desktops +///@{ +#define IDB_DESKTOPS_NODE_NAME "$ desktops" + ///< hash indexed by desktop name with dekstop netnode +#define IDB_DESKTOPS_TAG 'S' ///< tag to store desktop blob & timestamp +#define IDB_DESKTOPS_TIMESTAMP nodeidx_t(-1) + ///< desktop timestamp index +///@} + + +/// Get netnode for the specified address. + +idaman nodeidx_t ida_export ea2node(ea_t ea); +idaman ea_t ida_export node2ea(nodeidx_t ndx); +idaman nodeidx_t ida_export end_ea2node(ea_t ea); +inline netnode getnode(ea_t ea) { return netnode(ea2node(ea)); } + +//-------------------------------------------------------------------------- +// C O N V E N I E N C E F U N C T I O N S +//-------------------------------------------------------------------------- + +/// \name Get structure ID +/// Returns the struct id of the struct type at the specified address. +/// Use this function when is_struct()==true +///@{ + +idaman tid_t ida_export get_strid(ea_t ea); + +///@} + +/// \name xrefpos +///@{ +/// Position of cursor in the window with cross-references to the address. +/// Used by the user-interface. +struct xrefpos_t +{ + ea_t ea; + uchar type; // the type of xref (::cref_t & ::dref_t) + xrefpos_t(ea_t ea_ = BADADDR, uchar type_ = 0) : ea(ea_), type(type_) {} + bool is_valid() const { return ea != BADADDR; } +}; + +idaman ssize_t ida_export get_xrefpos(xrefpos_t *out, ea_t ea); +idaman void ida_export set_xrefpos(ea_t ea, const xrefpos_t *in); +inline void idaapi del_xrefpos(ea_t ea) { getnode(ea).supdel(NSUP_XREFPOS); } + +///@} + +/// \defgroup AFL_ Additional flags for the location +/// All 32-bits of the main flags (bytes.hpp) are used up. +/// Additional flags keep more information about addresses. +/// AFLNOTE: DO NOT use these flags directly unless there is absolutely no way. +/// They are too low level and may corrupt the database. +///@{ +#define AFL_LINNUM 0x00000001 ///< has line number info +#define AFL_USERSP 0x00000002 ///< user-defined SP value +#define AFL_PUBNAM 0x00000004 ///< name is public (inter-file linkage) +#define AFL_WEAKNAM 0x00000008 ///< name is weak +#define AFL_HIDDEN 0x00000010 ///< the item is hidden completely +#define AFL_MANUAL 0x00000020 ///< the instruction/data is specified by the user +#define AFL_NOBRD 0x00000040 ///< the code/data border is hidden +#define AFL_ZSTROFF 0x00000080 ///< display struct field name at 0 offset when displaying an offset. + ///< example: + ///< \v{offset somestruct.field_0} + ///< if this flag is clear, then + ///< \v{offset somestruct} +#define AFL_BNOT0 0x00000100 ///< the 1st operand is bitwise negated +#define AFL_BNOT1 0x00000200 ///< the 2nd operand is bitwise negated +#define AFL_LIB 0x00000400 ///< item from the standard library. + ///< low level flag, is used to set + ///< #FUNC_LIB of ::func_t +#define AFL_TI 0x00000800 ///< has typeinfo? (#NSUP_TYPEINFO); used only for addresses, not for member_t +#define AFL_TI0 0x00001000 ///< has typeinfo for operand 0? (#NSUP_OPTYPES) +#define AFL_TI1 0x00002000 ///< has typeinfo for operand 1? (#NSUP_OPTYPES+1) +#define AFL_LNAME 0x00004000 ///< has local name too (#FF_NAME should be set) +#define AFL_TILCMT 0x00008000 ///< has type comment? (such a comment may be changed by IDA) +#define AFL_LZERO0 0x00010000 ///< toggle leading zeroes for the 1st operand +#define AFL_LZERO1 0x00020000 ///< toggle leading zeroes for the 2nd operand +#define AFL_COLORED 0x00040000 ///< has user defined instruction color? +#define AFL_TERSESTR 0x00080000 ///< terse structure variable display? +#define AFL_SIGN0 0x00100000 ///< code: toggle sign of the 1st operand +#define AFL_SIGN1 0x00200000 ///< code: toggle sign of the 2nd operand +#define AFL_NORET 0x00400000 ///< for imported function pointers: doesn't return. + ///< this flag can also be used for any instruction + ///< which halts or finishes the program execution +#define AFL_FIXEDSPD 0x00800000 ///< sp delta value is fixed by analysis. + ///< should not be modified by modules +#define AFL_ALIGNFLOW 0x01000000 ///< the previous insn was created for alignment purposes only +#define AFL_USERTI 0x02000000 ///< the type information is definitive. + ///< (comes from the user or type library) + ///< if not set see #AFL_TYPE_GUESSED +#define AFL_RETFP 0x04000000 ///< function returns a floating point value +#define AFL_USEMODSP 0x08000000 ///< insn modifes SP and uses the modified value; + ///< example: pop [rsp+N] +#define AFL_NOTCODE 0x10000000 ///< autoanalysis should not create code here +#define AFL_NOTPROC 0x20000000 ///< autoanalysis should not create proc here +#define AFL_TYPE_GUESSED 0xC2000000 ///< who guessed the type information? +#define AFL_IDA_GUESSED 0x00000000 ///< the type is guessed by IDA +#define AFL_HR_GUESSED_FUNC 0x40000000 ///< the function type is guessed by the decompiler +#define AFL_HR_GUESSED_DATA 0x80000000 ///< the data type is guessed by the decompiler +#define AFL_HR_DETERMINED 0xC0000000 ///< the type is definitely guessed by the decompiler +///@} + +/// \name Work with additional location flags +/// See \ref AFL_ +///@{ +using aflags_t = flags_t; + +idaman void ida_export set_aflags(ea_t ea, aflags_t flags); +idaman void ida_export upd_abits(ea_t ea, aflags_t clr_bits, aflags_t set_bits); +idaman void ida_export set_abits(ea_t ea, aflags_t bits); +idaman void ida_export clr_abits(ea_t ea, aflags_t bits); +idaman aflags_t ida_export get_aflags(ea_t ea); +idaman void ida_export del_aflags(ea_t ea); + +inline constexpr bool has_aflag_linnum(aflags_t flags) { return (flags & AFL_LINNUM) != 0; } +inline constexpr bool is_aflag_usersp(aflags_t flags) { return (flags & AFL_USERSP) != 0; } +inline constexpr bool is_aflag_public_name(aflags_t flags) { return (flags & AFL_PUBNAM) != 0; } +inline constexpr bool is_aflag_weak_name(aflags_t flags) { return (flags & AFL_WEAKNAM) != 0; } +inline constexpr bool is_aflag_hidden_item(aflags_t flags) { return (flags & AFL_HIDDEN) != 0; } +inline constexpr bool is_aflag_manual_insn(aflags_t flags) { return (flags & AFL_MANUAL) != 0; } +inline constexpr bool is_aflag_hidden_border(aflags_t flags) { return (flags & AFL_NOBRD) != 0; } +inline constexpr bool is_aflag_zstroff(aflags_t flags) { return (flags & AFL_ZSTROFF) != 0; } +inline constexpr bool is_aflag__bnot0(aflags_t flags) { return (flags & AFL_BNOT0) != 0; } +inline constexpr bool is_aflag__bnot1(aflags_t flags) { return (flags & AFL_BNOT1) != 0; } +inline constexpr bool is_aflag_libitem(aflags_t flags) { return (flags & AFL_LIB) != 0; } +inline constexpr bool has_aflag_ti(aflags_t flags) { return (flags & AFL_TI) != 0; } +inline constexpr bool has_aflag_ti0(aflags_t flags) { return (flags & AFL_TI0) != 0; } +inline constexpr bool has_aflag_ti1(aflags_t flags) { return (flags & AFL_TI1) != 0; } +inline constexpr bool has_aflag_lname(aflags_t flags) { return (flags & AFL_LNAME) != 0; } +inline constexpr bool is_aflag_tilcmt(aflags_t flags) { return (flags & AFL_TILCMT) != 0; } +inline constexpr bool is_aflag_lzero0(aflags_t flags) { return (flags & AFL_LZERO0) != 0; } +inline constexpr bool is_aflag_lzero1(aflags_t flags) { return (flags & AFL_LZERO1) != 0; } +inline constexpr bool is_aflag_colored_item(aflags_t flags) { return (flags & AFL_COLORED) != 0; } +inline constexpr bool is_aflag_terse_struc(aflags_t flags) { return (flags & AFL_TERSESTR) != 0; } +inline constexpr bool is_aflag__invsign0(aflags_t flags) { return (flags & AFL_SIGN0) != 0; } +inline constexpr bool is_aflag__invsign1(aflags_t flags) { return (flags & AFL_SIGN1) != 0; } +inline constexpr bool is_aflag_noret(aflags_t flags) { return (flags & AFL_NORET) != 0; } +inline constexpr bool is_aflag_fixed_spd(aflags_t flags) { return (flags & AFL_FIXEDSPD) != 0; } +inline constexpr bool is_aflag_align_flow(aflags_t flags) { return (flags & AFL_ALIGNFLOW)!= 0; } +inline constexpr bool is_aflag_userti(aflags_t flags) { return (flags & AFL_USERTI) != 0; } +inline constexpr bool is_aflag_retfp(aflags_t flags) { return (flags & AFL_RETFP) != 0; } +inline constexpr bool uses_aflag_modsp(aflags_t flags) { return (flags & AFL_USEMODSP) != 0; } +inline constexpr bool is_aflag_notcode(aflags_t flags) { return (flags & AFL_NOTCODE) != 0; } +inline constexpr bool is_aflag_notproc(aflags_t flags) { return (flags & AFL_NOTPROC) != 0; } +inline constexpr bool is_aflag_type_guessed_by_ida(aflags_t flags) { return (flags & AFL_TYPE_GUESSED) == AFL_IDA_GUESSED; } +inline constexpr bool is_aflag_func_guessed_by_hexrays(aflags_t flags) { return (flags & AFL_TYPE_GUESSED) == AFL_HR_GUESSED_FUNC; } +inline constexpr bool is_aflag_data_guessed_by_hexrays(aflags_t flags) { return (flags & AFL_TYPE_GUESSED) == AFL_HR_GUESSED_DATA; } +inline constexpr bool is_aflag_type_determined_by_hexrays(aflags_t flags) { return (flags & AFL_TYPE_GUESSED) == AFL_HR_DETERMINED; } +inline constexpr bool is_aflag_type_guessed_by_hexrays(aflags_t flags) +{ + return (flags & AFL_TYPE_GUESSED) == AFL_HR_GUESSED_FUNC + || (flags & AFL_TYPE_GUESSED) == AFL_HR_GUESSED_DATA + || (flags & AFL_TYPE_GUESSED) == AFL_HR_DETERMINED; +} + +inline bool is_hidden_item(ea_t ea) { return is_aflag_hidden_item(get_aflags(ea)); } +inline void hide_item(ea_t ea) { set_abits(ea, AFL_HIDDEN); } +inline void unhide_item(ea_t ea) { clr_abits(ea, AFL_HIDDEN); } + +inline bool is_hidden_border(ea_t ea) { return is_aflag_hidden_border(get_aflags(ea)); } +inline void hide_border(ea_t ea) { set_abits(ea, AFL_NOBRD); } +inline void unhide_border(ea_t ea) { clr_abits(ea, AFL_NOBRD); } + +inline bool uses_modsp(ea_t ea) { return uses_aflag_modsp(get_aflags(ea)); } +inline void set_usemodsp(ea_t ea) { set_abits(ea, AFL_USEMODSP); } +inline void clr_usemodsp(ea_t ea) { clr_abits(ea, AFL_USEMODSP); } + +inline bool is_zstroff(ea_t ea) { return is_aflag_zstroff(get_aflags(ea)); } +inline void set_zstroff(ea_t ea) { set_abits(ea, AFL_ZSTROFF); } +inline void clr_zstroff(ea_t ea) { clr_abits(ea, AFL_ZSTROFF); } + +inline bool is__bnot0(ea_t ea) { return is_aflag__bnot0(get_aflags(ea)); } +inline void set__bnot0(ea_t ea) { set_abits(ea, AFL_BNOT0); } +inline void clr__bnot0(ea_t ea) { clr_abits(ea, AFL_BNOT0); } + +inline bool is__bnot1(ea_t ea) { return is_aflag__bnot1(get_aflags(ea)); } +inline void set__bnot1(ea_t ea) { set_abits(ea, AFL_BNOT1); } +inline void clr__bnot1(ea_t ea) { clr_abits(ea, AFL_BNOT1); } + +inline bool is_libitem(ea_t ea) { return is_aflag_libitem(get_aflags(ea)); } +inline void set_libitem(ea_t ea) { set_abits(ea, AFL_LIB); } +inline void clr_libitem(ea_t ea) { clr_abits(ea, AFL_LIB); } + +inline bool has_ti(ea_t ea) { return has_aflag_ti(get_aflags(ea)); } +inline void set_has_ti(ea_t ea) { set_abits(ea, AFL_TI); } +inline void clr_has_ti(ea_t ea) { clr_abits(ea, AFL_TI); } + +inline bool has_ti0(ea_t ea) { return has_aflag_ti0(get_aflags(ea)); } +inline void set_has_ti0(ea_t ea) { set_abits(ea, AFL_TI0); } +inline void clr_has_ti0(ea_t ea) { clr_abits(ea, AFL_TI0); } + +inline bool has_ti1(ea_t ea) { return has_aflag_ti1(get_aflags(ea)); } +inline void set_has_ti1(ea_t ea) { set_abits(ea, AFL_TI1); } +inline void clr_has_ti1(ea_t ea) { clr_abits(ea, AFL_TI1); } + +inline bool has_lname(ea_t ea) { return has_aflag_lname(get_aflags(ea)); } +inline void set_has_lname(ea_t ea) { set_abits(ea, AFL_LNAME); } +inline void clr_has_lname(ea_t ea) { clr_abits(ea, AFL_LNAME); } + +inline bool is_tilcmt(ea_t ea) { return is_aflag_tilcmt(get_aflags(ea)); } +inline void set_tilcmt(ea_t ea) { set_abits(ea, AFL_TILCMT); } +inline void clr_tilcmt(ea_t ea) { clr_abits(ea, AFL_TILCMT); } + +inline bool is_usersp(ea_t ea) { return is_aflag_usersp(get_aflags(ea)); } +inline void set_usersp(ea_t ea) { set_abits(ea, AFL_USERSP); } +inline void clr_usersp(ea_t ea) { clr_abits(ea, AFL_USERSP); } + +inline bool is_lzero0(ea_t ea) { return is_aflag_lzero0(get_aflags(ea)); } +inline void set_lzero0(ea_t ea) { set_abits(ea, AFL_LZERO0); } +inline void clr_lzero0(ea_t ea) { clr_abits(ea, AFL_LZERO0); } + +inline bool is_lzero1(ea_t ea) { return is_aflag_lzero1(get_aflags(ea)); } +inline void set_lzero1(ea_t ea) { set_abits(ea, AFL_LZERO1); } +inline void clr_lzero1(ea_t ea) { clr_abits(ea, AFL_LZERO1); } + +inline bool is_colored_item(ea_t ea) { return is_aflag_colored_item(get_aflags(ea)); } +inline void set_colored_item(ea_t ea) { set_abits(ea, AFL_COLORED); } // use set_item_color() +inline void clr_colored_item(ea_t ea) { clr_abits(ea, AFL_COLORED); } // use del_item_color() + +inline bool is_terse_struc(ea_t ea) { return is_aflag_terse_struc(get_aflags(ea)); } +inline void set_terse_struc(ea_t ea) { set_abits(ea, AFL_TERSESTR); } +inline void clr_terse_struc(ea_t ea) { clr_abits(ea, AFL_TERSESTR); } + +inline bool is__invsign0(ea_t ea) { return is_aflag__invsign0(get_aflags(ea)); } +inline void set__invsign0(ea_t ea) { set_abits(ea, AFL_SIGN0); } +inline void clr__invsign0(ea_t ea) { clr_abits(ea, AFL_SIGN0); } + +inline bool is__invsign1(ea_t ea) { return is_aflag__invsign1(get_aflags(ea)); } +inline void set__invsign1(ea_t ea) { set_abits(ea, AFL_SIGN1); } +inline void clr__invsign1(ea_t ea) { clr_abits(ea, AFL_SIGN1); } + +inline bool is_noret(ea_t ea) { return is_aflag_noret(get_aflags(ea)); } +inline void set_noret(ea_t ea) { set_abits(ea, AFL_NORET); } +inline void clr_noret(ea_t ea) { clr_abits(ea, AFL_NORET); } + +inline bool is_fixed_spd(ea_t ea) { return is_aflag_fixed_spd(get_aflags(ea)); } +inline void set_fixed_spd(ea_t ea) { set_abits(ea, AFL_FIXEDSPD); } +inline void clr_fixed_spd(ea_t ea) { clr_abits(ea, AFL_FIXEDSPD); } + +inline bool is_align_flow(ea_t ea) { return is_aflag_align_flow(get_aflags(ea)); } +inline void set_align_flow(ea_t ea) { set_abits(ea, AFL_ALIGNFLOW); } +inline void clr_align_flow(ea_t ea) { clr_abits(ea, AFL_ALIGNFLOW); } + +inline bool is_userti(ea_t ea) { return is_aflag_userti(get_aflags(ea)); } +inline void set_userti(ea_t ea) { upd_abits(ea, AFL_TYPE_GUESSED, AFL_USERTI); } +inline void clr_userti(ea_t ea) { clr_abits(ea, AFL_TYPE_GUESSED); } // use set_ida_guessed_type() + +inline bool is_retfp(ea_t ea) { return is_aflag_retfp(get_aflags(ea)); } +inline void set_retfp(ea_t ea) { set_abits(ea, AFL_RETFP); } +inline void clr_retfp(ea_t ea) { clr_abits(ea, AFL_RETFP); } + +inline bool is_notproc(ea_t ea) { return is_aflag_notproc(get_aflags(ea)); } +inline void set_notproc(ea_t ea) { set_abits(ea, AFL_NOTPROC); } +inline void clr_notproc(ea_t ea) { clr_abits(ea, AFL_NOTPROC); } + +inline bool is_type_guessed_by_ida(ea_t ea) { return is_aflag_type_guessed_by_ida(get_aflags(ea)); } +inline bool is_func_guessed_by_hexrays(ea_t ea) { return is_aflag_func_guessed_by_hexrays(get_aflags(ea)); } +inline bool is_data_guessed_by_hexrays(ea_t ea) { return is_aflag_data_guessed_by_hexrays(get_aflags(ea)); } +inline bool is_type_determined_by_hexrays(ea_t ea) { return is_aflag_type_determined_by_hexrays(get_aflags(ea)); } +inline bool is_type_guessed_by_hexrays(ea_t ea) { return is_aflag_type_guessed_by_hexrays(get_aflags(ea)); } + +inline void set_type_guessed_by_ida(ea_t ea) { upd_abits(ea, AFL_TYPE_GUESSED, AFL_IDA_GUESSED); } +inline void set_func_guessed_by_hexrays(ea_t ea) { upd_abits(ea, AFL_TYPE_GUESSED, AFL_HR_GUESSED_FUNC); } +inline void set_data_guessed_by_hexrays(ea_t ea) { upd_abits(ea, AFL_TYPE_GUESSED, AFL_HR_GUESSED_DATA); } +inline void set_type_determined_by_hexrays(ea_t ea) { upd_abits(ea, AFL_TYPE_GUESSED, AFL_HR_DETERMINED); } +///@} + +/// Mark address so that it cannot be converted to instruction +idaman void ida_export set_notcode(ea_t ea); + +/// Clear not-code mark +inline void clr_notcode(ea_t ea) { clr_abits(ea, AFL_NOTCODE); } + +/// Is the address marked as not-code? +inline bool is_notcode(ea_t ea) { return is_aflag_notcode(get_aflags(ea)); } + + +/// Change visibility of item at given ea + +inline void set_visible_item(ea_t ea, bool visible) +{ + if ( visible ) + unhide_item(ea); + else + hide_item(ea); +} + +/// Test visibility of item at given ea + +inline bool is_visible_item(ea_t ea) { return !is_hidden_item(ea); } + + +/// Is instruction visible? + +inline bool is_finally_visible_item(ea_t ea) +{ + return (inf_get_cmtflg() & SCF_SHHID_ITEM) != 0 || is_visible_item(ea); +} + + +/// \name Source line numbers +/// They are sometimes present in object files. +///@{ +idaman void ida_export set_source_linnum(ea_t ea, uval_t lnnum); +idaman uval_t ida_export get_source_linnum(ea_t ea); +idaman void ida_export del_source_linnum(ea_t ea); +///@} + +/// \name Absolute segment base address +/// These functions may be used if necessary (despite of the AFLNOTE above). +///@{ +inline ea_t get_absbase(ea_t ea) +{ + ea_t x; + return getnode(ea).supval(NALT_ABSBASE, &x, sizeof(x), atag) > 0 ? ea_t(x-1) : ea_t(-1); +} +inline void set_absbase(ea_t ea, ea_t x) +{ + x++; + getnode(ea).supset(NALT_ABSBASE, &x, sizeof(x), atag); +} +inline void del_absbase(ea_t ea) { getnode(ea).supdel(NALT_ABSBASE, atag); } +///@} + +/// \name Purged bytes +/// Number of bytes purged from the stack when a function is called indirectly +/// get_ind_purged() may be used if necessary (despite of the AFLNOTE above). +/// Use set_purged() to modify this value (do not use set_ind_purged()) +///@{ +idaman ea_t ida_export get_ind_purged(ea_t ea); +inline void set_ind_purged(ea_t ea, ea_t x) +{ + x++; + getnode(ea).supset(NALT_PURGE, &x, sizeof(x), atag); +} +inline void del_ind_purged(ea_t ea) { getnode(ea).supdel(NALT_PURGE, atag); } +///@} + +/// \name Get type of string +/// Use higher level function get_opinfo(). +///@{ +idaman uint32 ida_export get_str_type(ea_t ea); +idaman void ida_export set_str_type(ea_t ea, uint32 x); +idaman void ida_export del_str_type(ea_t ea); +///@} + +// Number of bytes per "units" in a string. E.g., ASCII, Windows-1252, +// UTF-8 all take up one byte per unit, while UTF-16 variations take 2 and +// UTF-32 variations take 4. +// (Note that an "unit" in this context is not necessarily a character, +// since UTF-8-encoded characters can be encoded in up to 4 bytes, +// and UTF-16-encoded characters can be encoded in up to 2 bytes.) +#define STRWIDTH_1B 0 +#define STRWIDTH_2B 1 +#define STRWIDTH_4B 2 +#define STRWIDTH_MASK 0x03 + +// The string layout; how the string is laid out in data. +#define STRLYT_TERMCHR 0 +#define STRLYT_PASCAL1 1 +#define STRLYT_PASCAL2 2 +#define STRLYT_PASCAL4 3 +#define STRLYT_MASK 0xFC +#define STRLYT_SHIFT 2 + + +/// \defgroup STRTYPE_ String type codes +///@{ +///< Character-terminated string. The termination characters are kept in +///< the next bytes of string type. +#define STRTYPE_TERMCHR (STRWIDTH_1B|STRLYT_TERMCHR<<STRLYT_SHIFT) +///< C-style string. +#define STRTYPE_C STRTYPE_TERMCHR +///< Zero-terminated 16bit chars +#define STRTYPE_C_16 (STRWIDTH_2B|STRLYT_TERMCHR<<STRLYT_SHIFT) +///< Zero-terminated 32bit chars +#define STRTYPE_C_32 (STRWIDTH_4B|STRLYT_TERMCHR<<STRLYT_SHIFT) +///< Pascal-style, one-byte length prefix +#define STRTYPE_PASCAL (STRWIDTH_1B|STRLYT_PASCAL1<<STRLYT_SHIFT) +///< Pascal-style, 16bit chars, one-byte length prefix +#define STRTYPE_PASCAL_16 (STRWIDTH_2B|STRLYT_PASCAL1<<STRLYT_SHIFT) +///< Pascal-style, 32bit chars, one-byte length prefix +#define STRTYPE_PASCAL_32 (STRWIDTH_4B|STRLYT_PASCAL1<<STRLYT_SHIFT) +///< Pascal-style, two-byte length prefix +#define STRTYPE_LEN2 (STRWIDTH_1B|STRLYT_PASCAL2<<STRLYT_SHIFT) +///< Pascal-style, 16bit chars, two-byte length prefix +#define STRTYPE_LEN2_16 (STRWIDTH_2B|STRLYT_PASCAL2<<STRLYT_SHIFT) +///< Pascal-style, 32bit chars, two-byte length prefix +#define STRTYPE_LEN2_32 (STRWIDTH_4B|STRLYT_PASCAL2<<STRLYT_SHIFT) +///< Pascal-style, four-byte length prefix +#define STRTYPE_LEN4 (STRWIDTH_1B|STRLYT_PASCAL4<<STRLYT_SHIFT) +///< Pascal-style, 16bit chars, four-byte length prefix +#define STRTYPE_LEN4_16 (STRWIDTH_2B|STRLYT_PASCAL4<<STRLYT_SHIFT) +///< Pascal-style, 32bit chars, four-byte length prefix +#define STRTYPE_LEN4_32 (STRWIDTH_4B|STRLYT_PASCAL4<<STRLYT_SHIFT) +///@} + +/// \name Work with string type codes +/// See \ref STRTYPE_ +///@{ +inline THREAD_SAFE uchar idaapi get_str_type_code(int32 strtype) { return uchar(strtype); } +inline THREAD_SAFE char get_str_term1(int32 strtype) { return char(strtype>>8); } +inline THREAD_SAFE char get_str_term2(int32 strtype) { return char(strtype>>16); } + // if the second termination character is + // '\0', then it doesn't exist. +/// Get index of the string encoding for this string +inline THREAD_SAFE uchar idaapi get_str_encoding_idx(int32 strtype) { return uchar(strtype>>24); } +/// Set index of the string encoding in the string type +inline THREAD_SAFE int32 set_str_encoding_idx(int32 strtype, int encoding_idx) +{ + return (strtype & 0xFFFFFF) | ((uchar)encoding_idx << 24); +} +/// Get string type for a string in the given encoding +inline THREAD_SAFE int32 make_str_type( + uchar type_code, + int encoding_idx, + uchar term1 = 0, + uchar term2 = 0) +{ + return type_code + | (term1 << 8) + | (term2 << 16) + | ((uchar)encoding_idx << 24); +} + + +inline THREAD_SAFE bool is_pascal(int32 strtype) +{ + int lyt = get_str_type_code(strtype) >> STRLYT_SHIFT; + return lyt >= STRLYT_PASCAL1 && lyt <= STRLYT_PASCAL4; +} + +inline THREAD_SAFE size_t get_str_type_prefix_length(int32 strtype) +{ + switch ( get_str_type_code(strtype) ) + { + case STRTYPE_LEN4_32: + case STRTYPE_LEN4_16: + case STRTYPE_LEN4: + return 4; + case STRTYPE_LEN2_32: + case STRTYPE_LEN2_16: + case STRTYPE_LEN2: + return 2; + case STRTYPE_PASCAL_32: + case STRTYPE_PASCAL_16: + case STRTYPE_PASCAL: + return 1; + } + return 0; +} +///@} + +#define STRENC_DEFAULT 0x00 ///< use default encoding for this type (see get_default_encoding_idx()) +#define STRENC_NONE 0xFF ///< force no-conversion encoding + +/// \name Alignment value +/// (should be power of 2) +/// These functions may be used if necessary (despite of the AFLNOTE above). +///@{ +inline uint32 get_alignment(ea_t ea) +{ + uint32 x; + return getnode(ea).supval(NALT_ALIGN, &x, sizeof(x), atag) > 0 ? uint32(x-1) : uint32(-1); +} +inline void set_alignment(ea_t ea, uint32 x) +{ + x++; + getnode(ea).supset(NALT_ALIGN, &x, sizeof(x), atag); +} +inline void del_alignment(ea_t ea) { getnode(ea).supdel(NALT_ALIGN, atag); } +///@} + + +/// \name Instruction/Data background color +///@{ +idaman void ida_export set_item_color(ea_t ea, bgcolor_t color); +idaman bgcolor_t ida_export get_item_color(ea_t ea); // returns DEFCOLOR if no color +idaman bool ida_export del_item_color(ea_t ea); +///@} + + +//------------------------------------------------------------------------- +/// \name Array representation +///@{ +/// Describes how to display an array +struct array_parameters_t +{ + int32 flags; +#define AP_ALLOWDUPS 0x00000001 ///< use 'dup' construct +#define AP_SIGNED 0x00000002 ///< treats numbers as signed +#define AP_INDEX 0x00000004 ///< display array element indexes as comments +#define AP_ARRAY 0x00000008 ///< create as array (this flag is not stored in database) +#define AP_IDXBASEMASK 0x000000F0 ///< mask for number base of the indexes +#define AP_IDXDEC 0x00000000 ///< display indexes in decimal +#define AP_IDXHEX 0x00000010 ///< display indexes in hex +#define AP_IDXOCT 0x00000020 ///< display indexes in octal +#define AP_IDXBIN 0x00000030 ///< display indexes in binary + + int32 lineitems; ///< number of items on a line + int32 alignment; ///< -1 - don't align. + ///< 0 - align automatically. + ///< else item width + + array_parameters_t(int32 _f=AP_ALLOWDUPS, int32 _l=0, int32 _a=-1) : flags(_f), lineitems(_l), alignment(_a) {} + bool is_default() const { return flags == AP_ALLOWDUPS && lineitems == 0 && alignment == -1; } +}; +idaman ssize_t ida_export get_array_parameters(array_parameters_t *out, ea_t ea); +idaman void ida_export set_array_parameters(ea_t ea, const array_parameters_t *in); +inline void idaapi del_array_parameters(ea_t ea) { getnode(ea).supdel(NSUP_ARRAY); } +///@} + +//-------------------------------------------------------------------------- +/// Information about a switch statement +struct switch_info_t +{ + uint32 flags; ///< \ref SWI_ +/// \defgroup SWI_ Switch info flags +/// Used by switch_info_t::flags +///@{ +#define SWI_SPARSE 0x00000001 ///< sparse switch (value table present), + ///< otherwise lowcase present +#define SWI_V32 0x00000002 ///< 32-bit values in table +#define SWI_J32 0x00000004 ///< 32-bit jump offsets +#define SWI_VSPLIT 0x00000008 ///< value table is split (only for 32-bit values) +#define SWI_USER 0x00000010 ///< user specified switch (starting from version 2) +#define SWI_DEF_IN_TBL 0x00000020 ///< default case is an entry in the jump table. + ///< This flag is applicable in 2 cases: + ///< - The sparse indirect switch (i.e. a switch with a values table) + ///< {jump table size} == {value table size} + 1. + ///< The default case entry is the last one in the table + ///< (or the first one in the case of an inversed jump table). + ///< - The switch with insns in the jump table. + ///< The default case entry is before the first entry of the table. \n + ///< See also the find_defjump_from_table() helper function. +#define SWI_JMP_INV 0x00000040 ///< jumptable is inversed. (last entry is + ///< for first entry in values table) +#define SWI_SHIFT_MASK 0x00000180 ///< use formula (element<<shift) + elbase to find jump targets +#define SWI_ELBASE 0x00000200 ///< elbase is present (otherwise the base of the switch + ///< segment will be used) +#define SWI_JSIZE 0x00000400 ///< jump offset expansion bit +#define SWI_VSIZE 0x00000800 ///< value table element size expansion bit +#define SWI_SEPARATE 0x00001000 ///< create an array of individual elements (otherwise separate items) +#define SWI_SIGNED 0x00002000 ///< jump table entries are signed +#define SWI_CUSTOM 0x00004000 ///< custom jump table. + ///< \ph{create_switch_xrefs} will be called to create code xrefs + ///< for the table. Custom jump table must be created by the + ///< module (see also #SWI_STDTBL) +//#define SWI_EXTENDED 0x00008000 ///< reserved +#define SWI_INDIRECT 0x00010000 ///< value table elements are used as indexes into the jump table + ///< (for sparse switches) +#define SWI_SUBTRACT 0x00020000 ///< table values are subtracted from the elbase instead of being added +#define SWI_HXNOLOWCASE 0x00040000 ///< lowcase value should not be used by the decompiler (internal flag) +#define SWI_STDTBL 0x00080000 ///< custom jump table with standard table formatting. + ///< ATM IDA doesn't use SWI_CUSTOM for switches with standard + ///< table formatting. So this flag can be considered as obsolete. +#define SWI_DEFRET 0x00100000 ///< return in the default case (defjump==BADADDR) +#define SWI_SELFREL 0x00200000 ///< jump address is relative to the element not to ELBASE +#define SWI_JMPINSN 0x00400000 ///< jump table entries are insns. For such entries SHIFT has a + ///< different meaning. It denotes the number of insns in the + ///< entry. For example, 0 - the entry contains the jump to the + ///< case, 1 - the entry contains one insn like a 'mov' and jump + ///< to the end of case, and so on. +#define SWI_VERSION 0x00800000 ///< the structure contains the VERSION member +///@} + + /// See #SWI_SHIFT_MASK. + /// possible answers: 0..3. + int get_shift(void) const { return ((flags & SWI_SHIFT_MASK) >> 7); } + + /// See #SWI_SHIFT_MASK + void set_shift(int shift) + { + flags &= ~SWI_SHIFT_MASK; + flags |= ((shift & 3) << 7); + } + + int get_jtable_element_size(void) const + { // this brain damaged logic is needed for compatibility with old versions + int code = flags & (SWI_J32|SWI_JSIZE); + if ( code == 0 ) + return 2; + if ( code == SWI_J32 ) + return 4; + if ( code == SWI_JSIZE ) + return 1; + return 8; + } + void set_jtable_element_size(int size) + { + flags &= ~SWI_J32|SWI_JSIZE; + switch ( size ) + { + case 4: + flags |= SWI_J32; + break; + case 1: + flags |= SWI_JSIZE; + break; + case 8: + flags |= SWI_J32|SWI_JSIZE; + break; + case 2: + break; + default: + INTERR(1297); + } + } + int get_vtable_element_size(void) const + { + int code = flags & (SWI_V32|SWI_VSIZE); + if ( code == 0 ) + return 2; + if ( code == SWI_V32 ) + return 4; + if ( code == SWI_VSIZE ) + return 1; + return 8; + } + void set_vtable_element_size(int size) + { + flags &= ~SWI_V32|SWI_VSIZE; + switch ( size ) + { + case 4: + flags |= SWI_V32; + break; + case 1: + flags |= SWI_VSIZE; + break; + case 8: + flags |= SWI_V32|SWI_VSIZE; + break; + case 2: + break; + default: + INTERR(1298); + } + } + + bool has_default(void) const { return defjump != BADADDR; } + bool has_elbase(void) const { return (flags & SWI_ELBASE) != 0; } + bool is_sparse(void) const { return (flags & SWI_SPARSE) != 0; } + bool is_custom(void) const { return (flags & SWI_CUSTOM) != 0; } + bool is_indirect(void) const { return (flags & SWI_INDIRECT) != 0; } + bool is_subtract(void) const { return (flags & SWI_SUBTRACT) != 0; } + bool is_nolowcase(void) const { return (flags & SWI_HXNOLOWCASE) != 0; } + bool use_std_table(void) const { return !is_custom() || (flags & SWI_STDTBL) != 0; } + bool is_user_defined() const + { + return get_version() >= 2 && (flags & SWI_USER) != 0; + } + + ushort ncases = 0; ///< number of cases (excluding default) + ea_t jumps = BADADDR; ///< jump table start address + union + { + ea_t values; ///< values table address (if #SWI_SPARSE is set) + uval_t lowcase; ///< the lowest value in cases + }; + ea_t defjump = BADADDR; ///< default jump address (#BADADDR if no default case) + ea_t startea = BADADDR; ///< start of the switch idiom + int jcases = 0; ///< number of entries in the jump table (SWI_INDIRECT) + + sval_t ind_lowcase = 0; + sval_t get_lowcase(void) const { return is_indirect() ? ind_lowcase : lowcase; } + ea_t elbase = 0; ///< element base + + int regnum = -1; ///< the switch expression as a value of the REGNUM register + ///< before the instruction at EXPR_EA. -1 means 'unknown' + op_dtype_t regdtype = 0; ///< size of the switch expression register as dtype + + int get_jtable_size(void) const { return is_indirect() ? jcases : ncases; } + void set_jtable_size(int size) + { + if ( is_indirect() ) + jcases = size; + else + ncases = uint16(size); + } + void set_elbase(ea_t base) + { + elbase = base; + flags |= SWI_ELBASE; + } + + void set_expr(int r, op_dtype_t dt) { regnum = r; regdtype = dt; } + + /// get separate parts of the switch + bool get_jrange_vrange(range_t *jrange = nullptr, range_t *vrange = nullptr) const + { + if ( !use_std_table() ) + return false; + if ( jrange != nullptr ) + { + int n = get_jtable_size(); + if ( (flags & SWI_DEF_IN_TBL) != 0 ) + ++n; + int jsize = get_jtable_element_size(); + *jrange = range_t(jumps, jumps + jsize * n); + } + if ( vrange != nullptr && is_sparse() ) + { + int vsize = get_vtable_element_size(); + *vrange = range_t(values, values + vsize * ncases); + } + return true; + } + + uval_t custom = 0; ///< information for custom tables (filled and used by modules) + + enum { SWITCH_INFO_VERSION = 2 }; + int version = SWITCH_INFO_VERSION; + int get_version() const { return (flags & SWI_VERSION) == 0 ? 1 : version; } + + // version 2 + ea_t expr_ea = BADADDR; ///< the address before that the switch expression is in REGNUM. + ///< If BADADDR, then the first insn marked as IM_SWITCH after + ///< STARTEA is used. + eavec_t marks; ///< the insns marked as IM_SWITCH. They are used to delete the switch. + + switch_info_t() : flags(SWI_VERSION), lowcase(0) {} + void clear() { *this = switch_info_t(); } +}; + +/// \name Switch info +/// See ::switch_info_t, xref.hpp for related functions +///@{ +idaman ssize_t ida_export get_switch_info(switch_info_t *out, ea_t ea); +idaman void ida_export set_switch_info(ea_t ea, const switch_info_t &in); +idaman void ida_export del_switch_info(ea_t ea); +///@} + +/// \name Switch parent +/// Address which holds the switch info (::switch_info_t). Used at the jump targets. +///@{ +inline ea_t get_switch_parent(ea_t ea) +{ + ea_t x; + return getnode(ea).supval(NALT_SWITCH, &x, sizeof(x), atag) > 0 ? ea_t(x-1) : ea_t(-1); +} +inline void set_switch_parent(ea_t ea, ea_t x) +{ + x++; + getnode(ea).supset(NALT_SWITCH, &x, sizeof(x), atag); +} +inline void del_switch_parent(ea_t ea) { getnode(ea).supdel(NALT_SWITCH, atag); } +///@} + +/// \name Custom data types +///@{ +/// Information about custom data types +struct custom_data_type_ids_t +{ + int16 dtid; ///< data type id + int16 fids[UA_MAXOP]; ///< data format ids + + void set(tid_t tid) + { + memset(fids, -1, sizeof(fids)); + dtid = uint16(tid); + fids[0] = uint16(tid >> 16); + } + tid_t get_dtid() const + { + return uint16(dtid) | (uint16(-1) << 16); + } +#ifndef SWIG + DECLARE_COMPARISONS(custom_data_type_ids_t); +#endif +}; +idaman int ida_export get_custom_data_type_ids(custom_data_type_ids_t *cdis, ea_t ea); +idaman void ida_export set_custom_data_type_ids(ea_t ea, const custom_data_type_ids_t *cdis); +inline void idaapi del_custom_data_type_ids(ea_t ea) { getnode(ea).supdel(NSUP_CUSTDT); } +///@} + +typedef uchar reftype_t; ///< see \ref reftype_ +/// \defgroup reftype_ Types of references +/// References are represented in the following form: +/// +/// \v{target + tdelta - base} +/// +/// If the target is not present, then it will be calculated using +/// +/// \v{target = operand_value - tdelta + base} +/// +/// The target must be present for LOW and HIGH reference types +///@{ +const reftype_t + V695_REF_OFF8 = 0, ///< reserved + REF_OFF16 = 1, ///< 16bit full offset + REF_OFF32 = 2, ///< 32bit full offset + REF_LOW8 = 3, ///< low 8bits of 16bit offset + REF_LOW16 = 4, ///< low 16bits of 32bit offset + REF_HIGH8 = 5, ///< high 8bits of 16bit offset + REF_HIGH16 = 6, ///< high 16bits of 32bit offset + V695_REF_VHIGH = 7, ///< obsolete + V695_REF_VLOW = 8, ///< obsolete + REF_OFF64 = 9, ///< 64bit full offset + REF_OFF8 = 10, ///< 8bit full offset + REF_LAST = REF_OFF8; +///@} + +/// Can the target be calculated using operand value? + +inline bool is_reftype_target_optional(reftype_t type); + +/// Get REF_... constant from size +/// Supported sizes: 1,2,4,8,16 +/// For other sizes returns reftype_t(-1) + +idaman reftype_t ida_export get_reftype_by_size(size_t size); + +/// Information about a reference +struct refinfo_t +{ + ea_t target; ///< reference target (#BADADDR-none) + ea_t base; ///< base of reference (may be BADADDR) + adiff_t tdelta; ///< offset from the target + uint32 flags; ///< \ref REFINFO_ +/// \defgroup REFINFO_ Reference info flags +/// Used by refinfo_t::flags +///@{ +#define REFINFO_TYPE 0x000F ///< reference type (reftype_t), or custom + ///< reference ID if REFINFO_CUSTOM set +#define REFINFO_RVAOFF 0x0010 ///< based reference (rva); + ///< refinfo_t::base will be forced to get_imagebase(); + ///< such a reference is displayed with the \ash{a_rva} keyword +#define REFINFO_PASTEND 0x0020 ///< reference past an item; + ///< it may point to an nonexistent address; + ///< do not destroy alignment dirs +#define REFINFO_CUSTOM 0x0040 ///< a custom reference. + ///< see custom_refinfo_handler_t. + ///< the id of the custom refinfo is + ///< stored under the REFINFO_TYPE mask. +#define REFINFO_NOBASE 0x0080 ///< don't create the base xref; + ///< implies that the base can be any value. + ///< nb: base xrefs are created only if the offset base + ///< points to the middle of a segment +#define REFINFO_SUBTRACT 0x0100 ///< the reference value is subtracted from the base value instead of (as usual) being added to it +#define REFINFO_SIGNEDOP 0x0200 ///< the operand value is sign-extended (only supported for REF_OFF8/16/32/64) +#define REFINFO_NO_ZEROS 0x0400 ///< an opval of 0 will be considered invalid +#define REFINFO_NO_ONES 0x0800 ///< an opval of ~0 will be considered invalid +#define REFINFO_SELFREF 0x1000 ///< the self-based reference; + ///< refinfo_t::base will be forced to the reference address +///@} + + reftype_t type(void) const + { + return reftype_t(flags & (REFINFO_TYPE | REFINFO_CUSTOM)); + } + + bool is_target_optional() const ///< \ref is_reftype_target_optional() + { + reftype_t rt = flags & (REFINFO_TYPE | REFINFO_CUSTOM); + return is_reftype_target_optional(rt); + } + + bool no_base_xref(void) const { return (flags & REFINFO_NOBASE) != 0; } + bool is_pastend(void) const { return (flags & REFINFO_PASTEND) != 0; } + bool is_rvaoff(void) const { return (flags & REFINFO_RVAOFF) != 0; } + bool is_custom(void) const { return (flags & REFINFO_CUSTOM) != 0; } + bool is_subtract(void) const { return (flags & REFINFO_SUBTRACT) != 0; } + bool is_signed(void) const { return (flags & REFINFO_SIGNEDOP) != 0; } + bool is_no_zeros(void) const { return (flags & REFINFO_NO_ZEROS) != 0; } + bool is_no_ones(void) const { return (flags & REFINFO_NO_ONES) != 0; } + bool is_selfref(void) const { return (flags & REFINFO_SELFREF) != 0; } + + // RT can include REFINFO_CUSTOM bit + void set_type(reftype_t rt) + { + flags &= ~(REFINFO_TYPE | REFINFO_CUSTOM); + flags |= rt; + } + + // init the structure with some default values + // reft_and_flags should be REF_xxx optionally ORed with some REFINFO_xxx flags + void init(uint32 reft_and_flags, ea_t _base = 0, ea_t _target = BADADDR, adiff_t _tdelta = 0) + { + flags = reft_and_flags; + base = _base; + target = _target; + tdelta = _tdelta; + } + + // internal use +#ifndef SWIG + bool _require_base() const { return !is_rvaoff() && !is_selfref(); } + DECLARE_COMPARISONS(refinfo_t); +#endif +}; + +/// Manage a custom refinfo type +/// Custom refinfos are usually used to handle custom fixups, +/// but can also be used to display non-standard references. +struct custom_refinfo_handler_t +{ + int32 cbsize; ///< size of this structure + const char *name; ///< Format name, must be unique + const char *desc; ///< Refinfo description to use in Ctrl-R dialog + int props; ///< properties (currently 0) +/// \defgroup RHF_ Refinfo handler properties +/// Used by custom_refinfo_handler_t::props +///@{ +#define RHF_TGTOPT 0x0001 ///< can the target be calculated using + ///< operand value? +///@} + + // this callback prepares the full offset expression in buf and + // returns 1 if it is a simple expression or 2 if it is a complex one. + // Or this callback checks the compliance of opval and fullvalue, + // and possibly updates values of target and fullvalue, + // and prepares the format, + // and returns 3 to continue standard processing with updated values. + // Or this callback just prepares the format and returns 4 to continue. + // It returns 0 in the case of error. + // It is guaranteed that before calling this callback, the + // calc_reference_data() callback is always called. + int (idaapi *gen_expr)( + qstring *buf, + qstring *format, // buffer for the format (if retcode>=3) + ea_t ea, + int opnum, + const refinfo_t &ri, + ea_t from, + adiff_t *opval, // the output value is not used + ea_t *target, // the target prepared by calc_reference_data() + ea_t *fullvalue, + int getn_flags); + + // this callback replaces calc_target. + // It calculates target and base, + // and calculates an internal variable fullvalue, + // and checks the compliance of opval and fullvalue, + // and returns the success flag. + bool (idaapi *calc_reference_data)( + ea_t *target, + ea_t *base, + ea_t from, + const refinfo_t &ri, + adiff_t opval); + + // just custom format + void (idaapi *get_format)(qstring *format); + +#ifndef SWIG + DECLARE_COMPARISONS(custom_refinfo_handler_t); +#endif +}; + + +/// Register a new custom refinfo type. + +idaman int ida_export register_custom_refinfo(const custom_refinfo_handler_t *crh); + + +/// Unregister a new custom refinfo type. + +idaman bool ida_export unregister_custom_refinfo(int crid); + + +/// Get id of a custom refinfo type. + +idaman int ida_export find_custom_refinfo(const char *name); + + +/// Get definition of a registered custom refinfo type. + +idaman const custom_refinfo_handler_t *ida_export get_custom_refinfo(int crid); + + +/// Get refinfo handler + +inline const custom_refinfo_handler_t *idaapi get_custom_refinfo_handler( + const refinfo_t &ri) +{ + return ri.is_custom() ? get_custom_refinfo(ri.type()) : nullptr; +} + +// inline implementaion +inline bool is_reftype_target_optional(reftype_t type) +{ + if ( (type & REFINFO_CUSTOM) != 0 ) + { + const custom_refinfo_handler_t *cfh = get_custom_refinfo(type); + if ( cfh == nullptr ) + return false; + return (cfh->props & RHF_TGTOPT) != 0; + } + switch ( type ) + { + case REF_OFF8: + case REF_OFF16: + case REF_OFF32: + case REF_OFF64: + return true; + } + return false; +} + + +/// Get descriptions of all standard and custom refinfo types. + +struct refinfo_desc_t +{ + uint32 type; ///< Refinfo type, see \ref REFINFO_ + ///< Custom refinfo has REFINFO_CUSTOM bit. + const char *name; ///< Refinfo name + const char *desc; ///< Refinfo description to use in Ctrl-R dialog +}; +DECLARE_TYPE_AS_MOVABLE(refinfo_desc_t); +typedef qvector<refinfo_desc_t> refinfo_desc_vec_t; +idaman void ida_export get_refinfo_descs(refinfo_desc_vec_t *descs); + + +#define MAXSTRUCPATH 32 ///< maximal inclusion depth of unions + +/// Information for structure offsets. +/// ids[0] contains the id of the structure. +/// ids[1..len-1] contain ids of the structure members used in the structure offset +/// expression. +/// len is the length of the path, i.e. the number of elements in 'ids' +struct strpath_t +{ + int len; + tid_t ids[MAXSTRUCPATH]; // for union member ids + adiff_t delta; +#ifndef SWIG + DECLARE_COMPARISONS(strpath_t); +#endif +}; + +/// See opinfo_t::ec +struct enum_const_t +{ + tid_t tid; + uchar serial; +#ifndef SWIG + DECLARE_COMPARISONS(enum_const_t) + { + COMPARE_FIELDS(tid); + COMPARE_FIELDS(serial); + return 0; + } +#endif +}; + +/// Additional information about an operand type +union opinfo_t +{ + refinfo_t ri; ///< for offset members + tid_t tid; ///< for struct, etc. members + strpath_t path; ///< for stroff + int32 strtype; ///< for strings (\ref STRTYPE_) + enum_const_t ec; ///< for enums + custom_data_type_ids_t cd; ///< for custom data +#ifndef SWIG + int compare_opinfos(const opinfo_t &r, flags64_t flag, int n) const; +#endif +}; + +//------------------------------------------------------------------------- + +struct printop_t +{ + uint32 unused = 0; // not used anymore, use flags64 instead (kept for backward compat) + opinfo_t ti; // new operand type +#define POF_VALID_TI 0x1 // is operand type initialized? +#define POF_VALID_AFLAGS 0x2 // internal +#define POF_IS_F64 0x4 // internal + uchar features; // features this instance holds + int suspop = 0; // out: will be set by print_operand() + aflags_t aflags = 0; // additional aflags + flags64_t flags = 0; // new operand representation flags + + printop_t() : features(POF_IS_F64) { memset(&ti, 0, sizeof(ti)); } + bool is_ti_initialized() const { return (features & POF_VALID_TI) != 0; } + void set_ti_initialized(bool v=true) { setflag(features, POF_VALID_TI, v); } + bool is_aflags_initialized() const { return (features & POF_VALID_AFLAGS) != 0; } + void set_aflags_initialized(bool v=true) { setflag(features, POF_VALID_AFLAGS, v); } + bool is_f64() const { return (features & POF_IS_F64) != 0; } + + const opinfo_t *get_ti() const { return is_ti_initialized() ? &ti : nullptr; } +}; + +/// \name Get/Set refinfo +/// n may be 0, 1, 2, #OPND_MASK. +/// #OPND_OUTER may be used too. +/// Don't use these functions, see get_opinfo(), set_opinfo() +///@{ +idaman bool ida_export set_refinfo_ex(ea_t ea, int n, const refinfo_t *ri); +idaman bool ida_export set_refinfo( + ea_t ea, + int n, + reftype_t type, + ea_t target=BADADDR, + ea_t base=0, + adiff_t tdelta=0); +idaman bool ida_export get_refinfo(refinfo_t *ri, ea_t ea, int n); +idaman bool ida_export del_refinfo(ea_t ea, int n); +///@} + +//-------------------------------------------------------------------------- +/// \name Structure paths +/// Structure paths for unions and structures with unions (strpath) +/// a structure path is an array of id's. +/// the first id is the id of the structure itself. +/// additional id's (if any) specify which member of a union we should select +/// the maximal size of array is #MAXSTRUCPATH. +/// strpaths are used to determine how to display structure offsets. +///@{ +idaman void ida_export write_struc_path(ea_t ea, int idx, const tid_t *path, int plen, adiff_t delta); +idaman int ida_export read_struc_path(tid_t *path, adiff_t *delta, ea_t ea, int idx); // returns plen +///@} + +///@} + + +//-------------------------------------------------------------------------- +// type information (ti) storage +// up to 256 operands are supported for ti. + +typedef uchar type_t; +typedef uchar p_list; +class tinfo_t; + +/// \name Types +/// Work with function/data types +/// These functions may be used if necessary (despite of the AFLNOTE above). +///@{ +idaman bool ida_export get_tinfo(tinfo_t *tif, ea_t ea); +idaman bool ida_export set_tinfo(ea_t ea, const tinfo_t *tif); +inline void idaapi del_tinfo(ea_t ea) { set_tinfo(ea, nullptr); } +///@} + +/// \name Operand types +/// These functions may be used if necessary (despite of the AFLNOTE above). +///@{ +idaman bool ida_export get_op_tinfo(tinfo_t *tif, ea_t ea, int n); +idaman bool ida_export set_op_tinfo(ea_t ea, int n, const tinfo_t *tif); +inline void idaapi del_op_tinfo(ea_t ea, int n) { set_op_tinfo(ea, n, nullptr); } +///@} + +//------------------------------------------------------------------------// +/// \defgroup RIDX_ Rootnode indexes: +///@{ + +// supvals +#define RIDX_FILE_FORMAT_NAME 1 ///< file format name for loader modules +#define RIDX_SELECTORS 2 ///< 2..63 are for selector_t blob (see init_selectors()) +#define RIDX_GROUPS 64 ///< segment group information (see init_groups()) +#define RIDX_H_PATH 65 ///< C header path +#define RIDX_C_MACROS 66 ///< C predefined macros +#define RIDX_SMALL_IDC_OLD 67 ///< Instant IDC statements (obsolete) +#define RIDX_NOTEPAD 68 ///< notepad blob, occupies 1000 indexes (1MB of text) +#define RIDX_INCLUDE 1100 ///< assembler include file name +#define RIDX_SMALL_IDC 1200 ///< Instant IDC statements, blob +#define RIDX_DUALOP_GRAPH 1300 ///< Graph text representation options +#define RIDX_DUALOP_TEXT 1301 ///< Text text representation options +#define RIDX_MD5 1302 ///< MD5 of the input file +#define RIDX_IDA_VERSION 1303 ///< version of ida which created the database + +#define RIDX_STR_ENCODINGS 1305 ///< a list of encodings for the program strings +#define RIDX_SRCDBG_PATHS 1306 ///< source debug paths, occupies 20 indexes +#define RIDX_DBG_BINPATHS 1328 ///< unused (20 indexes) +#define RIDX_SHA256 1349 ///< SHA256 of the input file +#define RIDX_ABINAME 1350 ///< ABI name (processor specific) +#define RIDX_ARCHIVE_PATH 1351 ///< archive file path +#define RIDX_PROBLEMS 1352 ///< problem lists +#define RIDX_SRCDBG_UNDESIRED 1353 ///< user-closed source files, occupies 20 indexes + +// altvals +#define RIDX_ALT_VERSION uval_t(-1) ///< initial version of database +#define RIDX_ALT_CTIME uval_t(-2) ///< database creation timestamp +#define RIDX_ALT_ELAPSED uval_t(-3) ///< seconds database stayed open +#define RIDX_ALT_NOPENS uval_t(-4) ///< how many times the database is opened +#define RIDX_ALT_CRC32 uval_t(-5) ///< input file crc32 +#define RIDX_ALT_IMAGEBASE uval_t(-6) ///< image base +#define RIDX_ALT_IDSNODE uval_t(-7) ///< ids modnode id (for import_module) +#define RIDX_ALT_FSIZE uval_t(-8) ///< input file size +#define RIDX_ALT_OUTFILEENC uval_t(-9) ///< output file encoding index +///@} + +//--------------------------------------------------------------------------- +/// Get file name only of the input file +idaman ssize_t ida_export get_root_filename(char *buf, size_t bufsize); + +/// Get debugger input file name/path (see #LFLG_DBG_NOPATH) +idaman ssize_t ida_export dbg_get_input_path(char *buf, size_t bufsize); + +// The following functions should eventually be replaced by exported functions +#ifndef __KERNEL__ +/// Get full path of the input file +inline ssize_t idaapi get_input_file_path(char *buf, size_t bufsize) +{ + return getinf_buf(INF_INPUT_FILE_PATH, buf, bufsize); +} + +/// Set full path of the input file +inline void set_root_filename(const char *file) { setinf_buf(INF_INPUT_FILE_PATH, file); } + +/// Get size of input file in bytes +inline size_t idaapi retrieve_input_file_size(void) { return getinf(INF_FSIZE); } + +/// Get input file crc32 stored in the database. +/// it can be used to check that the input file has not been changed. +inline uint32 idaapi retrieve_input_file_crc32(void) { return uint32(getinf(INF_CRC32)); } + +/// Get input file md5 +inline bool idaapi retrieve_input_file_md5(uchar hash[16]) { return getinf_buf(INF_MD5, hash, 16) == 16; } + +/// Get input file sha256 +inline bool idaapi retrieve_input_file_sha256(uchar hash[32]) { return getinf_buf(INF_SHA256, hash, 32) == 32; } + +/// Get name of the include file +inline ssize_t idaapi get_asm_inc_file(qstring *buf) { return getinf_str(buf, INF_INCLUDE); } + +/// Set name of the include file +inline bool idaapi set_asm_inc_file(const char *file) { return setinf_buf(INF_INCLUDE, file); } + +/// Get image base address +inline ea_t idaapi get_imagebase(void) { return getinf(INF_IMAGEBASE); } + +/// Set image base address +inline void idaapi set_imagebase(ea_t base) { setinf(INF_IMAGEBASE, base); } + +/// Get ids modnode +inline netnode idaapi get_ids_modnode(void) { return getinf(INF_IDSNODE); } + +/// Set ids modnode +inline void idaapi set_ids_modnode(netnode id) { setinf(INF_IDSNODE, id); } + +/// Get archive file path from which input file was extracted +inline ssize_t idaapi get_archive_path(qstring *out) { return getinf_str(out, INF_ARCHIVE_PATH); } + +/// Set archive file path from which input file was extracted +inline bool set_archive_path(const char *file) { return setinf_buf(INF_ARCHIVE_PATH, file); } + +/// Get file format name for loader modules +inline ssize_t idaapi get_loader_format_name(qstring *out) { return getinf_str(out, INF_FILE_FORMAT_NAME); } + +/// Set file format name for loader modules +inline void set_loader_format_name(const char *name) { setinf_buf(INF_FILE_FORMAT_NAME, name); } + +/// Get version of ida which created the database (string format like "7.5") +inline ssize_t idaapi get_initial_ida_version(qstring *out) { return getinf_str(out, INF_IDA_VERSION); } + +/// Get notepad text +inline ssize_t idaapi get_ida_notepad_text(qstring *out) { return getinf_str(out, INF_NOTEPAD); } + +/// Set notepad text +inline void idaapi set_ida_notepad_text(const char *text, size_t size=0) { setinf_buf(INF_NOTEPAD, text, size); } + +/// Get source debug paths +inline ssize_t idaapi get_srcdbg_paths(qstring *out) { return getinf_str(out, INF_SRCDBG_PATHS); } + +/// Set source debug paths +inline void idaapi set_srcdbg_paths(const char *paths) { setinf_buf(INF_SRCDBG_PATHS, paths); } + +/// Get user-closed source files +inline ssize_t idaapi get_srcdbg_undesired_paths(qstring *out) { return getinf_str(out, INF_SRCDBG_UNDESIRED); } + +/// Set user-closed source files +inline void idaapi set_srcdbg_undesired_paths(const char *paths) { setinf_buf(INF_SRCDBG_UNDESIRED, paths); } + +/// Get initial version of the database (numeric format like 700) +inline ushort idaapi get_initial_idb_version() { return getinf(INF_INITIAL_VERSION); } + +/// Get database creation timestamp +inline time_t idaapi get_idb_ctime() { return getinf(INF_CTIME); } + +/// Get seconds database stayed open +inline size_t idaapi get_elapsed_secs() { return getinf(INF_ELAPSED); } + +/// Get number of times the database is opened +inline size_t idaapi get_idb_nopens() { return getinf(INF_NOPENS); } + +#endif + +//--------------------------------------------------------------------------- +/// \name String encodings +/// Encoding names can be a codepage names (CP1251, windows-1251), +/// charset name (Shift-JIS, UTF-8), or just codepage number (866, 932). +/// user-accessible encodings are counted from 1 +/// (index 0 is reserved) +///@{ + +/// Get total number of encodings (counted from 0) + +idaman int ida_export get_encoding_qty(); + + +/// Get encoding name for specific index (1-based). +/// \param idx the encoding index (1-based) +/// \retval nullptr if IDX is out of bounds +/// \retval empty string if the encoding was deleted + +idaman const char *ida_export get_encoding_name(int idx); + + +/// Add a new encoding (e.g. "UTF-8"). +/// If it's already in the list, return its index. +/// \param encname the encoding name +/// \return its index (1-based); -1 means error + +idaman int ida_export add_encoding(const char *encname); + + +/// Delete an encoding +/// The encoding is not actually removed because its index may be used in +/// strtype. So the deletion just clears the encoding name. +/// The default encoding cannot be deleted. +/// \param idx the encoding index (1-based) + +idaman bool ida_export del_encoding(int idx); + + +/// Change name for an encoding +/// The number of bytes per unit (BPU) of the new encoding must match this +/// number of the existing default encoding. +/// Specifying the empty name simply deletes this encoding. +/// \param idx the encoding index (1-based) +/// \param encname the new encoding name + +idaman bool ida_export rename_encoding(int idx, const char *encname); + + +#define BPU_1B 1 +#define BPU_2B 2 +#define BPU_4B 4 + +/// Get the amount of bytes per unit (e.g., 2 for UTF-16, 4 for UTF-32) +/// for the encoding with the given index. +/// \param idx the encoding index (1-based) +/// \return the number of bytes per units (1/2/4); -1 means error + +idaman int ida_export get_encoding_bpu(int idx); + + +/// Get the amount of bytes per unit for the given encoding +/// \param encname the encoding name +/// \return the number of bytes per units (1/2/4); -1 means error +idaman int ida_export get_encoding_bpu_by_name(const char *encname); + + +//------------------------------------------------------------------------- +inline int get_strtype_bpu(int32 strtype) +{ + int w = get_str_type_code(strtype) & STRWIDTH_MASK; + return w == STRWIDTH_2B ? BPU_2B + : w == STRWIDTH_4B ? BPU_4B + : BPU_1B; +} + +/// Get default encoding index for a specific string type. +/// \param bpu the amount of bytes per unit (e.g., 1 for ASCII, CP1252, UTF-8..., 2 for UTF-16, 4 for UTF-32) +/// \retval 0 bad BPU argument + +idaman int ida_export get_default_encoding_idx(int bpu); + + +/// Set default encoding for a string type +/// \param bpu the amount of bytes per unit +/// \param idx the encoding index. It cannot be 0 + +idaman bool ida_export set_default_encoding_idx(int bpu, int idx); + + +/// Get encoding name for this strtype +/// \retval nullptr if STRTYPE has an incorrect encoding index +/// \retval empty string if the encoding was deleted + +inline const char *idaapi encoding_from_strtype(int32 strtype) +{ + uchar enc = get_str_encoding_idx(strtype); + if ( enc == STRENC_DEFAULT ) + enc = get_default_encoding_idx(get_strtype_bpu(strtype)); + return get_encoding_name(enc); +} + + +/// Get the index of the encoding used when producing files +/// \retval 0 the IDB's default 1 byte-per-unit encoding is used + +idaman int ida_export get_outfile_encoding_idx(); + + +/// set encoding to be used when producing files +/// \param idx the encoding index +/// IDX can be 0 to use the IDB's default 1-byte-per-unit encoding + +idaman bool ida_export set_outfile_encoding_idx(int idx); + + +///@} + +//------------------------------------------------------------------------// +/// \name Functions to work with imports +///@{ + +/// Get number of import modules + +idaman uint ida_export get_import_module_qty(); + + +/// Get import module name. +/// \retval true ok +/// \retval false bad index + +idaman bool ida_export get_import_module_name(qstring *buf, int mod_index); + + +/// Callback for enumerating imports. +/// \param ea import address +/// \param name import name (nullptr if imported by ordinal) +/// \param ord import ordinal (0 for imports by name) +/// \param param user parameter passed to enum_import_names() +/// \retval 1 ok +/// \retval 0 stop enumeration + +typedef int idaapi import_enum_cb_t(ea_t ea, const char *name, uval_t ord, void *param); + + +/// Enumerate imports from specific module. +/// \retval 1 finished ok +/// \retval -1 error +/// \retval other callback return value (<=0) + +idaman int ida_export enum_import_names(int mod_index, import_enum_cb_t *callback, void *param=nullptr); + + +/// Delete all imported modules information + +idaman void ida_export delete_imports(void); +///@} + + +/// Check consistency of name records, return number of bad ones + +idaman int ida_export validate_idb_names(bool do_repair); + + +#define GOTEA_NODE_NAME "$ got" ///< node containing address of .got section +#define GOTEA_NODE_IDX 0 + +//-------------------------------------------------------------------------- +// Set address of .got section +inline void set_gotea(ea_t gotea) +{ + netnode n; + n.create(GOTEA_NODE_NAME); + n.altset(GOTEA_NODE_IDX, ea2node(gotea)+1); +} + +//-------------------------------------------------------------------------- +// Get address of .got section +inline ea_t get_gotea(void) +{ + netnode n(GOTEA_NODE_NAME); + return exist(n) ? node2ea(n.altval(GOTEA_NODE_IDX) - 1) : BADADDR; +} + + +#ifndef BYTES_SOURCE // undefined bit masks so no one can use them directly +#undef AFL_LINNUM +#undef AFL_USERSP +#undef AFL_PUBNAM +#undef AFL_WEAKNAM +#undef AFL_HIDDEN +#undef AFL_MANUAL +#undef AFL_NOBRD +#undef AFL_ZSTROFF +#undef AFL_BNOT0 +#undef AFL_BNOT1 +#undef AFL_LIB +#undef AFL_TI +#undef AFL_TI0 +#undef AFL_TI1 +#undef AFL_LNAME +#undef AFL_TILCMT +#undef AFL_LZERO0 +#undef AFL_LZERO1 +#undef AFL_COLORED +#undef AFL_TERSESTR +#undef AFL_SIGN0 +#undef AFL_SIGN1 +#undef AFL_NORET +#undef AFL_FIXEDSPD +#undef NALT_ENUM +#undef NALT_WIDE +#undef NALT_SWITCH +//#undef NALT_STRUCT +#undef NALT_XREFPOS +#undef NALT_AFLAGS +#undef NALT_LINNUM +#undef NALT_ABSBASE +//#undef NALT_ENUM0 +//#undef NALT_ENUM1 +#undef NALT_PURGE +#undef NALT_STRTYPE +#undef NALT_ALIGN +#undef NALT_COLOR +#undef NSUP_CMT +#undef NSUP_REPCMT +#undef NSUP_FOP1 +#undef NSUP_FOP2 +#undef NSUP_JINFO +#undef NSUP_ARRAY +#undef NSUP_OMFGRP +#undef NSUP_FOP3 +#undef NSUP_SWITCH +#undef NSUP_REF0 +#undef NSUP_REF1 +#undef NSUP_REF2 +#undef NSUP_OREF0 +#undef NSUP_OREF1 +#undef NSUP_OREF2 +#undef NSUP_STROFF0 +#undef NSUP_STROFF1 +#undef NSUP_SEGTRANS +#undef NSUP_FOP4 +#undef NSUP_FOP5 +#undef NSUP_FOP6 +#undef NSUP_FOP7 +#undef NSUP_FOP8 +#undef NSUP_REF3 +#undef NSUP_REF4 +#undef NSUP_REF5 +#undef NSUP_REF6 +#undef NSUP_REF7 +#undef NSUP_OREF3 +#undef NSUP_OREF4 +#undef NSUP_OREF5 +#undef NSUP_OREF6 +#undef NSUP_OREF7 +#undef NSUP_MANUAL +#undef NSUP_FTAILS +#undef NSUP_GROUP +#endif + +#endif // NALT_HPP + +``` + +`IdaSDK/name.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _NAME_HPP +#define _NAME_HPP + +#include <ida.hpp> + +/*! \file name.hpp + + \brief Functions that deal with names. + + A non-tail address of the program may have a name. + Tail addresses (i.e. the addresses in the middle of an instruction or + data item) cannot have names. +*/ + +class func_t; // funcs.hpp +typedef uchar color_t; // lines.hpp + +/// Maximum length of a name in IDA (with the trailing zero) +#define MAXNAMELEN 512 + + +/// Name prefix used by IDA for the imported functions +#define FUNC_IMPORT_PREFIX "__imp_" + + +/// Set or delete name of an item at the specified address. +/// An item can be anything: instruction, function, data byte, word, string, +/// structure, etc... +/// Include name into the list of names. +/// \param ea linear address. +/// do nothing if ea is not valid (return 0). +/// tail bytes can't have names. +/// \param name new name. +/// - nullptr: do nothing (return 0). +/// - "" : delete name. +/// - otherwise this is a new name. +/// \param flags \ref SN_. +/// If a bit is not specified, then the corresponding action is not performed +/// and the name will retain the same bits as before calling this function. +/// For new names, default is: non-public, non-weak, non-auto. +/// \retval 1 ok, name is changed +/// \retval 0 failure, a warning is displayed + +idaman bool ida_export set_name(ea_t ea, const char *name, int flags=0); + +/// \defgroup SN_ Set name flags +/// Passed as 'flag' parameter to set_name(ea_t, const char *, int) +///@{ +#define SN_CHECK 0x00 ///< Fail if the name contains invalid characters. +#define SN_NOCHECK 0x01 ///< Replace invalid characters silently. + ///< If this bit is set, all invalid chars + ///< (not in NameChars or MangleChars) will be replaced + ///< by '_' + ///< List of valid characters is defined in ida.cfg +#define SN_PUBLIC 0x02 ///< if set, make name public +#define SN_NON_PUBLIC 0x04 ///< if set, make name non-public +#define SN_WEAK 0x08 ///< if set, make name weak +#define SN_NON_WEAK 0x10 ///< if set, make name non-weak +#define SN_AUTO 0x20 ///< if set, make name autogenerated +#define SN_NON_AUTO 0x40 ///< if set, make name non-autogenerated +#define SN_NOLIST 0x80 ///< if set, exclude name from the list. + ///< if not set, then include the name into + ///< the list (however, if other bits are set, + ///< the name might be immediately excluded + ///< from the list). +#define SN_NOWARN 0x100 ///< don't display a warning if failed +#define SN_LOCAL 0x200 ///< create local name. a function should exist. + ///< local names can't be public or weak. + ///< also they are not included into the list of names + ///< they can't have dummy prefixes. +#define SN_IDBENC 0x400 ///< the name is given in the IDB encoding; + ///< non-ASCII bytes will be decoded accordingly. + ///< Specifying SN_IDBENC also implies SN_NODUMMY +#define SN_FORCE 0x800 ///< if the specified name is already present + ///< in the database, try variations with a + ///< numerical suffix like "_123" +#define SN_NODUMMY 0x1000 ///< automatically prepend the name with '_' if it + ///< begins with a dummy suffix such as 'sub_'. + ///< See also SN_IDBENC +#define SN_DELTAIL 0x2000 ///< if name cannot be set because of a tail byte, + ///< delete the hindering item +#define SN_MULTI 0x4000 ///< if the specified address already has a name, + ///< then add the new name as a regular comment + ///< "Alternative name is ...". Except when the new name + ///< is public and the old one is not or when the old name + ///< is weak and the new one is not. In these cases we act + ///< as if bit SN_MULTI_FORCE is specified. + ///< If the new name only slightly differs from the old + ///< one, for example, only by the initial underscore or + ///< the artificial suffix '_##', then we ignore it. +#define SN_MULTI_FORCE 0x8000 ///< if the specified address already has a name, + ///< put this old name into a regular comment and + ///< set the specified name. + ///< This bit may be used only with SN_MULTI. +///@} + +inline bool force_name(ea_t ea, const char *name, int flags=0) +{ + return set_name(ea, name, flags|SN_FORCE|SN_NODUMMY); +} + +/// \name Delete a name of a program item +/// \param ea linear address +/// \retval 1 ok, name is deleted +/// \retval 0 failure, invalid address +///@{ +inline bool del_global_name(ea_t ea) { return set_name(ea,"", SN_NOWARN); } +inline bool del_local_name(ea_t ea) { return set_name(ea,"", SN_LOCAL|SN_NOWARN); } +///@} + +/// Give an autogenerated (dummy) name. +/// Autogenerated names have special prefixes (loc_...). +/// \param from linear address of the operand which references to the address +/// \param ea linear address +/// \retval 1 ok, dummy name is generated or the byte already had a name +/// \retval 0 failure, invalid address or tail byte + +idaman bool ida_export set_dummy_name(ea_t from, ea_t ea); // give dummy name + + +/// \name Set/Clear bit in flags for 'autogenerated but meaningful name' +/// This bit affects value of has_user_name(), has_auto_name() functions. +/// \param ea linear address +/// \retval 1 ok +/// \retval 0 no meaningful name is present at the specified address +///@{ +idaman bool ida_export make_name_auto(ea_t ea); +idaman bool ida_export make_name_user(ea_t ea); +///@} + + +enum ucdr_kind_t +{ + UCDR_STRLIT = 0x01, ///< string literals + UCDR_NAME = 0x02, ///< regular (unmangled) names + UCDR_MANGLED = 0x04, ///< mangled names + UCDR_TYPE = 0x08, ///< type names +}; + +enum nametype_t +{ + VNT_IDENT = UCDR_NAME|UCDR_MANGLED, ///< identifier (e.g., function name) + VNT_TYPE = UCDR_TYPE, ///< type name (can contain '<', '>', ...) + VNT_UDTMEM = UCDR_NAME, ///< UDT (structure, union, enum) member + VNT_STRLIT = UCDR_STRLIT, ///< string literal + VNT_VISIBLE = VNT_UDTMEM, ///< visible cp (obsolete; will be deleted) +}; + +/// Validate a name. +/// If SN_NOCHECK is specified, this function replaces all invalid characters +/// in the name with SUBSTCHAR. However, it will return false if name is valid +/// but not allowed to be an identifier (is a register name). +/// +/// \param[in,out] name ptr to name. the name will be modified +/// \param type the type of name we want to validate +/// \param flags see SN_* +/// +/// \return success + +idaman bool ida_export validate_name( + qstring *name, + nametype_t type, + int flags = SN_NOCHECK); + + +/// Is the given codepoint acceptable in the given context? + +idaman bool ida_export is_valid_cp(wchar32_t cp, nametype_t kind, void *data=nullptr); + + +/// Mark the given codepoint (or range) as acceptable or unacceptable in the given context +/// If 'endcp' is not BADCP, it is considered to be the end of the range: +/// [cp, endcp), and is not included in the range + +idaman void ida_export set_cp_validity(ucdr_kind_t kind, wchar32_t cp, wchar32_t endcp=BADCP, bool valid=true); + + +/// Is the given codepoint (or range) acceptable in the given context? +/// If 'endcp' is not BADCP, it is considered to be the end of the range: +/// [cp, endcp), and is not included in the range + +idaman bool ida_export get_cp_validity(ucdr_kind_t kind, wchar32_t cp, wchar32_t endcp=BADCP); + + +/// Can a character appear in a name? (present in ::NameChars or ::MangleChars) + +inline bool is_ident_cp(wchar32_t cp) { return is_valid_cp(cp, VNT_IDENT); } + + +/// Can a character appear in a string literal (present in ::StrlitChars) +/// If 'specific_ranges' are specified, those will be used instead of +/// the ones corresponding to the current culture (only if ::StrlitChars +/// is configured to use the current culture) + +inline bool is_strlit_cp(wchar32_t cp, const rangeset_crefvec_t *specific_ranges=nullptr) +{ return is_valid_cp(cp, VNT_STRLIT, (void *) specific_ranges); } + + +/// Can a character be displayed in a name? (present in ::NameChars) + +inline bool is_visible_cp(wchar32_t cp) +{ return is_valid_cp(cp, VNT_VISIBLE); } + + +/// Is a valid name? (including ::MangleChars) + +idaman bool ida_export is_ident(const char *name); + + +/// Is valid user-specified name? (valid name & !dummy prefix). +/// \param name name to test. may be nullptr. +/// \retval 1 yes +/// \retval 0 no + +idaman bool ida_export is_uname(const char *name); + + +/// Is valid type name? +/// \param name name to test. may be nullptr. +/// \retval 1 yes +/// \retval 0 no + +idaman bool ida_export is_valid_typename(const char *name); + + +/// Is dummy name? +/// \param name name to test. may be nullptr. +/// \return #BADADDR if not, otherwise the address denoted by the name + +idaman ea_t ida_export dummy_name_ea(const char *name); + + +/// Extract a name or address from the specified string. +/// \param[out] out output buffer for the identifier +/// \param line input string +/// \param x x coordinate of cursor +/// \return -1 if cannot extract. otherwise length of the name + +idaman ssize_t ida_export extract_name(qstring *out, const char *line, int x); + + +/// Remove name from the list of names +/// \param ea address of the name + +idaman void ida_export hide_name(ea_t ea); + + +/// Insert name to the list of names + +idaman void ida_export show_name(ea_t ea); + + +/// Get the address of a name. +/// This function resolves a name into an address. It can handle regular global +/// and local names, as well as debugger names. +/// \param from linear address where the name is used. If specified, +/// the local labels of the function at the specified address will +/// will be checked. #BADADDR means that local names won't be consulted. +/// \param name any name in the program or nullptr +/// \return address of the name or #BADADDR + +idaman ea_t ida_export get_name_ea(ea_t from, const char *name); + + +/// Get address of the name used in the expression for the address +/// \param from address of the operand which references to the address +/// \param to the referenced address +/// \return address of the name used to represent the operand + +idaman ea_t ida_export get_name_base_ea(ea_t from, ea_t to); + + +/// Get value of the name. +/// This function knows about: regular names, enums, special segments, etc. +/// \param[out] value pointer to variable with answer +/// \param from linear address where the name is used +/// if not applicable, then should be BADADDR +/// \param name any name in the program or nullptr +/// \return \ref NT_ + +idaman int ida_export get_name_value(uval_t *value, ea_t from, const char *name); + +/// \defgroup NT_ Name value result codes +/// Return values for get_name_value() +///@{ +#define NT_NONE 0 ///< name doesn't exist or has no value +#define NT_BYTE 1 ///< name is byte name (regular name) +#define NT_LOCAL 2 ///< name is local label +#define NT_STKVAR 3 ///< name is stack variable name +#define NT_ENUM 4 ///< name is symbolic constant +#define NT_ABS 5 ///< name is absolute symbol (#SEG_ABSSYM) +#define NT_SEG 6 ///< name is segment or segment register name +#define NT_STROFF 7 ///< name is structure member +#define NT_BMASK 8 ///< name is a bit group mask name +#define NT_REGVAR 9 ///< name is a renamed register (*value is idx into pfn->regvars) +///@} + + +/// Additional information for get_ea_name() function +struct getname_info_t +{ + size_t cb; ///< size of this struct + int32 inhibitor; ///< codes to inhibit parts of demangled name (see \ref MNG_). + ///< Usually this is one of \inf{short_demnames} or \inf{long_demnames}. + int32 demform; ///< demangle only if \inf{demnames} is equal to 'demform'. + int32 demcode; ///< out: return value of demangler + getname_info_t(void) : cb(sizeof(*this)), inhibitor(0), demform(0), demcode(0) {} +}; + + +/// Get name at the specified address. +/// \param[out] out buffer to hold the name +/// \param ea linear address +/// \param gtn_flags how exactly the name should be retrieved. +/// combination of \ref GN_ bits +/// \param gtni additional information for name demangling +/// Please use the convenience functions declared below instead of calling +/// get_ea_name directly. +/// \return success + +idaman ssize_t ida_export get_ea_name( + qstring *out, + ea_t ea, + int gtn_flags=0, + getname_info_t *gtni=nullptr); + +/// \defgroup GN_ bits for get_ea_name() function. There is a convenience +/// function calc_gtn_flags() to calculate the GN_LOCAL flag +///@{ +#define GN_VISIBLE 0x0001 ///< replace forbidden characters by SUBSTCHAR +#define GN_COLORED 0x0002 ///< return colored name +#define GN_DEMANGLED 0x0004 ///< return demangled name +#define GN_STRICT 0x0008 ///< fail if cannot demangle +#define GN_SHORT 0x0010 ///< use short form of demangled name +#define GN_LONG 0x0020 ///< use long form of demangled name +#define GN_LOCAL 0x0040 ///< try to get local name first; if failed, get global +#define GN_ISRET 0x0080 ///< for dummy names: use retloc +#define GN_NOT_ISRET 0x0100 ///< for dummy names: do not use retloc +#define GN_NOT_DUMMY 0x0200 ///< do not return a dummy name + +///@} + +// Convenience functions for get_ea_name returning ssize_t + +inline ssize_t get_name(qstring *out, ea_t ea, int gtn_flags=0) +{ + return get_ea_name(out, ea, gtn_flags); +} + +inline ssize_t idaapi get_visible_name(qstring *out, ea_t ea, int gtn_flags=0) +{ + return get_ea_name(out, ea, GN_VISIBLE|gtn_flags); +} + +inline ssize_t idaapi get_colored_name(qstring *out, ea_t ea, int gtn_flags=0) +{ + return get_ea_name(out, ea, GN_VISIBLE|GN_COLORED|gtn_flags); +} + +inline ssize_t idaapi get_short_name(qstring *out, ea_t ea, int gtn_flags=0) +{ + return get_ea_name(out, ea, GN_VISIBLE|GN_DEMANGLED|GN_SHORT|gtn_flags); +} + +inline ssize_t idaapi get_long_name(qstring *out, ea_t ea, int gtn_flags=0) +{ + return get_ea_name(out, ea, GN_VISIBLE|GN_DEMANGLED|GN_LONG|gtn_flags); +} + +inline ssize_t idaapi get_colored_short_name(qstring *out, ea_t ea, int gtn_flags=0) +{ + return get_ea_name(out, ea, GN_VISIBLE|GN_COLORED|GN_DEMANGLED|GN_SHORT|gtn_flags); +} + +inline ssize_t idaapi get_colored_long_name(qstring *out, ea_t ea, int gtn_flags=0) +{ + return get_ea_name(out, ea, GN_VISIBLE|GN_COLORED|GN_DEMANGLED|GN_LONG|gtn_flags); +} + +inline ssize_t idaapi get_demangled_name( + qstring *out, + ea_t ea, + int32 inhibitor, + int demform, + int gtn_flags=0) +{ + getname_info_t gtni; + gtni.inhibitor = inhibitor; + gtni.demform = demform; + gtn_flags |= GN_VISIBLE | GN_DEMANGLED; + return get_ea_name(out, ea, gtn_flags, >ni); +} + +inline ssize_t idaapi get_colored_demangled_name( + qstring *out, + ea_t ea, + int32 inhibitor, + int demform, + int gtn_flags=0) +{ + return get_demangled_name(out, ea, inhibitor, demform, gtn_flags|GN_COLORED); +} + +// Convenience functions for get_ea_name returning qstring + +inline qstring get_name(ea_t ea, int gtn_flags=0) +{ + qstring out; + get_ea_name(&out, ea, gtn_flags); + return out; +} + +inline qstring get_visible_name(ea_t ea, int gtn_flags=0) +{ + qstring out; + get_ea_name(&out, ea, GN_VISIBLE|gtn_flags); + return out; +} + +inline qstring idaapi get_colored_name(ea_t ea, int gtn_flags=0) +{ + qstring out; + get_ea_name(&out, ea, GN_VISIBLE|GN_COLORED|gtn_flags); + return out; +} + +inline qstring idaapi get_short_name(ea_t ea, int gtn_flags=0) +{ + qstring out; + get_ea_name(&out, ea, GN_VISIBLE|GN_DEMANGLED|GN_SHORT|gtn_flags); + return out; +} + +inline qstring idaapi get_long_name(ea_t ea, int gtn_flags=0) +{ + qstring out; + get_ea_name(&out, ea, GN_VISIBLE|GN_DEMANGLED|GN_LONG|gtn_flags); + return out; +} + +inline qstring idaapi get_colored_short_name(ea_t ea, int gtn_flags=0) +{ + qstring out; + get_ea_name(&out, ea, GN_VISIBLE|GN_COLORED|GN_DEMANGLED|GN_SHORT|gtn_flags); + return out; +} + +inline qstring idaapi get_colored_long_name(ea_t ea, int gtn_flags=0) +{ + qstring out; + get_ea_name(&out, ea, GN_VISIBLE|GN_COLORED|GN_DEMANGLED|GN_LONG|gtn_flags); + return out; +} + +inline qstring idaapi get_demangled_name( + ea_t ea, + int32 inhibitor, + int demform, + int gtn_flags=0) +{ + qstring out; + getname_info_t gtni; + gtni.inhibitor = inhibitor; + gtni.demform = demform; + gtn_flags |= GN_VISIBLE | GN_DEMANGLED; + get_ea_name(&out, ea, gtn_flags, >ni); + return out; +} + +inline qstring idaapi get_colored_demangled_name( + ea_t ea, + int32 inhibitor, + int demform, + int gtn_flags=0) +{ + qstring out; + get_demangled_name(&out, ea, inhibitor, demform, gtn_flags|GN_COLORED); + return out; +} + +/// Calculate flags for get_ea_name() function +#ifdef FUNCS_HPP +inline int calc_gtn_flags(ea_t from, ea_t ea) +{ + return func_contains(get_func(from), ea) ? GN_LOCAL : 0; +} +#endif + +/// Get name color. +/// \param from linear address where the name is used. +/// if not applicable, then should be #BADADDR. +/// The kernel returns a local name color if the reference is +/// within a function, i.e. 'from' and 'ea' belong to the same function. +/// \param ea linear address + +idaman color_t ida_export get_name_color(ea_t from, ea_t ea); + + +/// \defgroup GETN_ Name expression flags +/// Passed as 'flags' parameter to get_name_expr() +///@{ +#define GETN_APPZERO 0x0001 ///< meaningful only if the name refers to a structure. + ///< append a struct field name if the field offset is zero? +#define GETN_NOFIXUP 0x0002 ///< ignore the fixup information when producing the name +#define GETN_NODUMMY 0x0004 ///< do not create a new dummy name but pretend it exists +///@} + +/// Convert address to name expression (name with a displacement). +/// This function takes into account fixup information and returns +/// a colored name expression (in the form <name> +/- <offset>). +/// It also knows about structure members and arrays. +/// If the specified address doesn't have a name, a dummy name is generated. +/// \param[out] out output buffer for the name +/// \param from linear address of instruction operand or data referring to +/// the name. This address will be used to get fixup information, +/// so it should point to exact position of the operand in the +/// instruction. +/// \param n number of referencing operand. for data items specify 0 +/// \param ea address to convert to name expression +/// \param off the value of name expression. this parameter is used only to +/// check that the name expression will have the wanted value. +/// 'off' may be equal to BADADDR but this is discouraged +/// because it prohibits checks. +/// \param flags \ref GETN_ +/// \return < 0 if address is not valid, no segment or other failure. +/// otherwise the length of the name expression in characters. + +idaman ssize_t ida_export get_name_expr( + qstring *out, + ea_t from, + int n, + ea_t ea, + uval_t off, + int flags=GETN_APPZERO); + +/// Get a nice colored name at the specified address. +/// Ex: +/// - segment:sub+offset +/// - segment:sub:local_label +/// - segment:label +/// - segment:address +/// - segment:address+offset +/// +/// \param[out] buf buffer to hold the name +/// \param ea linear address +/// \param flags \ref GNCN_ +/// \return the length of the generated name in bytes. + +idaman ssize_t ida_export get_nice_colored_name( + qstring *buf, + ea_t ea, + int flags=0); + +/// \defgroup GNCN_ Nice colored name flags +/// Passed as 'flags' parameter to get_nice_colored_name() +///@{ +#define GNCN_NOSEG 0x0001 ///< ignore the segment prefix when producing the name +#define GNCN_NOCOLOR 0x0002 ///< generate an uncolored name +#define GNCN_NOLABEL 0x0004 ///< don't generate labels +#define GNCN_NOFUNC 0x0008 ///< don't generate funcname+... expressions +#define GNCN_SEG_FUNC 0x0010 ///< generate both segment and function names (default is to omit segment name if a function name is present) +#define GNCN_SEGNUM 0x0020 ///< segment part is displayed as a hex number +#define GNCN_REQFUNC 0x0040 ///< return 0 if the address does not belong to a function +#define GNCN_REQNAME 0x0080 ///< return 0 if the address can only be represented as a hex number +#define GNCN_NODBGNM 0x0100 ///< don't use debug names +#define GNCN_PREFDBG 0x0200 ///< if using debug names, prefer debug names over function names +///@} + + +/// Append names of struct fields to a name if the name is a struct name. +/// \param out pointer to the output buffer +/// \param disp displacement from the name +/// \param n operand number in which the name appears +/// \param path path in the struct. path is an array of id's. +/// maximal length of array is #MAXSTRUCPATH. +/// the first element of the array is the structure id. +/// consecutive elements are id's of used union members (if any). +/// \param plen size of path array +/// \param flags the input flags. they will be returned if the struct +/// cannot be found. +/// \param delta delta to add to displacement +/// \param appzero should append a struct field name if the displacement is zero? +/// \return flags of the innermost struct member or the input flags + +idaman flags64_t ida_export append_struct_fields( + qstring *out, + adiff_t *disp, + int n, + const tid_t *path, + int plen, + flags64_t flags, + adiff_t delta, + bool appzero); + + +/// Get offset within a structure if the operand refers to structure. +/// Ex: +/// \v{mov ax, somedata.field5-2 (before it was max ax, 3)} +/// for this instruction, op #1 the function will return +/// - disp: the value of 'field5', i.e. 5 +/// - delta: -2 +/// - path: the existing path if any +/// +/// \param disp pointer to displacement (answer will be here) +/// \param delta pointer to displacement delta (answer will be here) +/// \param path existing strpath (if any) +/// \param ea linear address of instruction/data +/// \param n 0..#UA_MAXOP-1 operand number +/// \return if success, then length of path + 1. +/// if failed, then 0. + +idaman int ida_export get_struct_operand( + adiff_t *disp, + adiff_t *delta, + tid_t *path, + ea_t ea, + int n); + + +/// \name Work with publicness of a name +///@{ +idaman bool ida_export is_public_name(ea_t ea); +idaman void ida_export make_name_public(ea_t ea); +idaman void ida_export make_name_non_public(ea_t ea); +///@} + + +/// \name Work with weak names. +///@{ +idaman bool ida_export is_weak_name(ea_t ea); +idaman void ida_export make_name_weak(ea_t ea); +idaman void ida_export make_name_non_weak(ea_t ea); +///@} + +/// \name Work with the list of names +///@{ + +/// Get number of names in the list + +idaman size_t ida_export get_nlist_size(void); + +/// Get index of the name in the list +/// \warning returns the closest match. +/// may return idx >= size. + +idaman size_t ida_export get_nlist_idx(ea_t ea); + +/// Is the name included into the name list? + +idaman bool ida_export is_in_nlist(ea_t ea); + +/// Get address from the list at 'idx' + +idaman ea_t ida_export get_nlist_ea(size_t idx); + +/// Get name using idx + +idaman const char *ida_export get_nlist_name(size_t idx); + +/// Rebuild the name list + +idaman void ida_export rebuild_nlist(void); +///@} + +/// Renumber dummy names + +idaman void ida_export reorder_dummy_names(void); + +/// Specify strategy for retrieving debug names +enum debug_name_how_t +{ + DEBNAME_EXACT, ///< find a name at exactly the specified address + DEBNAME_LOWER, ///< find a name with the address >= the specified address + DEBNAME_UPPER, ///< find a name with the address > the specified address + DEBNAME_NICE, ///< find a name with the address <= the specified address +}; + +/// ea, name pair +struct ea_name_t +{ + ea_t ea; + qstring name; + ea_name_t(void) : ea(BADADDR) {} + ea_name_t(ea_t _ea, const qstring &_name) : ea(_ea), name(_name) {} +}; +DECLARE_TYPE_AS_MOVABLE(ea_name_t); +typedef qvector<ea_name_t> ea_name_vec_t; ///< vector of ea,name pairs + +/// \name Debug names +/// Debug names exist during the debugging session. +/// The kernel does not verify them for anything and happily accepts +/// any string as a name. +///@{ +idaman int ida_export set_debug_names(const ea_t *addrs, const char *const *names, int qty); +idaman bool ida_export set_debug_name(ea_t ea, const char *name); +idaman ssize_t ida_export get_debug_name( + qstring *out, + ea_t *ea_ptr, + debug_name_how_t how); +idaman void ida_export del_debug_names(ea_t ea1, ea_t ea2); +idaman ea_t ida_export get_debug_name_ea(const char *name); +idaman void ida_export get_debug_names(ea_name_vec_t *names, ea_t ea1, ea_t ea2); +///@} + + +enum demreq_type_t +{ + DQT_NPURGED_8 = -8, ///< only calculate number of purged bytes (sizeof(arg)==8) + DQT_NPURGED_4 = -4, ///< only calculate number of purged bytes (sizeof(arg)==4) + DQT_NPURGED_2 = -2, ///< only calculate number of purged bytes (sizeof(arg)==2) + DQT_COMPILER = 0, ///< only detect compiler that generated the name + DQT_NAME_TYPE = 1, ///< only detect the name type (data/code) + DQT_FULL = 2, ///< really demangle +}; + +/// Demangle a name. +/// \param out output buffer +/// \param name name to demangle +/// \param disable_mask bits to inhibit parts of demangled name (see \ref MNG_). +/// by the M_COMPILER bits a specific compiler can be +/// selected (see \ref MT_). +/// \param demreq the request type \ref demreq_type_t +/// \return ME_... or MT__ bitmasks from demangle.hpp + +idaman int32 ida_export demangle_name( + qstring *out, + const char *name, + uint32 disable_mask, + demreq_type_t demreq=DQT_FULL); + +/// Demangle a name. +inline qstring idaapi demangle_name( + const char *name, + uint32 disable_mask, + demreq_type_t demreq=DQT_FULL) +{ + qstring out; + demangle_name(&out, name, disable_mask, demreq); + return out; +} + + +inline int32 detect_compiler_using_demangler(const char *name) +{ + return demangle_name(nullptr, name, 0, DQT_COMPILER); +} + +/// What name types to ignore +typedef int ignore_name_def_t; +const ignore_name_def_t + ignore_none = 0, + ignore_regvar = 1, + ignore_llabel = 2, + ignore_stkvar = 3, + ignore_glabel = 4; + +/// Is the name defined locally in the specified function? +/// \param pfn pointer to function +/// \param name name to check +/// \param ignore_name_def which names to ignore when checking +/// \param ea1 the starting address of the range inside the function (optional) +/// \param ea2 the ending address of the range inside the function (optional) +/// \return true if the name has been defined +idaman bool ida_export is_name_defined_locally( + func_t *pfn, + const char *name, + ignore_name_def_t ignore_name_def, + ea_t ea1=BADADDR, + ea_t ea2=BADADDR); + +// Clean a name. +// This function removes punctuation marks (underscores and dots) from both +// ends of the name, and other typical prefixes/suffixes. Name is assumed to +// have the following format: [j_][@][.][_*][imp_]name[@digits][_NN] +// \param out output buffer +// \param ea address of the name (used to remove the module name) +// if != BADADDR, the optional prefix (module name) will be +// removed +// \param name name to clean +// \param flags combination of CN_... bits +// \return true if returned a non-empty name +idaman bool ida_export cleanup_name( + qstring *out, + ea_t ea, + const char *name, + uint32 flags=0); + +#define CN_KEEP_TRAILING_DIGITS 0x01 ///< do not remove "_\d+" at the end of name +#define CN_KEEP_UNDERSCORES 0x02 ///< do not remove leading underscores. + ///< but it is ok to remove __imp_. + + +#endif // _NAME_HPP + +``` + +`IdaSDK/netnode.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _NETNODE_HPP +#define _NETNODE_HPP + +#include <functional> + +/*! \file netnode.hpp + + \brief Functions that provide the lowest level public interface to the database. + Namely, we use Btree. To learn more about BTree: + + https://en.wikipedia.org/wiki/B-tree + + We do not use Btree directly. Instead, we have another layer built on the top + of Btree. Here is a brief explanation of this layer. + + An object called "netnode" is modeled on the top of Btree. + Each netnode has a unique id: a 32-bit value (64-bit for ida64). + Initially there is a trivial mapping of the linear addresses used in + the program to netnodes (later this mapping may be modified using ea2node + and node2ea functions; this is used for fast database rebasings). + If we have additional information about an address (for example, a comment is + attached to it), this information is stored in the corresponding netnode. + See nalt.hpp to see how the kernel uses netnodes. + Also, some netnodes have no corresponding linear address (however, they still + have an id). They are used to store information not related to a particular + address. + + Each netnode _may_ have the following attributes: + + - a name: an arbitrary non-empty string, up to 255KB-1 bytes + + - a value: arbitrary sized object, max size is MAXSPECSIZE + + - altvals: a sparse array of 32-bit values. + indexes in this array may be 8-bit or 32-bit values + + - supvals: an array of arbitrary sized objects. (size of each + object is limited by MAXSPECSIZE) + indexes in this array may be 8-bit or 32-bit values + + - charvals: a sparse array of 8-bit values. + indexes in this array may be 8-bit or 32-bit values + + - hashvals: a hash (an associative array). + indexes in this array are strings + values are arbitrary sized (max size is MAXSPECSIZE) + + Initially a new netnode contains no information at all so no disk space + is used for it. As you add new information, the netnode grows. + + All arrays that are attached to the netnode behave in the same manner. + Initially: + - all members of altvals/charvals array are zeroes + - all members of supvals/hashvals array are undefined + + If you need to store objects bigger that MAXSPECSIZE, please note that + there are high-level functions to store arbitrary sized objects in supvals. + See setblob/getblob and other blob-related functions. + + You may use netnodes to store additional information about the program. + Limitations on the use of netnodes are the following: + + - use netnodes only if you could not find a kernel service to + store your type of information + + - do not create netnodes with valid identifier names. + Use the "$ " prefix (or any other prefix with characters not allowed + in the identifiers for the names of your netnodes. + Although you will probably not destroy anything by + accident, using already defined names for the names of your + netnodes is still discouraged. + + - you may create as many netnodes as you want (creation of an unnamed + netnode does not increase the size of the database). + however, since each netnode has a number, creating too many netnodes + could lead to the exhaustion of the netnode numbers (the numbering + starts at 0xFF000000) + + - remember that netnodes are automatically saved to the disk + by the kernel. + + Advanced info: + + In fact a netnode may contain up to 256 arrays of arbitrary sized + objects (not only the 4 listed above). Each array has an 8-bit tag. + Usually tags are represented by character constants. For example, altvals + and supvals are simply 2 of 256 arrays, with the tags 'A' and 'S' respectively. + +*/ + +#include <range.hpp> + +//-------------------------------------------------------------------------- +/// Maximum length of a netnode name. WILL BE REMOVED IN THE FUTURE +const int MAXNAMESIZE = 512; + +/// Maximum length of a name. We permit names up to 32KB-1 bytes. +const int MAX_NODENAME_SIZE = 32*1024; + +/// Maximum length of strings or objects stored in a supval array element +const int MAXSPECSIZE = 1024; + +/// \typedef{nodeidx_t, numbers are 64 bit for 64 bit IDA} +typedef uint64 nodeidx64_t; +typedef uint32 nodeidx32_t; +#ifdef __EA64__ +typedef nodeidx64_t nodeidx_t; +#else +typedef nodeidx32_t nodeidx_t; +#endif + +/// A number to represent a bad netnode reference +#define BADNODE nodeidx_t(-1) + +/// \defgroup nn_res Reserved netnode tags +/// Tags internally used in netnodes. You should not use them +/// for your tagged alt/sup/char/hash arrays. +///@{ +const uchar atag = 'A'; ///< Array of altvals +const uchar stag = 'S'; ///< Array of supvals +const uchar htag = 'H'; ///< Array of hashvals +const uchar vtag = 'V'; ///< Value of netnode +const uchar ntag = 'N'; ///< Name of netnode +const uchar ltag = 'L'; ///< Links between netnodes +///@} + +// Internal bit used to request ea2node() mapping of alt and sup indexes +const int NETMAP_IDX = 0x100; +// Internal bit used to request ea2node() mapping of alt values. +// Such values are stored after being incremented by one. +const int NETMAP_VAL = 0x200; +// Internal bit used to make sure a string obtained with getblob() is +// null-terminated. +const int NETMAP_STR = 0x400; +// Internal bit: use 8-bit indexes. +const int NETMAP_X8 = 0x800; +// Internal bit: use 8-bit values. +const int NETMAP_V8 = 0x1000; +// Internal bit: value is a netnode index +const int NETMAP_VAL_NDX = 0x2000; + +/// visitor to be used by altadjust2 to skip the adjustment of some altvals +struct altadjust_visitor_t +{ + virtual bool should_skip(nodeidx_t ea) = 0; +}; + +/// \name Helper functions +/// They should not be called directly! See ::netnode +///@{ +class netnode; +class linput_t; +idaman bool ida_export netnode_check(netnode *, const char *name, size_t namlen, bool create); +idaman void ida_export netnode_kill(netnode *); +idaman bool ida_export netnode_start(netnode *); +idaman bool ida_export netnode_end(netnode *); +idaman bool ida_export netnode_next(netnode *); +idaman bool ida_export netnode_prev(netnode *); +idaman ssize_t ida_export netnode_get_name(nodeidx_t num, qstring *out); +idaman bool ida_export netnode_rename(nodeidx_t num, const char *newname, size_t namlen); +idaman ssize_t ida_export netnode_valobj(nodeidx_t num, void *buf, size_t bufsize); +idaman ssize_t ida_export netnode_valstr(nodeidx_t num, char *buf, size_t bufsize); +idaman ssize_t ida_export netnode_qvalstr(nodeidx_t num, qstring *buf); +idaman bool ida_export netnode_set(nodeidx_t num, const void *value, size_t length); +idaman bool ida_export netnode_delvalue(nodeidx_t num); +idaman nodeidx_t ida_export netnode_altval(nodeidx_t num, nodeidx_t alt, int tag); +idaman uchar ida_export netnode_charval(nodeidx_t num, nodeidx_t alt, int tag); +idaman nodeidx_t ida_export netnode_altval_idx8(nodeidx_t num, uchar alt, int tag); +idaman uchar ida_export netnode_charval_idx8(nodeidx_t num, uchar alt, int tag); +idaman ssize_t ida_export netnode_supval(nodeidx_t num, nodeidx_t alt, void *buf, size_t bufsize, int tag); +idaman ssize_t ida_export netnode_supstr(nodeidx_t num, nodeidx_t alt, char *buf, size_t bufsize, int tag); +idaman ssize_t ida_export netnode_qsupstr(nodeidx_t num, qstring *buf, nodeidx_t alt, int tag); +idaman bool ida_export netnode_supset(nodeidx_t num, nodeidx_t alt, const void *value, size_t length, int tag); +idaman bool ida_export netnode_supdel(nodeidx_t num, nodeidx_t alt, int tag); +idaman nodeidx_t ida_export netnode_lower_bound(nodeidx_t num, nodeidx_t cur, int tag); +idaman nodeidx_t ida_export netnode_supfirst(nodeidx_t num, int tag); +idaman nodeidx_t ida_export netnode_supnext(nodeidx_t num, nodeidx_t cur, int tag); +idaman nodeidx_t ida_export netnode_suplast(nodeidx_t num, int tag); +idaman nodeidx_t ida_export netnode_supprev(nodeidx_t num, nodeidx_t cur, int tag); +idaman ssize_t ida_export netnode_supval_idx8(nodeidx_t num, uchar alt, void *buf, size_t bufsize, int tag); +idaman ssize_t ida_export netnode_supstr_idx8(nodeidx_t num, uchar alt, char *buf, size_t bufsize, int tag); +idaman ssize_t ida_export netnode_qsupstr_idx8(nodeidx_t num, qstring *buf, uchar alt, int tag); +idaman bool ida_export netnode_supset_idx8(nodeidx_t num, uchar alt, const void *value, size_t length, int tag); +idaman bool ida_export netnode_supdel_idx8(nodeidx_t num, uchar alt, int tag); +idaman nodeidx_t ida_export netnode_lower_bound_idx8(nodeidx_t num, uchar alt, int tag); +idaman nodeidx_t ida_export netnode_supfirst_idx8(nodeidx_t num, int tag); +idaman nodeidx_t ida_export netnode_supnext_idx8(nodeidx_t num, uchar alt, int tag); +idaman nodeidx_t ida_export netnode_suplast_idx8(nodeidx_t num, int tag); +idaman nodeidx_t ida_export netnode_supprev_idx8(nodeidx_t num, uchar alt, int tag); +idaman bool ida_export netnode_supdel_all(nodeidx_t num, int tag); +idaman int ida_export netnode_supdel_range(nodeidx_t num, nodeidx_t idx1, nodeidx_t idx2, int tag); +idaman int ida_export netnode_supdel_range_idx8(nodeidx_t num, nodeidx_t idx1, nodeidx_t idx2, int tag); +idaman ssize_t ida_export netnode_hashval(nodeidx_t num, const char *idx, void *buf, size_t bufsize, int tag); +idaman ssize_t ida_export netnode_hashstr(nodeidx_t num, const char *idx, char *buf, size_t bufsize, int tag); +idaman ssize_t ida_export netnode_qhashstr(nodeidx_t num, qstring *buf, const char *idx, int tag); +idaman nodeidx_t ida_export netnode_hashval_long(nodeidx_t num, const char *idx, int tag); +idaman bool ida_export netnode_hashset(nodeidx_t num, const char *idx, const void *value, size_t length, int tag); +idaman bool ida_export netnode_hashdel(nodeidx_t num, const char *idx, int tag); +idaman ssize_t ida_export netnode_hashfirst(nodeidx_t num, char *buf, size_t bufsize, int tag); +idaman ssize_t ida_export netnode_qhashfirst(nodeidx_t num, qstring *buf, int tag); +idaman ssize_t ida_export netnode_hashnext(nodeidx_t num, const char *idx, char *buf, size_t bufsize, int tag); +idaman ssize_t ida_export netnode_qhashnext(nodeidx_t num, qstring *buf, const char *idx, int tag); +idaman ssize_t ida_export netnode_hashlast(nodeidx_t num, char *buf, size_t bufsize, int tag); +idaman ssize_t ida_export netnode_qhashlast(nodeidx_t num, qstring *buf, int tag); +idaman ssize_t ida_export netnode_hashprev(nodeidx_t num, const char *idx, char *buf, size_t bufsize, int tag); +idaman ssize_t ida_export netnode_qhashprev(nodeidx_t num, qstring *buf, const char *idx, int tag); +idaman size_t ida_export netnode_blobsize(nodeidx_t num, nodeidx_t start, int tag); +idaman void *ida_export netnode_getblob(nodeidx_t num, void *buf, size_t *bufsize, nodeidx_t start, int tag); +idaman ssize_t ida_export netnode_qgetblob(nodeidx_t num, bytevec_t *buf, size_t elsize, nodeidx_t start, int tag); +idaman bool ida_export netnode_setblob(nodeidx_t num, const void *buf, size_t size, nodeidx_t start, int tag); +idaman int ida_export netnode_delblob(nodeidx_t num, nodeidx_t start, int tag); +idaman bool ida_export netnode_inited(void); +idaman bool ida_export netnode_is_available(void); +idaman size_t ida_export netnode_copy(nodeidx_t num, nodeidx_t count, nodeidx_t target, bool move); +idaman size_t ida_export netnode_altshift(nodeidx_t num, nodeidx_t from, nodeidx_t to, nodeidx_t size, int tag); +idaman size_t ida_export netnode_charshift(nodeidx_t num, nodeidx_t from, nodeidx_t to, nodeidx_t size, int tag); +idaman size_t ida_export netnode_supshift(nodeidx_t num, nodeidx_t from, nodeidx_t to, nodeidx_t size, int tag); +idaman size_t ida_export netnode_blobshift(nodeidx_t num, nodeidx_t from, nodeidx_t to, nodeidx_t size, int tag); +idaman void ida_export netnode_altadjust(nodeidx_t num, nodeidx_t from, nodeidx_t to, nodeidx_t size, bool (idaapi *should_skip)(nodeidx_t ea)); +idaman void ida_export netnode_altadjust2(nodeidx_t num, nodeidx_t from, nodeidx_t to, nodeidx_t size, altadjust_visitor_t &av); +idaman bool ida_export netnode_exist(const netnode &n); +///@} + +//-------------------------------------------------------------------------- +// N E T N O D E +//-------------------------------------------------------------------------- + +/// Definition of the IDA database node. +/// Note that the size of the 'netnode' class is 4 bytes and it can be +/// freely casted to 'uint32' and back. This makes it easy to store +/// information about the program location in the netnodes. +/// Please pass netnodes to functions by value. +class netnode +{ + /// \cond + friend class netlink; + /// \endcond +public: + + //-------------------------------------------------------------------------- + /// \name Constructors, conversions and assignments + ///@{ + + /// Constructor to create a netnode to access information about the + /// specified linear address (possibly missing) + netnode(nodeidx_t num=BADNODE) { netnodenumber = num; } + + /// Conversion from netnode to a linear address + operator nodeidx_t() const { return netnodenumber; } + + /// Construct an instance of netnode class to access the specified netnode. + /// \param _name name of netnode + /// \param namlen length of the name. may be omitted, in this + /// case the length will be calculated with strlen() + /// \param do_create true: create the netnode if it doesn't exist yet. + /// false: don't create the netnode, set netnumber to #BADNODE if + /// it doesn't exist + netnode(const char *_name, size_t namlen=0, bool do_create=false) + { + netnode_check(this, _name, namlen, do_create); + } + + ///@} + + /// Does the specified netnode exist?. + /// \retval true there is some information attached to the netnode. + /// \retval false unnamed netnode without any information + friend bool exist(const netnode &n) { return netnode_exist(n); } + + /// Does the netnode with the specified name exist? + static bool exist(const char *_name) { return netnode(_name) != BADNODE; } + + //-------------------------------------------------------------------------- + /// \name Create/Delete + /// \note You can check if a netnode already exists with exist() + ///@{ + + /// Create a named netnode. + /// \param _name name of netnode to create. + /// names of user-defined netnodes must have the "$ " prefix + /// in order to avoid clashes with program byte names. + /// \param namlen length of the name. if not specified, it will be + /// calculated using strlen() + /// \retval 1 ok, the node is created + /// \retval 0 the node already exists. you may use the netnode class to access it. + bool create(const char *_name, size_t namlen=0) + { + return netnode_check(this, _name, namlen, true); + } + + /// Create unnamed netnode. + /// \retval 1 ok + /// \retval 0 should not happen, indicates internal error + bool create(void) { return create(nullptr); } + + /// Delete a netnode with all information attached to it + void kill(void) { netnode_kill(this); } + + ///@} + + //-------------------------------------------------------------------------- + /// \name Netnode names + ///@{ + + /// Get the netnode name. + /// \return -1 if netnode is unnamed (buf is untouched in this case), + /// otherwise the name length + ssize_t get_name(qstring *buf) const + { + return netnode_get_name(*this, buf); + } + + /// Rename a netnode. + /// \param newname new name of netnode. nullptr or "" means to delete name. + /// names of user-defined netnodes must have the "$ " prefix + /// in order to avoid clashes with program byte names. + /// \param namlen length of new name. if not specified, it will be + /// calculated using strlen() + /// \retval 1 ok + /// \retval 0 failed, newname is already used + bool rename(const char *newname, size_t namlen=0) + { + return netnode_rename(*this, newname, namlen); + } + + ///@} + + //-------------------------------------------------------------------------- + /// \name Value of netnode + ///@{ + + /// Get value of netnode. + /// Netnode values are arbitrary sized objects with max size is #MAXSPECSIZE. + /// NB: do not use this function for strings - see valstr(). + /// \return length of value, -1 if no value present + ssize_t valobj(void *buf, size_t bufsize) const + { + return netnode_valobj(*this, buf, bufsize); + } + + /// Get string value of netnode. + /// See explanations for supstr() function about the differences between valobj() + /// and valstr() + /// \return length of value, -1 if no value present + ssize_t valstr(qstring *buf) const + { + return netnode_qvalstr(*this, buf); + } + + /// \sa valstr(qstring *buf) const + ssize_t valstr(char *buf, size_t bufsize) const + { + return netnode_valstr(*this, buf, bufsize); + } + + /// Set value of netnode. + /// \param value pointer to value + /// \param length length of value. if not specified, it will be calculated + /// using strlen() + /// \return 1 - ok + bool set(const void *value, size_t length=0) + { + return netnode_set(*this, value, length); + } + + /// Delete value of netnode. + /// \retval 1 ok + /// \retval 0 failed, netnode is bad or other error + bool delvalue(void) + { + return netnode_delvalue(*this); + } + + /// Value of netnode as a long number: + bool set_long(nodeidx_t x) { return set(&x, sizeof(x)); } + bool value_exists(void) const { return valobj(nullptr, 0) >= 0; } + nodeidx_t long_value(void) const + { + nodeidx_t v = 0; + if ( valobj(&v, sizeof(v)) > 0 ) + return v; + return BADNODE; + } + + ///@} + + //-------------------------------------------------------------------------- + /// \name Arrays of altvals. + /// altvals: a sparse array of 32-bit values. + /// indexes in this array may be 8-bit or 32-bit values + ///@{ + + /// Get altval element of the specified array. + /// \param alt index into array of altvals + /// \param tag tag of array. may be omitted + /// \return value of altval element. nonexistent altval members are returned + /// as zeroes + nodeidx_t altval(nodeidx_t alt, uchar tag=atag) const + { + return netnode_altval(*this, alt, tag); + } + nodeidx_t altval_ea(ea_t ea, uchar tag=atag) const + { + return netnode_altval(*this, ea, tag|NETMAP_IDX); + } + + /// Set value of altval array. + /// \param alt index into array of altvals + /// \param value new value of altval element + /// \param tag tag of array + /// \retval 1 ok + /// \retval 0 failed, normally should not occur + bool altset(nodeidx_t alt, nodeidx_t value, uchar tag=atag) + { + return netnode_supset(*this, alt, &value, sizeof(value), tag); + } + bool altset_ea(ea_t ea, nodeidx_t value, uchar tag=atag) + { + return netnode_supset(*this, ea, &value, sizeof(value), tag|NETMAP_IDX); + } + + /// Delete element of altval array. + /// \param alt index into array of altvals + /// \param tag tag of array + /// \retval 1 ok + /// \retval 0 failed, element doesn't exist + bool altdel(nodeidx_t alt, uchar tag=atag) + { + return netnode_supdel(*this, alt, tag); + } + bool altdel_ea(ea_t ea, uchar tag=atag) + { + return netnode_supdel(*this, ea, tag|NETMAP_IDX); + } + + /// Store/retrieve/delete an address value in the netnode that corresponds + /// to an address. + bool easet(ea_t ea, ea_t addr, uchar tag) + { + return netnode_supset(*this, ea, &addr, sizeof(addr), tag|NETMAP_IDX|NETMAP_VAL); + } + ea_t eaget(ea_t ea, uchar tag) const + { + return netnode_altval(*this, ea, tag|NETMAP_IDX|NETMAP_VAL); + } + bool eadel(ea_t ea, uchar tag) + { + return netnode_supdel(*this, ea, tag|NETMAP_IDX); + } + + bool easet_idx(nodeidx_t idx, ea_t addr, uchar tag) + { + return netnode_supset(*this, idx, &addr, sizeof(addr), tag|NETMAP_VAL); + } + ea_t eaget_idx(nodeidx_t idx, uchar tag) + { + return netnode_altval(*this, idx, tag|NETMAP_VAL); + } + + bool easet_idx8(uchar idx, ea_t addr, uchar tag) + { + return netnode_supset_idx8(*this, idx, &addr, sizeof(addr), tag|NETMAP_VAL); + } + ea_t eaget_idx8(uchar idx, uchar tag) const + { + return netnode_altval_idx8(*this, idx, tag|NETMAP_VAL); + } + bool eadel_idx8(uchar idx, uchar tag) + { + return netnode_supdel_idx8(*this, idx, tag); + } + + /// Get first existing element of altval array. + /// \param tag tag of array + /// \return index of first existing element of altval array, + /// #BADNODE if altval array is empty + nodeidx_t altfirst(uchar tag=atag) const + { + return supfirst(tag); + } + + /// Get next existing element of altval array. + /// \param cur current index + /// \param tag tag of array + /// \return index of the next existing element of altval array, + /// #BADNODE if no more altval array elements exist + nodeidx_t altnext(nodeidx_t cur, uchar tag=atag) const + { + return supnext(cur, tag); + } + + /// Get last element of altval array. + /// \param tag tag of array + /// \return index of last existing element of altval array, + /// #BADNODE if altval array is empty + nodeidx_t altlast(uchar tag=atag) const + { + return suplast(tag); + } + + /// Get previous existing element of altval array. + /// \param cur current index + /// \param tag tag of array + /// \return index of the previous existing element of altval array, + /// #BADNODE if no more altval array elements exist + nodeidx_t altprev(nodeidx_t cur, uchar tag=atag) const + { + return supprev(cur, tag); + } + + /// Shift the altval array elements. + /// Moves the array elements at (from..from+size) to (to..to+size) + /// \return number of shifted elements + size_t altshift(nodeidx_t from, nodeidx_t to, nodeidx_t size, uchar tag=atag) + { + return netnode_altshift(*this, from, to, size, tag); + } + + /// Adjust values of altval arrays elements. + /// All altvals in the range from+1..from+size+1 and adjusted to have + /// values in the range to+1..to+size+1. + /// The parameter should_skip() can be used to skip the adjustment of some altvals + void altadjust(nodeidx_t from, nodeidx_t to, nodeidx_t size, bool (idaapi *should_skip)(nodeidx_t ea)=nullptr) + { + netnode_altadjust(*this, from, to, size, should_skip); + } + void altadjust2(nodeidx_t from, nodeidx_t to, nodeidx_t size, altadjust_visitor_t &av) + { + netnode_altadjust2(*this, from, to, size, av); + } + + + ///@} + + /// \name Arrays of altvals: 8-bit values + /// The following functions behave in the same manner as the functions + /// described above. The only difference is that the array value is 8-bits. + /// - index: 32 bits + /// - value: 8 bits + ///@{ + uchar charval(nodeidx_t alt, uchar tag) const { return netnode_charval(*this, alt, tag); } + bool charset(nodeidx_t alt, uchar val, uchar tag) { return supset(alt, &val, sizeof(val), tag); } + bool chardel(nodeidx_t alt, uchar tag) { return supdel(alt, tag); } + uchar charval_ea(ea_t ea, uchar tag) const { return netnode_charval(*this, ea, tag|NETMAP_IDX); } + bool charset_ea(ea_t ea, uchar val, uchar tag) { return netnode_supset(*this, ea, &val, sizeof(val), tag|NETMAP_IDX); } + bool chardel_ea(ea_t ea, uchar tag) { return netnode_supdel(*this, ea, tag|NETMAP_IDX); } + nodeidx_t charfirst(uchar tag) const { return supfirst(tag); } + nodeidx_t charnext(nodeidx_t cur, uchar tag) const { return supnext(cur, tag); } + nodeidx_t charlast(uchar tag) const { return suplast(tag); } + nodeidx_t charprev(nodeidx_t cur, uchar tag) const { return supprev(cur, tag); } + size_t charshift(nodeidx_t from, nodeidx_t to, nodeidx_t size, uchar tag) + { return netnode_charshift(*this, from, to, size, tag); } + ///@} + + /// \name Arrays of altvals: 8-bit indexes + /// Another set of functions to work with altvals. + /// The only difference is that the array index is 8-bits, + /// and therefore the array may contain up to 256 elements only. + /// - index: 8 bits + /// - value: 32 bits + ///@{ + nodeidx_t altval_idx8(uchar alt, uchar tag) const { return netnode_altval_idx8(*this, alt, tag); } + bool altset_idx8(uchar alt, nodeidx_t val, uchar tag) { return supset_idx8(alt, &val, sizeof(val), tag); } + bool altdel_idx8(uchar alt, uchar tag) { return supdel_idx8(alt, tag); } + nodeidx_t altfirst_idx8(uchar tag) const { return supfirst_idx8(tag); } + nodeidx_t altnext_idx8(uchar cur, uchar tag) const { return supnext_idx8(cur, tag); } + nodeidx_t altlast_idx8(uchar tag) const { return suplast_idx8(tag); } + nodeidx_t altprev_idx8(uchar cur, uchar tag) const { return supprev_idx8(cur, tag); } + ///@} + + /// \name More altvals + /// Another set of functions to work with altvals. + /// - index: 8 bits + /// - value: 8 bits + ///@{ + uchar charval_idx8(uchar alt, uchar tag) const { return netnode_charval_idx8(*this, alt, tag); } + bool charset_idx8(uchar alt, uchar val, uchar tag) { return supset_idx8(alt, &val, sizeof(val), tag); } + bool chardel_idx8(uchar alt, uchar tag) { return supdel_idx8(alt, tag); } + nodeidx_t charfirst_idx8(uchar tag) const { return supfirst_idx8(tag); } + nodeidx_t charnext_idx8(uchar cur, uchar tag) const { return supnext_idx8(cur, tag); } + nodeidx_t charlast_idx8(uchar tag) const { return suplast_idx8(tag); } + nodeidx_t charprev_idx8(uchar cur, uchar tag) const { return supprev_idx8(cur, tag); } + ///@} + + /// \name Delete altvals + /// \note To delete range of elements in an altval array, see supdel_range() + ///@{ + + /// Delete all elements of altval array. + /// This function may be applied to 32-bit and 8-bit altval arrays. + /// This function deletes the whole altval array. + /// \return success + bool altdel(void) + { + return supdel_all(atag); + } + + /// Delete all elements of the specified altval array. + /// This function may be applied to 32-bit and 8-bit altval arrays. + /// This function deletes the whole altval array. + /// \param tag tag of array + /// \return success + bool altdel_all(uchar tag=atag) + { + return supdel_all(tag); + } + + ///@} + + //-------------------------------------------------------------------------- + /// \name Arrays of supvals + /// supvals: an array of arbitrary sized objects. + /// (size of each object is limited by #MAXSPECSIZE). + /// indexes in this array may be 8-bit or 32-bit values. + ///@{ + + /// Get value of the specified supval array element. + /// NB: do not use this function to retrieve strings, see supstr()! + /// \param alt index into array of supvals + /// \param buf output buffer, may be nullptr + /// \param bufsize size of output buffer + /// \param tag tag of array. Default: stag + /// \return size of value, -1 if element doesn't exist + ssize_t supval(nodeidx_t alt, void *buf, size_t bufsize, uchar tag=stag) const + { return netnode_supval(*this, alt, buf, bufsize, tag); } + ssize_t supval_ea(ea_t ea, void *buf, size_t bufsize, uchar tag=stag) const + { return netnode_supval(*this, ea, buf, bufsize, tag|NETMAP_IDX); } + + /// Get string value of the specified supval array element. + /// The differences between supval() and supstr() are the following: + /// -# Strings are stored with the terminating zero in the old databases. + /// supval() returns the exact size of the stored object (with + /// the terminating zero) but supstr returns the string length without + /// the terminating zero. supstr() can handle strings stored with or + /// without the terminating zero. + /// -# supstr() makes sure that the string is terminated with 0 even if + /// the string was stored in the database without it or the output + /// buffer is too small to hold the entire string. In the latter case + /// the string will be truncated but still will have the terminating zero. + /// + /// If you do not use the string length returned by supval/supstr() functions + /// and you are sure that the output buffer is big enough to hold the entire + /// string and the string has been stored in the database with the terminating + /// zero, then you can continue to use supval() instead of supstr(). + /// + /// \param buf output buffer, may be nullptr + /// \param alt index into array of supvals + /// \param tag tag of array. Default: stag + /// \return length of the output string, -1 if element doesn't exist + ssize_t supstr(qstring *buf, nodeidx_t alt, uchar tag=stag) const + { return netnode_qsupstr(*this, buf, alt, tag); } + ssize_t supstr_ea(qstring *buf, ea_t ea, uchar tag=stag) const + { return netnode_qsupstr(*this, buf, ea, tag|NETMAP_IDX); } + + /// \sa supstr(qstring *buf, nodeidx_t alt, uchar tag=stag) const + ssize_t supstr(nodeidx_t alt, char *buf, size_t bufsize, uchar tag=stag) const + { return netnode_supstr(*this, alt, buf, bufsize, tag); } + ssize_t supstr_ea(ea_t ea, char *buf, size_t bufsize, uchar tag=stag) const + { return netnode_supstr(*this, ea, buf, bufsize, tag|NETMAP_IDX); } + + /// Set value of supval array element. + /// \param alt index into array of supvals + /// \param value pointer to supval value + /// \param length length of 'value'. If not specified, the length is calculated + /// using strlen()+1. + /// \param tag tag of array + /// \retval 1 ok + /// \retval 0 should not occur - indicates internal error + bool supset(nodeidx_t alt, const void *value, size_t length=0, uchar tag=stag) + { return netnode_supset(*this, alt, value, length, tag); } + bool supset_ea(ea_t ea, const void *value, size_t length=0, uchar tag=stag) + { return netnode_supset(*this, ea, value, length, tag|NETMAP_IDX); } + + /// Delete supval element. + /// \param alt index into array of supvals + /// \param tag tag of array + /// \retval true deleted + /// \retval false element does not exist + bool supdel(nodeidx_t alt, uchar tag=stag) + { return netnode_supdel(*this, alt, tag); } + bool supdel_ea(ea_t ea, uchar tag=stag) + { return netnode_supdel(*this, ea, tag|NETMAP_IDX); } + + /// Get lower bound of existing elements of supval array. + /// \param cur current index + /// \param tag tag of array + /// \return index of first existing element of supval array >= cur + /// #BADNODE if supval array is empty + nodeidx_t lower_bound(nodeidx_t cur, uchar tag=stag) const + { return netnode_lower_bound(*this, cur, tag); } + nodeidx_t lower_bound_ea(ea_t ea, uchar tag=stag) const + { return netnode_lower_bound(*this, ea, tag|NETMAP_IDX); } + + /// Get first existing element of supval array. + /// \param tag tag of array + /// \return index of first existing element of supval array, + /// #BADNODE if supval array is empty + nodeidx_t supfirst(uchar tag=stag) const + { return netnode_supfirst(*this, tag); } + + /// Get next existing element of supval array. + /// \param cur current index + /// \param tag tag of array + /// \return index of the next existing element of supval array, + /// #BADNODE if no more supval array elements exist + nodeidx_t supnext(nodeidx_t cur, uchar tag=stag) const + { return netnode_supnext(*this, cur, tag); } + + /// Get last existing element of supval array. + /// \param tag tag of array + /// \return index of last existing element of supval array, + /// #BADNODE if supval array is empty + nodeidx_t suplast(uchar tag=stag) const + { return netnode_suplast(*this, tag); } + + /// Get previous existing element of supval array. + /// \param cur current index + /// \param tag tag of array + /// \return index of the previous existing element of supval array + /// #BADNODE if no more supval array elements exist + nodeidx_t supprev(nodeidx_t cur, uchar tag=stag) const + { return netnode_supprev(*this, cur, tag); } + + + /// Shift the supval array elements. + /// Moves the array elements at (from..from+size) to (to..to+size) + /// \return number of shifted elements + size_t supshift(nodeidx_t from, nodeidx_t to, nodeidx_t size, uchar tag=stag) + { return netnode_supshift(*this, from, to, size, tag); } + + ///@} + + /// \name Arrays of supvals: 8-bit indexes + /// The following functions behave in the same manner as the functions + /// described above. The only difference is that the array index is 8-bits + /// and therefore the array may contains up to 256 elements only. + ///@{ + ssize_t supval_idx8(uchar alt, void *buf, size_t bufsize, uchar tag) const { return netnode_supval_idx8(*this, alt, buf, bufsize, tag); } + ssize_t supstr_idx8(uchar alt, char *buf, size_t bufsize, uchar tag) const { return netnode_supstr_idx8(*this, alt, buf, bufsize, tag); } + ssize_t supstr_idx8(qstring *buf, uchar alt, uchar tag) const { return netnode_qsupstr_idx8(*this, buf, alt, tag); } + bool supset_idx8(uchar alt, const void *value, size_t length, uchar tag) { return netnode_supset_idx8(*this, alt, value, length, tag); } + bool supdel_idx8(uchar alt, uchar tag) { return netnode_supdel_idx8(*this, alt, tag); } + nodeidx_t lower_bound_idx8(uchar alt, uchar tag) const { return netnode_lower_bound_idx8(*this, alt, tag); } + nodeidx_t supfirst_idx8(uchar tag) const { return netnode_supfirst_idx8(*this, tag); } + nodeidx_t supnext_idx8(uchar alt, uchar tag) const { return netnode_supnext_idx8(*this, alt, tag); } + nodeidx_t suplast_idx8(uchar tag) const { return netnode_suplast_idx8(*this, tag); } + nodeidx_t supprev_idx8(uchar alt, uchar tag) const { return netnode_supprev_idx8(*this, alt, tag); } + ///@} + + /// \name Delete supvals + ///@{ + + /// Delete all elements of supval array. + /// This function may be applied to 32-bit and 8-bit supval arrays. + /// This function deletes the whole supval array. + /// \return success + bool supdel(void) + { + return supdel_all(stag); + } + + /// Delete all elements of the specified supval array. + /// This function may be applied to 32-bit and 8-bit supval arrays. + /// This function deletes the whole supval array. + /// \return success + bool supdel_all(uchar tag) + { + return netnode_supdel_all(*this, tag); + } + + /// Delete range of elements in the specified supval array. + /// Elements in range [idx1, idx2) will be deleted. + /// \note This function can also be used to delete a range of altval elements + /// \param idx1 first element to delete + /// \param idx2 last element to delete + 1 + /// \param tag tag of array + /// \return number of deleted elements + int supdel_range(nodeidx_t idx1, nodeidx_t idx2, uchar tag) + { + return netnode_supdel_range(*this, idx1, idx2, tag); + } + /// Same as above, but accepts 8-bit indexes + int supdel_range_idx8(uchar idx1, uchar idx2, uchar tag) + { + return netnode_supdel_range_idx8(*this, idx1, idx2, tag); + } + + ///@} + + //-------------------------------------------------------------------------- + /// \name Hashes + /// Associative arrays indexed by strings. + /// hashvals: Indexes in this array are strings. + /// Values are arbitrary sized (max size is #MAXSPECSIZE) + ///@{ + + /// Get value of the specified hash element. + /// \param idx index into hash + /// \param buf output buffer, may be nullptr + /// \param bufsize output buffer size + /// \param tag tag of hash. Default: htag + /// \return -1 if element doesn't exist or idx is nullptr. + /// otherwise returns the value size in bytes + ssize_t hashval(const char *idx, void *buf, size_t bufsize, uchar tag=htag) const + { return netnode_hashval(*this, idx, buf, bufsize, tag); } + + /// Similar to supstr(), but accepts a hash index + ssize_t hashstr(qstring *buf, const char *idx, uchar tag=htag) const + { return netnode_qhashstr(*this, buf, idx, tag); } + + /// \sa hashstr(qstring *buf, const char *idx, uchar tag=htag) const + ssize_t hashstr(const char *idx, char *buf, size_t bufsize, uchar tag=htag) const + { return netnode_hashstr(*this, idx, buf, bufsize, tag); } + + /// Get value of the specified hash element. + /// \param idx index into hash + /// \param tag tag of hash. Default: htag + /// \return value of hash element (it should be set using hashset(nodeidx_t)), + /// 0 if the element does not exist + nodeidx_t hashval_long(const char *idx, uchar tag=htag) const + { return netnode_hashval_long(*this, idx, tag); } + + /// Set value of hash element. + /// \param idx index into hash + /// \param value pointer to value + /// \param length length of 'value'. If not specified, the length is calculated + /// using strlen()+1. + /// \param tag tag of hash. Default: htag + /// \retval 1 ok + /// \retval 0 should not occur - indicates internal error + bool hashset(const char *idx, const void *value, size_t length=0, uchar tag=htag) + { return netnode_hashset(*this, idx, value, length, tag); } + + /// Set value of hash element to long value. + /// \param idx index into hash + /// \param value new value of hash element + /// \param tag tag of hash. Default: htag + /// \retval 1 ok + /// \retval 0 should not occur - indicates internal error + bool hashset(const char *idx, nodeidx_t value, uchar tag=htag) + { return hashset(idx, &value, sizeof(value), tag); } + + /// Delete hash element. + /// \param idx index into hash + /// \param tag tag of hash. Default: htag + /// \retval true deleted + /// \retval false element does not exist + bool hashdel(const char *idx, uchar tag=htag) + { return netnode_hashdel(*this, idx, tag); } + + /// Get first existing element of hash. + /// \note elements of hash are kept sorted in lexical order + /// \param buf output buffer, may be nullptr + /// \param tag tag of hash. Default: htag + /// \return size of index of first existing element of hash, + /// -1 if hash is empty + ssize_t hashfirst(qstring *buf, uchar tag=htag) const + { return netnode_qhashfirst(*this, buf, tag); } + + /// \sa hashfirst(qstring *buf, uchar tag=htag) const + ssize_t hashfirst(char *buf, size_t bufsize, uchar tag=htag) const + { return netnode_hashfirst(*this, buf, bufsize, tag); } + + /// Get next existing element of hash. + /// \note elements of hash are kept sorted in lexical order + /// \param buf output buffer, may be nullptr + /// \param idx current index into hash + /// \param tag tag of hash. Default: htag + /// \return size of index of the next existing element of hash, + /// -1 if no more hash elements exist + ssize_t hashnext(qstring *buf, const char *idx, uchar tag=htag) const + { return netnode_qhashnext(*this, buf, idx, tag); } + + /// \sa hashnext(qstring *buf, const char *idx, uchar tag=htag) const + ssize_t hashnext(const char *idx, char *buf, size_t bufsize, uchar tag=htag) const + { return netnode_hashnext(*this, idx, buf, bufsize, tag); } + + /// Get last existing element of hash. + /// \note elements of hash are kept sorted in lexical order + /// \param buf output buffer, may be nullptr + /// \param tag tag of hash. Default: htag + /// \return size of index of last existing element of hash, + /// -1 if hash is empty + ssize_t hashlast(qstring *buf, uchar tag=htag) const + { return netnode_qhashlast(*this, buf, tag); } + + /// \sa hashlast(qstring *buf, uchar tag=htag) const + ssize_t hashlast(char *buf, size_t bufsize, uchar tag=htag) const + { return netnode_hashlast(*this, buf, bufsize, tag); } + + /// Get previous existing element of supval array. + /// \note elements of hash are kept sorted in lexical order + /// \param buf output buffer, may be nullptr + /// \param idx current index into hash + /// \param tag tag of hash. Default: htag + /// \return size of index of the previous existing element of hash, + /// -1 if no more hash elements exist + ssize_t hashprev(qstring *buf, const char *idx, uchar tag=htag) const + { return netnode_qhashprev(*this, buf, idx, tag); } + + /// \sa hashprev(qstring *buf, const char *idx, uchar tag=htag) const + ssize_t hashprev(const char *idx, char *buf, size_t bufsize, uchar tag=htag) const + { return netnode_hashprev(*this, idx, buf, bufsize, tag); } + + /// Delete all elements of hash. + /// This function deletes the whole hash. + /// \param tag tag of hash. Default: htag + /// \return success + bool hashdel_all(uchar tag=htag) + { + return supdel_all(tag); + } + + ///@} + + //-------------------------------------------------------------------------- + /// \name Blobs + /// Virtually unlimited size binary objects. + /// Blobs are stored in several supval array elements. + ///@{ + + /// Get size of blob. + /// \param _start index of the first supval element used to store blob + /// \param tag tag of supval array + /// \return number of bytes required to store a blob + size_t blobsize(nodeidx_t _start, uchar tag) + { + return netnode_blobsize(*this, _start, tag); + } + size_t blobsize_ea(ea_t ea, uchar tag) + { + return netnode_blobsize(*this, ea, tag|NETMAP_IDX); + } + + /// Get blob from a netnode. + /// \param buf buffer to read into. if nullptr, the buffer will be + /// allocated using qalloc() + /// \param[in, out] bufsize in: size of 'buf' in bytes (if buf == nullptr then meaningless). + /// out: size of the blob if it exists. + /// bufsize may be nullptr + /// \param _start index of the first supval element used to store blob + /// \param tag tag of supval array + /// \return nullptr if blob doesn't exist, + /// otherwise returns pointer to blob + void *getblob( + void *buf, + size_t *bufsize, + nodeidx_t _start, + uchar tag) + { + return netnode_getblob(*this, buf, bufsize, _start, tag); + } + void *getblob_ea( + void *buf, + size_t *bufsize, + ea_t ea, + uchar tag) + { + return netnode_getblob(*this, buf, bufsize, ea, tag|NETMAP_IDX); + } + + /// Get blob from a netnode. + /// \param blob output ::qvector buffer + /// \param _start index of the first supval element used to store blob + /// \param tag tag of supval array + /// \return -1 if blob doesn't exist, size of blob otherwise + template <class T> + ssize_t getblob( + qvector<T> *blob, + nodeidx_t _start, + uchar tag) + { + return netnode_qgetblob(*this, (bytevec_t *)blob, sizeof(T), _start, tag); + } + template <class T> + ssize_t getblob_ea( + qvector<T> *blob, + ea_t ea, + uchar tag) + { + return netnode_qgetblob(*this, (bytevec_t *)blob, sizeof(T), nodeidx_t(ea), tag|NETMAP_IDX); + } + + /// Get blob from a netnode into a qstring* and make sure the string is + /// null-terminated. + /// \param buf output ::qstring buffer + /// \param _start index of the first supval element used to store blob + /// \param tag tag of supval array + /// \return -1 if blob doesn't exist + /// size of string (including terminating null) otherwise + ssize_t getblob( + qstring *buf, + nodeidx_t _start, + uchar tag) + { + return netnode_qgetblob(*this, (bytevec_t *)buf, 1, _start, tag|NETMAP_STR); + } + + /// Store a blob in a netnode. + /// \param buf pointer to blob to save + /// \param size size of blob in bytes + /// \param _start index of the first supval element used to store blob + /// \param tag tag of supval array + /// \return success + bool setblob( + const void *buf, + size_t size, + nodeidx_t _start, + uchar tag) + { + return netnode_setblob(*this, buf, size, _start, tag); + } + bool setblob_ea( + const void *buf, + size_t size, + ea_t ea, + uchar tag) + { + return netnode_setblob(*this, buf, size, ea, tag|NETMAP_IDX); + } + + /// Delete a blob. + /// \param _start index of the first supval element used to store blob + /// \param tag tag of supval array + /// \return number of deleted supvals + int delblob(nodeidx_t _start, uchar tag) + { + return netnode_delblob(*this, _start, tag); + } + int delblob_ea(ea_t ea, uchar tag) + { + return netnode_delblob(*this, ea, tag|NETMAP_IDX); + } + + /// Shift the blob array elements. + /// Moves the array elements at (from..from+size) to (to..to+size) + /// \return number of shifted elements + size_t blobshift(nodeidx_t from, nodeidx_t to, nodeidx_t size, uchar tag) + { + return netnode_blobshift(*this, from, to, size, tag); + } + + ///@} + + //-------------------------------------------------------------------------- + /// \name Enumerate all netnodes + ///@{ + + /// Get first netnode in the graph. + /// Sets netnodenumber to the lowest existing number. + /// \retval true ok + /// \retval false graph is empty + bool start(void) + { + return netnode_start(this); + } + + /// Get last netnode in the graph. + /// Sets netnodenumber to the highest existing number. + /// \retval true ok + /// \retval false graph is empty + bool end(void) + { + return netnode_end(this); + } + + /// Get next netnode in the graph. + /// Sets netnodenumber to the next existing number + /// \retval true ok + /// \retval false no more netnodes + bool next(void) + { + return netnode_next(this); + } + + /// Get prev netnode in the graph. + /// Sets netnodenumber to the previous existing number + /// \retval true ok + /// \retval false no more netnodes + bool prev(void) + { + return netnode_prev(this); + } + + ///@} + + //-------------------------------------------------------------------------- + /// \name Move and copy netnodes + /// \param destnode the destination netnode + /// \param count how many netnodes to copy + /// \return number of copied/moved keys, #BADNODE if failure or not enough memory + ///@{ + size_t copyto(netnode destnode, nodeidx_t count=1) { return netnode_copy(netnodenumber, count, destnode.netnodenumber, false); } + size_t moveto(netnode destnode, nodeidx_t count=1) { return netnode_copy(netnodenumber, count, destnode.netnodenumber, true); } + ///@} + + //-------------------------------------------------------------------------- + /// \name Netnode comparisons + ///@{ + bool operator==(netnode &n) const { return netnodenumber == n.netnodenumber; } + bool operator!=(netnode &n) const { return netnodenumber != n.netnodenumber; } + bool operator==(nodeidx_t x) const { return netnodenumber == x; } + bool operator!=(nodeidx_t x) const { return netnodenumber != x; } + ///@} + + + static bool inited(void) { return netnode_inited(); } + static bool is_available(void) { return netnode_is_available(); } + +private: + // The netnode number. + // Usually this is the linear address that the netnode keeps information about. + nodeidx_t netnodenumber; +}; +#ifdef __EA64__ +CASSERT(sizeof(netnode) == 8); +#else +CASSERT(sizeof(netnode) == 4); +#endif + + +#endif // _NETNODE_HPP + +``` + +`IdaSDK/network.hpp`: + +```hpp +#ifndef NETWORK_HPP +#define NETWORK_HPP + +#include <pro.h> +#include <md5.h> + +#ifdef __NT__ +# if !defined(AF_MAX) +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include <ws2tcpip.h> +# endif +# define SYSTEM "Windows" +# define socklen_t int +# define SHUT_RD SD_RECEIVE +# define SHUT_WR SD_SEND +# define SHUT_RDWR SD_BOTH +#else // not NT, i.e. UNIX +# include <netinet/in.h> +# include <netinet/tcp.h> +# include <arpa/inet.h> +# include <netdb.h> +# define closesocket(s) close(s) +# define SOCKET size_t +# define INVALID_SOCKET size_t(-1) +# define SOCKET_ERROR (-1) +# if defined(__LINUX__) +# if defined(__ARM__) +# if defined(__ANDROID__) +# define SYSTEM "Android" +# else +# define SYSTEM "ARM Linux" +# endif +# else +# if defined(__ANDROID__) +# define SYSTEM "Android x86" +# define SYSTEM "Android x86" +# else +# define SYSTEM "Linux" +# endif +# endif + // linux debugger cannot be multithreaded because it uses thread_db. + // i doubt that this library is meant to be used with multiple + // applications simultaneously. +# define __SINGLE_THREADED_SERVER__ +# elif defined(__MAC__) +# define SYSTEM "Mac OS X" +# else +# error "Unknown platform" +# endif +# include <sys/socket.h> +# include <netinet/in.h> +#endif + +#ifndef __X86__ +# define _SYSBITS " 64-bit" +#else +# define _SYSBITS " 32-bit" +#endif + +#ifdef TESTABLE_BUILD +# ifdef __EA64__ +# define SYSBITS _SYSBITS " (sizeof ea=64)" +# else +# define SYSBITS _SYSBITS " (sizeof ea=32)" +# endif +#else +# define SYSBITS _SYSBITS +#endif + +#ifdef __SINGLE_THREADED_SERVER__ +# define __SERVER_TYPE__ "ST" +#else +# define __SERVER_TYPE__ "MT" +#endif + +#define TIMEOUT (1000/25) // timeout for polling (ms) +#define TIMEOUT_INFINITY -1 +#define RECV_HELLO_TIMEOUT 1000 // timeout for the first packet (ms) +#define RECV_TIMEOUT_PERIOD 10000 // timeout for recv (ms) + +// bidirectional codes (client <-> server) +enum base_packet_id_t +{ + RPC_OK = 0, // response: function call succeeded + RPC_UNK, // response: unknown function code + RPC_MEM, // response: no memory + base_packet_id_last +}; + +#define RPC_OPEN 3 // server->client: i'm ready, the very first packet + +#define RPC_EVENT 4 // server->client: debug event ready, followed by debug_event +#define RPC_EVOK 5 // client->server: event processed (in response to RPC_EVENT) +#define RPC_CANCELLED 6 // client->server: operation was cancelled by the user +// we need EVOK to handle the situation when the debug +// event was detected by the server during polling and +// was sent to the client using RPC_EVENT but client has not received it yet +// and requested GET_DEBUG_EVENT. In this case we should not +// call remote_get_debug_event() but instead force the client +// to use the event sent by RPC_EVENT. +// In other words, if the server has sent RPC_EVENT but has not +// received RPC_EVOK, it should fail all GET_DEBUG_EVENTS. + +// client->server codes +#define RPC_INIT 10 +#define RPC_TERM 11 +#define RPC_GET_PROCESSES 12 +#define RPC_START_PROCESS 13 +#define RPC_EXIT_PROCESS 14 +#define RPC_ATTACH_PROCESS 15 +#define RPC_DETACH_PROCESS 16 +#define RPC_GET_DEBUG_EVENT 17 +#define RPC_PREPARE_TO_PAUSE_PROCESS 18 +#define RPC_STOPPED_AT_DEBUG_EVENT 19 +#define RPC_CONTINUE_AFTER_EVENT 20 +#define RPC_TH_SUSPEND 21 +#define RPC_TH_CONTINUE 22 +#define RPC_SET_RESUME_MODE 23 +#define RPC_GET_MEMORY_INFO 24 +#define RPC_READ_MEMORY 25 +#define RPC_WRITE_MEMORY 26 +#define RPC_UPDATE_BPTS 27 +#define RPC_UPDATE_LOWCNDS 28 +#define RPC_EVAL_LOWCND 29 +#define RPC_ISOK_BPT 30 +#define RPC_READ_REGS 31 +#define RPC_WRITE_REG 32 +#define RPC_GET_SREG_BASE 33 +#define RPC_SET_EXCEPTION_INFO 34 + +#define RPC_OPEN_FILE 35 +#define RPC_CLOSE_FILE 36 +#define RPC_READ_FILE 37 +#define RPC_WRITE_FILE 38 +#define RPC_IOCTL 39 // both client and the server may send this packet +#define RPC_UPDATE_CALL_STACK 40 +#define RPC_APPCALL 41 +#define RPC_CLEANUP_APPCALL 42 +#define RPC_REXEC 43 +#define RPC_GET_SCATTERED_IMAGE 44 +#define RPC_GET_IMAGE_UUID 45 +#define RPC_GET_SEGM_START 46 +#define RPC_BIN_SEARCH 47 +#define RPC_GET_DYNREGS 48 + +// server->client codes +#define RPC_SET_DEBUG_NAMES 50 +#define RPC_SYNC_STUB 51 +#define RPC_ERROR 52 +#define RPC_MSG 53 +#define RPC_WARNING 54 +#define RPC_HANDLE_DEBUG_EVENT 55 +#define RPC_REPORT_IDC_ERROR 56 +#define RPC_IMPORT_DLL 57 + +#pragma pack(push, 1) + +struct PACKED rpc_packet_t +{ // fields are always sent in the network order + uint32 length; // length of the packet (do not count length & code) + uchar code; // function code +}; +CASSERT(sizeof(rpc_packet_t) == 5); +#pragma pack(pop) + +enum rpc_notification_type_t +{ + rnt_unknown = 0, + rnt_msg, + rnt_warning, + rnt_error, +}; + +#define DEFINE_ONE_NOTIFICATION_FUNCTION(FuncName, NotifCode, RpcEngineInst) \ + AS_PRINTF(2, 3) void FuncName(const char *format, ...) \ + { \ + va_list va; \ + va_start(va, format); \ + dvnotif(NotifCode, RpcEngineInst, format, va); \ + va_end(va); \ + } + +#define DEFINE_ALL_NOTIFICATION_FUNCTIONS(RpcEngineInst) \ + DEFINE_ONE_NOTIFICATION_FUNCTION(dmsg, 0, RpcEngineInst) \ + DEFINE_ONE_NOTIFICATION_FUNCTION(dwarning, 1, RpcEngineInst) \ + DEFINE_ONE_NOTIFICATION_FUNCTION(derror, -1, RpcEngineInst) + +class rpc_engine_t; +class recording_rpc_engine_t; + +//------------------------------------------------------------------------- +AS_PRINTF(2, 0) ssize_t dvnotif_client( + int code, + const char *format, + va_list va); + +#ifdef __NT__ +# define IRSERR_TIMEOUT WAIT_TIMEOUT +#else +# define IRSERR_TIMEOUT ETIME +#endif +#define IRSERR_CANCELLED -0xE5CA7E // escape +#define IRSERR_SKIP_ITER -0x5217 // skip recv() in rpc_engine_t's recv_data loop + +//------------------------------------------------------------------------- +// idarpc_stream_t +//------------------------------------------------------------------------- +// the idarpc_stream_t structure is not defined. +// it is used as an opaque type provided by the transport level. +// the transport level defines its own local type for it. +struct idarpc_stream_t; + +idarpc_stream_t *irs_new(bool use_tls=false); + +//------------------------------------------------------------------------- +struct host_port_t +{ + qstring host; + ushort port = 0; + + host_port_t() {} + host_port_t(const qstring &_host, ushort _port) : host(_host), port(_port) {} + + void clear() { host.clear(); port = 0; } + bool is_valid() const { return !host.empty() && port != 0; } + + bool operator==(const host_port_t &r) const + { + return host == r.host && port == r.port; + } + + void swap(host_port_t &r) + { + host.swap(r.host); + qswap(port, r.port); + } + + qstring connstr(int default_port=0) const + { + qstring s; + if ( port != 0 && port != default_port ) + { + if ( host[0] != '[' && host.find(':') != qstring::npos ) + s.sprnt("[%s]:%u", host.c_str(), port); // IPv6 address + else + s.sprnt("%s:%u", host.c_str(), port); + } + else + { + s = host; + } + return s; + } +}; +DECLARE_TYPE_AS_MOVABLE(host_port_t); + +//------------------------------------------------------------------------- +struct endpoint_credentials_t : public host_port_t +{ + qstring username; + // holds passwords provided through command-line parameters, + // or environment variable. A password retrieved from the + // keychain is not stored here. + qstring password_override; + + endpoint_credentials_t() {} + endpoint_credentials_t(const qstring &_host, ushort _port) + : host_port_t(_host, _port) {} + + //------------------------------------------------------------------------- + void clear() + { + host_port_t::clear(); + username.clear(); + password_override.clear(); + } + + //------------------------------------------------------------------------- + // Accepts the following schemes: + // `hostname:port` + // `user@hostname:port` + // `user:pass@hostname:port` +#define PCF_MERGE 1 + static bool parse_connstr( + endpoint_credentials_t *out, + const qstring &s, + uint32 flags=0) + { + if ( (flags & PCF_MERGE) == 0 ) + out->clear(); + if ( s.empty() ) + return false; + qstrvec_t parts; + s.split(&parts, "@"); + if ( parts.size() > 2 ) + return false; + int addr_idx = 0; + if ( parts.size() == 2 ) + { // the username is present + const qstring &p0 = parts[0]; + if ( p0.empty() ) + return false; + qstrvec_t identity_parts; + p0.split(&identity_parts, ":"); + if ( identity_parts.size() > 2 ) + return false; + out->username.swap(identity_parts[0]); + if ( identity_parts.size() == 2 ) + out->password_override.swap(identity_parts[1]); + addr_idx = 1; + } + qstring host; + qstring &addr = parts[addr_idx]; + const char *ptr = addr.c_str(); + const char *colon = nullptr; + if ( ptr[0] == '[' ) // IPv6 address? + { + const char *br = strchr(ptr, ']'); + if ( br == nullptr || br == ptr+1 ) + return false; + host = qstring(ptr+1, br-ptr-1); + addr.remove(0, br-ptr); + ptr = addr.c_str(); + if ( ptr[0] == ':' ) + colon = ptr; + } + else + { + if ( strnieq(ptr, "::ffff:", 7) // IPv6 prefix for IPv4 addresses + && strchr(ptr, '.') != nullptr ) // old style IPv4 address seems to follow + { + ptr += 7; // skip the prefix + } + colon = strchr(ptr, ':'); + } + if ( colon != nullptr ) + { + char *end; + int port = strtoul(++colon, &end, 10); + if ( colon == end ) + return false; + while ( *end == ' ' ) + ++end; + if ( *end != '\0' ) + return false; + addr.resize(colon - ptr - 1); + out->port = port; + } + out->host.swap(host.empty() ? addr : host); + return true; + } + + //------------------------------------------------------------------------- + bool parse_connstr(const qstring &s, uint32 flags=0) + { + return parse_connstr(this, s, flags); + } + +#define BCF_INCLUDE_USER 1 +#define BCF_INCLUDE_PASS 2 + void build_connstr( + qstring *out, + uint32 flags=0) const + { + if ( host.empty() ) + return; + + qstring buf; + if ( (flags & BCF_INCLUDE_USER) != 0 && !username.empty() ) + buf.append(username); + if ( (flags & BCF_INCLUDE_PASS) != 0 && !password_override.empty() ) + { + if ( !buf.empty() ) + buf.append(':'); + buf.append(password_override); + } + if ( !buf.empty() ) + buf.append('@'); + buf.append(connstr()); + out->swap(buf); + } + qstring build_connstr(uint32 flags=0) const + { + qstring tmp; + build_connstr(&tmp, flags); + return tmp; + } +}; +DECLARE_TYPE_AS_MOVABLE(endpoint_credentials_t); + +//------------------------------------------------------------------------- +typedef bool irs_cancel_connect_cb_t(void *); + +enum progress_loop_ctrl_t +{ + plc_proceed, + plc_skip_iter, + plc_cancel, +}; +typedef progress_loop_ctrl_t irs_progress_cb_t(bool receiving, size_t processed, size_t total, void *); +void irs_set_progress_cb(idarpc_stream_t *irs, int ms, irs_progress_cb_t cb, void *ud=nullptr); +struct irs_cancellable_op_t +{ + idarpc_stream_t *irs; + irs_cancellable_op_t(idarpc_stream_t *_irs, bool receiving, size_t goal=0); + ~irs_cancellable_op_t(); + void inc_progress(size_t progress); +}; + +//------------------------------------------------------------------------- +template <typename T> +struct irs_cb_info_t +{ + T *cb; + void *ud; + int ms; + + irs_cb_info_t() { reset(); } + void reset() + { + cb = nullptr; + ud = nullptr; + ms = -1; + } + + bool set(int _ms, T *_cb, void *_ud) + { + reset(); + if ( _cb != nullptr && _ms > -1 ) + { + cb = _cb; + ud = _ud; + ms = _ms; + } + return cb != nullptr; + } + + T *get(int *out_ms=nullptr, void **out_ud=nullptr) const + { + if ( out_ms != nullptr ) + *out_ms = ms; + if ( out_ud != nullptr ) + *out_ud = ud; + return cb; + } +}; + +using progress_cb_info_t = irs_cb_info_t<irs_progress_cb_t>; +using cancel_connect_cb_info_t = irs_cb_info_t<irs_cancel_connect_cb_t>; + +//------------------------------------------------------------------------- +struct irs_client_opts_t +{ + size_t cb = sizeof(*this); + endpoint_credentials_t server; + endpoint_credentials_t proxy; + cancel_connect_cb_info_t cancel_connect_cb_info; + + irs_client_opts_t() {} + irs_client_opts_t(const qstring &_host, int _port) + : server(_host, _port) {} +}; +DECLARE_TYPE_AS_MOVABLE(irs_client_opts_t); + +//------------------------------------------------------------------------- +struct irs_server_opts_t +{ + size_t cb = sizeof(*this); + host_port_t bind; + qstring certchain_path; + qstring privkey_path; + + irs_server_opts_t() {} + irs_server_opts_t(const qstring &_host, int _port) + : bind(_host, _port) {} +}; +DECLARE_TYPE_AS_MOVABLE(irs_server_opts_t); + +bool irs_init_client(idarpc_stream_t *irs, const irs_client_opts_t &options); +bool irs_init_server(idarpc_stream_t *irs, const irs_server_opts_t &options); +bool irs_accept(idarpc_stream_t *irs, idarpc_stream_t *listener); +bool irs_handshake(idarpc_stream_t *irs, int timeout_ms = -1); +int irs_ready(idarpc_stream_t *irs, int timeout_ms = -1); +ssize_t irs_recv(idarpc_stream_t *irs, void *buf, size_t n); +ssize_t irs_send(idarpc_stream_t *irs, const void *buf, size_t n); +void irs_term(idarpc_stream_t **pirs, int shutdown_flags = -1); +int irs_get_error(idarpc_stream_t *irs); +const char *irs_strerror(idarpc_stream_t *irs); +bool irs_peername(idarpc_stream_t *irs, qstring *out, bool lookupname = true); +bool irs_sockname(idarpc_stream_t *irs, qstring *out, bool lookupname = true); +bool irs_sockport(idarpc_stream_t *irs, int *out); +// convenience functions +ssize_t irs_send_data(idarpc_stream_t *irs, const void *buf, size_t n); +ssize_t irs_recv_data( + idarpc_stream_t *irs, + void *buf, + size_t n, + int timeout_ms = -1); +bool irs_recv_str(idarpc_stream_t *irs, qstring *out, int timeout_ms = -1); +bool irs_send_str(idarpc_stream_t *irs, const qstring &str); + +//------------------------------------------------------------------------- +typedef qtime64_t utc_timestamp_t; +constexpr uint32 TIMESTAMP_UTC = 0x01; +constexpr uint32 TIMESTAMP_ISO8601 = 0x02; +constexpr uint32 TIMESTAMP_WITH_MS = 0x04; + +//------------------------------------------------------------------------- +idaman THREAD_SAFE bool ida_export parse_timestamp( + utc_timestamp_t *out, + const char *in, + uint32 flags = 0); + +//------------------------------------------------------------------------- +constexpr size_t TIMESTAMP_BUFSZ = 25; +idaman THREAD_SAFE bool ida_export format_timestamp( + char *out, + size_t out_size, + utc_timestamp_t ts, + uint32 flags = 0); + +//------------------------------------------------------------------------- +typedef uint64 lofi_timestamp_t; // low-fidelity timestamp. Only encodes up to 1/10th seconds +//------------------------------------------------------------------------- +THREAD_SAFE inline lofi_timestamp_t to_lofi_timestamp(qtime64_t ts) +{ + const uint64 s = get_secs(ts); + const uint64 us = get_usecs(ts); + return s * 10 + us / (100 * 1000); +} + +//------------------------------------------------------------------------- +THREAD_SAFE inline qtime64_t from_lofi_timestamp(lofi_timestamp_t lts) +{ + return make_qtime64(lts / 10, (lts % 10) * (100 * 1000)); +} + +//------------------------------------------------------------------------- +// base_dispatcher_t + network_client_handler_t +//------------------------------------------------------------------------- +struct network_client_handler_t +{ + FILE *channels[16]; + idarpc_stream_t *irs; + qstring peer_name; + uint32 session_id; + utc_timestamp_t session_start; + bool verbose; + + void close_all_channels(); + void clear_channels(); + int find_free_channel() const; + + network_client_handler_t(idarpc_stream_t *_irs, bool _verbose); + virtual ~network_client_handler_t(); + + virtual bool handle() = 0; // true - delete this + virtual void shutdown_gracefully(int signum) = 0; + + void term_irs(); + + AS_PRINTF(2, 3) int lprintf(const char *format, ...) const + { + va_list va; + va_start(va, format); + int code = vlprintf(format, va); + va_end(va); + return code; + } + AS_PRINTF(2, 0) int vlprintf(const char *format, va_list va) const; + +private: + DECLARE_UNCOPYABLE(network_client_handler_t); +}; + +//------------------------------------------------------------------------- +struct client_handlers_list_t +{ + typedef std::map<network_client_handler_t *, qthread_t> storage_t; + storage_t storage; + + virtual ~client_handlers_list_t() {} + virtual void lock() {} + virtual void unlock() {} + virtual bool is_multi_threaded() const { return false; } +}; + +//------------------------------------------------------------------------- +struct mt_client_handlers_list_t : public client_handlers_list_t +{ + qmutex_t mutex; + + mt_client_handlers_list_t() { mutex = qmutex_create(); QASSERT(1540, mutex != nullptr); } + virtual ~mt_client_handlers_list_t() { qmutex_free(mutex); } + virtual void lock() override { qmutex_lock(mutex); } + virtual void unlock() override { qmutex_unlock(mutex); } + virtual bool is_multi_threaded() const override { return true; } +}; + +#define SERVER_CLIOPT_DEFAULTS 0x0 +#define SERVER_CLIOPT_NOTLS 0x1 // note: will always be enabled for TESTABLE_BUILD +#define SERVER_CLIOPT_RECORDING 0x2 // note: will always be enabled for TESTABLE_BUILD + +//------------------------------------------------------------------------- +struct base_dispatcher_t +{ + qstring ipv4_address; + qstring certchain; + qstring privkey; + idarpc_stream_t *irs = nullptr; + client_handlers_list_t *clients_list = nullptr; + utc_timestamp_t start_time = qtime64(); + ushort port_number = -1; + bool use_tls = true; + bool verbose = false; + + base_dispatcher_t(bool multi_threaded); + virtual ~base_dispatcher_t(); + void dispatch(); + + virtual void collect_cliopts(cliopts_t *out, uint32 features); + + // + void install_signal_handlers(); + + // + virtual network_client_handler_t *new_client_handler(idarpc_stream_t *_irs) = 0; + void delete_client_handler(network_client_handler_t *inst); + + virtual void shutdown_gracefully(int signum); + +private: + void handle_session(network_client_handler_t *handler); + void add_to_clients_list(network_client_handler_t *handler, qthread_t t); + DECLARE_UNCOPYABLE(base_dispatcher_t); +}; + +// [- +//------------------------------------------------------------------------- +// server_dispatcher_t +//------------------------------------------------------------------------- +struct server_cmdline_cfg_t +{ + qstring connection_string; + qstring log_file_path; + qstring license_file_path; + qstring config_file_path; +}; + +struct product_entry_t; +typedef qvector<product_entry_t> prodvec_t; +struct license_info_t; + +namespace licsrv_rpc +{ + struct license_t; +} + +//------------------------------------------------------------------------- +void default_check_license_activation( + qstring *out_matched_macaddr, + const licsrv_rpc::license_t &lic); + +//------------------------------------------------------------------------- +void default_print_license_info(const licsrv_rpc::license_t &lic); + +//------------------------------------------------------------------------- +struct license_file_visitor_t +{ + virtual void check_license_type( + const licsrv_rpc::license_t & /*lic*/) + { + } + + virtual void check_license_activation( + qstring *out_matched_macaddr, + const licsrv_rpc::license_t &lic) + { + default_check_license_activation(out_matched_macaddr, lic); + } + + virtual void fill_server_info( + const product_entry_t & /*p*/, + const qstring & /*name*/, + const qstring & /*email*/, + const qstring & /*macaddr*/) + { + } + + virtual int on_parse_error(int code) { return code; } + + virtual void print_license_info(const licsrv_rpc::license_t &lic) const + { + default_print_license_info(lic); + } +}; + +//------------------------------------------------------------------------- +struct licmgr_logger_t; +struct server_dispatcher_t : public base_dispatcher_t +{ + typedef base_dispatcher_t inherited; + + qstring license_file_name; + qstring badreqdir; // directory holding a dump of requests that caused an INTERR or unexpected exceptions + FILE *log_file = nullptr; + licmgr_logger_t *licmgr_logger = nullptr; + bool should_recreate_schema = false; + bool may_upgrade_schema = false; + qstring convdir; +#ifdef TESTABLE_BUILD + bool check_license_activation = true; +#endif + + server_dispatcher_t( + bool _multi_threaded, + int default_port, + const char *default_license_file_name); + virtual ~server_dispatcher_t(); + void check_license_file(license_file_visitor_t &lv) const; + const licsrv_rpc::license_t *read_license_file(license_file_visitor_t &lv) const; + void get_license_file_contents(bytevec_t *data) const; + + virtual void collect_cliopts(cliopts_t *out, uint32 features) override; + void apply_config(); + void setup_logging_output(); + void maybe_start_recording(recording_rpc_engine_t *e); + + void log_request(const bytevec_t &request, int interr_code=-1) const; +}; + +NORETURN AS_PRINTF(2, 3) void lerror(int code, const char *format, ...); +// ]- + +//------------------------------------------------------------------------- +// packing/unpacking utils +//------------------------------------------------------------------------- +bytevec_t prepare_rpc_packet(uchar code); +void finalize_packet(bytevec_t &pkt); + +//------------------------------------------------------------------------- +// rpc_engine_t +//------------------------------------------------------------------------- +#define VERBOSE_ENABLED +#ifdef VERBOSE_ENABLED +#define verb(x) do { if ( verbose ) msg x; } while(0) +#define verb_eng(engine, x) do { if ( (engine)->verbose ) msg x; } while(0) +#else +#define verb(x) //msg x +#define verb_eng(engine, x) +#endif +#define verbev(x) //msg x + +//------------------------------------------------------------------------- +struct rpc_packet_data_t +{ + uchar code; + + rpc_packet_data_t(uchar _code) : code(_code) {} + virtual ~rpc_packet_data_t() {} + virtual void serialize(bytevec_t *out, int version) const = 0; + virtual bool deserialize(const uchar **ptr, size_t len, int version) = 0; +}; + +//------------------------------------------------------------------------- +typedef int ioctl_handler_t( + class rpc_engine_t *rpc, + int fn, + const void *buf, + size_t size, + void **poutbuf, + ssize_t *poutsize); + +//------------------------------------------------------------------------- +typedef rpc_packet_data_t *rpc_packet_instantiator_t(const uchar *ptr, size_t len, int version); + +//------------------------------------------------------------------------- +struct rpc_packet_type_desc_t +{ + uchar code; + const char *name; + rpc_packet_instantiator_t *instantiate; +}; +DECLARE_TYPE_AS_MOVABLE(rpc_packet_type_desc_t); +typedef qvector<rpc_packet_type_desc_t> rpc_packet_type_desc_vec_t; + +//------------------------------------------------------------------------- +struct rpc_pkt_timeout_t +{ + uchar pkt_code; + int recv_timeout; // miliseconds +}; + +//--------------------------------------------------------------------------- +class rpc_engine_t +{ +public: + bool network_error = false; + qstring last_errstr; + + // pointer to the ioctl request handler, in case you + // need to handle ioctl requests from the server. + ioctl_handler_t *ioctl_handler = nullptr; + + // This array specifies non-standard timeouts for some RPC calls + const rpc_pkt_timeout_t *pkt_timeouts = nullptr; + size_t n_pkt_timeouts = 0; + + int recv_timeout; + bool is_client; + bool logged_in = false; + +protected: + void register_packet_type_descs(const rpc_packet_type_desc_t *ptypes, size_t cnt); + const rpc_packet_type_desc_t *find_packet_type_desc(int code) const; + const rpc_packet_type_desc_t *find_packet_type_desc(const char *name) const; + int get_timeout_for_request(uchar pkt_code) const; + +public: + rpc_engine_t( + bool _is_client, + const rpc_pkt_timeout_t *_pkt_timeouts = nullptr, + size_t _n_pkt_timeouts = 0); + virtual ~rpc_engine_t() {} + + int handle_ioctl_packet(bytevec_t &pkt, const uchar *ptr, const uchar *end); + + // low-level: deal with bytes, and don't handle "conversations". + int send_data(bytevec_t &data); + rpc_packet_t *recv_packet(uchar pkt_code); + + virtual rpc_packet_t *send_request_and_receive_reply(uchar pkt_code, bytevec_t &pkt) = 0; + + virtual idarpc_stream_t *get_irs() const = 0; + AS_PRINTF(3, 0) virtual ssize_t send_notif(int code, const char *format, va_list va); + + virtual bool get_broken_connection(void) { return false; } + virtual void set_broken_connection(void) {} + + int send_ioctl(int fn, const void *buf, size_t size, void **poutbuf, ssize_t *poutsize); + void set_ioctl_handler(ioctl_handler_t *h) { ioctl_handler = h; } + void set_pkt_timeouts(const rpc_pkt_timeout_t *_pkt_timeouts, size_t _n_pkt_timeouts) + { + pkt_timeouts = _pkt_timeouts; + n_pkt_timeouts = _n_pkt_timeouts; + } + + DEFINE_ALL_NOTIFICATION_FUNCTIONS(this); + +private: + rpc_packet_type_desc_vec_t ptypes; + + int recv_data(void *out, size_t len, int timeout_ms); + + AS_PRINTF(3,0) static ssize_t dvnotif(int code, rpc_engine_t *rpc, const char *format, va_list va); +}; + +//------------------------------------------------------------------------- +class recording_rpc_engine_t : public rpc_engine_t +{ +#ifdef TESTABLE_BUILD + struct + { + FILE *fp = nullptr; + qstring dumps_dir_path; + mutable uint64 packet_nr = 0; + } recording; +#endif + idarpc_stream_t *rpc_irs; + bool our_irs; // should we terminate rpc_irs in the destructor? + int protocol_version; + +protected: + void record( + const rpc_packet_data_t &data, + const uchar *bytes, + size_t len, + bool as_input) const; + void record_input(const rpc_packet_data_t &data, const rpc_packet_t *rp) const; + void record_output(const rpc_packet_data_t &data, const bytevec_t &bytes) const; + // must be implemented if an instance is meant to be able to record. + virtual bool get_conversation_name(qstring * /*out*/) const newapi { INTERR(1581); } + // formatter is mandatory + virtual void format_packet_data( + qstring *out, + const struct rpc_packet_data_t &data, + const char *line_prefix=nullptr) const newapi; + virtual void get_packet_name( + qstring *out, + const struct rpc_packet_data_t &data) const newapi; + bool reinit_irs(qstring *errbuf, bool use_tls, const char *server_host, int port); + +public: + recording_rpc_engine_t( + const rpc_packet_type_desc_t *descs, + size_t cnt, + idarpc_stream_t *_irs, + bool _our_irs, + bool _is_client, + const rpc_pkt_timeout_t *_pkt_timeouts = nullptr, + size_t _n_pkt_timeouts = 0, + int _protocol_version = 0) + : rpc_engine_t(_is_client, _pkt_timeouts, _n_pkt_timeouts), + rpc_irs(_irs), our_irs(_our_irs), protocol_version(_protocol_version) + { + register_packet_type_descs(descs, cnt); + } + virtual ~recording_rpc_engine_t(); + + bool start_recording(const qstring &convs_dir); + void stop_recording(); + bool is_recording() const; + + virtual rpc_packet_data_t *request_and_response( + const rpc_packet_data_t &request) newapi; + virtual rpc_packet_t *send_request_and_receive_reply(uchar pkt_code, bytevec_t &pkt) override; + rpc_packet_data_t *packet_data_from_raw(const rpc_packet_t *rp); + virtual idarpc_stream_t *get_irs() const override { return rpc_irs; } + bool is_our_irs() const { return our_irs; } + void cancel_irs(); + int get_protocol_version() const { return protocol_version; } + void set_protocol_version(int _protocol_version) + { +#ifdef TESTABLE_BUILD + QASSERT(2757, _protocol_version <= protocol_version); +#endif + protocol_version = _protocol_version; + } +}; + +//------------------------------------------------------------------------- +// This class can automatically reconnect if the connections drops. +class generic_client_t +{ + typedef recording_rpc_engine_t *rpc_engine_creator_t(idarpc_stream_t *_irs); + +protected: + rpc_engine_creator_t *create_rpc_engine; + recording_rpc_engine_t *rpc_engine = nullptr; +private: + qstring wait_dialog_contents; + const char *const server_name; + bool started_receiving_response = false; + bool was_user_cancelled = false; + + virtual void init(idarpc_stream_t *_irs); + +public: + generic_client_t( + const char *_server_name, + rpc_engine_creator_t *engine_creator, + idarpc_stream_t *_irs) + : create_rpc_engine(engine_creator), + server_name(_server_name) + { + generic_client_t::init(_irs); + } + virtual ~generic_client_t(); +protected: + virtual bool try_reconnect(qstring *errbuf) = 0; + virtual rpc_packet_data_t *create_failure_packet(const char *errmsg) = 0; + virtual bool is_handshake_packet(const rpc_packet_data_t &) { return false; } +}; + +//------------------------------------------------------------------------- +AS_PRINTF(3, 0) ssize_t dvnotif_rpc( + int code, + rpc_engine_t *rpc, + const char *format, + va_list va); + +//--------------------------------------------------------------------------- +AS_PRINTF(1, 0) int vlprintf(const char *format, va_list va); +AS_PRINTF(1, 2) int lprintf(const char *format, ...); +ssize_t lwrite(const void *data, size_t size); +void set_lprintf_output(FILE *out); + +//--------------------------------------------------------------------------- +void format_hex_dump( + qstrvec_t *out, + const uchar *buf, + size_t size, + bool for_comments, + size_t nhex_per_line=16); + +#define REPEAT_THRESHOLD 100 // that many or more equal bytes will + // be printed using the REPEAT_BYTES_MARKER +#define REPEAT_BYTES_MARKER '#' + +//------------------------------------------------------------------------- +struct login_credentials_t : public endpoint_credentials_t +{ + endpoint_credentials_t proxy; +#define LCS_NO_TLS 0x1 +#define LCS_SEEN_PROXY_OPTION 0x2 +#define LCS_RESERVED_BITS 8 + uint32 state = 0; + + login_credentials_t(const qstring &_host, ushort _port) + : endpoint_credentials_t(_host, _port) {} + virtual ~login_credentials_t() {} + virtual bool process_switch(const char *) newapi; + virtual void clear() newapi { this->endpoint_credentials_t::clear(); proxy.clear(); state = 0; } + + bool load_password(qstring *out, qstring *errbuf) const + { + if ( !password_override.empty() ) + { + *out = password_override; + return true; + } + return do_load_password(out, errbuf); + } + + bool load_proxy_password(qstring *out, qstring *errbuf) const + { + if ( !proxy.password_override.empty() ) + { + *out = proxy.password_override; + return true; + } + return do_load_proxy_password(out, errbuf); + } + + bool use_tls() const { return (state & LCS_NO_TLS) == 0; } + void set_use_tls(bool use_tls) { setflag(state, LCS_NO_TLS, !use_tls); } + bool has_seen_proxy_option() const { return (state & LCS_SEEN_PROXY_OPTION) != 0; } + +protected: + bool load_pass_from_keychain(qstring * /*out*/, qstring * /*errbuf*/, const char * /*app_name*/) const; + virtual bool do_load_password(qstring * /*out*/, qstring * /*errbuf*/) const newapi { return false; } + virtual bool do_load_proxy_password(qstring * /*out*/, qstring * /*errbuf*/) const newapi { return false; } + virtual bool write(qstring * /*errbuf*/) const newapi { return false; } +}; + +//------------------------------------------------------------------------- +#define VAULT_APP_NAME "hexvault" +#define VAULT_PROXY_APP_NAME VAULT_APP_NAME "_proxy" +#define DEFAULT_VAULT_HOST "hexvault" +#define DEFAULT_VAULT_PORT 65433 + +//------------------------------------------------------------------------- +struct credential_validator_t +{ + // during the call to `validate()`, + // `login_credentials_t::password_override` will hold + // the candidate new password to be used for validation. + virtual bool validate(login_credentials_t &cred) = 0; +}; + +//------------------------------------------------------------------------- +bool license_server_enabled(); + +//------------------------------------------------------------------------- +struct vault_credentials_t : public login_credentials_t +{ + typedef login_credentials_t inherited; + qstring sitename; +#define VCS_SEEN_SITE_OPTION (0x1 << LCS_RESERVED_BITS) +#define VCS_UPDATE_REG_INFO (0x2 << LCS_RESERVED_BITS) +#define VCS_USE_PROXY (0x4 << LCS_RESERVED_BITS) + + vault_credentials_t() : login_credentials_t(DEFAULT_VAULT_HOST, DEFAULT_VAULT_PORT) {} + virtual ~vault_credentials_t() {} + void init(); + virtual bool process_switch(const char *arg) override; + virtual void clear() override + { + this->login_credentials_t::clear(); + sitename.clear(); + } + virtual bool do_load_password(qstring *out, qstring *errbuf) const override; + virtual bool do_load_proxy_password(qstring *out, qstring *errbuf) const override; + virtual bool write(qstring *errbuf) const override; + enum ask_user_result_t + { + AUR_CANCELLED = 0, // user rejected the prompt + AUR_VALID, // the validator (if any) succeeded + AUR_INVALID, // the validator (if any) failed + }; +#define AUF_SILENT_UPDATE_FAILURE 0x1 // don't warn user if persisting the credentials failed + ask_user_result_t ask_user( + credential_validator_t *validator, + qstring *errbuf, + uint32 auf_flags=0); + + bool update(qstring *errbuf) const; // set/del reg info depending on 'VCS_UPDATE_REG_INFO' + bool del(qstring *errbuf) const; + void reg_set_site(const char *site) const; + + bool load_site(); + void load_proxy_info(); + + bool has_seen_site_option() const { return (state & VCS_SEEN_SITE_OPTION) != 0; } + + static bool reg_should_store_info(); + static void reg_set_store_info(bool store_pass); + static bool reg_del_store_info(); + +private: + void get_reg_key(qstring *out) const; +}; + +//------------------------------------------------------------------------- +#ifdef IDAFREE +# define PUBLIC_LUMINA_HOST "public-lumina.hex-rays.com" +# define PUBLIC_DEC_HOST "public-lumina.hex-rays.com" +# define PUBLIC_TLM_HOST "public-lumina.hex-rays.com" +#else +# define PUBLIC_LUMINA_HOST "lumina.hex-rays.com" +# define PUBLIC_DEC_HOST "lumina.hex-rays.com" +# define PUBLIC_TLM_HOST "tlm.hex-rays.com" +#endif +#define DEFAULT_LUMINA_PORT 443 +#define PUBLIC_DEC_PORT 443 +#define PUBLIC_TLM_PORT 443 + +#define LUMINA_APP_NAME "lumina" + +//------------------------------------------------------------------------- +struct lumina_credentials_t : public login_credentials_t +{ + bool is_primary; + + lumina_credentials_t() + : login_credentials_t("", 0) + {} + virtual ~lumina_credentials_t() {} + + void init(bool set_as_primary=true); + + virtual bool do_load_password(qstring *out, qstring *errbuf) const override; + virtual bool write(qstring *errbuf) const override; +}; + +//------------------------------------------------------------------------- +struct dec_credentials_t : public login_credentials_t +{ + dec_credentials_t() : login_credentials_t(PUBLIC_DEC_HOST, PUBLIC_DEC_PORT) {} + virtual ~dec_credentials_t() {} +}; + +//------------------------------------------------------------------------- +struct tlm_credentials_t : public login_credentials_t +{ + tlm_credentials_t() : login_credentials_t(PUBLIC_TLM_HOST, PUBLIC_TLM_PORT) {} + virtual ~tlm_credentials_t() {} + + void init(); +}; + +//------------------------------------------------------------------------- +#define NOSIG -1 +void call_exit_handlers(int signum); +void call_exit_handlers(); +void install_signal_and_exit_handlers(); + +struct exit_handler_t +{ + virtual void handle(int signum) = 0; + + exit_handler_t(); + virtual ~exit_handler_t(); + exit_handler_t(const exit_handler_t &) = delete; + exit_handler_t &operator=(const exit_handler_t &r) = delete; +}; + + +struct kc_ctx_t; +kc_ctx_t *get_keychain_context(); + +void ensure_one_server(const char *name); + + +/// Does the path point to an entity with the required features? + +#define VPAF_NATURE_FILE 0x01 +#define VPAF_NATURE_DIR 0x02 +#define VPAF_NATURE_ABSENT 0x03 +#define VPAF_NATURE_MASK 0x0F +#define VPAF_RESTRICTED 0x10 // must not be world-readable (not available on Windows) + +void validate_path_argument( + const char *path_identity, + const char *path, + uint32 flags); + +#endif // NETWORK_HPP + +``` + +`IdaSDK/offset.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _OFFSET_HPP +#define _OFFSET_HPP + +#include <nalt.hpp> +#include <segment.hpp> + +/*! \file offset.hpp + + \brief Functions that deal with offsets. + + "Being an offset" is a characteristic of an operand. + This means that operand or its part represent offset from + some address in the program. This linear address is called + "offset base". Some operands may have 2 offsets simultaneously. + Generally, IDA doesn't handle this except for Motorola outer offsets. + Thus there may be two offset values in an operand: simple offset and + outer offset. + + Outer offsets are handled by specifying special operand number: + it should be ORed with #OPND_OUTER value. + + See bytes.hpp for further explanation of operand numbers. +*/ + + +/// Get default reference type depending on the segment. +/// \return one of ::REF_OFF8, ::REF_OFF16, ::REF_OFF32, ::REF_OFF64 + +idaman reftype_t ida_export get_default_reftype(ea_t ea); + + +/// Convert operand to a reference. +/// To delete an offset, use clr_op_type() function. +/// \param ea linear address. +/// if 'ea' has unexplored bytes, try to convert them to +/// - no segment: fail +/// - 16bit segment: to 16bit word data +/// - 32bit segment: to dword +/// \param n operand number (may be ORed with #OPND_OUTER) +/// - 0: first +/// - 1: second +/// - ... +/// - 7: eighth operand +/// - #OPND_MASK: all operands +/// \param ri reference information +/// \return success + +idaman bool ida_export op_offset_ex(ea_t ea, int n, const refinfo_t *ri); + + +/// See op_offset_ex() + +idaman bool ida_export op_offset( + ea_t ea, + int n, + uint32 type_and_flags, + ea_t target=BADADDR, + ea_t base=0, + adiff_t tdelta=0); + + +/// Convert operand to a reference with the default reference type + +inline bool op_plain_offset(ea_t ea, int n, ea_t base) +{ + reftype_t reftype = get_default_reftype(ea); + return op_offset(ea, n, reftype, BADADDR, base) != 0; +} + + +/// Get offset base value +/// \param ea linear address +/// \param n 0..#UA_MAXOP-1 operand number +/// \return offset base or #BADADDR + +inline ea_t get_offbase(ea_t ea, int n) +{ + refinfo_t ri; + if ( !get_refinfo(&ri, ea, n) ) + return BADADDR; + return ri.base; +} + + +/// Get offset expression (in the form "offset name+displ"). +/// This function uses offset translation function (\ph{translate}) if your IDP +/// module has such a function. Translation function is used to map linear +/// addresses in the program (only for offsets). +/// +/// Example: suppose we have instruction at linear address 0x00011000: +/// \v{mov ax, [bx+7422h]} +/// and at ds:7422h: +/// \v{array dw ...} +/// We want to represent the second operand with an offset expression, so +/// then we call: +/// \v{ +/// get_offset_expresion(0x001100, 1, 0x001102, 0x7422, buf); +/// | | | | | +/// | | | | +output buffer +/// | | | +value of offset expression +/// | | +address offset value in the instruction +/// | +the second operand +/// +address of instruction +/// } +/// and the function will return a colored string: +/// \v{offset array} +/// \param buf output buffer to hold offset expression +/// \param ea start of instruction or data with the offset expression +/// \param n operand number (may be ORed with #OPND_OUTER) +/// - 0: first operand +/// - 1: second operand +/// - ... +/// - 7: eighth operand +/// \param from linear address of instruction operand or data referring to +/// the name. This address will be used to get fixup information, +/// so it should point to exact position of operand in the +/// instruction. +/// \param offset value of operand or its part. The function will return +/// text representation of this value as offset expression. +/// \param getn_flags combination of: +/// - #GETN_APPZERO: meaningful only if the name refers to +/// a structure. appends the struct field name +/// if the field offset is zero +/// - #GETN_NODUMMY: do not generate dummy names for the expression +/// but pretend they already exist +/// (useful to verify that the offset expression +/// can be represented) +/// \retval 0 can't convert to offset expression +/// \retval 1 ok, a simple offset expression +/// \retval 2 ok, a complex offset expression + + +idaman int ida_export get_offset_expression( + qstring *buf, + ea_t ea, + int n, + ea_t from, + adiff_t offset, + int getn_flags=0); + + +/// See get_offset_expression() + +idaman int ida_export get_offset_expr( + qstring *buf, + ea_t ea, + int n, + const refinfo_t &ri, + ea_t from, + adiff_t offset, + int getn_flags=0); + + +/// Does the specified address contain a valid OFF32 value?. +/// For symbols in special segments the displacement is not taken into account. +/// If yes, then the target address of OFF32 will be returned. +/// If not, then #BADADDR is returned. + +idaman ea_t ida_export can_be_off32(ea_t ea); + + +/// Try to calculate the offset base +/// This function takes into account the fixup information, +/// current ds and cs values. +/// \param ea the referencing instruction/data address +/// \param n operand number +/// - 0: first operand +/// - 1: second operand +/// - ... +/// - 7: eighth operand +/// \return output base address or #BADADDR + +idaman ea_t ida_export calc_offset_base(ea_t ea, int n); + + +/// Try to calculate the offset base. +/// 2 bases are checked: current ds and cs. +/// If fails, return #BADADDR + +idaman ea_t ida_export calc_probable_base_by_value(ea_t ea, uval_t off); + + +/// Calculate the target and base addresses of an offset expression. +/// The calculated target and base addresses are returned in the locations +/// pointed by 'base' and 'target'. In case 'ri.base' is #BADADDR, the +/// function calculates the offset base address from the referencing +/// instruction/data address. +/// The target address is copied from ri.target. If ri.target is #BADADDR +/// then the target is calculated using the base address and 'opval'. +/// This function also checks if 'opval' matches the full value of the +/// reference and takes in account the memory-mapping. +/// \param target output target address +/// \param base output base address +/// \param from the referencing instruction/data address +/// \param ri reference info block from the database +/// \param opval operand value (usually op_t::value or op_t::addr) +/// \return success +idaman bool ida_export calc_reference_data( + ea_t *target, + ea_t *base, + ea_t from, + const refinfo_t &ri, + adiff_t opval); + + +/// Add xrefs for a reference from the given instruction (\insn_t{ea}). +/// This function creates a cross references to the target and the base. +/// insn_t::add_off_drefs() calls this function to create xrefs for +/// 'offset' operand. +/// \param insn the referencing instruction +/// \param from the referencing instruction/data address +/// \param ri reference info block from the database +/// \param opval operand value (usually op_t::value or op_t::addr) +/// \param type type of xref +/// \param opoff offset of the operand from the start of instruction +/// \return the target address of the reference +idaman ea_t ida_export add_refinfo_dref( + const insn_t &insn, + ea_t from, + const refinfo_t &ri, + adiff_t opval, + dref_t type, + int opoff); + + +/// Calculate the target using the provided refinfo_t +inline ea_t calc_target(ea_t from, adiff_t opval, const refinfo_t &ri) +{ + ea_t target; + if ( !calc_reference_data(&target, nullptr, from, ri, opval) ) + return BADADDR; + return target; +} + +/// Retrieve refinfo_t structure and calculate the target +inline ea_t calc_target(ea_t from, ea_t ea, int n, adiff_t opval) +{ + refinfo_t ri; + return get_refinfo(&ri, ea, n) ? calc_target(from, opval, ri) : BADADDR; +} + +/// Calculate the value of the reference base. +inline ea_t calc_basevalue(ea_t target, ea_t base) +{ + return base - get_segm_base(getseg(target)); +} + + +#endif // _OFFSET_HPP + +``` + +`IdaSDK/parsejson.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef PARSEJSON_HPP +#define PARSEJSON_HPP + +/*! \file parsejson.hpp + + \brief Tools for parsing JSON-formatted input + + See also lex.hpp/parse.hpp for finer-grained functions & documentation. +*/ +#include <lex.hpp> + +//--------------------------------------------------------------------------- +enum jtype_t +{ + JT_UNKNOWN = 0, + JT_NUM, + JT_STR, + JT_OBJ, + JT_ARR, + JT_BOOL, + JT_NULL, + JT_DBL, +}; + +//--------------------------------------------------------------------------- +struct jobj_t; +struct jarr_t; + +#define DECLARE_JVALUE_HELPERS(decl) \ + decl void ida_export jvalue_t_clear(jvalue_t *); \ + decl void ida_export jvalue_t_copy(jvalue_t *, const jvalue_t &); + + +struct jvalue_t; +DECLARE_JVALUE_HELPERS(idaman) + +//------------------------------------------------------------------------- +struct jvalue_t +{ + jvalue_t() : _type(JT_UNKNOWN), _num(0) {} + jvalue_t(const jvalue_t &o) : _type(JT_UNKNOWN) { jvalue_t_copy(this, o); } + ~jvalue_t() { clear(); } + + void clear() { jvalue_t_clear(this); } + + jvalue_t &operator=(const jvalue_t &o) { jvalue_t_copy(this, o); return *this; } + + jtype_t type() const { return _type; } + int64 num() const { QASSERT(1277, _type == JT_NUM); return _num; } + const char *str() const { QASSERT(1278, _type == JT_STR); return _str->c_str(); } + const qstring &qstr() const { QASSERT(1623, _type == JT_STR); return *_str; } + const jobj_t &obj() const { QASSERT(1279, _type == JT_OBJ); return *_obj; } + const jarr_t &arr() const { QASSERT(1280, _type == JT_ARR); return *_arr; } + bool vbool() const { QASSERT(1281, _type == JT_BOOL); return _bool; } + double vdouble() const { QASSERT(2928, _type == JT_DBL); return _double; } + jobj_t &obj() { QASSERT(1282, _type == JT_OBJ); return *_obj; } + jarr_t &arr() { QASSERT(1283, _type == JT_ARR); return *_arr; } + + bool is_null() const { QASSERT(2929, _type == JT_NULL); return true; } + + //lint -sem(jvalue_t::set_str, custodial(1)) function takes ownership of its argument + //lint -sem(jvalue_t::set_obj, custodial(1)) function takes ownership of its argument + //lint -sem(jvalue_t::set_arr, custodial(1)) function takes ownership of its argument + void set_num(int64 i) { if ( _type != JT_UNKNOWN ) clear(); _type = JT_NUM; _num = i; } + void set_double(double dbl) { if ( _type != JT_UNKNOWN ) clear(); _type = JT_DBL; _double = dbl; } + void set_str(const char *s) { set_str(new qstring(s)); } + void set_str(qstring *s) { if ( _type != JT_UNKNOWN ) clear(); _type = JT_STR; _str = s; } + void set_obj(jobj_t *o) { if ( _type != JT_UNKNOWN ) clear(); _type = JT_OBJ; _obj = o; } + void set_arr(jarr_t *a) { if ( _type != JT_UNKNOWN ) clear(); _type = JT_ARR; _arr = a; } + void set_bool(bool b) { if ( _type != JT_UNKNOWN ) clear(); _type = JT_BOOL; _bool = b; } + void set_null() { if ( _type != JT_UNKNOWN ) clear(); _type = JT_NULL; } + + jobj_t *extract_obj() { QASSERT(1624, _type == JT_OBJ); jobj_t *o = _obj; _obj = nullptr; _type = JT_UNKNOWN; return o; } + jarr_t *extract_arr() { QASSERT(1625, _type == JT_ARR); jarr_t *a = _arr; _arr = nullptr; _type = JT_UNKNOWN; return a; } + + void swap(jvalue_t &r) + { + qswap(_type, r._type); + qswap(_str, r._str); + } + +private: + DECLARE_JVALUE_HELPERS(friend) + + jtype_t _type; + + union + { + int64 _num; + double _double; + qstring *_str; + jobj_t *_obj; + jarr_t *_arr; + bool _bool; + }; +}; +DECLARE_TYPE_AS_MOVABLE(jvalue_t); +typedef qvector<jvalue_t> jvalues_t; + +//--------------------------------------------------------------------------- +struct kvp_t +{ + qstring key; + jvalue_t value; +}; +DECLARE_TYPE_AS_MOVABLE(kvp_t); + +//------------------------------------------------------------------------- +struct jobj_t : public qvector<kvp_t> +{ + bool has_value(const char *k) const { return get_value(k) != nullptr; } + jvalue_t *get_value(const char *k, jtype_t t=JT_UNKNOWN) + { + jvalue_t *v = nullptr; + for ( size_t i = 0, _n = size(); i < _n; ++i ) + { + if ( at(i).key == k ) + { + if ( t == JT_UNKNOWN || at(i).value.type() == t ) + v = &at(i).value; + break; + } + } + return v; + } + + const jvalue_t *get_value(const char *k, jtype_t t=JT_UNKNOWN) const + { + return ((jobj_t *) this)->get_value(k, t); + } + + const jvalue_t *get_value_or_fail(const char *k, jtype_t t=JT_UNKNOWN) const + { + const jvalue_t *v = get_value(k, t); + QASSERT(1289, v != nullptr); + return v; + } + + jvalue_t *get_value_or_new(const char *key) + { + jvalue_t *v = get_value(key); + if ( v == nullptr ) + { + kvp_t &kvp = push_back(); + kvp.key = key; + v = &kvp.value; + } + return v; + } + + bool del_value(const char *k) + { + for ( size_t i = 0, _n = size(); i < _n; ++i ) + { + if ( at(i).key == k ) + { + erase(begin() + i); + return true; + } + } + return false; + } + + int64 get_num(const char *k) const { return get_value_or_fail(k)->num(); } + bool get_bool(const char *k) const { return get_value_or_fail(k)->vbool(); } + double get_double(const char *k) const { return get_value_or_fail(k)->vdouble(); } + const char *get_str(const char *k) const { return get_value_or_fail(k)->str(); } + const jobj_t &get_obj(const char *k) const { return get_value_or_fail(k)->obj(); } + const jarr_t &get_arr(const char *k) const { return get_value_or_fail(k)->arr(); } + +#define DEFINE_FLAG_GETTER(Type, JType, GetExpr) \ + bool get(Type *out, const char *k) const \ + { \ + const jvalue_t *v = get_value(k, JType); \ + bool ok = v != nullptr; \ + if ( ok ) \ + *out = GetExpr; \ + return ok; \ + } +#define DEFINE_DFLT_GETTER(Type, JType, GetExpr) \ + Type get(const char *k, Type dflt) const \ + { \ + const jvalue_t *v = get_value(k, JType); \ + return v != nullptr ? GetExpr : dflt; \ + } +#define DEFINE_SETTER(Type, SetExpr) \ + void put(const char *key, Type value) \ + { \ + jvalue_t *v = get_value_or_new(key); \ + SetExpr; \ + } +#define DEFINE_ACCESSORS(Type, ConstType, JType, GetExpr, SetExpr) \ + DEFINE_FLAG_GETTER(ConstType, JType, GetExpr) \ + DEFINE_DFLT_GETTER(ConstType, JType, GetExpr) \ + DEFINE_SETTER(Type, SetExpr) + + DEFINE_ACCESSORS(int, int, JT_NUM, v->num(), v->set_num(value)); + DEFINE_ACCESSORS(int64, int64, JT_NUM, v->num(), v->set_num(value)); + DEFINE_ACCESSORS(double, double, JT_DBL, v->vdouble(), v->set_double(value)); + DEFINE_ACCESSORS(bool, bool, JT_BOOL, v->vbool(), v->set_bool(value)); + //lint -sem(jobj_t::put(const char *, struct jarr_t *), custodial(2)) function takes ownership of its argument + DEFINE_ACCESSORS(jarr_t *, const jarr_t *, JT_ARR, &v->arr(), v->set_arr(value)); + //lint -sem(jobj_t::put(const char *, struct jobj_t *), custodial(2)) function takes ownership of its argument + DEFINE_ACCESSORS(jobj_t *, const jobj_t *, JT_OBJ, &v->obj(), v->set_obj(value)); + DEFINE_ACCESSORS(const char *, const char *, JT_STR, v->str(), v->set_str(new qstring(value))); +#undef DEFINE_ACCESSORS +#undef DEFINE_SETTER +#undef DEFINE_DFLT_GETTER +#undef DEFINE_FLAG_GETTER + + bool get(qstring *out, const char *k) const + { + const jvalue_t *v = get_value(k, JT_STR); + bool ok = v != nullptr; + if ( ok ) + *out = v->qstr(); + return ok; + } + + const qstring &get(const char *k, const qstring &dflt) const + { + const jvalue_t *v = get_value(k, JT_STR); + return v != nullptr ? v->qstr() : dflt; + } + + void put(const char *key, const qstring &value) + { + jvalue_t *v = get_value_or_new(key); + v->set_str(new qstring(value)); + } + + void put(const char *key, const jobj_t &value) + { + jvalue_t *v = get_value_or_new(key); + v->set_obj(new jobj_t(value)); + } +}; +DECLARE_TYPE_AS_MOVABLE(jobj_t); + +//--------------------------------------------------------------------------- +struct jarr_t +{ + jvalues_t values; + + size_t count_items_with_type(jtype_t t) const + { + size_t cnt = 0; + for ( size_t i = 0, n = values.size(); i < n; ++i ) + if ( values[i].type() == t ) + ++cnt; + return cnt; + } + + bool is_homogeneous(jtype_t t) const + { + return count_items_with_type(t) == values.size(); + } +}; +DECLARE_TYPE_AS_MOVABLE(jarr_t); + +//--------------------------------------------------------------------------- +// Note: If 'ungot_tokens' is not nullptr, its contents will be used before fetching tokens from the lexer +idaman THREAD_SAFE error_t ida_export parse_json(jvalue_t *out, lexer_t *lx, tokenstack_t *ungot_tokens = nullptr); +idaman THREAD_SAFE error_t ida_export parse_json_string(jvalue_t *out, const char *s, qstring *errbuf = nullptr, const char *file_path = nullptr); +idaman THREAD_SAFE error_t ida_export parse_json_file(jvalue_t *out, const char *path, qstring *errbuf = nullptr); + +//------------------------------------------------------------------------- +#define SJF_PRETTY 0x1 +#define SJF_SORTED_KEYS 0x2 +idaman THREAD_SAFE bool ida_export serialize_json( + qstring *out, + const jvalue_t &v, + uint32 flags=0); + +inline THREAD_SAFE bool serialize_json( + qstring *out, + const jobj_t *o, + uint32 flags=0) +{ + jvalue_t v; + v.set_obj((jobj_t *) o); + bool rc = serialize_json(out, v, flags); + v.extract_obj(); + return rc; +} + +#endif // PARSEJSON_HPP + +``` + +`IdaSDK/pro.h`: + +```h +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _PRO_H +#define _PRO_H + +/*! \file pro.h + + \brief This is the first header included in the IDA project. + + It defines the most common types, functions and data. + Also, it tries to make system dependent definitions. + + The following preprocessor macros are used in the project + (the list may be incomplete) + + Platform must be specified as one of: + + __NT__ - MS Windows (all platforms) \n + __LINUX__ - Linux \n + __MAC__ - MAC OS X + + __EA64__ - 64-bit address size (sizeof(ea_t)==8) \n + + __X86__ - 32-bit debug servers (sizeof(void*)==4) \n + __X64__ - x64 processor (sizeof(void*)==8) default \n + __PPC__ - PowerPC \n + __ARM__ - ARM +*/ + +/// IDA SDK v9.2 +#define IDA_SDK_VERSION 920 + +//--------------------------------------------------------------------------- +#if !defined(__NT__) && !defined(__LINUX__) && !defined(__MAC__) +# if defined(_MSC_VER) +# define __NT__ +# elif defined(__APPLE__) +# define __MAC__ +# elif defined(__linux__) +# define __LINUX__ +# else +# error "Please define one of: __NT__, __LINUX__, __MAC__" +# endif +#endif + +// Linux or Mac imply Unix +#if defined(__LINUX__) || defined(__MAC__) +#define __UNIX__ +#endif + +/// \def{BADMEMSIZE, Invalid memory size} +#ifndef __X86__ +#define BADMEMSIZE 0x7FFFFFFFFFFFFFFFull +#else +#define BADMEMSIZE 0x7FFFFFFFu +#endif + +/// \def{ENUM_SIZE, Specify size of enum values} +#define ENUM_SIZE(t) : t + +// this is necessary to have S_IFMT, S_IFREG and S_IFDIR defined in windows +// in order to define S_ISDIR and S_ISREG +#define _CRT_DECLARE_NONSTDC_NAMES 1 +#ifndef SWIG +#include <stdlib.h> /* size_t, nullptr, memory */ +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <assert.h> +#include <limits.h> +#include <ctype.h> +#include <time.h> +#include <stdint.h> +#ifdef __cplusplus +#include <memory> +#include <new> +#include <string> +#endif +#if defined(__NT__) +# include <malloc.h> +#endif + +/// \def{WIN32_LEAN_AND_MEAN, compile faster} +#if defined(_MSC_VER) +# define WIN32_LEAN_AND_MEAN +# include <string.h> +# include <io.h> +# include <direct.h> +#else +# include <wchar.h> +# include <string.h> +# include <unistd.h> +# include <sys/stat.h> +# include <errno.h> +#endif +#ifdef __cplusplus +# include <set> +# include <map> +# include <algorithm> +#endif +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#endif // SWIG + +#define STL_SUPPORT_PRESENT + +//--------------------------------------------------------------------------- +/// \def{EXTERNC, specify C linkage} +/// \def{C_INCLUDE, helper for 'extern "C" {}' statements} +/// \def{C_INCLUDE_END, \copydoc C_INCLUDE} +/// \def{INLINE, inline keyword for c++} +#if defined(__cplusplus) || defined(SWIG) +#define EXTERNC extern "C" +#define C_INCLUDE EXTERNC \ + { + +#define C_INCLUDE_END } +#define INLINE inline +#else +#define EXTERNC +#define C_INCLUDE +#define C_INCLUDE_END +#define INLINE __inline +#endif + +//--------------------------------------------------------------------------- +#ifndef MAXSTR +#define MAXSTR 1024 ///< maximum string size +#endif + +#define SMAXSTR QSTRINGIZE(MAXSTR) ///< get #MAXSTR as a string + +/// \def{NT_CDECL, Some NT functions require __cdecl calling convention} +#ifdef __NT__ +#define NT_CDECL __cdecl +#else +#define NT_CDECL +#endif + +/// \def{DEPRECATED, identifies parts of the IDA API that are considered deprecated} +/// \def{NORETURN, function does not return} +/// \def{PACKED, type is packed} +/// \def{PACKED_ALIGNED, type is packed but its start address is aligned} +/// \def{AS_PRINTF, function accepts printf-style format and args} +/// \def{AS_SCANF, function accepts scanf-style format and args} +/// \def{WARN_UNUSED_RESULT, warn if a function returns a result that is never used} +#if defined(SWIG) +#define constexpr +#define DEPRECATED +#define NORETURN +#define PACKED +#define PACKED_ALIGNED(al) +#define AS_STRFTIME(format_idx) +#define AS_PRINTF(format_idx, varg_idx) +#define AS_SCANF(format_idx, varg_idx) +#define WARN_UNUSED_RESULT +#elif defined(__GNUC__) +#define DEPRECATED __attribute__((deprecated)) +#define NORETURN __attribute__((noreturn)) +#define PACKED __attribute__((__packed__)) +#define PACKED_ALIGNED(al) __attribute__((__packed__)) __attribute__((__aligned__(al))) +#define AS_STRFTIME(format_idx) __attribute__((format(strftime, format_idx, 0))) +#define AS_PRINTF(format_idx, varg_idx) __attribute__((format(printf, format_idx, varg_idx))) +#define AS_SCANF(format_idx, varg_idx) __attribute__((format(scanf, format_idx, varg_idx))) +#define WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) +#else +#define DEPRECATED __declspec(deprecated) +#define NORETURN __declspec(noreturn) +#define PACKED +#define PACKED_ALIGNED(al) +#define AS_STRFTIME(format_idx) +#define AS_PRINTF(format_idx, varg_idx) +#define AS_SCANF(format_idx, varg_idx) +#define WARN_UNUSED_RESULT _Check_return_ +#endif + +/// \def{GCC_DIAG_OFF, disable a specific GCC warning for the following code} +/// \def{GCC_DIAG_ON, enable or restore a specific GCC warning for the following code} +#if defined(__GNUC__) && !defined(SWIG) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402 +#define GCC_DIAG_JOINSTR(x,y) _QSTRINGIZE(x ## y) +# define GCC_DIAG_DO_PRAGMA(x) _Pragma (#x) +# define GCC_DIAG_PRAGMA(x) GCC_DIAG_DO_PRAGMA(GCC diagnostic x) +# if (((__GNUC__ * 100) + __GNUC_MINOR__) >= 406) || defined(__clang__) +# define GCC_DIAG_OFF(x) GCC_DIAG_PRAGMA(push) \ + GCC_DIAG_PRAGMA(ignored GCC_DIAG_JOINSTR(-W,x)) +# define GCC_DIAG_ON(x) GCC_DIAG_PRAGMA(pop) +# else +# define GCC_DIAG_OFF(x) GCC_DIAG_PRAGMA(ignored GCC_DIAG_JOINSTR(-W,x)) +# define GCC_DIAG_ON(x) GCC_DIAG_PRAGMA(warning GCC_DIAG_JOINSTR(-W,x)) +# endif +#else +# define GCC_DIAG_OFF(x) +# define GCC_DIAG_ON(x) +#endif + +#if defined(_MSC_VER) && !defined(SWIG) +# define MSC_DIAG_OFF(x) __pragma(warning(push)) \ + __pragma(warning(disable : x)) +# define MSC_DIAG_ON(x) __pragma(warning(pop)) +#else +# define MSC_DIAG_OFF(x) +# define MSC_DIAG_ON(x) +#endif + +// A function attribute to disable ASAN +#if defined(__clang__) || defined (__GNUC__) +# define DISABLE_ASAN __attribute__((no_sanitize_address)) +#else +# define DISABLE_ASAN +#endif + +#if defined(DONT_DEPRECATE) +#undef DEPRECATED +#define DEPRECATED +#endif + +//--------------------------------------------------------------------------- + +#define __MF__ 0 ///< byte sex of our platform (Most significant byte First). + ///< 0: little endian (Intel 80x86). + ///< 1: big endian (PowerPC). + +//--------------------------------------------------------------------------- +/// Macro to avoid of message 'Parameter x is never used' +#define qnotused(x) (void)x + +#ifdef __clang__ +# define NONNULL _Nonnull +#else +# define NONNULL +#endif + +// this macro can be used as a suffix for declarations/definitions instead of qnotused() +#if defined(__clang__) || defined(__GNUC__) +# define QUNUSED __attribute__((unused)) +#else +# define QUNUSED +#endif + +/// \def{va_argi, GNU C complains about some data types in va_arg because they are promoted to int and proposes to replace them by int} +#ifdef __GNUC__ +#define va_argi(va, type) ((type)va_arg(va, int)) +#else +#define va_argi(va, type) va_arg(va, type) +#endif + +//--------------------------------------------------------------------------- +#define CONST_CAST(x) const_cast<x> ///< cast a const to non-const +#define _QSTRINGIZE(x) #x ///< return x as a string. See #SMAXSTR for example +#define QSTRINGIZE(x) _QSTRINGIZE(x) ///< see #_QSTRINGIZE + +//--------------------------------------------------------------------------- + +/// \def{idaapi, specifies __stdcall calling convention} +/// \def{ida_export, functions marked with this keyword are available as part of the IDA SDK} +/// \def{idaman, specifies c linkage} +/// \def{ida_export_data, data items marked with this keyword are available as part of the IDA SDK} +/// \def{ida_module_data, identifies a data item that will be exported} +/// \def{ida_local, identifies a non-public type definition} +#if defined(SWIG) // for SWIG + #define idaapi + #define idaman + #define ida_export + #define ida_export_data + #define ida_module_data + #define __fastcall + #define ida_local +#elif defined(APIDEF) // for API DEF files + #define idaapi + #define idaman + #define ida_export ida_export + #define ida_export_data ida_export_data + #define ida_module_data + #define __fastcall + #define ida_local +#elif defined(__NT__) // MS Windows + #define idaapi __stdcall + #define ida_export idaapi + #ifdef __CODE_CHECKER__ + // tell lint that this function will be exported + #define idaman EXTERNC __declspec(dllexport) + #else + #define idaman EXTERNC + #endif + #if defined(__KERNEL__) // kernel + #define ida_export_data + #define ida_module_data + #else // modules + #define ida_export_data __declspec(dllimport) + #define ida_module_data __declspec(dllexport) + #endif + #define ida_local +#elif defined(__UNIX__) // for unix + #define idaapi + #if defined(__MAC__) + #define idaman EXTERNC __attribute__((visibility("default"))) + #define ida_local __attribute__((visibility("hidden"))) + #else + #if __GNUC__ >= 4 + #define idaman EXTERNC __attribute__ ((visibility("default"))) + #define ida_local __attribute__((visibility("hidden"))) + #else + #define idaman EXTERNC + #define ida_local + #endif + #endif + #define ida_export + #define ida_export_data + #define ida_module_data + #define __fastcall +#endif + +/// Functions callable from any thread are marked with this keyword +#define THREAD_SAFE + +/// This keyword is used to introduce new virtual functions that do not override +/// any existing virtual function +#define newapi + +//--------------------------------------------------------------------------- +#ifndef __cplusplus +typedef int bool; +#define false 0 +#define true 1 +#endif + +//--------------------------------------------------------------------------- +// Linux C mode compiler already has these types defined +#if !defined(__LINUX__) || defined(__cplusplus) +typedef unsigned char uchar; ///< unsigned 8 bit value +typedef unsigned short ushort; ///< unsigned 16 bit value +typedef unsigned int uint; ///< unsigned 32 bit value +#endif + +typedef char int8; ///< signed 8 bit value +typedef signed char sint8; ///< signed 8 bit value +typedef unsigned char uint8; ///< unsigned 8 bit value +typedef short int16; ///< signed 16 bit value +typedef unsigned short uint16; ///< unsigned 16 bit value +typedef int int32; ///< signed 32 bit value +typedef unsigned int uint32; ///< unsigned 32 bit value + +#include <llong.hpp> + + +/// \fn{int64 qatoll(const char *nptr), Convert string to 64 bit integer} +#if defined(__UNIX__) +INLINE int64 qatoll(const char *nptr) { return nptr != nullptr ? atoll(nptr) :0; } +#elif defined(_MSC_VER) +INLINE int64 qatoll(const char *nptr) { return nptr != nullptr ? _atoi64(nptr) :0; } +#else +INLINE int64 qatoll(const char *nptr) { return nptr != nullptr ? atol(nptr) : 0; } +#endif + +// VS2010 lacks strtoull +#ifdef _MSC_VER +#define strtoull _strtoui64 +#endif + +/// \typedef{wchar16_t, 2-byte char} +/// \typedef{wchar32_t, 4-byte char} +#if defined(_MSC_VER) +typedef wchar_t wchar16_t; +typedef uint32 wchar32_t; +#elif defined(__GNUC__) +typedef uint16 wchar16_t; +typedef uint32 wchar32_t; +#endif + +/// Signed size_t - used to check for size overflows when the counter becomes +/// negative. Also signed size_t allows us to signal an error condition using +/// a negative value, for example, as a function return value. +#if !defined(_SSIZE_T_DEFINED) && !defined(__ssize_t_defined) && !defined(__GNUC__) +typedef ptrdiff_t ssize_t; +#endif + +/// \def{FMT_64, compiler-specific printf format specifier for 64-bit numbers} +/// \def{FMT_Z, compiler-specific printf format specifier for size_t} +/// \def{FMT_ZX, compiler-specific printf format specifier for size_t, hex} +/// \def{FMT_ZS, compiler-specific printf format specifier for ssize_t} +#if defined(__GNUC__) && !defined(__MINGW32__) + #define FMT_64 "ll" + #define FMT_Z "zu" + #define FMT_ZX "zX" + #define FMT_ZS "zd" +#elif defined(_MSC_VER) && _MSC_VER >= 1900 + #define FMT_64 "I64" + #define FMT_Z "zu" + #define FMT_ZX "zX" + #define FMT_ZS "td" +#elif defined(_MSC_VER) || defined(__MINGW32__) + #define FMT_64 "I64" + #ifndef __X86__ + #define FMT_Z "I64u" + #define FMT_ZX "I64X" + #define FMT_ZS "I64d" + #else + #define FMT_Z "u" + #define FMT_ZX "X" + #define FMT_ZS "d" + #endif +#elif !defined(SWIG) + #error "unknown compiler" +#endif + +/// \typedef{ea_t, effective address} +/// \typedef{sel_t, segment selector} +/// \typedef{asize_t, memory chunk size} +/// \typedef{adiff_t, address difference} +/// \def{SVAL_MIN, minimum value for an object of type int} +/// \def{SVAL_MAX, maximum value for an object of type int} +/// \def{FMT_EA, format specifier for ::ea_t values} +#ifdef __EA64__ + typedef uint64 ea_t; + typedef uint64 sel_t; + typedef uint64 asize_t; + typedef int64 adiff_t; + #define FMT_EA FMT_64 + #ifdef __GNUC__ + #define SVAL_MIN LLONG_MIN + #define SVAL_MAX LLONG_MAX + #else + #define SVAL_MIN _I64_MIN + #define SVAL_MAX _I64_MAX + #endif +#else + typedef uint32 ea_t; + typedef uint32 sel_t; + typedef uint32 asize_t; + typedef int32 adiff_t; + #define SVAL_MIN INT_MIN + #define SVAL_MAX INT_MAX + #define FMT_EA "" +#endif + +typedef asize_t uval_t; ///< unsigned value used by the processor. + ///< - for 32-bit ::ea_t - ::uint32 + ///< - for 64-bit ::ea_t - ::uint64 +typedef adiff_t sval_t; ///< signed value used by the processor. + ///< - for 32-bit ::ea_t - ::int32 + ///< - for 64-bit ::ea_t - ::int64 + +typedef uint32 ea32_t; ///< 32-bit address, regardless of IDA bitness. + ///< this type can be used when we know in advance + ///< that 32 bits are enough to hold an address. +typedef uint64 ea64_t; ///< 64-bit address, regardless of IDA bitness. + ///< we need this type for interoperability with + ///< debug servers, lumina, etc + +/// Error code (errno) +typedef int error_t; + +typedef uint8 op_dtype_t; + +/// The inode_t type is the specialization specific inode number. +/// For example, it can represent a local type ordinal or a structure id. +typedef uval_t inode_t; + +// A position in the difference source. +// This is an abstract value that depends on the difference source. +// It should be something that can be used to conveniently retrieve information +// from a difference source. For example, for the name list it can be the index +// in the name list. For structure view it can be the position in the list of structs. +// Please note that this is not necessarily an address. However, for the purpose +// of comparing the contents of the disassembly listing it can be an address. +// +// diffpos_t instances must have the following property: adding or removing +// items to diff_source_t should not invalidate the existing diffpos_t instances. +// They must stay valid after adding or removing items to diff_source_t. +// Naturally, deleting an item pointed by diffpos_t may render it incorrect, +// this is acceptable and expected. +typedef size_t diffpos_t; +constexpr diffpos_t BADDIFF = diffpos_t(-1); + +#ifdef __cplusplus +#define DEFARG(decl, val) decl = val +#else +#define DEFARG(decl, val) decl +#endif + +#ifndef SWIG +#define BADADDR ea_t(-1) ///< this value is used for 'bad address' +#define BADSEL sel_t(-1) ///< 'bad selector' value +#define BADADDR32 ea32_t(-1) +#define BADADDR64 ea64_t(-1) + +//------------------------------------------------------------------------- +// Time related functions + +typedef int32 qtime32_t; ///< we use our own time type because time_t + ///< can be 32-bit or 64-bit depending on the compiler +typedef uint64 qtime64_t; ///< 64-bit time value expressed as seconds and + ///< microseconds since the Epoch + +/// Get the 'seconds since the epoch' part of a qtime64_t + +INLINE THREAD_SAFE uint32 get_secs(qtime64_t t) +{ + return (uint32)(t>>32); +} + + +/// Get the microseconds part of a qtime64_t + +INLINE THREAD_SAFE uint32 get_usecs(qtime64_t t) +{ + return (uint32)(t); +} + + +/// Get a ::qtime64_t instance from a seconds value and microseconds value. +/// \param secs seconds +/// \param usecs microseconds + +INLINE THREAD_SAFE qtime64_t make_qtime64(uint32 secs, DEFARG(int32 usecs, 0)) +{ + return ((qtime64_t)(secs) << 32) | usecs; +} + + +/// Converts calendar time into a string. +/// Puts 'wrong timestamp\\n' into the buffer if failed +/// \param buf output buffer +/// \param bufsize size of the output buffer +/// \param t calendar time +/// \return success +/// See also qstrftime() + +idaman THREAD_SAFE bool ida_export qctime(char *buf, size_t bufsize, qtime32_t t); + + +/// Converts calendar time into a string using Coordinated Universal Time (UTC). +/// Function is equivalent to asctime(gmtime(t)). +/// Puts 'wrong timestamp\\n' into the buffer if failed. +/// \param buf output buffer +/// \param bufsize of the output buffer +/// \param t calendar time +/// \return success + +idaman THREAD_SAFE bool ida_export qctime_utc(char *buf, size_t bufsize, qtime32_t t); + + +/// Converts a time value to a tm structure (local time) +/// \param[out] _tm result +/// \param t calendar time +/// \return success + +idaman THREAD_SAFE bool ida_export qlocaltime(struct tm *_tm, time_t t); + + +/// Same as qlocaltime(struct tm *, time_t), but accepts a 64-bit time value + +INLINE THREAD_SAFE bool qlocaltime64(struct tm *_tm, qtime64_t t) +{ + return qlocaltime(_tm, get_secs(t)); +} + + +/// Converts a time value to a tm structure (UTC time) +/// \param[out] _tm result +/// \param t calendar time +/// \return success + +idaman bool ida_export qgmtime(struct tm *_tm, time_t t); + + +/// Same as qgmtime(struct tm *, time_t), but accepts a 64-bit time value + +INLINE THREAD_SAFE bool qgmtime64(struct tm *_tm, qtime64_t t) +{ + return qgmtime(_tm, get_secs(t)); +} + + +// Inverse of qgmtime() + +idaman time_t ida_export qtimegm(const struct tm *ptm); + + +/// Get string representation of a time_t (local time) +/// Copies into 'buf' the content of 'format', expanding its format specifiers into the +/// corresponding values that represent the time described in 't', with a limit of 'bufsize' characters +/// see http://www.cplusplus.com/reference/ctime/strftime/ for more +/// \param buf output buffer +/// \param bufsize of the output buffer +/// \param format format string +/// \param t calendar time value +/// \return length of the resulting string +/// See also qctime() + +idaman AS_STRFTIME(3) THREAD_SAFE size_t ida_export qstrftime( + char *buf, + size_t bufsize, + const char *format, + time_t t); + + +/// Same as qstrftime(), but accepts a 64-bit time value + +idaman AS_STRFTIME(3) THREAD_SAFE size_t ida_export qstrftime64( + char *buf, + size_t bufsize, + const char *format, + qtime64_t t); + + +/// Suspend execution for given number of milliseconds + +idaman THREAD_SAFE void ida_export qsleep(int milliseconds); + + +/// High resolution timer. +/// On Unix systems, returns current time in nanoseconds. +/// On Windows, returns a high resolution counter (QueryPerformanceCounter) +/// \return stamp in nanoseconds + +idaman THREAD_SAFE uint64 ida_export get_nsec_stamp(void); + +/// Get the current time with microsecond resolution (in fact the resolution +/// is worse on windows) + +idaman THREAD_SAFE qtime64_t ida_export qtime64(void); + + +/// Generate a random buffer. +/// \param[out] buffer pointer to result +/// \param bufsz size of buffer +/// \return success + +idaman THREAD_SAFE bool ida_export gen_rand_buf(void *buffer, size_t bufsz); + + +#define qoff64_t int64 ///< file offset + +/// Describes miscellaneous file attributes +struct qstatbuf +{ + uint64 qst_dev; ///< ID of device containing file + uint32 qst_ino; ///< inode number + uint32 qst_mode; ///< protection + uint32 qst_nlink; ///< number of hard links + uint32 qst_uid; ///< user ID of owner + uint32 qst_gid; ///< group ID of owner + uint64 qst_rdev; ///< device ID (if special file) + qoff64_t qst_size; ///< total size, in bytes + int32 qst_blksize; ///< blocksize for file system I/O + int32 qst_blocks; ///< number of 512B blocks allocated + qtime64_t qst_atime; ///< time of last access + qtime64_t qst_mtime; ///< time of last modification + qtime64_t qst_ctime; ///< time of last status change +}; + +// non standard functions are missing: +#ifdef _MSC_VER +#if _MSC_VER <= 1200 +# define for if(0); else for ///< MSVC <= 1200 is not compliant to the ANSI standard +#else +# pragma warning(disable : 4200) ///< zero-sized array in structure (non accept from cmdline) +# if _MSC_VER >= 1921 && _MSC_VER < 1924 // avoid compiler bug: +# pragma function(memmove) // https://developercommunity.visualstudio.com/content/problem/583227/vs-2019-cl-1921277022-memmove-instrinsic-optimizat.html +# endif +#endif +/// \name VS posix names +/// Shut up Visual Studio (VS deprecated posix names but there seems to be no good reason for that) +///@{ +#define chdir _chdir +#define fileno _fileno +#define getcwd _getcwd +#define memicmp _memicmp +# define F_OK 0 +# define W_OK 2 +# define R_OK 4 +///@} +#endif + +//--------------------------------------------------------------------------- +/* error codes */ +/*--------------------------------------------------*/ + +#define eOk 0 ///< no error +#define eOS 1 ///< os error, see errno +#define eDiskFull 2 ///< disk full +#define eReadError 3 ///< read error +#define eFileTooLarge 4 ///< file too large + + +/// Set qerrno + +idaman THREAD_SAFE error_t ida_export set_qerrno(error_t code); + + +/// Get qerrno + +idaman THREAD_SAFE error_t ida_export get_qerrno(void); + +//--------------------------------------------------------------------------- +// debugging macros +/// \def{ZZZ, debug print} +/// \def{BPT, trigger a breakpoint from IDA. also see #INTERR} +#define ZZZ msg("%s:%d\n", __FILE__, __LINE__) +#if defined(__GNUC__) +# define BPT __builtin_trap() +#elif defined(_MSC_VER) // Visual C++ +# define BPT __debugbreak() +# ifdef __CODE_CHECKER__ + NORETURN void __debugbreak(void); +# endif +#endif + +/// \def{CASSERT, results in a compile error if the cnd is not true} +#ifdef __CODE_CHECKER__ +#define CASSERT(cnd) static_assert((cnd), "") +#else +#define CASSERT(cnd) static_assert((cnd), QSTRINGIZE(cnd)) +#endif + +/// \def{INTERR, Show internal error message and terminate execution abnormally. +/// When IDA is being run under a debugger this will ensure that +/// the debugger will break immediately.} +#ifdef __CODE_CHECKER__ +#define INTERR(code) interr(code) +#else +#define INTERR(code) do { if ( under_debugger ) BPT; interr(code); } while(1) +#endif + +#define QASSERT(code, cond) do if ( !(cond) ) INTERR(code); while (0) ///< run time assertion +#define QBUFCHECK(buf, size, src) ida_fill_buffer(buf, size, src, __FILE__, __LINE__) ///< run time assertion +idaman bool ida_export_data under_debugger; ///< is IDA running under a debugger? + +#ifdef TESTABLE_BUILD +# define TB_QASSERT(Code, Expr) QASSERT(Code, Expr) +# define TB_INTERR(Code) INTERR(Code) +# define TB_CCHECK(expr) if ( !(expr) ) warning("Coherency check failed at %s:%d: " #expr, __FILE__, __LINE__) +# define TB_INTERR_OR_RETURN(code, ...) INTERR(code) +#else +# define TB_QASSERT(Code, Expr) +# define TB_INTERR(Code) +# define TB_CCHECK(expr) if ( !(expr) ) msg("Coherency check failed: " #expr "\n") +# define TB_INTERR_OR_RETURN(code, ...) return __VA_ARGS__ +#endif + +#define INTERR_EXC_FMT "Internal error %d occurred when running a script. Either\n" \ + " - the script misused the IDA API, or\n" \ + " - there is a logic error in IDA\n" \ + "Please check the script first.\n" \ + "If it appears correct, send a bug report to <support@hex-rays.com>.\n" \ + "In any case we strongly recommend you to restart IDA as soon as possible." + +#ifdef __cplusplus +struct interr_exc_t : public std::exception +{ + int code; + interr_exc_t(int _code) : code(_code) {} +}; +#endif // __cplusplus +idaman THREAD_SAFE NORETURN void ida_export interr(int code); ///< Show internal error message and terminate execution + +// set the behavior of 'interr()' +/// \param enable if true, interr() throws interr_exc_t +/// otherwise it terminates IDA after showing an error message +/// \return previous setting +idaman THREAD_SAFE bool ida_export set_interr_throws(bool enable); + +//--------------------------------------------------------------------------- +idaman THREAD_SAFE void *ida_export qalloc(size_t size); ///< System independent malloc +idaman THREAD_SAFE void *ida_export qrealloc(void *alloc, size_t newsize); ///< System independent realloc +idaman THREAD_SAFE void *ida_export qcalloc(size_t nitems, size_t itemsize); ///< System independent calloc +idaman THREAD_SAFE void ida_export qfree(void *alloc); ///< System independent free +idaman THREAD_SAFE char *ida_export qstrdup(const char *string); ///< System independent strdup +#define qnew(t) ((t*)qalloc(sizeof(t))) ///< create a new object in memory +/// \def{qnewarray, qalloc_array() is safer than qnewarray} +#define qnewarray(t,n) use_qalloc_array + +/// Use this class to avoid integer overflows when allocating arrays +#ifdef __cplusplus +template <class T> +T *qalloc_array(size_t n) +{ + return (T *)qcalloc(n, sizeof(T)); +} + +/// Use this class to avoid integer overflows when allocating arrays +template <class T> +T *qrealloc_array(T *ptr, size_t n) +{ + size_t nbytes = n * sizeof(T); + if ( nbytes < n ) + return nullptr; // integer overflow + return (T *)qrealloc(ptr, nbytes); +} + +/// \def{qnumber, determine capacity of an array} +#ifdef __GNUC__ +# define qnumber(arr) ( \ + 0 * sizeof(reinterpret_cast<const ::qnumber_check_type *>(arr)) \ + + 0 * sizeof(::qnumber_check_type::check_type((arr), &(arr))) \ + + sizeof(arr) / sizeof((arr)[0]) ) + struct qnumber_check_type + { + struct is_pointer; + struct is_array {}; + template <typename T> + static is_pointer check_type(const T *, const T *const *); + static is_array check_type(const void *, const void *); + }; +#elif defined(_MSC_VER) && !defined(__CODE_CHECKER__) +# define qnumber(array) _countof(array) +#else // poor man's implementation for other compilers and lint +# define qnumber(array) (sizeof(array)/sizeof(array[0])) +#endif +#endif // __cplusplus + +#define qoffsetof offsetof + +/// \def{set_vva, extracts a va_list passed as a variadic function argument} +/// \def{va_copy, copy a va_list} +#if defined(__GNUC__) && !defined(__X86__) && !(defined(__clang__) && defined(__ARM__)) + // gcc64 and clang x86_64 use special array-type va_list, so we have to resort to tricks like these + #define set_vva(va2, vp) va_copy(va2, *(va_list*)va_arg(vp, void*)) +#else + #ifndef va_copy + #define va_copy(dst, src) dst = src + #endif + #if defined(__clang__) + #define set_vva(va2, vp) va2 = va_arg(vp, va_list) + #else + #define set_vva(va2, vp) va_copy(va2, va_arg(vp, va_list)) + #endif +#endif + +#if defined(__LINUX__) && defined(__ARM__) +# define NULL_VA_LIST() va_list() +#else +# define NULL_VA_LIST() nullptr +#endif + +/// Reverse memory block. +/// Analog of strrev() function +/// \param buf pointer to buffer to reverse +/// \param size size of buffer +/// \return pointer to buffer + +idaman THREAD_SAFE void *ida_export memrev(void *buf, ssize_t size); + +#if defined(__GNUC__) && !defined(_WIN32) +idaman THREAD_SAFE int ida_export memicmp(const void *x, const void *y, size_t size); +#endif + +//--------------------------------------------------------------------------- +/* strings */ +/// \def{strnicmp, see 'VS posix names'} +/// \def{stricmp, see 'VS posix names'} +#ifdef __GNUC__ +#define strnicmp strncasecmp +#define stricmp strcasecmp +#elif defined(_MSC_VER) +#define strnicmp _strnicmp +#define stricmp _stricmp +#endif + + +/// Replace all occurrences of a character within a string. +/// \param str to modify +/// \param char1 char to be replaced +/// \param char2 replacement char +/// \return pointer to resulting string + +idaman THREAD_SAFE char *ida_export strrpl(char *str, int char1, int char2); + + +/// Get tail of a string +INLINE THREAD_SAFE char *tail(char *str) { return strchr(str, '\0'); } +#ifdef __cplusplus +/// \copydoc tail(char *) +inline THREAD_SAFE const char *tail(const char *str) { return strchr(str, '\0'); } +#endif + + +/// A safer strncpy - makes sure that there is a terminating zero. +/// nb: this function doesn't fill the whole buffer zeroes as strncpy does +/// nb: ssize_t(dstsize) must be > 0 + +idaman THREAD_SAFE char *ida_export qstrncpy(char *dst, const char *src, size_t dstsize); + + +/// A safer stpncpy - returns pointer to the end of the destination +/// nb: ssize_t(dstsize) must be > 0 + +idaman THREAD_SAFE char *ida_export qstpncpy(char *dst, const char *src, size_t dstsize); + + +/// A safer strncat - accepts the size of the 'dst' as 'dstsize' and returns dst +/// nb: ssize_t(dstsize) must be > 0 + +idaman THREAD_SAFE char *ida_export qstrncat(char *dst, const char *src, size_t dstsize); + + +/// Thread-safe version of strtok + +idaman THREAD_SAFE char *ida_export qstrtok(char *s, const char *delim, char **save_ptr); + + +/// Convert the string to lowercase + +idaman THREAD_SAFE char *ida_export qstrlwr(char *str); + + +/// Convert the string to uppercase + +idaman THREAD_SAFE char *ida_export qstrupr(char *str); + + +/// Find one string in another (Case insensitive analog of strstr()). +/// \param s1 string to be searched +/// \param s2 string to search for +/// \return a pointer to the first occurrence of s2 within s1, nullptr if none exists + +idaman THREAD_SAFE const char *ida_export stristr(const char *s1, const char *s2); + + +#ifdef __cplusplus +/// Same as stristr(const char *, const char *) but returns a non-const result +inline char *idaapi stristr(char *s1, const char *s2) { return CONST_CAST(char *)(stristr((const char *)s1, s2)); } +#endif + +/// \defgroup ctype Functions to test ASCII char attributes +/// The is...() functions in ctype.h will misbehave with 'char' argument. We introduce more robust functions. +/// These functions only operate on ascii chars and are intended to be locale-independent. +///@{ +INLINE THREAD_SAFE bool ida_local qisascii(char c) { return (c & ~0x7f) == 0; } +INLINE THREAD_SAFE bool ida_local qisspace(char c) { return qisascii(c) && isspace((uchar)(c)) != 0; } +INLINE THREAD_SAFE bool ida_local qisalpha(char c) { return qisascii(c) && isalpha((uchar)(c)) != 0; } +INLINE THREAD_SAFE bool ida_local qisalnum(char c) { return qisascii(c) && isalnum((uchar)(c)) != 0; } +INLINE THREAD_SAFE bool ida_local qispunct(char c) { return qisascii(c) && ispunct((uchar)(c)) != 0; } +INLINE THREAD_SAFE bool ida_local qislower(char c) { return qisascii(c) && islower((uchar)(c)) != 0; } +INLINE THREAD_SAFE bool ida_local qisupper(char c) { return qisascii(c) && isupper((uchar)(c)) != 0; } +INLINE THREAD_SAFE bool ida_local qisprint(char c) { return qisascii(c) && isprint((uchar)(c)) != 0; } +INLINE THREAD_SAFE bool ida_local qisdigit(char c) { return qisascii(c) && isdigit((uchar)(c)) != 0; } +INLINE THREAD_SAFE bool ida_local qisxdigit(char c) { return qisascii(c) && isxdigit((uchar)(c)) != 0; } +///@} + +/// Get lowercase equivalent of given char +INLINE THREAD_SAFE int ida_local qtolower(char c) { return tolower((uchar)(c)); } +/// Get uppercase equivalent of given char +INLINE THREAD_SAFE int ida_local qtoupper(char c) { return toupper((uchar)(c)); } + +// We forbid using dangerous functions in IDA +#if !defined(USE_DANGEROUS_FUNCTIONS) && !defined(__CODE_CHECKER__) +#undef strcpy +#define strcpy dont_use_strcpy ///< use qstrncpy() +#define stpcpy dont_use_stpcpy ///< use qstpncpy() +#define strncpy dont_use_strncpy ///< use qstrncpy() +#define strcat dont_use_strcat ///< use qstrncat() +#define strncat dont_use_strncat ///< use qstrncat() +#define gets dont_use_gets ///< use qfgets() +#define sprintf dont_use_sprintf ///< use qsnprintf() +#define snprintf dont_use_snprintf ///< use qsnprintf() +#define wsprintfA dont_use_wsprintf ///< use qsnprintf() +#undef strcmpi +#undef strncmpi +#define strcmpi dont_use_strcmpi ///< use stricmp() +#define strncmpi dont_use_strncmpi ///< use strnicmp() +#define getenv dont_use_getenv ///< use qgetenv() +#define setenv dont_use_setenv ///< use qsetenv() +#define putenv dont_use_putenv ///< use qsetenv() +#define strtok dont_use_strrok ///< use qstrtok() +#undef strlwr +#undef strupr +#define strlwr dont_use_strlwr ///< use qstrlwr() +#define strupr dont_use_strupr ///< use qstrupr() +#define waitid dont_use_waitid ///< use qwait() +#define waitpid dont_use_waitpid ///< use qwait() +#define wait dont_use_wait ///< use qwait() +#endif + +//--------------------------------------------------------------------------- +/// \defgroup qsnprintf qsnprintf/qsscanf +/// safer versions of sprintf/sscanf +/// +/// Our definitions of sprintf-like functions support one additional format specifier +/// +/// "%a" which corresponds to ::ea_t +/// +/// Usual optional fields like the width can be used too: %04a. +/// The width specifier will be doubled for 64-bit version. +/// These function return the number of characters _actually written_ to the output string. +/// excluding the terminating zero. (which is different from the snprintf). +/// They always terminate the output with a zero byte (if n > 0). +///@{ +idaman AS_PRINTF(3, 4) THREAD_SAFE int ida_export qsnprintf(char *buffer, size_t n, const char *format, ...); ///< A safer snprintf +idaman AS_SCANF (2, 3) THREAD_SAFE int ida_export qsscanf(const char *input, const char *format, ...); ///< A safer sscanf +idaman AS_PRINTF(3, 0) THREAD_SAFE int ida_export qvsnprintf(char *buffer, size_t n, const char *format, va_list va); ///< See qsnprintf() +idaman AS_SCANF (2, 0) THREAD_SAFE int ida_export qvsscanf(const char *input, const char *format, va_list va); ///< See qsscanf() +idaman AS_PRINTF(3, 4) THREAD_SAFE int ida_export append_snprintf(char *buf, const char *end, const char *format, ...); ///< Append result of sprintf to 'buf' +///@} + +//--------------------------------------------------------------------------- +/// qsnprintf that does not check its arguments. +/// Normally gcc complains about the non-literal formats. However, sometimes we +/// still need to call qsnprintf with a dynamically built format string. +/// OTOH, there are absolutely no checks of the input arguments, so be careful! +GCC_DIAG_OFF(format-nonliteral); +INLINE int nowarn_qsnprintf(char *buf, size_t size, const char *format, ...) +{ + va_list va; + int code; + va_start(va, format); +#ifdef __cplusplus + code = ::qvsnprintf(buf, size, format, va); +#else + code = qvsnprintf(buf, size, format, va); +#endif + va_end(va); + return code; +} +GCC_DIAG_ON(format-nonliteral); + +//--------------------------------------------------------------------------- +/// \def{QMAXPATH, maximum number of characters in a path specification} +/// \def{QMAXFILE, maximum number of characters in a filename specification} +#if defined(__NT__) +#define QMAXPATH 260 +#define QMAXFILE 260 +#else +#define QMAXPATH PATH_MAX +#define QMAXFILE PATH_MAX +#endif + +idaman THREAD_SAFE char *ida_export vqmakepath(char *buf, size_t bufsize, const char *s1, va_list); ///< See qmakepath() + + +/// Construct a path from a null-terminated sequence of strings. +/// \param buf output buffer. Can be == s1, but must not be nullptr +/// \param bufsize size of buffer +/// \param s1 the first path component. it may be followed by more components. +/// the argument list must end with nullptr. +/// \return pointer to result + +idaman THREAD_SAFE char *ida_export qmakepath(char *buf, size_t bufsize, const char *s1, ...); + + +/// Get the current working directory. +/// \param buf output buffer +/// \param bufsize size of buffer +/// This function calls error() if any problem occurs. + +idaman void ida_export qgetcwd(char *buf, size_t bufsize); + + +/// Change the current working directory. +/// \param path the new directory +/// The possible return values are the same as those of the POSIX 'chdir' + +idaman int ida_export qchdir(const char *path); + + +/// Get the directory part of the path. +/// path and buf may point to the same buffer +/// \param[out] buf buffer for the directory part. can be nullptr. +/// \param[out] bufsize size of this buffer +/// \param path path to split +/// \retval true ok +/// \retval false input buffer did not have the directory part. +/// In this case the buffer is filled with "." + +idaman THREAD_SAFE bool ida_export qdirname(char *buf, size_t bufsize, const char *path); + + +/// Construct filename from base name and extension. +/// \param buf output buffer. Can be == base, but must not be nullptr +/// \param bufsize size of buffer +/// \param base base name +/// \param ext extension +/// \return pointer to result + +idaman THREAD_SAFE char *ida_export qmakefile( + char *buf, + size_t bufsize, + const char *base, + const char *ext); + + +/// Split filename into base name and extension. +/// \param file filename, may be changed +/// \param base filled with base part, can be nullptr +/// \param ext filled with extension part, can be nullptr +/// \return the base part + +idaman THREAD_SAFE char *ida_export qsplitfile(char *file, char **base, char **ext); + + +/// Is the file name absolute (not relative to the current dir?) + +idaman THREAD_SAFE bool ida_export qisabspath(const char *file); + + +/// Get the file name part of the given path. +/// \return nullptr if path is nullptr + +idaman THREAD_SAFE const char *ida_export qbasename(const char *path); + +#ifdef __cplusplus +/// Same as qbasename(const char *), but accepts and returns non-const char pointers +inline char *qbasename(char *path) { return CONST_CAST(char *)(qbasename((const char *)path)); } +#endif + + +/// Convert relative path to absolute path + +idaman THREAD_SAFE char *ida_export qmake_full_path(char *dst, size_t dstsize, const char *src); + + +/// Search for a file in the PATH environment variable or the current directory. +/// \param buf output buffer to hold the full file path +/// \param bufsize output buffer size +/// \param file the file name to look for. If the file is an absolute path +/// then buf will return the file value. +/// \param search_cwd search the current directory if file was not found in the PATH +/// \return true if the file was found and false otherwise + +idaman THREAD_SAFE bool ida_export search_path( + char *buf, + size_t bufsize, + const char *file, + bool search_cwd); + +/// Delimiter of directory lists +#if defined(__UNIX__) +#define DELIMITER ":" ///< for Unix - ';' for Windows +#else +#define DELIMITER ";" ///< for MS DOS, Windows, other systems - ':' for Unix +#endif + + +/// Set file name extension unconditionally. +/// \param outbuf buffer to hold the answer. may be the same +/// as the file name. +/// \param bufsize output buffer size +/// \param file the file name +/// \param ext new extension (with or without '.') +/// \return pointer to the new file name + +idaman THREAD_SAFE char *ida_export set_file_ext( + char *outbuf, + size_t bufsize, + const char *file, + const char *ext); + + +/// Get pointer to extension of file name. +/// \param file filename +/// \return pointer to the file extension or nullptr if extension doesn't exist + +idaman THREAD_SAFE const char *ida_export get_file_ext(const char *file); + +/// Does the given file name have an extension? +#ifdef __cplusplus +inline THREAD_SAFE bool idaapi has_file_ext(const char *file) + { return get_file_ext(file) != nullptr; } +#endif + + +/// Set file name extension if none exists. +/// This function appends the extension to a file name. +/// It won't change file name if extension already exists +/// \param buf output buffer +/// \param bufsize size of the output buffer +/// \param file file name +/// \param ext extension (with or without '.') +/// \return pointer to the new file name + +#ifdef __cplusplus +inline THREAD_SAFE char *idaapi make_file_ext( + char *buf, + size_t bufsize, + const char *file, + const char *ext) +{ + if ( has_file_ext(file) ) + return ::qstrncpy(buf, file, bufsize); + else + return set_file_ext(buf, bufsize, file, ext); +} +#endif + + +/// Sanitize the file name. +/// Remove the directory path, and replace wildcards ? * and chars<' ' with _. +/// If the file name is empty, then: +/// - namesize != 0: generate a new temporary name, return true +/// - namesize == 0: return false + +idaman THREAD_SAFE bool ida_export sanitize_file_name(char *name, size_t namesize); + + +/// Match a name against a pattern. +/// Only * and ? wildcards are supported. +/// \param name name to match +/// \param pattern pattern to match against +/// \return true is matched + +bool wildcard_match(const char *name, const char *pattern); + + +/// Match a path against a pattern. +/// **, *, ?, and ranges like [a-zA-Z] are supported. +/// \param name name to match +/// \param _pattern pattern to match against +/// \param flags combination of WPM_... bits +/// \return true is matched + +bool wildcard_path_match(const char *name, const char *_pattern, int flags=0); + +#define WPM_EXPLICIT_DOT 0x01 // match dots at the beginning of a path component explicitly + // example: with this bit set, + // * does not match .hidden + // .* matches .hidden + // * matches file.ext + +/// Match a string with a regular expression. +/// \retval 0 no match +/// \retval 1 match +/// \retval -1 error + +idaman int ida_export regex_match(const char *str, const char *pattern, bool sense_case); + + +//--------------------------------------------------------------------------- +/* input/output */ +/*--------------------------------------------------*/ +#if !defined(__NT__) && !defined(_MSC_VER) +#define O_BINARY 0 +#endif + +#ifndef SEEK_SET +# define SEEK_SET 0 ///< beginning of file +# define SEEK_CUR 1 ///< current position of the file * +# define SEEK_END 2 ///< end of file * +#endif + +#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG) + #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) + #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif + +/*--------------------------------------------------*/ +/* you should use these functions for file i/o */ + +/// Works the same as it's counterpart from Clib. +/// The only difference is that it sets 'qerrno' variable too + +idaman THREAD_SAFE int ida_export qopen(const char *file, int mode); + + +/// Open file with given sharing_mode (use O_RDONLY, O_WRONLY, O_RDWR flags), sets qerrno + +idaman THREAD_SAFE int ida_export qopen_shared(const char *file, int mode, int share_mode); + + +/// Create new file with O_RDWR, sets qerrno + +idaman THREAD_SAFE int ida_export qcreate(const char *file, int stat); + +idaman THREAD_SAFE int ida_export qread(int h, void *buf, size_t n); ///< \copydoc qopen +idaman THREAD_SAFE int ida_export qwrite(int h, const void *buf, size_t n); ///< \copydoc qopen +idaman THREAD_SAFE qoff64_t ida_export qtell(int h); ///< \copydoc qopen +idaman THREAD_SAFE qoff64_t ida_export qseek(int h, int64 offset, int whence); ///< \copydoc qopen +idaman THREAD_SAFE int ida_export qclose(int h); ///< \copydoc qopen +idaman THREAD_SAFE int ida_export qdup(int h); ///< \copydoc qopen +idaman THREAD_SAFE int ida_export qfsync(int h); ///< \copydoc qopen + + +/// Get the file size. +/// This function may return 0 if the file is not found. + +idaman THREAD_SAFE uint64 ida_export qfilesize(const char *fname); + +/// Get file length in bytes. +/// \param h file descriptor +/// \return file length in bytes, -1 if error + +idaman THREAD_SAFE uint64 ida_export qfilelength(int h); + +/// Change file size. +/// \param h file descriptor +/// \param fsize desired size +/// \retval 0 on success +/// \retval -1 otherwise and qerrno is set + +idaman THREAD_SAFE int ida_export qchsize(int h, uint64 fsize); + +/// Create an empty directory. +/// \param file name (or full path) of directory to be created +/// \param mode permissions (only used on unix systems) +/// \return error code +/// \retval 0 success +/// \retval -1 otherwise and qerrno is set + +idaman THREAD_SAFE int ida_export qmkdir(const char *file, int mode); + +/// Delete a directory. +/// \param file name (or full path) of directory to be removed +/// \return error code +/// \retval 0 success +/// \retval -1 otherwise and qerrno is set + +idaman THREAD_SAFE int ida_export qrmdir(const char *file); + +/// Does the given file exist? + +idaman THREAD_SAFE bool ida_export qfileexist(const char *file); + + +/// Does the given path specify a directory? + +idaman THREAD_SAFE bool ida_export qisdir(const char *file); + +/// Get file status +idaman THREAD_SAFE int ida_export qstat(const char *path, struct qstatbuf *buf); +idaman THREAD_SAFE int ida_export qfstat(int fd, struct qstatbuf *buf); + +/// touch: set access and modification times of the file to the current time + +idaman THREAD_SAFE int ida_export qtouchfile(const char *file_name); + +//--------------------------------------------------------------------------- +/// Add a function to be called at exit time + +idaman THREAD_SAFE void ida_export qatexit(void (idaapi *func)(void)); + + +/// Remove a previously added exit-time function + +idaman THREAD_SAFE void ida_export del_qatexit(void (idaapi*func)(void)); + +#endif // SWIG + + +/// Call qatexit functions, shut down UI and kernel, and exit. +/// \param code exit code + +idaman THREAD_SAFE NORETURN void ida_export qexit(int code); + +//--------------------------------------------------------------------------- +#define qmin(a,b) ((a) < (b)? (a): (b)) ///< universal min +#define qmax(a,b) ((a) > (b)? (a): (b)) ///< universal max +#ifdef __cplusplus +template <class T> T qabs(T x) { return x < 0 ? -x : x; } +#else +int qabs(int x) { return x < 0 ? -x : x; } +#endif + +//---------------------------------------------------------------------- +/// Test if 'bit' is set in 'bitmap' +INLINE THREAD_SAFE bool idaapi test_bit(const uchar *bitmap, size_t bit) +{ + return (bitmap[bit/8] & (1<<(bit&7))) != 0; +} +/// Set 'bit' in 'bitmap' +INLINE THREAD_SAFE void idaapi set_bit(uchar *bitmap, size_t bit) +{ + uchar *p = bitmap + bit/8; + *p = (uchar)(*p | (1<<(bit&7))); +} +/// Clear 'bit' in 'bitmap' +INLINE THREAD_SAFE void idaapi clear_bit(uchar *bitmap, size_t bit) +{ + uchar *p = bitmap + bit/8; + *p = (uchar)(*p & ~(1<<(bit&7))); +} +/// Set bits between [low, high) in 'bitmap' +INLINE THREAD_SAFE void idaapi set_bits(uchar *bitmap, size_t low, size_t high) +{ + size_t bit; + for ( bit = low; bit < high; ++bit ) + set_bit(bitmap, bit); +} +/// Clear bits between [low, high) in 'bitmap' +INLINE THREAD_SAFE void idaapi clear_bits(uchar *bitmap, size_t low, size_t high) +{ + size_t bit; + for ( bit = low; bit < high; ++bit ) + clear_bit(bitmap, bit); +} +/// Set first 'nbits' of 'bitmap' +INLINE THREAD_SAFE void idaapi set_all_bits(uchar *bitmap, size_t nbits) +{ + memset(bitmap, 0xFF, nbits/8); + if ( (nbits & 7) != 0 ) + { + uchar *p = bitmap + nbits/8; + *p |= ((1 << (nbits&7))-1); + } +} +/// Clear first 'nbits' of 'bitmap' +INLINE THREAD_SAFE void idaapi clear_all_bits(uchar *bitmap, size_t nbits) +{ + memset(bitmap, 0, nbits/8); + if ( (nbits & 7) != 0 ) + { + uchar *p = bitmap + nbits/8; + *p &= ~((1 << (nbits&7))-1); + } +} + +/// calculate ceil(log2(d64)) or floor(log2(d64)), +/// it returns 0 if d64 == 0 +idaman int ida_export log2ceil(uint64 d64); +idaman int ida_export log2floor(uint64 d64); + +/// count the number of non-zero bits (the population count) +idaman int ida_export bitcount(uint64 x); + +/// count the number of consecutive trailing zero bits +/// (line C++20 std::countr_zero()) +idaman int ida_export bitcountr_zero(uint64 x); + +/// round up or down to a power of 2 +idaman uint32 ida_export round_up_power2(uint32 x); +idaman uint32 ida_export round_down_power2(uint32 x); + +/// is power of 2? (or zero) +template <class T> constexpr bool is_pow2(T val) +{ + return ((val - 1) & val) == 0; +} + +/// round up or down to an arbitrary number +template <class T> T round_up(T val, T base) +{ + T r = val % base; + return r != 0 ? val + base - r : val; +} +template <class T> T round_down(T val, T base) +{ + return val - val % base; +} + +#ifdef __cplusplus +//---------------------------------------------------------------------- +/// Functions to work with intervals +namespace interval +{ + /// max offset of the interval (assume s != 0) + inline THREAD_SAFE constexpr uval_t last(uval_t off, asize_t s) + { + return off + s - 1; + } + /// Do (off1,s1) and (off2,s2) overlap? + inline THREAD_SAFE constexpr bool overlap(uval_t off1, asize_t s1, uval_t off2, asize_t s2) + { + return s1 != 0 && s2 != 0 && off2 <= last(off1, s1) && off1 <= last(off2, s2); + } + /// Does (off1,s1) include (off2,s2)? + inline THREAD_SAFE constexpr bool includes(uval_t off1, asize_t s1, uval_t off2, asize_t s2) + { + return s1 != 0 && off2 >= off1 && last(off2, s2) <= last(off1, s1); + } + /// Does (off1,s1) contain off? + inline THREAD_SAFE constexpr bool contains(uval_t off1, asize_t s1, uval_t off) + { + return s1 != 0 && off >= off1 && off <= last(off1, s1); + } +} +#endif + +//---------------------------------------------------------------------- +#ifdef __cplusplus +/// Shift by the amount exceeding the operand size*8 is undefined by the standard. +/// Indeed, GNUC may decide not to rotate the operand in some cases. +/// We have to check this manually. +template <class T> constexpr T left_shift(const T &value, int shift) +{ + return shift >= sizeof(T)*8 ? 0 : (value << shift); +} +/// \copydoc left_shift +template <class T> constexpr T right_ushift(const T &value, int shift) +{ + return shift >= sizeof(T)*8 ? 0 : (value >> shift); +} +/// \copydoc left_shift +template <class T> constexpr T right_sshift(const T &value, int shift) +{ + return shift >= sizeof(T)*8 ? (value >= 0 ? 0 : -1) : (value >> shift); +} + +/// Rotate left +template<class T> T qrotl(T value, size_t count) +{ + const size_t nbits = sizeof(T) * 8; + count %= nbits; + + T high = value >> (nbits - count); + value <<= count; + value |= high; + return value; +} + +/// Rotate right +template<class T> T qrotr(T value, size_t count) +{ + const size_t nbits = sizeof(T) * 8; + count %= nbits; + + T low = value << (nbits - count); + value >>= count; + value |= low; + return value; +} + +/// Make a mask of 'count' bits +template <class T> constexpr T make_mask(int count) +{ + return left_shift<T>(1, count) - 1; +} + +/// Set a 'bit' in 'where' if 'value' if not zero +template<class T, class U> void idaapi setflag(T &where, U bit, bool cnd) +{ + if ( cnd ) + where = T(where | bit); + else + where = T(where & ~T(bit)); +} + +/// Check that unsigned multiplication does not overflow +template<class T> bool is_mul_ok(T count, T elsize) +{ + CASSERT((T)(-1) > 0); // make sure T is unsigned + if ( elsize == 0 || count == 0 ) + return true; + return count <= ((T)(-1)) / elsize; +} + +/// Check that unsigned or unsigned+signed addition does not overflow +template<class U, class T> bool is_add_ok(U x, T y) +{ + CASSERT((U)(-1) > 0); // make sure U is unsigned + return y >= 0 ? y <= ((U)(-1)) - x : (0-y) <= x; +} + +/// Check that unsigned division is permissible +template <class T> bool is_udiv_ok(T, T b) +{ + CASSERT((T)(-1) > 0); // make sure T is unsigned + return b != 0; // forbid x/0 +} + +/// Check that signed division is permissible +template <class T> bool is_sdiv_ok(T a, T b) +{ + CASSERT((T)(-1) < 0); // make sure T is signed + T minval = left_shift((T)1, sizeof(T)*8-1); + return b != 0 && !(a == minval && b == -1); // forbid x/0, MINVAL/-1 +} + +/// Sign, or zero-extend V depending on the high bit of V. +/// V is considered to be of NBITS bits. +template <typename T> +inline THREAD_SAFE constexpr T extend_sign_bits(T v, int nbits) +{ + if ( nbits <= 0 ) + return 0; + if ( nbits >= sizeof(T) * 8 ) + return v; + T signbit = T(1) << (nbits - 1); + T mask = (signbit << 1) - 1; + if ( (v & signbit) != 0 ) + v |= ~mask; + else + v &= mask; + return v; +} + +/// \def{OPERATOR_NEW, GCC does not check for an integer overflow in 'operator new[]'. We have to do it +/// ourselves. Please note that 'char' arrays can still be allocated with +/// plain 'operator new'.} +#ifdef __GNUC__ +# define OPERATOR_NEW(type, count) (is_mul_ok(size_t(count), sizeof(type)) \ + ? new type[count] \ + : (type *)qalloc_or_throw(BADMEMSIZE)) +#else +# define OPERATOR_NEW(type, count) new type[count] +#endif + +#endif // __cplusplus + +//------------------------------------------------------------------------- +/// Sign-, or zero-extend the value 'v' to occupy 64 bits. +/// The value 'v' is considered to be of size 'nbytes'. + +idaman uint64 ida_export extend_sign(uint64 v, int nbytes, bool sign_extend); + + +/// We cannot use multi-character constants because they are not portable - use this macro instead +#define MC2(c1, c2) ushort(((c2)<<8)|c1) +#define MC3(c1, c2, c3) uint32(((((c3)<<8)|(c2))<<8)|c1) ///< \copydoc MC2 +#define MC4(c1, c2, c3, c4) uint32(((((((c4)<<8)|(c3))<<8)|(c2))<<8)|c1) ///< \copydoc MC2 + + +//--------------------------------------------------------------------------- +/// Read at most 4 bytes from file. +/// \param h file handle +/// \param res value read from file +/// \param size size of value in bytes (1,2,4) +/// \param mf is MSB first? +/// \return 0 on success, nonzero otherwise + +idaman THREAD_SAFE int ida_export readbytes(int h, uint32 *res, int size, bool mf); + + +/// Write at most 4 bytes to file. +/// \param h file handle +/// \param l value to write +/// \param size size of value in bytes (1,2,4) +/// \param mf is MSB first? +/// \return 0 on success, nonzero otherwise + +idaman THREAD_SAFE int ida_export writebytes(int h, uint32 l, int size, bool mf); + + +/// Read a 2 byte entity from a file. +/// \param h file handle +/// \param res value read from file +/// \param mf is MSB first? +/// \return 0 on success, nonzero otherwise + +idaman THREAD_SAFE int ida_export read2bytes(int h, uint16 *res, bool mf); + +#define read4bytes(h, res, mf) readbytes(h, res, 4, mf) ///< see readbytes() +#define write2bytes(h, l, mf) writebytes(h, l, 2, mf) ///< see writebytes() +#define write4bytes(h, l, mf) writebytes(h, l, 4, mf) ///< see writebytes() + +//--------------------------------------------------------------------------- +/// \fn{uint32 swap32(uint32 x), Switch endianness of given value} +/// \fn{ushort swap16(ushort x), \copydoc swap32} +/// \def{swap32, Switch endianness of given value} +/// \def{swap16, \copydoc swap32} +#ifdef __cplusplus +# ifndef swap32 +inline THREAD_SAFE constexpr uint32 swap32(uint32 x) + { return (x>>24) | (x<<24) | ((x>>8) & 0x0000FF00) | ((x<<8) & 0x00FF0000); } +# endif +# ifndef swap16 +inline THREAD_SAFE constexpr ushort swap16(ushort x) + { return ushort((x<<8) | (x>>8)); } +# endif +#else +# ifndef swap32 +# define swap32(x) uint32((x>>24) | (x<<24) | ((x>>8) & 0x0000FF00) | ((x<<8) & 0x00FF0000)) +# endif +# ifndef swap16 +# define swap16(x) ushort((x<<8) | (x>>8)) +# endif +#endif + +/// \def{swapea, Switch endianness of an ::ea_t value} +#ifdef __EA64__ +#define swapea swap64 +#else +#define swapea swap32 +#endif + +/// \def{qhtons, \copydoc swap32} +/// \def{qntohs, \copydoc swap32} +/// \def{qhtonl, \copydoc swap32} +/// \def{qntohl, \copydoc swap32} +#if __MF__ +#define qhtonl(x) (x) +#define qntohl(x) (x) +#define qhtons(x) (x) +#define qntohs(x) (x) +#else +#define qhtons(x) swap16(x) +#define qntohs(x) swap16(x) +#define qhtonl(x) swap32(x) +#define qntohl(x) swap32(x) +#endif + + +/// Swap endianness of a given value in memory. +/// \param dst result of swap +/// \param src value to be swapped +/// \param size size of value: can be 1, 2, 4, 8, or 16. +/// For any other values of size this function does nothing + +idaman THREAD_SAFE void ida_export swap_value(void *dst, const void *src, int size); + + +idaman THREAD_SAFE void ida_export reloc_value(void *value, int size, adiff_t delta, bool mf); + + +/// Rotate left - can be used to rotate a value to the right if the count is negative. +/// \param x value to rotate +/// \param count shift amount +/// \param bits number of bits to rotate (32 will rotate a dword) +/// \param offset number of first bit to rotate. +/// (bits=8 offset=16 will rotate the third byte of the value) +/// \return the rotated value + +idaman THREAD_SAFE uval_t ida_export rotate_left(uval_t x, int count, size_t bits, size_t offset); + + +#ifdef __cplusplus +/// Swap 2 objects of the same type using memory copies +template <class T> inline THREAD_SAFE void qswap(T &a, T &b) +{ + char temp[sizeof(T)]; + memcpy(&temp, &a, sizeof(T)); + memcpy(&a, &b, sizeof(T)); + memcpy(&b, &temp, sizeof(T)); +} + +//--------------------------------------------------------------------------- +#ifndef SWIG +/// \defgroup pack Pack/Unpack +/// Functions for packing and unpacking values +//{ + +/// Pack a byte into a character string. +/// This function encodes numbers using an encoding similar to UTF. +/// The smaller the number, the better the packing. +/// \param ptr pointer to output buffer +/// \param end pointer to end of output buffer +/// \param x value to pack +/// \return pointer to end of resulting string + +THREAD_SAFE inline uchar *idaapi pack_db(uchar *ptr, uchar *end, uchar x) +{ + if ( ptr < end ) + *ptr++ = x; + return ptr; +} + + +/// Unpack a byte from a character string, pack_db() + +THREAD_SAFE inline uchar idaapi unpack_db(const uchar **pptr, const uchar *end) +{ + const uchar *ptr = *pptr; + uchar x = 0; + if ( ptr < end ) + x = *ptr++; + *pptr = ptr; + return x; +} + +idaman THREAD_SAFE uchar *ida_export pack_dw(uchar *ptr, uchar *end, uint16 x); ///< pack a word, see pack_db() +idaman THREAD_SAFE uchar *ida_export pack_dd(uchar *ptr, uchar *end, uint32 x); ///< pack a double word, see pack_db() +idaman THREAD_SAFE uchar *ida_export pack_dq(uchar *ptr, uchar *end, uint64 x); ///< pack a quadword, see pack_db() +idaman THREAD_SAFE ushort ida_export unpack_dw(const uchar **pptr, const uchar *end); ///< unpack a word, see unpack_db() +idaman THREAD_SAFE uint32 ida_export unpack_dd(const uchar **pptr, const uchar *end); ///< unpack a double word, see unpack_db() +idaman THREAD_SAFE uint64 ida_export unpack_dq(const uchar **pptr, const uchar *end); ///< unpack a quadword, see unpack_db() + +/// Pack an ea value into a character string, see pack_dd()/pack_dq() + +THREAD_SAFE inline uchar *pack_ea(uchar *ptr, uchar *end, ea_t ea) +{ +#ifdef __EA64__ + return pack_dq(ptr, end, ea); +#else + return pack_dd(ptr, end, ea); +#endif +} + +/// Unpack an ea value, see unpack_dd()/unpack_dq() + +THREAD_SAFE inline ea_t unpack_ea(const uchar **ptr, const uchar *end) +{ +#ifdef __EA64__ + return unpack_dq(ptr, end); +#else + return unpack_dd(ptr, end); +#endif +} + +/// Unpack an ea value (always use 64bit, use delta 1) +THREAD_SAFE inline ea64_t unpack_ea64(const uchar **ptr, const uchar *end) +{ + return unpack_dq(ptr, end) - 1; +} + + +/// Unpack an object of a known size. +/// \param destbuf output buffer +/// \param destsize size of output buffer +/// \param pptr pointer to packed object +/// \param end pointer to end of packed object +/// \return pointer to the destination buffer. +/// if any error, returns nullptr. + +THREAD_SAFE inline void *idaapi unpack_obj( + void *destbuf, + size_t destsize, + const uchar **pptr, + const uchar *end) +{ + const uchar *src = *pptr; + const uchar *send = src + destsize; + if ( send < src || send > end ) + return nullptr; + *pptr = send; + return memcpy(destbuf, src, destsize); +} + + +/// Unpack an object of an unknown size (packed with append_buf()). +/// \param pptr pointer to packed object +/// \param end pointer to end of packed object +/// \return pointer to the destination buffer, which is allocated in the dynamic memory. \n +/// the caller should use qfree() to deallocate it. \n +/// if any error, returns nullptr. \n +/// NB: zero size objects will return nullptr too. + +THREAD_SAFE inline void *idaapi unpack_buf(const uchar **pptr, const uchar *end) +{ + size_t size = unpack_dd(pptr, end); + if ( size == 0 ) + return nullptr; + const uchar *src = *pptr; + const uchar *srcend = src + size; + if ( srcend < src || srcend > end ) + return nullptr; + void *dst = qalloc(size); + if ( dst != nullptr ) + { + memcpy(dst, src, size); + *pptr = srcend; + } + return dst; +} + + +/// In-place version of unpack_obj(). +/// It does not copy any data. It just returns a pointer to the object in the packed string. +/// If any error, it returns nullptr. + +THREAD_SAFE inline const void *idaapi unpack_obj_inplace( + const uchar **pptr, + const uchar *end, + size_t objsize) +{ + const uchar *ret = *pptr; + const uchar *rend = ret + objsize; + if ( rend < ret || rend > end ) + return nullptr; + *pptr = rend; + return ret; +} + + +/// In-place version of unpack_buf(). +/// It does not copy any data. It just returns a pointer to the object in the packed string. +/// If any error, it returns nullptr. + +THREAD_SAFE inline const void *idaapi unpack_buf_inplace( + const uchar **pptr, + const uchar *end) +{ + size_t objsize = unpack_dd(pptr, end); + return unpack_obj_inplace(pptr, end, objsize); +} + + +/// Pack a string. +/// \param ptr pointer to output buffer +/// \param end pointer to end of output buffer +/// \param x string to pack. If nullptr, empty string is packed +/// \param len number of chars to pack. If 0, the length of given string is used +/// \return pointer to end of packed string + +idaman THREAD_SAFE uchar *ida_export pack_ds( + uchar *ptr, + uchar *end, + const char *x, + size_t len=0); + + +/// Unpack a string. +/// \param pptr pointer to packed string +/// \param end pointer to end of packed string +/// \param empty_null if true, then return nullptr for empty strings. \n +/// otherwise return an empty string (not nullptr). +/// \return pointer to unpacked string. \n +/// this string will be allocated in dynamic memory. \n +/// the caller should use qfree() to deallocate it. + +idaman THREAD_SAFE char *ida_export unpack_ds( + const uchar **pptr, + const uchar *end, + bool empty_null); + +/// Unpack a string. +/// \param dst pointer to buffer string will be copied to +/// \param dstsize buffer size +/// \param pptr pointer to packed string +/// \param end pointer to end of packed string +/// \return success +THREAD_SAFE inline bool unpack_ds_to_buf( + char *dst, + size_t dstsize, + const uchar **pptr, + const uchar *end) +{ + const void *buf = unpack_buf_inplace(pptr, end); + if ( buf == nullptr ) + return false; + size_t size = *pptr - (const uchar *)buf; + if ( size >= dstsize ) + size = dstsize - 1; + memcpy(dst, buf, size); + dst[size] = '\0'; + return true; +} + + +/// Unpack an LEB128 encoded (DWARF-3 style) signed/unsigned value. +/// Do not use this function directly - see \ref unp_templates + +idaman THREAD_SAFE bool ida_export unpack_xleb128( + void *res, + int nbits, + bool is_signed, + const uchar **pptr, + const uchar *end); + +/// \defgroup unp_templates Template unpacking +/// Template functions that can unpack values +///@{ + +template <class T> +inline THREAD_SAFE bool unpack_uleb128(T *res, const uchar **pptr, const uchar *end) +{ + CASSERT((T)(-1) > 0); // make sure T is unsigned + return unpack_xleb128(res, sizeof(T)*8, false, pptr, end); +} + +template <class T> +inline THREAD_SAFE bool unpack_sleb128(T *res, const uchar **pptr, const uchar *end) +{ + CASSERT((T)(-1) < 0); // make sure T is signed + return unpack_xleb128(res, sizeof(T)*8, true, pptr, end); +} + +///@} Template unpacking functions + +// packed sizes +/// \cond +static constexpr int ea_packed_size = sizeof(ea_t) + sizeof(ea_t)/4; // 5 or 10 bytes +static constexpr int dq_packed_size = 10; +static constexpr int dd_packed_size = 5; +static constexpr int dw_packed_size = 3; +/// \endcond + +inline THREAD_SAFE int ds_packed_size(const char *s) { return s ? int(strlen(s)+dd_packed_size) : 1; } + +//---------------------------------------------------------------------------- +inline THREAD_SAFE constexpr int dw_size(uchar first_byte) +{ + return (first_byte & 0x80) == 0 ? 1 + : (first_byte & 0xC0) == 0xC0 ? 3 + : 2; +} + +//---------------------------------------------------------------------------- +inline THREAD_SAFE constexpr int dd_size(uchar first_byte) +{ + return (first_byte & 0x80) == 0x00 ? 1 + : (first_byte & 0xC0) != 0xC0 ? 2 + : (first_byte & 0xE0) == 0xE0 ? 5 + : 4; +} + +//---------------------------------------------------------------------------- +// unpack data from an object which must have the following functions: +// ssize_t read(void *buf, size_t count) +// bool eof() - return true if there is no more data to read +template <class T> +inline THREAD_SAFE uchar extract_db(T &v) +{ + uchar x = 0; + v.read(&x, 1); + return x; +} + +template <class T> +inline THREAD_SAFE void *extract_obj(T &v, void *destbuf, size_t destsize) +{ + if ( destsize == 0 ) + return nullptr; + return v.read(destbuf, destsize) == destsize ? destbuf : nullptr; +} + +template <class T> +inline THREAD_SAFE uint16 extract_dw(T &v) +{ + uchar packed[dw_packed_size]; + packed[0] = extract_db(v); + int psize = dw_size(packed[0]); + extract_obj(v, &packed[1], psize-1); + const uchar *ptr = packed; + return unpack_dw(&ptr, packed + psize); +} + +template <class T> +inline THREAD_SAFE uint32 extract_dd(T &v) +{ + uchar packed[dd_packed_size]; + packed[0] = extract_db(v); + int psize = dd_size(packed[0]); + extract_obj(v, &packed[1], psize-1); + const uchar *ptr = packed; + return unpack_dd(&ptr, packed + psize); +} + +template <class T> +inline THREAD_SAFE uint64 extract_dq(T &v) +{ + uint32 l = extract_dd(v); + uint32 h = extract_dd(v); + return make_uint64(l, h); +} + +template <class T> +inline THREAD_SAFE ea_t extract_ea(T &v) +{ +#ifdef __EA64__ + return extract_dq(v); +#else + return extract_dd(v); +#endif +} + +template <class T> +inline THREAD_SAFE void *extract_buf(T &v, size_t size) +{ + void *buf = qalloc(size); + if ( buf == nullptr ) + return nullptr; + return extract_obj(v, buf, size); +} + +template <class T> +inline THREAD_SAFE void *extract_array(T &v, size_t *sz, size_t maxsize) +{ + size_t size = extract_dd(v); + if ( size == 0 || size > maxsize ) + return nullptr; + *sz = size; + return extract_buf(v, size); +} + +inline const char *unpack_str(const uchar **pptr, const uchar *end) +{ // zero terminated string, return inplace ptr + const uchar *ptr = *pptr; + const uchar *str = ptr; + do + if ( ptr >= end ) + return nullptr; // no terminating zero? + while ( *ptr++ != '\0' ); + *pptr = ptr; + return (char*)str; +} + +///@} Packing functions +#endif // SWIG +#endif // cplusplus + +/// \name Safe buffer append +/// In the following macros, 'buf' must be always less than 'end'. +/// When we run up to the end, we put a 0 there and don't increase buf anymore +///@{ +/// Append a character to the buffer checking the buffer size +#define APPCHAR(buf, end, chr) \ + do \ + { \ + char __chr = (chr); \ + QASSERT(518, (buf) < (end)); \ + *(buf)++ = __chr; \ + if ( (buf) >= (end) ) \ + { \ + (buf) = (end)-1; \ + (buf)[0] = '\0'; \ + } \ + } while (0) + +/// Put a zero byte at buffer. +/// NB: does not increase buf pointer! +#define APPZERO(buf, end) \ + do \ + { \ + QASSERT(519, (buf) < (end)); \ + *(buf) = '\0'; \ + } while (0) + +/// Append a string to the buffer checking the buffer size +#define APPEND(buf, end, name) \ + do \ + { \ + QASSERT(520, (buf) < (end)); \ + const char *__ida_in = (name); \ + while ( true ) \ + { \ + if ( (buf) == (end)-1 ) \ + { \ + (buf)[0] = '\0'; \ + break; \ + } \ + if ( (*(buf) = *__ida_in++) == '\0' ) \ + break; \ + (buf)++; \ + } \ + } while ( 0 ) +///@} + +/// qalloc() 'size' bytes, and throw a "not enough memory" error if failed + +idaman THREAD_SAFE void *ida_export qalloc_or_throw(size_t size); + + +/// qrealloc() 'ptr' by 'size', and throw a "not enough memory" error if failed + +idaman THREAD_SAFE void *ida_export qrealloc_or_throw(void *ptr, size_t size); + + +/// Change capacity of given qvector. +/// \param vec a pointer to a qvector +/// \param old a pointer to the qvector's array +/// \param cnt number of elements to reserve +/// \param elsize size of each element +/// \return a pointer to the newly allocated array + +idaman THREAD_SAFE void *ida_export qvector_reserve(void *vec, void *old, size_t cnt, size_t elsize); + +#if defined(__cplusplus) + /// \def{PLACEMENT_DELETE, bcc complains about placement delete} + /// \def{DEFINE_MEMORY_ALLOCATION_FUNCS, + /// Convenience macro to declare memory allocation functions. + /// It must be used for all classes that can be allocated/freed by the IDA kernel.} + #if defined(SWIG) + #define DEFINE_MEMORY_ALLOCATION_FUNCS() + #else + #define PLACEMENT_DELETE void operator delete(void *, void *) {} + #define DEFINE_MEMORY_ALLOCATION_FUNCS() \ + void *operator new (size_t _s) { return qalloc_or_throw(_s); } \ + void *operator new[](size_t _s) { return qalloc_or_throw(_s); } \ + void *operator new(size_t /*size*/, void *_v) { return _v; } \ + void operator delete (void *_blk) { qfree(_blk); } \ + void operator delete[](void *_blk) { qfree(_blk); } \ + PLACEMENT_DELETE + #endif + +/// Macro to declare standard inline comparison operators +#define DECLARE_COMPARISON_OPERATORS(type) \ + bool operator==(const type &r) const { return compare(r) == 0; } \ + bool operator!=(const type &r) const { return compare(r) != 0; } \ + bool operator< (const type &r) const { return compare(r) < 0; } \ + bool operator> (const type &r) const { return compare(r) > 0; } \ + bool operator<=(const type &r) const { return compare(r) <= 0; } \ + bool operator>=(const type &r) const { return compare(r) >= 0; } + +/// Macro to declare comparisons for our classes. +/// All comparison operators call the compare() function which returns -1/0/1 +#define DECLARE_COMPARISONS(type) \ + DECLARE_COMPARISON_OPERATORS(type) \ + int compare(const type &r) const + +// Internal declarations to detect movable types +/// \cond +// Can we move around objects of type T using simple memcpy/memmove?. +// This class can be specialized for any type T to improve qvector's behavior. +template <class T> struct ida_movable_type +{ + static constexpr bool value = std::is_pod<T>::value; +}; +#define DECLARE_TYPE_AS_MOVABLE(T) template <> struct ida_movable_type<T> { static constexpr bool value = true; } +template <class T> inline constexpr THREAD_SAFE bool may_move_bytes(void) +{ + return ida_movable_type<T>::value; +} +/// \endcond + +/// Move data down in memory. +/// \param dst destination ptr +/// \param src source ptr +/// \param cnt number of elements to move +template<class T> +inline void shift_down(T *dst, T *src, size_t cnt) +{ + if ( may_move_bytes<T>() ) + { + memmove(dst, src, cnt*sizeof(T)); + } + else + { + ssize_t s = cnt; + while ( --s >= 0 ) + { + new(dst) T(std::move(*src)); + src->~T(); + ++src; + ++dst; + } + } +} +/// Move data up in memory. +/// \param dst destination ptr +/// \param src source ptr +/// \param cnt number of elements to move +template<class T> +inline void shift_up(T *dst, T *src, size_t cnt) +{ + if ( may_move_bytes<T>() ) + { + memmove(dst, src, cnt*sizeof(T)); + } + else + { + ssize_t s = cnt; + dst += s; + src += s; + while ( --s >= 0 ) + { + --src; + --dst; + new(dst) T(std::move(*src)); + src->~T(); + } + } +} + + +//--------------------------------------------------------------------------- +/// Reimplementation of vector class from STL. +/// Only the most essential functions are implemented. \n +/// The vector container accepts objects agnostic to their positions +/// in the memory because it will move them arbitrarily (realloc and memmove). \n +/// The reason why we have it is because it is not compiler dependent +/// (hopefully) and therefore can be used in IDA API. +template <class T> class qvector +{ + T *array; + size_t n, alloc; + friend void *ida_export qvector_reserve(void *vec, void *old, size_t cnt, size_t elsize); + /// Copy contents of given qvector into this one + qvector<T> &assign(const qvector<T> &x) + { + size_t _newsize = x.n; + if ( _newsize > 0 ) + { + array = (T*)qalloc_or_throw(_newsize * sizeof(T)); + alloc = _newsize; + copy_range(x, 0, _newsize); + } + return *this; + } + /// Copies a range of elements from another qvector. + void copy_range(const qvector<T> &x, size_t from, size_t _newsize) + { + if ( std::is_trivially_copyable<T>::value ) + { + memcpy(array + from, x.array + from, (_newsize-from)*sizeof(T)); + } + else + { + for ( size_t i = from; i < _newsize; i++ ) + new(array+i) T(x.array[i]); + } + n = _newsize; + } + void free_memory() + { + qfree(array); + array = nullptr; + alloc = 0; + } + /// Resizes to a smaller size, destroying elements if needed. + void resize_less(size_t _newsize) + { + if ( !std::is_trivially_destructible<T>::value ) + { + size_t _size = n; + for ( size_t i = _newsize; i < _size; i++ ) + array[i].~T(); + } + n = _newsize; +#ifdef TESTABLE_BUILD + /// This helps to find use-after-frees in empty containers: + if ( n == 0 ) + free_memory(); +#endif + } + /// Resizes to a bigger size, and zeroes the new elements (they + /// should be of a std::is_trivially_constructible type). + void resize_more_trivial(size_t _newsize) + { + reserve(_newsize); + memset(array+n, 0, (_newsize-n)*sizeof(T)); + n = _newsize; + } + /// Resizes to a bigger size with a given element. + void resize_more(size_t _newsize, const T &x) + { + reserve(_newsize); + for ( size_t i = n; i < _newsize; i++ ) + new(array+i) T(x); + n = _newsize; + } + + bool ref_within_range(const T &x) const + { + const T *const p = &x; + return p >= array && p < array + alloc; + } + +public: + typedef T value_type; ///< the type of objects contained in this qvector + /// Constructor + qvector(void) : array(nullptr), n(0), alloc(0) {} + /// Constructor - creates a new qvector identical to 'x' + qvector(const qvector<T> &x) : array(nullptr), n(0), alloc(0) { assign(x); } +#ifndef SWIG + /// Move constructor + qvector(qvector<T> &&x) noexcept + { + array = x.array; x.array = nullptr; + n = x.n; x.n = 0; + alloc = x.alloc; x.alloc = 0; + } +#endif + /// Destructor + ~qvector(void) + { + if ( array != nullptr ) + { + resize_less(0); + free_memory(); + } + } + DEFINE_MEMORY_ALLOCATION_FUNCS() + /// Append a new element to the end the qvector. + void push_back(const T &x) + { + TB_QASSERT(1907, !ref_within_range(x)); + T val(x); + reserve(n+1); + new (array+n) T(std::move(val)); + ++n; + } +#ifndef SWIG + /// Append a new element to the end the qvector with a move semantics. + void push_back(T &&x) + { + bool aliasing = ref_within_range(x); + TB_QASSERT(1977, !aliasing); + if ( aliasing ) + { + // If x aliases an element inside the same container and + // reserve triggers a reallocation, the reference x becomes + // dangling immediately. Any attempt to read/move from x + // after reserve would be UB. + // Therefore, we capture the value first while x still refers + // to a valid object. + T val(std::move(x)); + reserve(n+1); + new (array+n) T(std::move(val)); + } + else + { + reserve(n+1); + new (array+n) T(std::move(x)); + } + ++n; + } +#endif + +#ifndef SWIG + /// Construct and append a new element to the end the qvector with a + /// forwarding semantics. + template <typename... Args> + void emplace_back(Args && ... args) + { + // If any args... are references to elements inside this same container, + // and reserve reallocates, those references dangle before we use them. + reserve(n+1); + new (array+n) T(std::forward<Args>(args)...); + ++n; + } +#endif + + /// Append a new empty element to the end of the qvector. + /// \return a reference to this new element + T &push_back(void) + { + reserve(n+1); + T *ptr = array + n; + new (ptr) T; + ++n; + return *ptr; + } + /// Remove the last element in the qvector + void pop_back(void) + { + if ( n > 0 ) + { + array[--n].~T(); +#ifdef TESTABLE_BUILD + /// This helps to find use-after-frees in empty containers: + if ( n == 0 ) + free_memory(); +#endif + } + } + size_t size(void) const { return n; } ///< Get the number of elements in the qvector + bool empty(void) const { return n == 0; } ///< Does the qvector have 0 elements? + const T &operator[](size_t _idx) const { return array[_idx]; } ///< Allows use of typical c-style array indexing for qvectors + T &operator[](size_t _idx) { return array[_idx]; } ///< Allows use of typical c-style array indexing for qvectors + const T &at(size_t _idx) const { return array[_idx]; } ///< Get element at index '_idx' + T &at(size_t _idx) { return array[_idx]; } ///< Get element at index '_idx' + const T &front(void) const { return array[0]; } ///< Get the first element in the qvector + T &front(void) { return array[0]; } ///< Get the first element in the qvector + const T &back(void) const { return array[n-1]; } ///< Get the last element in the qvector + T &back(void) { return array[n-1]; } ///< Get the last element in the qvector + /// Destroy all elements but do not free memory + void qclear(void) + { + resize_less(0); + } + /// Destroy all elements and free memory + void clear(void) + { + if ( array != nullptr ) + { + qclear(); + free_memory(); + } + } + /// Allow assignment of one qvector to another using '=' + qvector<T> &operator=(const qvector<T> &x) + { + qvector<T> copy(x); + return operator=(std::move(copy)); + } +#ifndef SWIG + /// Move assignment operator + qvector<T> &operator=(qvector<T> &&x) noexcept + { + qvector<T> m(std::forward<qvector<T>>(x)); + clear(); + array = m.array; m.array = nullptr; + n = m.n; m.n = 0; + alloc = m.alloc; m.alloc = 0; + return *this; + } +#endif + /// Resize to the given size. + /// If the given size (_newsize) is less than the current size (n) of the qvector, then the last n - _newsize elements are simply deleted. \n + /// If the given size is greater than the current size, the qvector is grown to _newsize, and the last _newsize - n elements will be filled with copies of 'x'. \n + /// If the given size is equal to the current size, this function does nothing. + void resize(size_t _newsize, const T &x) + { +#ifdef TESTABLE_BUILD + QASSERT(1908, !ref_within_range(x)); +#endif + if ( _newsize < n ) + resize_less(_newsize); + else if ( _newsize > n ) + { + if ( ref_within_range(x) ) + resize_more(_newsize, T(x)); + else + resize_more(_newsize, x); + } + } + /// Same as resize(size_t, const T &), but extra space is filled with empty elements + void resize(size_t _newsize) + { + if ( std::is_trivially_constructible<T>::value && _newsize > n ) + resize_more_trivial(_newsize); + else if ( _newsize == n ) + return; +#if __cplusplus >= 201703 + else if constexpr ( std::is_default_constructible<T>::value ) + resize(_newsize, T()); + else if ( _newsize < n ) + resize_less(_newsize); + else + QASSERT(3396, false); +#else + resize(_newsize, T()); +#endif + } +#ifndef SWIG + // Resize the array but do not initialize elements + void resize_noinit(size_t _newsize) + { + CASSERT(std::is_trivially_constructible<T>::value); + CASSERT(std::is_trivially_destructible<T>::value); + reserve(_newsize); + n = _newsize; + } +#endif + /// Add an element to the end of the qvector, which will be a new T() if x is not given + void grow(const T &x=T()) + { +#ifdef TESTABLE_BUILD + QASSERT(1909, !ref_within_range(x)); +#endif + T val(x); + reserve(n+1); + new(array+n) T(std::move(val)); + ++n; + } + /// Get the number of elements that this qvector can contain - not the same + /// as the number of elements currently in the qvector (size()) + size_t capacity(void) const { return alloc; } + /// Increase the capacity of the qvector. If cnt is not greater than the current capacity + /// this function does nothing. + void reserve(size_t cnt) + { + if ( cnt > alloc ) + { + if ( may_move_bytes<T>() ) + { + array = (T *)qvector_reserve(this, array, cnt, sizeof(T)); + } + else + { + size_t old_alloc = alloc; + T *new_array = (T *)qvector_reserve(this, nullptr, cnt, sizeof(T)); + size_t new_alloc = alloc; + alloc = old_alloc; + shift_down(new_array, array, n); + qfree(array); + array = new_array; + alloc = new_alloc; + } + } + } + /// Shrink the capacity down to the current number of elements + void truncate(void) + { + if ( alloc > n ) + { + array = (T*)qrealloc(array, n*sizeof(T)); // should not fail + alloc = n; + } + } + /// Replace all attributes of this qvector with that of 'r', and vice versa. + /// Effectively sets this = r and r = this without copying/allocating any memory. + void swap(qvector<T> &r) noexcept + { + T *array2 = array; + size_t n2 = n; + size_t alloc2 = alloc; + + array = r.array; + n = r.n; + alloc = r.alloc; + + r.array = array2; + r.n = n2; + r.alloc = alloc2; + } + /// Empty the qvector and return a pointer to it's contents. + /// The caller must free the result of this function + T *extract(void) + { + truncate(); + alloc = 0; + n = 0; + T *res = array; + array = nullptr; + return res; + } + /// Populate the qvector with dynamic memory. + /// The qvector must be empty before calling this method! + void inject(T *s, size_t len) + { + array = s; + alloc = len; + n = len; + } + /// Allow ability to test the equality of two qvectors using '=='. + bool operator == (const qvector<T> &r) const + { + if ( n != r.n ) + return false; + for ( size_t i=0; i < n; i++ ) + if ( array[i] != r[i] ) + return false; + return true; + } + /// Allow ability to test equality of two qvectors using '!=' + bool operator != (const qvector<T> &r) const { return !(*this == r); } + + typedef T *iterator; + typedef const T *const_iterator; + + iterator begin(void) { return array; } ///< Get an iterator that points to the first element in the qvector + iterator end(void) { return array + n; } ///< Get an iterator that points to the end of the qvector (NOT the last element) + const_iterator begin(void) const { return array; } ///< Get a const iterator that points to the first element in the qvector + const_iterator end(void) const { return array + n; } ///< Get a const iterator that points to the end of the qvector (NOT the last element) + /// Insert an element into the qvector at a specified position. + /// \param it an iterator that points to the desired position of the new element + /// \param x the element to insert + /// \return an iterator that points to the newly inserted element + iterator insert(iterator it, const T &x) + { +#ifdef TESTABLE_BUILD + QASSERT(1910, !ref_within_range(x)); +#endif + T val(x); + size_t idx = it - array; + reserve(n+1); + T *p = array + idx; + size_t rest = end() - p; + shift_up(p+1, p, rest); + new(p) T(std::move(val)); + n++; + return iterator(p); + } +#ifndef SWIG + /// Insert an element into the qvector with a move semantics. + iterator insert(iterator it, T &&x) + { +#ifdef TESTABLE_BUILD + QASSERT(1978, !ref_within_range(x)); +#endif + T val(std::forward<T>(x)); + size_t idx = it - array; + reserve(n+1); + T *p = array + idx; + size_t rest = end() - p; + shift_up(p+1, p, rest); + new(p) T(std::move(val)); + n++; + return iterator(p); + } +#endif + /// Insert a several elements to the qvector at a specified position. + /// \param it position at which new elements will be inserted + /// \param first pointer to first element to be inserted + /// \param last pointer to end of elements to be inserted (the element pointed to by 'last' will not be included) + /// \return an iterator that points to the first newly inserted element. + template <class it2> iterator insert(iterator it, it2 first, it2 last) + { + size_t cnt = last - first; + if ( cnt == 0 ) + return it; + + size_t idx = it - array; + reserve(n+cnt); + T *p = array + idx; + size_t rest = end() - p; + shift_up(p+cnt, p, rest); + while ( first != last ) + { + new(p) T(*first); + ++p; + ++first; + } + n += cnt; + return iterator(array+idx); + } + /// Remove an element from the qvector. + /// \param it pointer to element to be removed + /// \return pointer to the element that took its place + iterator erase(iterator it) + { + it->~T(); + size_t rest = end() - it - 1; + shift_down(it, it+1, rest); + n--; + return it; + } + /// Remove a subset of the qvector. + /// \param first pointer to head of subset to be removed + /// \param last pointer to end of subset to be removed (element pointed to by last will not be removed) + /// \return a pointer to the element that took the place of 'first' + iterator erase(iterator first, iterator last) + { + for ( T *p=first; p != last; ++p ) + p->~T(); + size_t rest = end() - last; + shift_down(first, last, rest); + n -= last - first; + return first; + } + // non-standard extensions: + /// Find an element in the qvector. + /// \param x element to find + /// \return an iterator that points to the first occurrence of 'x' + iterator find(const T &x) + { + iterator p; + const_iterator e; + for ( p=begin(), e=end(); p != e; ++p ) + if ( x == *p ) + break; + return p; + } + /// \copydoc find + const_iterator find(const T &x) const + { + const_iterator p, e; + for ( p=begin(), e=end(); p != e; ++p ) + if ( x == *p ) + break; + return p; + } +#ifndef SWIG + /// Find index of the specified value or return -1 + ssize_t index(const T &x) const + { + for ( const_iterator p=begin(), e=end(); p != e; ++p ) + if ( x == *p ) + return p - begin(); + return -1; + } + /// Add an element to the end of the qvector + void add(const T &x) { push_back(x); } + void add(T &&x) { push_back(x); } +#endif + /// Does the qvector contain x? + bool has(const T &x) const { return find(x) != end(); } + /// Add an element to the end of the qvector - only if it isn't already present. + /// \param x the element to add + /// \return false if 'x' is already in the qvector, true otherwise + bool add_unique(const T &x) + { + if ( has(x) ) + return false; + push_back(x); + return true; + } + /// Find an element and remove it. + /// \param x the element to remove + /// \return false if 'x' was not found, true otherwise + bool del(const T &x) + { + iterator p = find(x); + if ( p == end() ) + return false; + erase(p); + return true; + } +#ifndef SWIG + const char *dstr(void) const; // debug print +#endif +}; + +typedef qvector<uval_t> uvalvec_t; ///< vector of unsigned values +typedef qvector<sval_t> svalvec_t; ///< vector of signed values +typedef qvector<ea_t> eavec_t; ///< vector of addresses +typedef qvector<int> intvec_t; ///< vector of integers +typedef qvector<bool> boolvec_t; ///< vector of bools +typedef qvector<size_t> sizevec_t; ///< vector of sizes + +/// Reimplementation of stack class from STL. +/// The reason why we have it is because it is not compiler dependent +/// (hopefully) and therefore can be used in IDA API +template<class T> +class qstack : public qvector<T> +{ + typedef qvector<T> inherited; +public: + T pop(void) + { + T v = inherited::back(); + inherited::pop_back(); + return v; + } + const T &top(void) const + { + return inherited::back(); + } + T &top(void) { return CONST_CAST(T&)(CONST_CAST(const qstack<T>*)(this)->top()); } + void push(const T &v) + { + inherited::push_back(v); + } +}; + +//--------------------------------------------------------------------------- +/// A custom allocator for containers. +/// May be useful for std::map and std::set, when the objects are small in size. +template<typename T> +class pool_allocator_t +{ + // allocate 4MB at once. this way for 16GB we would use 4096 pools. + enum + { + pool_size = 4*1024*1024, + pool_nelems = pool_size / sizeof(T), + }; + // we will reuse the freed space to maintain a list of free slots. + // for this we need the slot size to be big enough to hold a pointer. + // also, pool elements must be smaller than the pool size. + CASSERT(sizeof(T) >= sizeof(void *) && pool_nelems > 0); + + qvector<T *> pools; + T *free_list = nullptr; // singly linked list + T *pool_ptr = nullptr; + T *pool_end = nullptr; + size_t live_objects = 0; + + //--------------------------------------------------------------------------- + void free_entire_pool() + { + for ( T *p : pools ) + qfree(p); + pools.clear(); + free_list = nullptr; + pool_ptr = nullptr; + pool_end = nullptr; + } + +public: + // boilerplate definitions that are required for allocators: + typedef T value_type; + typedef T *pointer; + typedef const T *const_pointer; + typedef T &reference; + typedef const T &const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + pool_allocator_t() {} + template<typename U> pool_allocator_t(const pool_allocator_t<U> &) {} + pool_allocator_t(const pool_allocator_t &) {} + pool_allocator_t &operator=(const pool_allocator_t &) { return *this; } + pool_allocator_t(pool_allocator_t && ) = default; + pool_allocator_t &operator=(pool_allocator_t && ) = default; + bool operator==(const pool_allocator_t &r) const { return this == &r; } + + //--------------------------------------------------------------------------- + T *allocate(size_t n) + { + if ( n != 1 ) + return (T*)qalloc_or_throw(n*sizeof(T)); + + live_objects++; + if ( free_list != nullptr ) + { + T *ptr = free_list; + free_list = *(T**)ptr; + return ptr; + } + + if ( pool_ptr == pool_end ) + { + pool_ptr = (T*)qalloc_or_throw(pool_nelems*sizeof(T)); + pool_end = pool_ptr + pool_nelems; + pools.push_back(pool_ptr); + } + return pool_ptr++; + } + + //--------------------------------------------------------------------------- + void deallocate(T *ptr, size_t n) + { + if ( n != 1 ) + { + qfree(ptr); + } + else + { + *(T**)ptr = free_list; + free_list = ptr; + if ( --live_objects == 0 ) + free_entire_pool(); + } + } +}; + +//--------------------------------------------------------------------------- +/// Standard lexical comparison. +/// \return -1 if a < b, 1 if a > b, and 0 if a == b +template <class T> int lexcompare(const T &a, const T &b) +{ + if ( a < b ) + return -1; + if ( a > b ) + return 1; + return 0; +} + +//--------------------------------------------------------------------------- +/// Lexical comparison of two vectors. Also see lexcompare(). +/// \return 0 if the two vectors are identical +/// 1 if 'a' is larger than 'b' +/// -1 if 'a' is smaller than 'b' +/// otherwise return the first nonzero lexical comparison between each element in 'a' and 'b' +template <class T> int lexcompare_vectors(const T &a, const T &b) +{ + if ( a.size() != b.size() ) + return a.size() > b.size() ? 1 : -1; + for ( int i=0; i < a.size(); i++ ) + { + int code = lexcompare(a[i], b[i]); + if ( code != 0 ) + return code; + } + return 0; +} + +//--------------------------------------------------------------------------- +/// Smart pointer to objects derived from ::qrefcnt_obj_t +template <class T> +class qrefcnt_t +{ + T *ptr; + void delref(void) + { + if ( ptr != nullptr && --ptr->refcnt == 0 ) + ptr->release(); + } +public: + explicit qrefcnt_t(T *p) : ptr(p) {} + qrefcnt_t(const qrefcnt_t &r) : ptr(r.ptr) + { + if ( ptr != nullptr ) + ptr->refcnt++; + } + qrefcnt_t &operator=(const qrefcnt_t &r) + { + delref(); + ptr = r.ptr; + if ( ptr != nullptr ) + ptr->refcnt++; + return *this; + } + ~qrefcnt_t(void) + { + delref(); + } + void reset(void) + { + delref(); + ptr = nullptr; + } + operator T *() const + { + return ptr; + } + T *operator ->() const + { + return ptr; + } + T &operator *() const + { + return *ptr; + } +}; + +//--------------------------------------------------------------------------- +/// Base class for reference count objects +class qrefcnt_obj_t +{ +public: + int refcnt; ///< counter + /// Constructor + qrefcnt_obj_t(void) : refcnt(1) {} + /// Call destructor. + /// We use release() instead of operator delete() to maintain binary + /// compatibility with all compilers (vc and gcc use different vtable layouts + /// for operator delete) + virtual void idaapi release(void) = 0; +}; + +//--------------------------------------------------------------------------- +/// Interface class for iterator types. +template <class T> +class qiterator : public qrefcnt_obj_t +{ +public: + typedef T value_type; + virtual bool idaapi first(void) = 0; + virtual bool idaapi next(void) = 0; + virtual T idaapi operator *(void) = 0; + virtual T get(void) newapi { return this->operator*(); } +}; + + +//--------------------------------------------------------------------------- +/// \name qstrlen +/// Get the length of the given string +///@{ +inline THREAD_SAFE size_t idaapi qstrlen(const char *s) { return strlen(s); } +inline THREAD_SAFE size_t idaapi qstrlen(const uchar *s) { return strlen((const char *)s); } +idaman THREAD_SAFE size_t ida_export qstrlen(const wchar16_t *s); +///@} + +/// \name qstrcmp +/// Lexical comparison of strings. +/// \return code +/// \retval 0 if two strings are identical +/// \retval > 0 if 's1' is larger than 's2' +/// \retval < 0 if 's2' is larger than 's1' +/// \retval otherwise return first nonzero comparison between chars in 's1' and 's2' +///@{ +inline THREAD_SAFE int idaapi qstrcmp(const char *s1, const char *s2) { return strcmp(s1, s2); } +inline THREAD_SAFE int idaapi qstrcmp(const uchar *s1, const uchar *s2) { return strcmp((const char *)s1, (const char *)s2); } +idaman THREAD_SAFE int ida_export qstrcmp(const wchar16_t *s1, const wchar16_t *s2); +///@} + +/// \name qstrncmp +/// Lexical comparison of string prefixes. +/// \return code +/// \retval 0 if two strings are identical +/// \retval > 0 if 's1' is larger than 's2' +/// \retval < 0 if 's2' is larger than 's1' +/// \retval otherwise return first nonzero comparison between chars in 's1' and 's2' +///@{ +inline THREAD_SAFE int idaapi qstrncmp(const char *s1, const char *s2, size_t len) { return strncmp(s1, s2, len); } +inline THREAD_SAFE int idaapi qstrncmp(const uchar *s1, const uchar *s2, size_t len) { return strncmp((const char *)s1, (const char *)s2, len); } +idaman THREAD_SAFE int ida_export qstrncmp(const wchar16_t *s1, const wchar16_t *s2, size_t len); +///@} + +/// \name qstrstr +/// Find a string within another string. +/// \return a pointer to the first occurrence of 's2' within 's1', nullptr if s2 is not found in s1 +///@{ +inline THREAD_SAFE char *idaapi qstrstr(char *s1, const char *s2) { return strstr(s1, s2); } +inline THREAD_SAFE const char *idaapi qstrstr(const char *s1, const char *s2) { return strstr(s1, s2); } +inline THREAD_SAFE const uchar *idaapi qstrstr(const uchar *s1, const uchar *s2) { return (const uchar *)strstr((const char *)s1, (const char *)s2); } +///@} + +/// \name strchr +/// Find a character within a string. +/// \return a pointer to the first occurrence of 'c' within 's1', nullptr if c is not found in s1 +///@{ +inline THREAD_SAFE char *idaapi qstrchr(char *s1, char c) { return strchr(s1, c); } +inline THREAD_SAFE const char *idaapi qstrchr(const char *s1, char c) { return strchr(s1, c); } +inline THREAD_SAFE uchar *idaapi qstrchr(uchar *s1, uchar c) { return (uchar *)strchr((char *)s1, c); } +inline THREAD_SAFE const uchar *idaapi qstrchr(const uchar *s1, uchar c) { return (const uchar *)strchr((const char *)s1, c); } +idaman THREAD_SAFE const wchar16_t *ida_export qstrchr(const wchar16_t *s1, wchar16_t c); +inline THREAD_SAFE wchar16_t *idaapi qstrchr(wchar16_t *s1, wchar16_t c) + { return (wchar16_t *)qstrchr((const wchar16_t *)s1, c); } +///@} + +/// \name qstrrchr +/// Find a last occurrence of a character within a string. +/// \return a pointer to the last occurrence of 'c' within 's1', nullptr if c is not found in s1 +///@{ +inline THREAD_SAFE const char *idaapi qstrrchr(const char *s1, char c) { return strrchr(s1, c); } +inline THREAD_SAFE char *idaapi qstrrchr(char *s1, char c) { return strrchr(s1, c); } +inline THREAD_SAFE const uchar *idaapi qstrrchr(const uchar *s1, uchar c) { return (const uchar *)strrchr((const char *)s1, c); } +inline THREAD_SAFE uchar *idaapi qstrrchr(uchar *s1, uchar c) { return (uchar *)strrchr((const char *)s1, c); } +idaman THREAD_SAFE const wchar16_t *ida_export qstrrchr(const wchar16_t *s1, wchar16_t c); +inline THREAD_SAFE wchar16_t *idaapi qstrrchr(wchar16_t *s1, wchar16_t c) + { return (wchar16_t *)qstrrchr((const wchar16_t *)s1, c); } +///@} + +/// \defgroup qstring_split_flags Flags for _qstring::split +///@{ +#define SSF_DROP_EMPTY 0x1 ///< drop empty parts +///@} + +//--------------------------------------------------------------------------- +#define streq(s1, s2) (::qstrcmp((s1), (s2)) == 0) ///< convenient check for string equality +#define strneq(s1, s2, count) (::qstrncmp((s1), (s2), (count)) == 0) ///< see #streq +#define strieq(s1, s2) (stricmp((s1), (s2)) == 0) ///< see #streq +#define strnieq(s1, s2, count) (strnicmp((s1), (s2), (count)) == 0) ///< see #streq + +//--------------------------------------------------------------------------- +/// Reimplementation of the string class from STL. +/// Only the most essential functions are implemented. +/// The reason why we have this is because it is not compiler dependent +/// (hopefully) and therefore can be used in IDA API +template<class qchar> +class _qstring +{ + qvector<qchar> body; + void assign(const qchar *ptr, size_t len) + { + body.resize_noinit(len+1); + memmove(body.begin(), ptr, len*sizeof(qchar)); + body[len] = '\0'; + } +public: + using value_type = qchar; + + /// Constructor + _qstring(void) {} + /// Constructor - creates a new qstring from an existing char * + _qstring(const qchar *ptr) + { + if ( ptr != nullptr ) + assign(ptr, ::qstrlen(ptr)); + } + /// Constructor - creates a new qstring using first 'len' chars from 'ptr' + _qstring(const qchar *ptr, size_t len) + { + if ( len > 0 ) + assign(ptr, len); + } + /// Constructor - constructs the string with 'count' copies of character 'ch' + _qstring(size_t count, qchar ch) + { + if ( count > 0 ) + { + body.resize(count+1, ch); + body[count] = 0; // ensure the terminating zero + } + } +#ifndef SWIG + /// Move constructor + _qstring(_qstring &&x) : body(std::move(x.body)) {} + /// Copy constructor (if not declared, move constructor causes it to be deleted) + _qstring(const _qstring &r) : body(r.body) {} +#endif + void swap(_qstring<qchar> &r) { body.swap(r.body); } ///< Swap contents of two qstrings. see qvector::swap() + size_t length(void) const { size_t l = body.size(); return l ? l - 1 : 0; } ///< Get number of chars in this qstring (not including terminating zero) + size_t size(void) const { return body.size(); } ///< Get number of chars in this qstring (including terminating zero) + size_t capacity(void) const { return body.capacity(); } ///< Get number of chars this qstring can contain (including terminating zero) + + /// Resize to the given size. + /// The resulting qstring will have length() = s, and size() = s+1 \n + /// if 's' is greater than the current size then the extra space is filled with 'c'. \n + /// if 's' is less than the current size then the trailing chars are removed + void resize(size_t s, qchar c) + { + size_t oldsize = body.size(); + if ( oldsize != 0 && s >= oldsize ) + body[oldsize-1] = c; // erase the terminating zero + body.resize(s+1, c); + body[s] = 0; // ensure the terminating zero + } + /// Similar to resize(size_t, qchar) - but any extra space is filled with zeroes + void resize(size_t s) + { + if ( s == 0 ) + { + body.clear(); + } + else + { + body.resize(s+1); + body[s] = 0; // ensure the terminating zero + } + } + void remove_last(int cnt=1) + { + ssize_t len = body.size() - cnt; + if ( len <= 1 ) + { + body.clear(); + } + else + { + body.resize_noinit(len); + body[len-1] = 0; + } + } + void reserve(size_t cnt) { body.reserve(cnt); } ///< Increase capacity the qstring. see qvector::reserve() + void clear(void) { body.clear(); } ///< Clear qstring and free memory + void qclear(void) { body.qclear(); } ///< Clear qstring but do not free memory yet + bool empty(void) const { return body.size() <= 1; } ///< Does the qstring have 0 non-null elements? + /// Convert the qstring to a char * + const qchar *c_str(void) const + { + static const qchar nullstr[] = { 0 }; + return body.empty() ? nullstr : &body[0]; + } + typedef qchar *iterator; + typedef const qchar *const_iterator; + iterator begin(void) { return body.begin(); } ///< Get a pointer to the beginning of the qstring + const_iterator begin(void) const { return body.begin(); } ///< Get a const pointer to the beginning of the qstring + iterator end(void) { return body.end(); } ///< Get a pointer to the end of the qstring (this is not the terminating zero) + const_iterator end(void) const { return body.end(); } ///< Get a const pointer to the end of the qstring (this is not the terminating zero) + /// Allow assignment of qstrings using '=' + _qstring &operator=(const qchar *str) + { + size_t len = str == nullptr ? 0 : ::qstrlen(str); + if ( len > 0 ) + assign(str, len); + else + qclear(); + return *this; + } + _qstring &operator=(const _qstring &qstr) + { + if ( this != &qstr ) + { + size_t len = qstr.length(); + if ( len > 0 ) + assign(qstr.begin(), len); + else + qclear(); + } + return *this; + } +#ifndef SWIG + /// Move assignment operator + _qstring &operator=(_qstring &&x) noexcept + { + body = std::move(x.body); + return *this; + } +#endif + /// Append a char using '+=' + _qstring &operator+=(qchar c) + { + return append(c); + } + /// Append another qstring using '+=' + _qstring &operator+=(const _qstring &r) + { + return append(r); + } + /// Get result of appending two qstrings using '+' + _qstring operator+(const _qstring &r) const + { + _qstring s = *this; + s += r; + return s; + } + DECLARE_COMPARISONS(_qstring) + { + return ::qstrcmp(c_str(), r.c_str()); + } + /// Test equality of a qstring and a const char* using '==' + bool operator==(const qchar *r) const + { + return ::qstrcmp(c_str(), r) == 0; + } + bool operator!=(const qchar *r) const { return !(*this == r); } ///< Test equality of a qstring and a const char* with '!=' + /// Compare two qstrings using '<'. see qstrcmp() + bool operator<(const qchar *r) const + { + return ::qstrcmp(c_str(), r) < 0; + } + /// Does the string start with the specified prefix? + bool starts_with(const _qstring &str) const + { + return starts_with(str.begin(), str.length()); + } + bool starts_with(const qchar *ptr, ssize_t len = -1) const + { + if ( ptr == nullptr ) + return true; + if ( len == -1 ) + len = ::qstrlen(ptr); + if ( len == 0 ) + return true; + if ( length() < len ) + return false; + return strneq(begin(), ptr, len); + } + /// Does the string end with the specified suffix? + bool ends_with(const _qstring &str) const + { + return ends_with(str.begin(), str.length()); + } + bool ends_with(const qchar *ptr, ssize_t len = -1) const + { + if ( ptr == nullptr ) + return true; + if ( len == -1 ) + len = ::qstrlen(ptr); + if ( len == 0 ) + return true; + size_t l = length(); + if ( l < len ) + return false; + return strneq(begin() + l - len, ptr, len); + } + /// Retrieve char at index 'idx' using '[]' + const qchar &operator[](size_t idx) const + { + if ( !body.empty() || idx ) + return body[idx]; + static const qchar nullstr[] = { 0 }; + return nullstr[0]; + } + /// Retrieve char at index 'idx' using '[]' + qchar &operator[](size_t idx) + { + if ( !body.empty() || idx ) + return body[idx]; + static qchar nullstr[] = { 0 }; + return nullstr[0]; + } + const qchar &at(size_t idx) const { return body.at(idx); } ///< Retrieve const char at index 'idx' + qchar &at(size_t idx) { return body.at(idx); } ///< Retrieve char at index 'idx' + /// Extract C string from _qstring. Must qfree() it. + qchar *extract(void) { return body.extract(); } + /// Assign this qstring to an existing char *. + /// See qvector::inject(T *, size_t) + void inject(qchar *s, size_t len) + { + body.inject(s, len); + } + /// Same as to inject(qchar *, size_t), with len = strlen(s) + void inject(qchar *s) + { + if ( s != nullptr ) + { + size_t len = ::qstrlen(s) + 1; + body.inject(s, len); + } + } + /// Get the last qchar in the string (for concatenation checks) + qchar last(void) const + { + size_t len = length(); + return len == 0 ? '\0' : body[len-1]; + } + /// Find a substring. + /// \param str the substring to look for + /// \param pos starting position + /// \return the position of the beginning of the first occurrence of str, _qstring::npos of none exists + size_t find(const qchar *str, size_t pos=0) const + { + if ( pos <= length() ) + { + const qchar *beg = c_str(); + const qchar *ptr = ::qstrstr(beg+pos, str); + if ( ptr != nullptr ) + return ptr - beg; + } + return npos; + } + /// Replace all occurrences of 'what' with 'with'. + /// \return false if 'what' is not found in the qstring, true otherwise + bool replace(const qchar *what, const qchar *with) + { + _qstring result; + size_t len_what = ::qstrlen(what); + const qchar *_start = c_str(); + const qchar *last_pos = _start; + while ( true ) + { + const qchar *pos = ::qstrstr(last_pos, what); + if ( pos == nullptr ) + break; + size_t n = pos - last_pos; + if ( n > 0 ) + result.append(last_pos, n); + result.append(with); + last_pos = pos + len_what; + } + // no match at all? + if ( last_pos == _start ) + return false; + // any pending characters? + if ( *last_pos ) + result.append(last_pos); + swap(result); + return true; + } + /// Same as find(const qchar *, size_t), but takes a qstring parameter + size_t find(const _qstring &str, size_t pos=0) const { return find(str.c_str(), pos); } + /// Find a character in the qstring. + /// \param c the character to look for + /// \param pos starting position + /// \return index of first occurrence of 'c' if c is found, _qstring::npos otherwise + size_t find(qchar c, size_t pos=0) const + { + if ( pos <= length() ) + { + const qchar *beg = c_str(); + const qchar *ptr = qstrchr(beg+pos, c); + if ( ptr != nullptr ) + return ptr - beg; + } + return npos; + } + /// Search backwards for a character in the qstring. + /// \param c the char to look for + /// \param pos starting position + /// \return index of first occurrence of 'c' if c is found, _qstring::npos otherwise + size_t rfind(qchar c, size_t pos=0) const + { + if ( pos <= length() ) + { + const qchar *beg = c_str(); + const qchar *ptr = qstrrchr(beg+pos, c); + if ( ptr != nullptr ) + return ptr - beg; + } + return npos; + } + /// Get a substring. + /// \param pos starting position + /// \param n ending position (non-inclusive) + /// \return the resulting substring + _qstring<qchar> substr(size_t pos=0, size_t n=npos) const + { + size_t endp = qmin(length(), n); + if ( pos >= endp ) + pos = endp; + return _qstring<qchar>(c_str()+pos, endp-pos); + } + /// Remove characters from the qstring. + /// \param idx starting position + /// \param cnt number of characters to remove + _qstring &remove(size_t idx, size_t cnt) + { + size_t len = length(); + if ( idx < len && cnt != 0 ) + { + cnt += idx; + if ( cnt < len ) + { + iterator p1 = body.begin() + cnt; + iterator p2 = body.begin() + idx; + memmove(p2, p1, (len-cnt)*sizeof(qchar)); + idx += len - cnt; + } + body.resize_noinit(idx+1); + body[idx] = '\0'; + } + return *this; + } + /// Insert a character into the qstring. + /// \param idx position of insertion (if idx >= length(), the effect is the same as append) + /// \param c char to insert + _qstring &insert(size_t idx, qchar c) + { + size_t len = length(); + body.resize_noinit(len+2); + body[len+1] = '\0'; + if ( idx < len ) + { + iterator p1 = body.begin() + idx; + memmove(p1+1, p1, (len-idx)*sizeof(qchar)); + len = idx; + } + body[len] = c; + return *this; + } + /// Insert a string into the qstring. + /// \param idx position of insertion (if idx >= length(), the effect is the same as append) + /// \param str the string to insert + /// \param addlen number of chars from 'str' to insert + _qstring &insert(size_t idx, const qchar *str, size_t addlen) + { + size_t len = length(); + body.resize_noinit(len+addlen+1); + body[len+addlen] = '\0'; + if ( idx < len ) + { + iterator p1 = body.begin() + idx; + iterator p2 = p1 + addlen; + memmove(p2, p1, (len-idx)*sizeof(qchar)); + len = idx; + } + memmove(body.begin()+len, str, addlen*sizeof(qchar)); + return *this; + } + /// Same as insert(size_t, const qchar *, size_t), but all chars in str are inserted + _qstring &insert(size_t idx, const qchar *str) + { + if ( str != nullptr ) + { + size_t addlen = ::qstrlen(str); + insert(idx, str, addlen); + } + return *this; + } + /// Same as insert(size_t, const qchar *), but takes a qstring parameter + _qstring &insert(size_t idx, const _qstring &qstr) + { + size_t len = length(); + size_t add = qstr.length(); + body.resize_noinit(len+add+1); + body[len+add] = '\0'; + if ( idx < len ) + { + iterator p1 = body.begin() + idx; + iterator p2 = p1 + add; + memmove(p2, p1, (len-idx)*sizeof(qchar)); + len = idx; + } + memcpy(body.begin()+len, qstr.begin(), add*sizeof(qchar)); + return *this; + } + _qstring &insert(qchar c) { return insert(0, c); } ///< Prepend the qstring with 'c' + _qstring &insert(const qchar *str) { return insert(0, str); } ///< Prepend the qstring with 'str' + _qstring &insert(const _qstring &qstr) { return insert(0, qstr); } ///< Prepend the qstring with 'qstr' + /// Append c to the end of the qstring + _qstring &append(qchar c) + { + size_t len = length(); + body.resize_noinit(len+2); + body[len] = c; + body[len+1] = '\0'; + return *this; + } + /// Append a string to the qstring. + /// \param str the string to append + /// \param addlen number of characters from 'str' to append + _qstring &append(const qchar *str, size_t addlen) + { + size_t len = length(); + body.resize_noinit(len+addlen+1); + body[len+addlen] = '\0'; + memmove(body.begin()+len, str, addlen*sizeof(qchar)); + return *this; + } + /// Same as append(const qchar *, size_t), but all chars in 'str' are appended + _qstring &append(const qchar *str) + { + if ( str != nullptr ) + { + size_t addlen = ::qstrlen(str); + append(str, addlen); + } + return *this; + } + /// Same as append(const qchar *), but takes a qstring argument + _qstring &append(const _qstring &qstr) + { + size_t add = qstr.length(); + if ( add != 0 ) + { + size_t len = length(); + body.resize_noinit(len+add+1); + body[len+add] = '\0'; + memcpy(body.begin()+len, qstr.begin(), add*sizeof(qchar)); + } + return *this; + } + /// Append result of qvsnprintf() to qstring + AS_PRINTF(2, 0) _qstring &cat_vsprnt(const char *format, va_list va) + { // since gcc64 forbids reuse of va_list, we make a copy for the second call: + va_list copy; + va_copy(copy, va); + size_t add = ::qvsnprintf(nullptr, 0, format, va); + if ( add != 0 ) + { + size_t len = length(); + body.resize_noinit(len+add+1); + ::qvsnprintf(body.begin()+len, add+1, format, copy); + } + va_end(copy); + return *this; + } + /// Replace qstring with the result of qvsnprintf() + AS_PRINTF(2, 0) _qstring &vsprnt(const char *format, va_list va) + { // since gcc64 forbids reuse of va_list, we make a copy for the second call: + va_list copy; + va_copy(copy, va); + body.clear(); + size_t add = ::qvsnprintf(nullptr, 0, format, va); + if ( add != 0 ) + { + body.resize_noinit(add+1); + ::qvsnprintf(body.begin(), add+1, format, copy); + } + va_end(copy); + return *this; + } + /// Append result of qsnprintf() to qstring + AS_PRINTF(2, 3) _qstring &cat_sprnt(const char *format, ...) + { + va_list va; + va_start(va, format); + cat_vsprnt(format, va); + va_end(va); + return *this; + } + /// Replace qstring with the result of qsnprintf() + AS_PRINTF(2, 3) _qstring &sprnt(const char *format, ...) + { + va_list va; + va_start(va, format); + vsprnt(format, va); + va_end(va); + return *this; + } + /// Replace qstring with the result of qsnprintf() + /// \sa inline int nowarn_qsnprintf(char *buf, size_t size, const char *format, ...) + GCC_DIAG_OFF(format-nonliteral); + _qstring &nowarn_sprnt(const char *format, ...) + { + va_list va; + va_start(va, format); + vsprnt(format, va); + va_end(va); + return *this; + } + GCC_DIAG_ON(format-nonliteral); + /// Fill qstring with a character. + /// The qstring is resized if necessary until 'len' chars have been filled + /// \param pos starting position + /// \param c the character to fill + /// \param len number of positions to fill with 'c' + _qstring &fill(size_t pos, qchar c, size_t len) + { + size_t endp = pos + len + 1; + if ( body.size() < endp ) + { + body.resize_noinit(endp); + body[endp-1] = '\0'; + } + memset(body.begin()+pos, c, len); + return *this; + } + /// Clear contents of qstring and fill with 'c' + _qstring &fill(qchar c, size_t len) + { + body.qclear(); + if ( len > 0 ) + resize(len, c); + return *this; + } + /// Remove all instances of the specified char from the beginning of the qstring + _qstring <rim(qchar blank = ' ') + { + if ( !empty() ) + { + iterator b = body.begin(); + iterator e = body.end()-1; + while ( b < e && *b == blank ) + b++; + if ( b > body.begin() ) + { + memmove(body.begin(), b, sizeof(qchar)*(e-b+1)); + resize(e-b); + } + } + return *this; + } + /// Remove all instances of the specified char from the end of the qstring + _qstring &rtrim(qchar blank, size_t minlen = 0) + { + if ( size() > minlen + 1 ) + { + iterator b = body.begin() + minlen; + iterator e = body.end() - 1; + // assert: e > b + while ( e > b && *(e-1) == blank ) + e--; + resize(e - body.begin()); + } + return *this; + } + /// Remove all whitespace from the end of the qstring + _qstring &rtrim() + { + if ( !empty() ) + { + iterator b = body.begin(); + iterator e = body.end() - 1; + while ( e > b && qisspace(e[-1]) ) + --e; + resize(e - b); + } + return *this; + } + /// Remove all instances of the specified char from both ends of the qstring + _qstring &trim2(qchar blank = ' ') + { + rtrim(blank); + ltrim(blank); + return *this; + } + + // Pushes the character 'c' into the qstring + // This adds support for functions from <algorithm> + void push_back(qchar c) + { + append(c); + } + + /// Split a string on SEP, appending the parts to OUT + /// \param out storage + /// \param sep the separator to split on + /// \param flags a combination of \ref qstring_split_flags + void split(qvector<_qstring<qchar> > *out, const qchar *sep, uint32 flags=0) const; + + /// Join the provided parts into a single string with each element + /// separated by SEP + /// \param parts the parts to join + /// \param sep the separator to join on (it can be an empty string) + /// \return the combined string + static _qstring<qchar> join(const qvector<_qstring<qchar> > &parts, const qchar *sep); + + /// Invalid position + static constexpr size_t npos = (size_t) -1; +}; +typedef _qstring<char> qstring; ///< regular string +typedef _qstring<uchar> qtype; ///< type string +typedef _qstring<wchar16_t> qwstring; ///< unicode string +typedef qvector<qstring> qstrvec_t; ///< vector of strings +typedef qvector<qwstring> qwstrvec_t; ///< vector of unicode strings + +#ifndef SWIG // avoid "Warning 317: Specialization of non-template 'hash'." + +// allow qstring in hashed containers +namespace std +{ + template<class T> + struct hash<_qstring<T>> + { + size_t operator()(const _qstring<T> &str) const noexcept + { + // FNV-1a, as per Wikipedia +#ifdef __X86__ + const size_t FNV_BASIS = 0x811c9dc5; + const size_t FNV_PRIME = 0x01000193; +#else + const size_t FNV_BASIS = 0xcbf29ce484222325; + const size_t FNV_PRIME = 0x100000001b3; +#endif + size_t sum = FNV_BASIS; + for ( T c : str ) + { + sum ^= c; + sum *= FNV_PRIME; + } + return sum; + } + }; +} +#endif + +template <> inline +void qstring::split(qstrvec_t *out, const char *sep, uint32 flags) const +{ + size_t seplen = ::qstrlen(sep); + const char *p = begin(); + const char *const end = p + length(); + while ( p < end ) + { + const char *psep = ::qstrstr(p, sep); + size_t rem = (psep != nullptr ? psep : end) - p; + if ( rem > 0 || (flags & SSF_DROP_EMPTY) == 0 ) + out->push_back().append(p, rem); + p = psep != nullptr ? psep + seplen : end; + } + // Account for trailing separator sequence + if ( ends_with(sep, seplen) && (flags & SSF_DROP_EMPTY) == 0 ) + out->push_back(); // add an empty string +} + +template <> inline +qstring qstring::join(const qstrvec_t &parts, const char *sep) +{ + qstring buf; + size_t nparts = parts.size(); + if ( nparts > 0 ) + { + size_t seplen = ::qstrlen(sep); + size_t total = (nparts - 1) * seplen; // separators + for ( const auto &one : parts ) + total += one.length(); + buf.reserve(total); + for ( const auto &one : parts ) + { + if ( !buf.empty() ) + buf.append(sep, seplen); + buf.append(one); + } + } + return buf; +} + +/// Vector of bytes (use for dynamic memory) +class bytevec_t: public qvector<uchar> +{ +public: + /// Constructor + bytevec_t() {} + /// Constructor - fill bytevec with 'sz' bytes from 'buf' + bytevec_t(const void *buf, size_t sz) { append(buf, sz); } + /// Append bytes to the bytevec + /// \param buf pointer to buffer that will be appended + /// \param sz size of buffer + bytevec_t &append(const void *buf, size_t sz) + { + if ( sz > 0 ) + { + size_t cur_sz = size(); + size_t new_sz = cur_sz + sz; + if ( new_sz < cur_sz ) + new_sz = BADMEMSIZE; // integer overflow, ask too much and it will throw + resize(new_sz); + memcpy(begin() + cur_sz, buf, sz); + } + return *this; + } + /// Pack a byte and append the result to the bytevec + void pack_db(uint8 x) { push_back(x); } + /// Pack a word and append the result to the bytevec + void pack_dw(uint16 x) + { + uchar packed[dw_packed_size]; + size_t len = ::pack_dw(packed, packed+sizeof(packed), x) - packed; + append(packed, len); + } + /// Pack a dword and append the result to the bytevec + void pack_dd(uint32 x) + { + uchar packed[dd_packed_size]; + size_t len = ::pack_dd(packed, packed+sizeof(packed), x) - packed; + append(packed, len); + } + /// Pack a quadword and append the result to the bytevec + void pack_dq(uint64 x) + { + uchar packed[dq_packed_size]; + size_t len = ::pack_dq(packed, packed+sizeof(packed), x) - packed; + append(packed, len); + } + /// Pack an ea value and append the result to the bytevec + void pack_ea(ea_t x) + { + uchar packed[ea_packed_size]; + size_t len = ::pack_ea(packed, packed+sizeof(packed), x) - packed; + append(packed, len); + } + /// Pack an ea value (64bits) and append the result to the bytevec + /// We pass ea_t as a 64-bit quantity (to be able to debug 32-bit programs with ida64) + /// adding 1 to the address ensures that BADADDR is passed correctly. + /// without it, 32-bit server would return 0xffffffff and ida64 would not consider it + /// as a BADADDR. + void pack_ea64(ea64_t ea) + { +#ifdef __X86__ + if ( ea == BADADDR ) + ea = 0xFFFFFFFFFFFFFFFFULL; +#endif + return pack_dq(ea+1); + } + /// Pack a string (length+contents) and append the result to the bytevec + void pack_ds(const char *x) + { + if ( x == nullptr ) + x = ""; + size_t len = strlen(x); +#ifndef __X86__ + QASSERT(4, len <= 0xFFFFFFFF); +#endif + pack_dd(len); + append(x, len); + } + /// Pack a string (zero-terminated) and append the result to the bytevec + void pack_str(const char *str) + { + if ( str == nullptr ) + str = ""; + size_t len = strlen(str) + 1; + append(str, len); + } + /// Pack a string (zero-terminated) and append the result to the bytevec + void pack_str(const qstring &s) + { + // the opposite operation is 'unpack_str()' which gets the length + // when it encounters a terminating '\0'. Since we don't store the + // string length, we cannot store zeroes that 's' might contain + // and thus we cannot rely on its length(). + pack_str(s.c_str()); + } + /// Pack an object of size 'len' and append the result to the bytevec + void pack_buf(const void *buf, size_t len) + { +#ifndef __X86__ + QASSERT(5, len <= 0xFFFFFFFF); +#endif + pack_dd(len); + append(buf, len); + } + /// Pack an object of size 'len' and append the result to the bytevec + void pack_bytevec(const bytevec_t &b) + { + pack_buf(b.begin(), b.size()); + } + /// Pack an eavec and append the result to the bytevec. + /// Also see unpack_eavec(). + /// \param ea when we pack an eavec, we only store the differences between each + /// value and this parameter. \n + /// This is because groups of ea values will likely be similar, and therefore + /// the differences will usually be small. \n + /// A good example is packing the addresses of a function prologue. \n + /// One can pass the start ea of the function as this parameter, + /// which results in a quick and efficient packing/unpacking. \n + /// (Just be sure to use the func's start ea when unpacking, of course) + /// \param vec eavec to pack + void pack_eavec(ea_t ea, const eavec_t &vec) + { + int nelems = vec.size(); + pack_dw(nelems); // 16bits, fixme! + ea_t old = ea; + for ( int i=0; i < nelems; i++ ) + { + ea_t nea = vec[i]; + pack_ea(nea-old); + old = nea; + } + } + + /// Grow the bytevec and fill with a value + /// \param sz number of bytes to add to bytevec + /// \param filler filler value + bytevec_t &growfill(size_t sz, uchar filler=0) + { + if ( sz > 0 ) + { + size_t cur_sz = size(); + size_t new_sz = cur_sz + sz; + if ( new_sz < cur_sz ) + new_sz = BADMEMSIZE; // integer overflow, ask too much and it will throw + resize(new_sz, filler); + } + return *this; + } + /// See qvector::inject(T *, size_t) + void inject(void *buf, size_t len) + { + qvector<uchar>::inject((uchar *)buf, len); + } + + /// Append the hexadecimal representation of bytes to the string + void tohex(qstring *out, bool upper_case=true) const + { + size_t len = out->length(); + out->resize(len + size() * 2); + char *p = out->begin() + len; + char *end = out->end(); + for ( uchar c: *this ) + { + ::qsnprintf(p, end - p, upper_case ? "%02X" : "%02x", c); + p += 2; + } + } + + /// Produce a hexadecimal representation of bytes + qstring tohex(bool upper_case=true) const + { + qstring buf; + tohex(&buf, upper_case); + return buf; + } + + /// Initialize from a hexadecimal string + /// It returns 'false' if the string is invalid + bool fromhex(const qstring &str) + { + resize(str.length() / 2); + const char *p = str.begin(); + for ( uchar &c: *this ) + { + uint b = 0; + if ( ::qsscanf(p, "%02X", &b) != 1 || b > 0xFF ) + break; + c = uchar(b); + p += 2; + } + return p == str.begin() + str.length(); + } + + /// Is the specified bit set in the bytevec? + bool test_bit(size_t bit) const { return ::test_bit(begin(), bit); } + /// Set the specified bit + void set_bit(size_t bit) { ::set_bit(begin(), bit); } + /// Clear the specified bit + void clear_bit(size_t bit) { ::clear_bit(begin(), bit); } + /// See set_all_bits(uchar *, size_t) + void set_all_bits(size_t nbits) { resize_noinit((nbits+7)/8); ::set_all_bits(begin(), nbits); } + /// See clear_all_bits(uchar *, size_t) + void clear_all_bits(size_t nbits) { ::clear_all_bits(begin(), nbits); } + /// Are all bits cleared? + bool all_zeros() const + { + for ( size_t i = 0; i < size(); ++i ) + if ( at(i) != 0 ) + return false; + return true; + } + /// For each bit that is set in 'b', set the corresponding bit in this bytevec + void set_bits(const bytevec_t &b) + { + size_t nbytes = b.size(); + if ( size() < nbytes ) + resize(nbytes); + for ( size_t i=0; i < nbytes; i++ ) + at(i) |= b[i]; + } + /// Set each bit between [low, high) + void set_bits(size_t low, size_t high) { ::set_bits(begin(), low, high); } + /// For each bit that is set in 'b', the clear the corresponding bit in this bytevec + void clear_bits(const bytevec_t &b) + { + size_t nbytes = qmin(size(), b.size()); + iterator p = begin(); + for ( size_t i=0; i < nbytes; i++, ++p ) + *p = (uchar)(*p & ~b[i]); + } + /// Clear each bit between [low, high) + void clear_bits(size_t low, size_t high) { ::clear_bits(begin(), low, high); } +}; + +/// Relocation information (relocatable objects - see ::relobj_t) +struct reloc_info_t : public bytevec_t +{ +/// \defgroup RELOBJ_ Relocatable object info flags +/// used by relobj_t::ri +///@{ +#define RELOBJ_MASK 0xF ///< the first byte describes the relocation entry types +#define RELSIZE_1 0 ///< 8-bit relocations +#define RELSIZE_2 1 ///< 16-bit relocations +#define RELSIZE_4 2 ///< 32-bit relocations +#define RELSIZE_8 3 ///< 64-bit relocations +#define RELSIZE_CUST 15 ///< custom relocations, should be handled internally +#define RELOBJ_CNT 0x80 ///< counter present (not used yet) +///@} +}; + +idaman THREAD_SAFE bool ida_export relocate_relobj(struct relobj_t *_relobj, ea_t ea, bool mf); + +/// Relocatable object +struct relobj_t : public bytevec_t +{ + ea_t base; ///< current base + reloc_info_t ri; ///< relocation info + + relobj_t(void) : base(0) {} + bool relocate(ea_t ea, bool mf) { return relocate_relobj(this, ea, mf); } ///< mf=1:big endian +}; + +#define QLIST_DEFINED ///< signal that the qlist class has been defined +/// Linked list +/// Note: linked list is not movable! +template <class T> class qlist +{ + struct listnode_t + { + listnode_t *next; + listnode_t *prev; + void fix_links(size_t len) + { + if ( len == 0 ) + { + next = this; + prev = this; + } + else + { + next->prev = this; + prev->next = this; + } + } + }; + + struct datanode_t : public listnode_t + { + T data; + }; + + listnode_t node; + size_t length; + + void init(void) + { + node.next = &node; + node.prev = &node; + length = 0; + } + +public: + typedef T value_type; + class const_iterator; +/// Used for defining the 'iterator' and 'const_iterator' classes for qlist +#define DEFINE_LIST_ITERATOR(iter, constness, cstr) \ + class iter \ + { \ + friend class qlist<T>; \ + constness listnode_t *cur; \ + iter(constness listnode_t *x) : cur(x) {} \ + public: \ + typedef constness T value_type; \ + iter(void) : cur(nullptr) {} \ + iter(const iter &x) : cur(x.cur) {} \ + cstr \ + iter &operator=(const iter &x) { cur = x.cur; return *this; } \ + bool operator==(const iter &x) const { return cur == x.cur; } \ + bool operator!=(const iter &x) const { return cur != x.cur; } \ + constness T &operator*(void) const { return ((datanode_t*)cur)->data; } \ + constness T *operator->(void) const { return &(operator*()); } \ + iter &operator++(void) /* prefix ++ */ \ + { \ + cur = cur->next; \ + return *this; \ + } \ + iter operator++(int) /* postfix ++ */ \ + { \ + iter tmp = *this; \ + ++(*this); \ + return tmp; \ + } \ + iter &operator--(void) /* prefix -- */ \ + { \ + cur = cur->prev; \ + return *this; \ + } \ + iter operator--(int) /* postfix -- */ \ + { \ + iter tmp = *this; \ + --(*this); \ + return tmp; \ + } \ + }; + DEFINE_LIST_ITERATOR(iterator,, friend class const_iterator; ) + DEFINE_LIST_ITERATOR(const_iterator, const, const_iterator(const iterator &x) : cur(x.cur) {} ) + +/// Used to define qlist::reverse_iterator and qlist::const_reverse_iterator +#define DEFINE_REVERSE_ITERATOR(riter, iter) \ + class riter \ + { \ + iter p; \ + public: \ + riter(void) {} \ + riter(const iter &x) : p(x) {} \ + typename iter::value_type &operator*(void) const { iter q=p; return *--q; } \ + typename iter::value_type *operator->(void) const { return &(operator*()); } \ + riter &operator++(void) { --p; return *this; } \ + riter operator++(int) { iter q=p; --p; return q; } \ + riter &operator--(void) { ++p; return *this; } \ + riter operator--(int) { iter q=p; ++p; return q; } \ + bool operator==(const riter &x) const { return p == x.p; } \ + bool operator!=(const riter &x) const { return p != x.p; } \ + }; + DEFINE_REVERSE_ITERATOR(reverse_iterator, iterator) + DEFINE_REVERSE_ITERATOR(const_reverse_iterator, const_iterator) +#undef DEFINE_LIST_ITERATOR +#undef DEFINE_REVERSE_ITERATOR + /// Constructor + qlist(void) { init(); } + /// Constructor - creates a qlist identical to 'x' + qlist(const qlist<T> &x) + { + init(); + insert(begin(), x.begin(), x.end()); + } + /// Destructor + ~qlist(void) + { + clear(); + } + DEFINE_MEMORY_ALLOCATION_FUNCS() + + /// Construct a new qlist using '=' + qlist<T> &operator=(const qlist<T> &x) + { + if ( this != &x ) + { + iterator first1 = begin(); + iterator last1 = end(); + const_iterator first2 = x.begin(); + const_iterator last2 = x.end(); + while ( first1 != last1 && first2 != last2 ) + *first1++ = *first2++; + if ( first2 == last2 ) + erase(first1, last1); + else + insert(last1, first2, last2); + } + return *this; + } + /// Set this = x and x = this, without copying any memory + void swap(qlist<T> &x) + { + std::swap(node, x.node); + std::swap(length, x.length); + node.fix_links(length); + x.node.fix_links(x.length); + } + + iterator begin(void) { return node.next; } ///< Get a pointer to the head of the list + iterator end(void) { return &node; } ///< Get a pointer to the end of the list + bool empty(void) const { return length == 0; } ///< Get true if the list has 0 elements + size_t size(void) const { return length; } ///< Get the number of elements in the list + T &front(void) { return *begin(); } ///< Get the first element in the list + T &back(void) { return *(--end()); } ///< Get the last element in the list + + const_iterator begin(void) const { return node.next; } ///< \copydoc begin + const_iterator end(void) const { return &node; } ///< \copydoc end + const T&front(void) const { return *begin(); } ///< \copydoc front + const T&back(void) const { return *(--end()); } ///< \copydoc end + + reverse_iterator rbegin() { return reverse_iterator(end()); } ///< Get a reverse iterator that points to end of list. See DEFINE_REVERSE_ITERATOR + reverse_iterator rend() { return reverse_iterator(begin()); } ///< Get a reverse iterator that points to beginning of list. See DEFINE_REVERSE_ITERATOR + const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } ///< See rbegin() + const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } ///< See rend() + + /// Insert an element into the qlist. + /// \param p the position to insert the element + /// \param x the element to be inserted + /// \return position of newly inserted element + iterator insert(iterator p, const T &x) + { + datanode_t *tmp = (datanode_t*)qalloc_or_throw(sizeof(datanode_t)); + new (&(tmp->data)) T(x); + linkin(p, tmp); + return tmp; + } + /// Insert an empty element into the qlist. + /// \param p position to insert the element + /// \return reference to this new element + iterator insert(iterator p) + { + datanode_t *tmp = (datanode_t*)qalloc_or_throw(sizeof(datanode_t)); + new (&(tmp->data)) T(); + linkin(p, tmp); + return tmp; + } + /// Insert all elements between 'first' and 'last' (non-inclusive) + /// at position pointed to by 'p' + template <class it2> void insert(iterator p, it2 first, it2 last) + { + while ( first != last ) + insert(p, *first++); + } + /// Insert at beginning of list + void push_front(const T &x) { insert(begin(), x); } + /// Insert at end of list + void push_back(const T &x) { insert(end(), x); } + /// Insert empty element at end of list + T &push_back(void) + { + iterator p = insert(end()); + return ((datanode_t *)p.cur)->data; + } + /// Erase element at position pointed to by 'p' + iterator erase(iterator p) + { + listnode_t *q = p.cur->next; + p.cur->prev->next = p.cur->next; + p.cur->next->prev = p.cur->prev; + ((datanode_t*)p.cur)->data.~T(); + qfree(p.cur); + --length; + return q; + } + /// Erase all elements between 'p1' and 'p2' + void erase(iterator p1, iterator p2) + { + while ( p1 != p2 ) + p1 = erase(p1); + } + /// Erase all elements in the qlist + void clear(void) { erase(begin(), end()); } + /// Erase first element of the qlist + void pop_front(void) { erase(begin()); } + /// Erase last element of the qlist + void pop_back(void) { iterator tmp = end(); erase(--tmp); } + /// Splice a sublist of another list into the current list + void splice(iterator pos, qlist<T> &other, iterator first, iterator last) + { + // Nothing to do for empty range + if ( first == last ) + return; + + listnode_t *first_node = first.cur; + listnode_t *last_node = last.cur->prev; + + // Compute the length of the other list + size_t sublength = 1; + + // Special case: the range is the entire other list + if ( first == other.begin() && last == other.end() ) + { + sublength = other.length; + } + else // Otherwise, count the size of the range + { + listnode_t *curr_node = first_node; + while ( curr_node != last_node ) + { + curr_node = curr_node->next; + ++sublength; + } + } + + // Detach [first, last) from the other list (update links and length) + first_node->prev->next = last_node->next; + last_node->next->prev = first_node->prev; + other.length -= sublength; + + // Attach [first, last) to the current list at pos (update links and length) + listnode_t *pos_node = pos.cur; + listnode_t *before_pos = pos_node->prev; + before_pos->next = first_node; + first_node->prev = before_pos; + last_node->next = pos_node; + pos_node->prev = last_node; + length += sublength; + } + /// Compare two qlists with '==' + bool operator==(const qlist<T> &x) const + { + if ( length != x.length ) + return false; + const_iterator q=x.begin(); + for ( const_iterator p=begin(), e=end(); p != e; ++p,++q ) + if ( *p != *q ) + return false; + return true; + } + /// Compare two qlists with != + bool operator!=(const qlist<T> &x) const { return !(*this == x); } +private: + void linkin(iterator p, listnode_t *tmp) + { + tmp->next = p.cur; + tmp->prev = p.cur->prev; + p.cur->prev->next = tmp; + p.cur->prev = tmp; + ++length; + } +}; + +// Our containers do not care about their addresses. They can be moved around with simple memcpy +/// \cond +template <class T> struct ida_movable_type<qvector<T> > { static constexpr bool value = true; }; +template <class T> struct ida_movable_type<_qstring<T> > { static constexpr bool value = true; }; +template <class T> struct ida_movable_type<qlist<T> > { static constexpr bool value = false; }; +template <class T> struct ida_movable_type<qiterator<T> > { static constexpr bool value = true; }; +/// \endcond + +//---------------------------------------------------------------------------- +#ifndef SWIG +/// Unpack a vector of ea values. +/// \param[out] vec resulting vector +/// \param ea base value that was used to pack the eavec (see pack_eavec()) +/// \param ptr pointer to packed eavec +/// \param end pointer to end of packed eavec + +THREAD_SAFE inline void unpack_eavec( + eavec_t *vec, + ea_t ea, + const uchar **ptr, + const uchar *end) +{ + ea_t old = ea; + int n = unpack_dw(ptr, end); + vec->resize_noinit(n); + for ( int i=0; i < n; i++ ) + { + old += unpack_ea(ptr, end); + vec->at(i) = old; + } +} + +THREAD_SAFE inline bool unpack_bytevec( + bytevec_t *out, + const uchar **pptr, + const uchar *end) +{ + uint32 nbytes = unpack_dd(pptr, end); + if ( nbytes == 0 ) + return true; + const size_t old_size = out->size(); + out->resize_noinit(old_size + nbytes); + return unpack_obj(out->begin() + old_size, nbytes, pptr, end) != nullptr; +} + +inline bool unpack_str(qstring *out, const uchar **pptr, const uchar *end) +{ // zero terminated string, append to qstring + const char *str = unpack_str(pptr, end); + if ( str == nullptr ) + return false; + out->append(str, ((char*)*pptr-str) - 1); + return true; +} + +// Convenience struct for unpacking a data stream +THREAD_SAFE struct memory_deserializer_t +{ + const uchar *ptr; + const uchar *end; + + memory_deserializer_t(const qstring &s) : ptr((uchar*)s.begin()), end(ptr+s.size()) {} + memory_deserializer_t(const bytevec_t &b) : ptr(b.begin()), end(b.end()) {} + memory_deserializer_t(const uchar *p, const uchar *e) : ptr(p), end(e) {} + memory_deserializer_t(const void *p, size_t s) : ptr((uchar*)p), end(ptr+s) {} + bool empty() const { return ptr >= end; } + size_t size() const { return end-ptr; } + bool advance(size_t s) { if ( size() < s ) return false; ptr += s; return true; } + uint8 unpack_db() { return ::unpack_db(&ptr, end); } + uint16 unpack_dw() { return ::unpack_dw(&ptr, end); } + uint32 unpack_dd() { return ::unpack_dd(&ptr, end); } + uint64 unpack_dq() { return ::unpack_dq(&ptr, end); } + ea_t unpack_ea() { return ::unpack_ea(&ptr, end); } + ea64_t unpack_ea64() { return ::unpack_ea64(&ptr, end); } + // unpack zero terminated string + const char *unpack_str() { return ::unpack_str(&ptr, end); } + bool unpack_str(qstring *out) { return ::unpack_str(out, &ptr, end); } + // string with length prefix (dd), return string allocated in the heap + char *unpack_ds(bool empty_null=false) + { + return ::unpack_ds(&ptr, end, empty_null); + } + // string with length prefix (dd), return in the specified buffer + bool unpack_ds_to_buf(char *buf, size_t bufsize) + { + return ::unpack_ds_to_buf(buf, bufsize, &ptr, end); + } + const void *unpack_obj_inplace(size_t objsize) + { + return ::unpack_obj_inplace(&ptr, end, objsize); + } + const void *unpack_buf_inplace() + { + return ::unpack_buf_inplace(&ptr, end); + } + const void *unpack_obj(void *obj, size_t objsize) + { + return ::unpack_obj(obj, objsize, &ptr, end); + } + const void *unpack_buf() + { + return ::unpack_buf(&ptr, end); + } + void unpack_eavec(eavec_t *vec, ea_t ea) + { + ::unpack_eavec(vec, ea, &ptr, end); + } + bool unpack_bytevec(bytevec_t *out) + { + return ::unpack_bytevec(out, &ptr, end); + } + #define SCALAR_TYPE(n) class T, typename std::enable_if<std::is_scalar<T>::value && sizeof(T) == n, int>::type = 0 + template <SCALAR_TYPE(1)> void unpack(T *out) { *out = (T)unpack_db(); } + template <SCALAR_TYPE(2)> void unpack(T *out) { *out = unpack_dw(); } + template <SCALAR_TYPE(4)> void unpack(T *out) { *out = unpack_dd(); } + template <SCALAR_TYPE(8)> void unpack(T *out) { *out = unpack_dq(); } + #undef SCALAR_TYPE + void unpack(qstring *out) { *out = unpack_str(); } + template <class T> + void unpack(qvector<T> *out) + { + uint32 cnt = unpack_dd(); + out->qclear(); + out->reserve(cnt); + for ( size_t i = 0; i < cnt; i++ ) + unpack(&out->push_back()); + } + // linput_t like interface + ssize_t read(void *obj, size_t objsize) { return unpack_obj(obj, objsize) ? objsize : -1; } + bool eof() const { return empty(); } +}; +#define DECLARE_MEMORY_DESERIALIZER(name) \ + name(const void *p, size_t s) : memory_deserializer_t(p, s) {} \ + using memory_deserializer_t::unpack; \ + +struct memory_serializer_t : public bytevec_t +{ + #define SCALAR_TYPE(n) class T, typename std::enable_if<std::is_scalar<T>::value && sizeof(T) == n, int>::type = 0 + template <SCALAR_TYPE(1)> void pack(T value) { pack_db(value); } + template <SCALAR_TYPE(2)> void pack(T value) { pack_dw(value); } + template <SCALAR_TYPE(4)> void pack(T value) { pack_dd(value); } + template <SCALAR_TYPE(8)> void pack(T value) { pack_dq(value); } + #undef SCALAR_TYPE + void pack(const qstring &value) { pack_str(value); } + template <class T> + void pack(const qvector<T> &value) + { + pack_dd(value.size()); + for ( const auto &item: value ) + pack(item); + } +}; +#define DECLARE_MEMORY_SERIALIZER(name) \ + using memory_serializer_t::pack; \ + +#endif // SWIG + +//------------------------------------------------------------------------- +/// Resource janitor to facilitate use of the RAII idiom +template <typename T> +struct janitor_t +{ + janitor_t(T &r) : resource(r) {} ///< Constructor + ~janitor_t(); ///< We provide no implementation for this function, you should + ///< provide specialized implementation yourself +protected: + T &resource; +}; + +// Custom deleter to be used with std::unique_ptr which calls qfree +template <typename T> +struct qfree_deleter_t +{ + void operator() ( T *ptr ) { qfree(ptr); } +}; + +template <typename T> +using qalloc_janitor_t = std::unique_ptr<T, qfree_deleter_t<T>>; +#ifndef SWIG +//------------------------------------------------------------------------- +/// Template to compare any 2 values of the same type. Returns -1/0/1 +template <typename, typename = void> +struct has_compare_method : std::false_type {}; +// std::void_t is from c++17, so we declare it ourselves +template< class... > using qvoid_t = void; +template <typename T> +struct has_compare_method<T, qvoid_t<decltype(std::declval<T>().compare(std::declval<T>()))>> + : std::true_type {}; +template <class T, typename std::enable_if<has_compare_method<T>::value, int>::type = 0> +int compare(const T &a, const T &b) +{ + return a.compare(b); +} +template <class T, typename std::enable_if<!has_compare_method<T>::value, int>::type = 0> +int compare(const T &a, const T &b) +{ + if ( a < b ) + return -1; + if ( a > b ) + return 1; + return 0; +} + +//------------------------------------------------------------------------- +template <class T> +int compare(const qvector<T> &a, const qvector<T> &b) +{ + return compare_containers(a, b); +} + +//------------------------------------------------------------------------- +template <class T> +int compare(const qlist<T> &a, const qlist<T> &b) +{ + return compare_containers(a, b); +} + +//------------------------------------------------------------------------- +template <class T, class U> +int compare(const std::pair<T, U> &a, const std::pair<T, U> &b) +{ + int code = compare(a.first, b.first); + if ( code != 0 ) + return code; + return compare(a.second, b.second); +} + +//------------------------------------------------------------------------- +/// Template to compare any 2 containers of the same type. Returns -1/0/1 +template <class T> +int compare_containers(const T &l, const T &r) +{ + auto p = std::begin(l); + auto pe = std::end(l); + auto q = std::begin(r); + auto qe = std::end(r); + for ( ; p != pe && q != qe; ++p,++q ) + { + int code = compare(*p, *q); + if ( code != 0 ) + return code; + } + if ( p == pe && q != qe ) + return -1; + if ( p != pe && q == qe ) + return 1; + return 0; +} + +#define COMPARE_POINTERS2(ptr, cmp) \ + do \ + { \ + if ( ptr != nullptr && r.ptr != nullptr ) \ + { \ + int _code = cmp(*ptr, *r.ptr); \ + if ( _code != 0 ) \ + return _code; \ + } \ + else if ( r.ptr != nullptr ) \ + { \ + return -1; \ + } \ + else if ( ptr != nullptr ) \ + { \ + return 1; \ + } \ + } while (0) + +#define COMPARE_POINTERS(ptr) COMPARE_POINTERS2(ptr, ::compare) + +#define COMPARE_FIELDS(fld) \ + do \ + { \ + int _code = ::compare(fld, r.fld); \ + if ( _code != 0 ) \ + return _code; \ + } while (0) + +// reverse order +#define COMPARE_FIELDS_REV(fld) \ + do \ + { \ + int _code = ::compare(r.fld, fld); \ + if ( _code != 0 ) \ + return _code; \ + } while (0) + +template <class T, class U> +int compare(const std::map<T, U> &a, const std::map<T, U> &b) +{ + return compare_containers(a, b); +} + +template <class T> +int compare(const std::set<T> &a, const std::set<T> &b) +{ + return compare_containers(a, b); +} + +#endif + +//------------------------------------------------------------------------- +/// Align element up to nearest boundary +template <class T> T align_up(T val, int elsize) +{ + int mask = elsize - 1; + val += mask; + val &= ~mask; + return val; +} + +//------------------------------------------------------------------------- +/// Align element down to nearest boundary +template <class T> T align_down(T val, int elsize) +{ + int mask = elsize - 1; + val &= ~mask; + return val; +} + +//------------------------------------------------------------------------- +/// Declare class as uncopyable. +/// (copy assignment and copy ctr are undefined, so if anyone calls them, +/// there will be a compilation or link error) +#define DECLARE_UNCOPYABLE(T) T &operator=(const T &); T(const T &); + +#ifndef SWIG +//------------------------------------------------------------------------- +// check the variable type +/// \cond +#define IS_QSTRING(v) (std::is_base_of<qstring, std::remove_reference<decltype(v)>::type>::value) +#define IS_SIZEVEC_T(v) (std::is_base_of<sizevec_t, std::remove_reference<decltype(v)>::type>::value) +#define IS_QSTRVEC_T(v) (std::is_base_of<qstrvec_t, std::remove_reference<decltype(v)>::type>::value) + +/// \endcond +#endif + +#endif // __cplusplus + +#ifndef __cplusplus +typedef struct bytevec_tag bytevec_t; +typedef struct qstring_tag qstring; +typedef struct qwstring_tag qwstring; +#endif + +//---------------------------------------------------------------------------- + +/// Calculate CRC32 (polynom 0xEDB88320, zlib compatible). +/// \note in IDA versions before 6.0 a different, incompatible algorithm was used + +idaman THREAD_SAFE uint32 ida_export calc_crc32(uint32 crc, const void *buf, size_t len); + + +/// Calculate an input source CRC32 + +idaman THREAD_SAFE uint32 ida_export calc_file_crc32(class linput_t *fp); + + +/// Encode base64 + +idaman THREAD_SAFE bool ida_export base64_encode(qstring *output, const void *input, size_t size); + +/// Decode base64 + +idaman THREAD_SAFE bool ida_export base64_decode(bytevec_t *output, const char *input, size_t size); ///< Decode base64 + + +/// Convert tabulations to spaces +/// \param out output buffer to append to +/// \param str input string. cannot be equal to out->c_str() +/// \param tabsize tabulation size +/// \return true-replaced some tabs + +idaman THREAD_SAFE bool ida_export replace_tabs(qstring *out, const char *str, int tabsize); + + +/// \defgroup c_str_conv Functions: c strings +/// String C-style conversions (convert \\n to a newline and vice versa) +///@{ +idaman THREAD_SAFE char *ida_export str2user(char *dst, const char *src, size_t dstsize); ///< Make a user representation +idaman THREAD_SAFE char *ida_export user2str(char *dst, const char *src, size_t dstsize); ///< Make an internal representation +idaman THREAD_SAFE char ida_export back_char(const char **p); ///< Translate char after '\\' +#ifdef __cplusplus +idaman THREAD_SAFE void ida_export qstr2user(qstring *dst, const char *src, int nsyms=-1);///< see str2user() +inline THREAD_SAFE void qstr2user(qstring *dst, const qstring &src) { qstr2user(dst, src.c_str(), src.length()); } +idaman THREAD_SAFE void ida_export user2qstr(qstring *dst, const qstring &src); ///< see user2str() +#else +idaman THREAD_SAFE void ida_export qstr2user(qstring *dst, const qstring *src); ///< see str2user() +idaman THREAD_SAFE void ida_export user2qstr(qstring *dst, const qstring *src); ///< see user2str() +#endif +///@} + + +/// Does this byte correspond to the head of a UTF-8 byte sequence? +/// +/// Note that this does not tell us anything regarding the number of bytes +/// that compose the byte sequence; 1-byte sequences (i.e., ASCII) are still +/// UTF-8 byte sequences. +/// +/// \param in the byte to check +/// \return success + +inline constexpr bool is_utf8_head(char in) { return (in & 0xC0) != 0x80; } + + +/// Does this byte correspond to the tail of a UTF-8 byte sequence? +/// +/// \param in the byte to check +/// \return success + +inline constexpr bool is_utf8_tail(char in) { return !is_utf8_head(in); } + + +/// Does byte sequence consist of valid UTF-8-encoded codepoints? +/// \param in the byte sequence +/// \return success + +idaman THREAD_SAFE bool ida_export is_valid_utf8(const char *in); + + +#ifdef __cplusplus + +/// UTF-8 -> UTF-16 +/// \param out the output buffer +/// \param in the input UTF-8 byte stream +/// \param nsyms the number of UTF-8-encoded codepoints in the byte stream +/// \return success +idaman THREAD_SAFE bool ida_export utf8_utf16(qwstring *out, const char *in, int nsyms=-1); + + +/// UTF-16 -> UTF-8 +/// \param out the output buffer +/// \param in the input UTF-16 stream +/// \param nsyms the number of 16-bit items in 'in'. This does not necessarily +/// correspond to the number of codepoints: each surrogate pair +/// will take 2 items. +/// \return success +idaman THREAD_SAFE bool ida_export utf16_utf8(qstring *out, const wchar16_t *in, int nsyms=-1); + + +inline constexpr bool is_lead_surrogate(wchar32_t wch) { return 0xD800 <= wch && wch < 0xDC00; } +inline constexpr bool is_tail_surrogate(wchar32_t wch) { return 0xDC00 <= wch && wch <= 0xDFFF; } +inline constexpr wchar32_t utf16_surrogates_to_cp(wchar16_t lead_surrogate, wchar16_t tail_surrogate) +{ + return (0x10000 + (wchar32_t(lead_surrogate & 0x3FF) << 10)) | (tail_surrogate & 0x3FF); +} + + +/// \defgroup IDBDEC_ IDB default encoding -> UTF-8 encoding flags +/// used by idb_utf8 +///@{ +#define IDBDEC_ESCAPE 0x00000001 ///< convert non-printable characters to C escapes (\n, \xNN, \uNNNN) +///@} + +/// IDB default C string encoding -> UTF-8 +/// \return success (i.e., all bytes converted) + +idaman THREAD_SAFE bool ida_export idb_utf8(qstring *out, const char *in, int nsyms=-1, int flags=0); + + +#ifdef __NT__ +// These are typically used in the text UI (TUI), and +// also to convert argv to UTF-8 at startup. +idaman THREAD_SAFE bool ida_export change_codepage( + qstring *out, + const char *in, + int incp, + int outcp); +#ifndef CP_ACP +#define CP_ACP 0 +#endif +#ifndef CP_OEM +#define CP_OEM 1 +#endif +#ifndef CP_UTF8 +#define CP_UTF8 65001 +#endif +INLINE THREAD_SAFE bool acp_utf8(qstring *out, const char *in) +{ + return change_codepage(out, in, CP_ACP, CP_UTF8); +} +#else // !__NT__ +INLINE THREAD_SAFE bool idaapi change_codepage(qstring *, const char *, int, int) { return false; } +#endif // __NT__ + + +//------------------------------------------------------------------------- +// helpers to compose 16/32-bit wchar's from [M]UTF-8-encoded data +inline THREAD_SAFE constexpr wchar16_t utf8_wchar16(uchar b0, uchar b1) +{ + return (wchar16_t(b0 & 0x1f) << 6) | (b1 & 0x3f); +} + +//------------------------------------------------------------------------- +inline THREAD_SAFE constexpr wchar16_t utf8_wchar16(uchar b0, uchar b1, uchar b2) +{ + return (wchar16_t(b0 & 0x0f) << 12) + | (wchar16_t(b1 & 0x3f) << 6) + | (b2 & 0x3f); +} + +//------------------------------------------------------------------------- +inline THREAD_SAFE constexpr wchar32_t utf8_wchar32(uchar b0, uchar b1, uchar b2, uchar b3) +{ + return (wchar32_t(b0 & 0x07) << 18) + | (wchar32_t(b1 & 0x3f) << 12) + | (wchar32_t(b2 & 0x3f) << 6) + | (b3 & 0x3f); +} + +#endif // __cplusplus + + +#define BADCP wchar32_t(-1) + +/// Read one UTF-8 character from string. if error, return BADCP + +idaman THREAD_SAFE wchar32_t ida_export get_utf8_char(const char **pptr); + + +/// Get the UTF-8 character from string, before 'p'. +/// +/// \param out_cp the output codepoint storage. May be nullptr. +/// \param p the pointer, pointing in the 'begin' string right after the UTF-8-encoded codepoint we want to retrieve +/// \param begin the beginning of the string +/// \return success + +idaman THREAD_SAFE bool ida_export prev_utf8_char(wchar32_t *out_cp, const char **p, const char *begin); + + +/// Advance by n codepoints into the UTF-8 buffer. +/// +/// Each bad byte (i.e., can't be decoded as UTF-8) will count as 1 codepoint. +/// In addition, encountering an unexpected end-of-string (i.e., '\0') will +/// cause this function to stop and return a non-zero value. +/// +/// \param putf8 a pointer to the UTF-8 bytes buffer to advance into +/// \param n the number of codepoints to advance into the buffer +/// \return the number of codepoints that we failed to decode, thus: +/// 0 - success, >0 - a terminating zero was encountered. + +idaman THREAD_SAFE size_t ida_export skip_utf8(const char **putf8, size_t n); + + +/// Encode the codepoint into a UTF-8 byte sequence, and add terminating zero +/// \param out output buffer (must be at least MAX_UTF8_SEQ_LEN bytes wide) +/// \param cp the codepoint to encode +/// \return how many bytes were put into the output buffer +/// (without the terminating zero), or size_t(-1) on failure + +idaman THREAD_SAFE ssize_t ida_export put_utf8_char(char *out, wchar32_t cp); + + +/// Is the provided codepoint graphical? + +idaman THREAD_SAFE bool ida_export is_cp_graphical(wchar32_t cp); + + +// Get number of codepoints in UTF-8 string. Any 'bad' byte +// (i.e., can't be decoded) counts for 1 codepoint. + +idaman THREAD_SAFE size_t ida_export qustrlen(const char *utf8); + + +/// A safer strncpy - makes sure that there is a terminating zero. +/// nb: this function doesn't truncate the last UTF-8 character. +/// \sa qstrncpy() +/// \retval false if the input buffer was truncated + +idaman THREAD_SAFE bool ida_export qustrncpy(char *dst, const char *utf8, size_t dstsize); + + +// A few Unicode-related helpful defines + +#define CP_BOM 0xFEFF +#define UTF8_BOM "\xEF\xBB\xBF" +#define UTF8_BOM_SZ (sizeof(UTF8_BOM) - 1) + +#define UTF16LE_BOM "\xFF\xFE" +#define UTF16BE_BOM "\xFE\xFF" +#define UTF16_BOM_SZ (sizeof(UTF16LE_BOM) - 1) + +#define UTF32LE_BOM "\xFF\xFE\x00\x00" +#define UTF32BE_BOM "\x00\x00\xFE\xFF" +#define UTF32_BOM_SZ (sizeof(UTF32LE_BOM) - 1) + +#define CP_ELLIPSIS 0x2026 +#define UTF8_ELLIPSIS "\xE2\x80\xA6" +#define UTF8_ELLIPSIS_SZ (sizeof(UTF8_ELLIPSIS) - 1) + +#define CP_REPLCHAR 0xFFFD +#define UTF8_REPLCHAR "\xEF\xBF\xBD" +#define UTF8_REPLCHAR_SZ (sizeof(UTF8_REPLCHAR) - 1) + + +// To cover unicode, 4 bytes is enough. Still, from the UTF-8 spec at +// https://tools.ietf.org/html/rfc3629: +// "Another security issue occurs when encoding to UTF-8: the ISO/IEC +// 10646 description of UTF-8 allows encoding character numbers up to +// U+7FFFFFFF, yielding sequences of up to 6 bytes. There is therefore +// a risk of buffer overflow if the range of character numbers is not +// explicitly limited to U+10FFFF or if buffer sizing doesn't take into +// account the possibility of 5- and 6-byte sequences." +// Furthermore, since buffers holding UTF-8 sequences are usually placed +// onto the stack, it's probably not a bad thing to make them 8-bytes +// aligned -- and keep room for a terminating zero, too. +#define MAX_UTF8_SEQ_LEN (6 + 1 + 1) + +//------------------------------------------------------------------------ +/// is IDA converting IDB into I64? +idaman bool ida_export is_cvt64(); + + + +/// \defgroup CEF_ Convert encoding flags +/// used by convert_encoding +///@{ +#define CEF_RETERR 0x1 // return -1 if iconv() returns -1 +///@} + +/// Convert data from encoding fromcode into tocode. +/// \param out the output buffer +/// \param fromcode the encoding of the input data +/// \param tocode the encoding of the output data +/// \param indata the input data +/// \param insize size of input data in bytes +/// \param flags \ref CEF_* +/// \return number of input bytes converted (can be less than actual size if there was an invalid character) +/// -1 if source or target encoding is not supported +/// possible encoding names: windows codepages ("CP1251" etc), charset names ("Shift-JIS"), and many encodings supported by iconv + +idaman ssize_t ida_export convert_encoding( + bytevec_t *out, + const char *fromcode, + const char *tocode, + const uchar *indata, + ssize_t insize, + DEFARG(int flags,0)); + +#ifdef __cplusplus +inline ssize_t convert_encoding( + bytevec_t *out, + const char *fromcode, + const char *tocode, + const bytevec_t *indata, + DEFARG(int flags,0)) +{ + QASSERT(1451, ssize_t(indata->size()) >= 0); + return convert_encoding(out, fromcode, tocode, indata->begin(), indata->size(), flags); +} +#endif + +#define ENC_WIN1252 "windows-1252" +#define ENC_UTF8 "UTF-8" +#define ENC_MUTF8 "MUTF-8" // modified UTF-8, used by Dalvik and Java (https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8) +#define ENC_UTF16 "UTF-16" +#define ENC_UTF16LE "UTF-16LE" +#define ENC_UTF16BE "UTF-16BE" +#define ENC_UTF32 "UTF-32" +#define ENC_UTF32LE "UTF-32LE" +#define ENC_UTF32BE "UTF-32BE" + + + +#ifndef CP_UTF8 +#define CP_UTF8 65001 ///< UTF-8 codepage +#endif + +#ifndef CP_UTF16 +#define CP_UTF16 1200 ///< UTF-16 codepage +#endif + +#ifdef __NT__ +# ifndef INVALID_FILE_ATTRIBUTES +# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) ///< old Visual C++ compilers were not defining this +# endif +# ifndef BELOW_NORMAL_PRIORITY_CLASS +# define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 ///< \copydoc INVALID_FILE_ATTRIBUTES +# endif +#endif + +#define SUBSTCHAR '_' ///< default char, used if a char cannot be represented in a codepage + +typedef uint32 flags_t; ///< 32-bit flags for each address +typedef uint64 flags64_t; ///< 64-bit flags for each address +typedef ea_t tid_t; ///< type id (for enums, structs, etc) + +typedef uint32 bgcolor_t; ///< background color in RGB +#define DEFCOLOR bgcolor_t(-1) ///< default color (used in function, segment definitions) + +//------------------------------------------------------------------------- +// Command line +//------------------------------------------------------------------------- + +#ifdef __cplusplus +/// Tools for command line parsing +struct channel_redir_t +{ + int fd; ///< channel number + qstring file; ///< file name to redirect to/from. + ///< if empty, the channel must be closed. + int flags; ///< \ref IOREDIR_ +/// \defgroup IOREDIR_ i/o redirection flags +/// used by channel_redir_t::flags +///@{ +#define IOREDIR_INPUT 0x01 ///< input redirection +#define IOREDIR_OUTPUT 0x02 ///< output redirection +#define IOREDIR_APPEND 0x04 ///< append, do not overwrite the output file +#define IOREDIR_QUOTED 0x08 ///< the file name was quoted +///@} + bool is_input(void) const { return (flags & IOREDIR_INPUT) != 0; } + bool is_output(void) const { return (flags & IOREDIR_OUTPUT) != 0; } + bool is_append(void) const { return (flags & IOREDIR_APPEND) != 0; } + bool is_quoted(void) const { return (flags & IOREDIR_QUOTED) != 0; } + int start; ///< begin of the redirection string in the command line + int length; ///< length of the redirection string in the command line +}; +typedef qvector<channel_redir_t> channel_redirs_t; ///< vector of channel_redir_t objects +#else +typedef struct channel_redirs_tag channel_redirs_t; +typedef struct qstrvec_tag qstrvec_t; +#endif + +/// Parse a space separated string (escaping with backslash is supported). +/// \param[out] args a string vector to hold the results +/// \param[out] redirs map of channel redirections found in cmdline +/// - if nullptr, redirections won't be parsed +/// - if there are syntax errors in redirections, consider them as arguments +/// \param cmdline the string to be parsed +/// \param flags #LP_PATH_WITH_ARGS or 0 +/// \return the number of parsed arguments + +idaman THREAD_SAFE size_t ida_export parse_command_line( + qstrvec_t *args, + channel_redirs_t *redirs, + const char *cmdline, + int flags); + + +/// Copy and expand command line arguments. +/// For '@filename' arguments the file contents are inserted into the resulting argv. +/// Format of the file: one switch per line, ';' for comment lines +/// On windows, argv will also be interpreted as OEM codepage, and +/// will be decoded as such and re-encoded into UTF-8. +/// \param[out] p_argc size of the returned argv array +/// \param argc number of entries in argv array +/// \param argv array of strings +/// \return new argv (terminated by nullptr). +/// It must be freed with free_argv() + +char **expand_argv(int *p_argc, int argc, const char *const argv[]); + + +/// Free 'argc' elements of 'argv' + +INLINE void free_argv(int argc, char **argv) +{ + int i; + if ( argv != nullptr ) + { + for ( i = 0; i < argc; i++ ) + qfree(argv[i]); + qfree(argv); + } +} + + +/// Quote a command line argument if it contains escape characters. +/// For example, *.c will be converted into "*.c" because * may be inadvertently +/// expanded by the shell +/// \return true: modified 'arg' + +idaman bool ida_export quote_cmdline_arg(qstring *arg); + +//------------------------------------------------------------------------- +// Command-line tools +//------------------------------------------------------------------------- +typedef void cliopt_handler_t(const char *value, void *ud); +typedef void cliopt_poly_handler_t(int argc, const char **argv, void *ud); +struct cliopt_t +{ + char shortname; + const char *longname; + const char *help; + cliopt_handler_t *handler; + int nargs; // number of arguments. Can be 0, 1 or -1. + // If '-1', it means 'poly_handler' will be used +}; +DECLARE_TYPE_AS_MOVABLE(cliopt_t); + +struct cliopts_t; +#ifndef SWIG +# define DEFINE_CLIOPTS_T_HELPERS(decl) \ + decl void ida_export cliopts_t_add(cliopts_t &, const cliopt_t *, size_t); \ + decl int ida_export cliopts_t_apply(cliopts_t &, int, const char *[], void *); \ + decl const cliopt_t *ida_export cliopts_t_find_short(const cliopts_t &, char); \ + decl const cliopt_t *ida_export cliopts_t_find_long(const cliopts_t &, const char *); \ + decl NORETURN void ida_export cliopts_t_usage(const cliopts_t &, bool); +#else +# define DEFINE_CLIOPTS_T_HELPERS(decl) +#endif // SWIG +DEFINE_CLIOPTS_T_HELPERS(idaman) + +struct cliopts_t : public qvector<cliopt_t> +{ + qstring prog_name; + qstring epilog; + typedef AS_PRINTF(1, 2) int usage_printer_t(const char *format, ...); + usage_printer_t *printer; + bool print_usage; + + cliopts_t(usage_printer_t *_printer, bool _print_usage = true) + : printer(_printer) + , print_usage(_print_usage) + {} + + void add(const cliopt_t *opts, size_t nopts) { cliopts_t_add(*this, opts, nopts); } + int apply(int argc, const char *argv[], void *ud=nullptr) { return cliopts_t_apply(*this, argc, argv, ud); } + const cliopt_t *find_short(char shortname) const { return cliopts_t_find_short(*this, shortname); } + const cliopt_t *find_long(const char *longname) const { return cliopts_t_find_long(*this, longname); } + void usage(bool is_error=true) const { return cliopts_t_usage(*this, is_error); } + +private: + DEFINE_CLIOPTS_T_HELPERS(friend); +}; + +struct plugin_option_t; +#ifndef SWIG +# define DEFINE_PLUGIN_OPTION_T_HELPERS(decl) \ + decl bool ida_export plugin_option_t_get_bool(const plugin_option_t *, bool *, const char *, bool); +#else +# define DEFINE_PLUGIN_OPTION_T_HELPERS(decl) +#endif // SWIG + +DEFINE_PLUGIN_OPTION_T_HELPERS(idaman) +/// Named option, supports two kinds of options: +/// string option: <name>=<value> +/// bool option: <name>=[on|off] +struct plugin_option_t +{ + qstring name; + qstring value; + const char *get_value(const char *default_value) const + { + return value.empty() ? default_value : value.c_str(); + } + bool get_string(qstring *out, const char *desired_name, const char *default_value) const + { + if ( name != desired_name ) + return false; + if ( out != nullptr ) + *out = get_value(default_value); + return true; + } + bool get_bool(bool *out, const char *desired_name, bool default_value) const + { + return plugin_option_t_get_bool(this, out, desired_name, default_value); + } + +private: + DEFINE_PLUGIN_OPTION_T_HELPERS(friend); +}; +DECLARE_TYPE_AS_MOVABLE(plugin_option_t); +typedef qvector<plugin_option_t> plugin_option_vec_t; + +struct plugin_options_t : public plugin_option_vec_t +{ + const plugin_option_t *find(const qstring &name) const + { + for ( const auto &one : *this ) + if ( one.name == name ) + return &one; + return nullptr; + } + + bool erase(const char *name) + { + for ( size_t i = 0; i < size(); ++i ) + { + if ( at(i).name == name ) + { + qvector::erase(begin() + i); + return true; + } + } + return false; + } +}; + +/// Parse plugin options from IDA command line specified by -O<plugin_name>:<optstring> +/// Note such options can be used not only for plugins, for example, +/// currently we use them for merge (-Omerge:...) +/// and vault server credentials (-Ovault:...) +/// \param[out] opts pointer to vector for parsed options +/// \param optstring option string <name1>=<value1>:... +idaman bool ida_export parse_plugin_options(plugin_options_t *opts, const char *optstring); + +/// Build the plugin options, suitable for IDA command line of the form `<name1>=<value1>:...` +/// If `optname` is provided, the `-O<optname>:` token will be prepended +/// \param[out] out the output buffer +/// \param opts the plugin options to serialize +/// \param optname the optional option name to prepend to the plugin options string +idaman void ida_export build_plugin_options(qstring *out, const plugin_options_t &opts, const char *optname=nullptr); + +//------------------------------------------------------------------------- +// INSTANT DEBUGGING +//------------------------------------------------------------------------- + +#ifdef __cplusplus +/// Options for instant debugging +struct instant_dbgopts_t +{ + qstring debmod; ///< name of debugger module + qstring env; ///< config variables for debmod. example: DEFAULT_CPU=13;MAXPACKETSIZE=-1 + qstring host; ///< remote hostname (if remote debugging) + qstring pass; ///< password for the remote debugger server + int port = 0; ///< port number for the remote debugger server + int pid = -1; ///< process to attach to (-1: ask the user) + int event_id = -1; ///< event to trigger upon attaching + bool attach = false; ///< should attach to a process? +}; +#else +struct instant_dbgopts_t; +#endif + +/// Parse the -r command line switch (for instant debugging). +/// r_switch points to the value of the -r switch. Example: win32@localhost+ +/// \return true-ok, false-parse error + +idaman bool ida_export parse_dbgopts(struct instant_dbgopts_t *ido, const char *r_switch); + + +//------------------------------------------------------------------------- +// PROCESSES +//------------------------------------------------------------------------- + +/// Information for launching a process with IDA API +/// Note: all string data such as paths (e.g., 'path', 'args' & 'startdir') +/// or 'env' should be UTF-8 encoded. +struct launch_process_params_t +{ + size_t cb = sizeof(*this); ///< size of this structure + int flags = 0; ///< \ref LP_ +/// \defgroup LP_ Launch process flags +/// used by launch_process_params_t::flags +///@{ +#define LP_NEW_CONSOLE 0x0001 ///< create new console (only ms windows) +#define LP_TRACE 0x0002 ///< debug: unix: ptrace(TRACEME), windows: DEBUG_PROCESS +#define LP_PATH_WITH_ARGS 0x0004 ///< 'args' contains executable path too +#define LP_USE_SHELL 0x0008 ///< use shell to launch the command. + ///< 'path' is ignored in this case. +#define LP_LAUNCH_32_BIT 0x0010 ///< prefer to launch 32-bit part of file (only mac) +#define LP_LAUNCH_64_BIT 0x0020 ///< prefer to launch 64-bit part of file (only mac); + ///< only one of LP_LAUNCH_*_BIT bits can be specified +#define LP_NO_ASLR 0x0040 ///< disable ASLR (mac and linux) +#define LP_DETACH_TTY 0x0080 ///< detach the current tty (unix) +#define LP_HIDE_WINDOW 0x0100 ///< tries to hide new window on startup (only windows) +#define LP_SUSPENDED 0x0200 ///< suspends the process on startup (only mac) +#define LP_DETACHED 0x0400 ///< no need to reap the child (this bit is ignored on windows) +#define LP_REPLACE_ENV 0x0800 ///< completely replace env for process +///@} + const char *path = nullptr; ///< file to run + const char *args = nullptr; ///< command line arguments + ssize_t in_handle = -1; ///< handle for stdin or -1 + ssize_t out_handle = -1; ///< handle for stdout or -1 + ssize_t err_handle = -1; ///< handle for stderr or -1 + char *env = nullptr; ///< zero separated environment variables that will be appended + ///< to the existing environment block (existing variables will be updated). + ///< each variable has the following form: var=value\0 + ///< must be terminated with two zero bytes! + const char *startdir = nullptr;///< current directory for the new process + void *info = nullptr; ///< os specific info (on windows it points to PROCESS_INFORMATION) + ///< on unix, not used +}; + +/// Launch the specified process in parallel. +/// \return handle (unix: child pid), nullptr - error + +#ifdef __cplusplus +idaman THREAD_SAFE void *ida_export launch_process( + const launch_process_params_t &lpp, + qstring *errbuf=nullptr); +#else +idaman THREAD_SAFE void *ida_export launch_process( + const struct launch_process_params_t *lpp, + qstring *errbuf); +#endif + + +/// Forcibly terminate a running process. +/// \return 0-ok, otherwise an error code that can be passed to winerr() + +idaman THREAD_SAFE int ida_export term_process(void *handle); + + +/// Wait for state changes in a child process (UNIX only). +/// Here: child, status, flags - the same as in system call waitpid() +/// Param 'timeout_ms' is a timeout in milliseconds +/// \return PID of the process with the changed status + +idaman THREAD_SAFE int ida_export qwait_timed(int *status, int child, int flags, int timeout_ms); + +#if defined(__UNIX__) +# ifdef WCONTINUED +# define QWCONTINUED WCONTINUED +# else +# define QWCONTINUED 8 +# endif +# ifdef WNOHANG +# define QWNOHANG WNOHANG +# else +# define QWNOHANG 1 +# endif +inline THREAD_SAFE int qwait(int *status, int child, int flags) +{ + return qwait_timed(status, child, flags, (flags & QWNOHANG) != 0 ? 0 : -1); +} +#endif + + +/// Check whether process has terminated or not. +/// \param handle process handle to wait for +/// \param[out] exit_code pointer to the buffer for the exit code +/// \param msecs how long to wait. special values: +/// - 0: do not wait +/// - 1 or -1: wait infinitely +/// - other values: timeout in milliseconds +/// \retval 0 process has exited, and the exit code is available. +/// if *exit_code < 0: the process was killed with a signal -*exit_code +/// \retval 1 process has not exited yet +/// \retval -1 error happened, see error code for winerr() in *exit_code + +idaman THREAD_SAFE int ida_export check_process_exit( + void *handle, + int *exit_code, + DEFARG(int msecs,-1)); + +/// Teletype control +enum tty_control_t +{ + TCT_UNKNOWN = 0, + TCT_OWNER, + TCT_NOT_OWNER +}; + + +/// Check if the current process is the owner of the TTY specified +/// by 'fd' (typically an opened descriptor to /dev/tty). + +idaman THREAD_SAFE enum tty_control_t ida_export is_control_tty(int fd); + + +/// If the current terminal is the controlling terminal of the calling +/// process, give up this controlling terminal. +/// \note The current terminal is supposed to be /dev/tty + +idaman THREAD_SAFE void ida_export qdetach_tty(void); + + +/// Make the current terminal the controlling terminal of the calling +/// process. +/// \note The current terminal is supposed to be /dev/tty + +idaman THREAD_SAFE void ida_export qcontrol_tty(void); + +//------------------------------------------------------------------------- +/// THREADS +//------------------------------------------------------------------------- + +/// Thread callback function +typedef int idaapi qthread_cb_t(void *ud); + +/// Thread opaque handle +#ifdef __cplusplus +#define OPAQUE_HANDLE(n) typedef struct __ ## n {} *n +#else +#define OPAQUE_HANDLE(n) typedef struct __ ## n { char __dummy; } *n +#endif +OPAQUE_HANDLE(qthread_t); + + +/// Create a thread and return a thread handle + +idaman THREAD_SAFE qthread_t ida_export qthread_create(qthread_cb_t *thread_cb, void *ud); + + +/// Free a thread resource (does not kill the thread) +/// (calls pthread_detach under unix) + +idaman THREAD_SAFE void ida_export qthread_free(qthread_t q); + + +/// Wait a thread until it terminates + +idaman THREAD_SAFE bool ida_export qthread_join(qthread_t q); + + +/// Forcefully kill a thread (calls pthread_cancel under unix) + +idaman THREAD_SAFE bool ida_export qthread_kill(qthread_t q); + + +/// Get current thread. Must call qthread_free() to free it! + +idaman THREAD_SAFE qthread_t ida_export qthread_self(void); + + +/// Is the current thread the same as 'q'? + +idaman THREAD_SAFE bool ida_export qthread_same(qthread_t q); + + +/// Are two threads equal? + +idaman THREAD_SAFE bool ida_export qthread_equal(qthread_t q1, qthread_t q2); + + +/// Are we running in the main thread? + +idaman THREAD_SAFE bool ida_export is_main_thread(void); + + +/// Thread safe function to work with the environment + +idaman THREAD_SAFE bool ida_export qsetenv(const char *varname, const char *value); +idaman THREAD_SAFE bool ida_export qgetenv(const char *varname, DEFARG(qstring *buf, nullptr)); ///< \copydoc qsetenv + + +//------------------------------------------------------------------------- +/// Semaphore. +/// Named semaphores are public, nameless ones are local to the process +//------------------------------------------------------------------------- +OPAQUE_HANDLE(qsemaphore_t); + +idaman THREAD_SAFE qsemaphore_t ida_export qsem_create(const char *name, int init_count); ///< Create a new semaphore +idaman THREAD_SAFE bool ida_export qsem_free(qsemaphore_t sem); ///< Free a semaphore +idaman THREAD_SAFE bool ida_export qsem_post(qsemaphore_t sem); ///< Unlock a semaphore +idaman THREAD_SAFE bool ida_export qsem_wait(qsemaphore_t sem, int timeout_ms); ///< Lock and decrement a semaphore. timeout = -1 means block indefinitely + +//------------------------------------------------------------------------- +/// Mutex +//------------------------------------------------------------------------- +OPAQUE_HANDLE(qmutex_t); +idaman THREAD_SAFE bool ida_export qmutex_free(qmutex_t m); ///< Free a mutex +idaman THREAD_SAFE qmutex_t ida_export qmutex_create(void); ///< Create a new mutex +idaman THREAD_SAFE bool ida_export qmutex_lock(qmutex_t m); ///< Lock a mutex +idaman THREAD_SAFE bool ida_export qmutex_unlock(qmutex_t m); ///< Unlock a mutex + + +#ifdef __cplusplus +/// Mutex locker object. Will lock a given mutex upon creation and unlock it when the object is destroyed +class qmutex_locker_t +{ + qmutex_t lock; +public: + qmutex_locker_t(qmutex_t _lock) : lock(_lock) { qmutex_lock(lock); } + ~qmutex_locker_t(void) { qmutex_unlock(lock); } +}; +#endif + +//------------------------------------------------------------------------- +// PIPES +//------------------------------------------------------------------------- +#ifdef __NT__ +typedef void *qhandle_t; ///< MS Windows HANDLE +const qhandle_t NULL_PIPE_HANDLE = nullptr; +#else +typedef int qhandle_t; ///< file handle in Unix +const qhandle_t NULL_PIPE_HANDLE = -1; +#endif + + +/// Create a pipe. +/// \param[out] handles +/// - handles[0] : read handle +/// - handles[1] : write handle +/// \return error code (0-ok) + +idaman THREAD_SAFE int ida_export qpipe_create(qhandle_t handles[2]); + + +/// Read from a pipe. \return number of read bytes. -1-error + +idaman THREAD_SAFE ssize_t ida_export qpipe_read(qhandle_t handle, void *buf, size_t size); + +/// Read a specific amount of bytes from a pipe. +/// \param handle pipe handle to read from +/// \param[out] out_bytes byte vector to which the bytes will be appended +/// \param n number of bytes to read +/// \return success + +idaman THREAD_SAFE bool ida_export qpipe_read_n(qhandle_t handle, bytevec_t *out_bytes, size_t n); + +/// Write to a pipe. \return number of written bytes. -1-error + +idaman THREAD_SAFE ssize_t ida_export qpipe_write(qhandle_t handle, const void *buf, size_t size); + + +/// Close a pipe. \return error code (0-ok) + +idaman THREAD_SAFE int ida_export qpipe_close(qhandle_t handle); + + +/// Launch a process and establish 2-way comminucation with it. +/// The process's stdin and stdout will be redirected to 'out_handles'. +/// \param[out] read_handle handle from which the caller will read. can be nullptr +/// \param[out] write_handle handle to which the caller will write. can be nullptr +/// \param lpp process to run +/// \param[out] errbuf buffer for the error message, if any +/// \return nullptr on error, otherwise the process handle + +idaman void *ida_export pipe_process( + qhandle_t *read_handle, + qhandle_t *write_handle, + launch_process_params_t *lpp, + qstring *errbuf=nullptr); + + +/// Wait for file/socket/pipe handles. +/// \note On Windows this function just calls WaitForMultipleObjects(). +/// So it cannot wait for file/socket/pipe handles. +/// It simply returns 0 and sets idx to 0 for such handles. +/// \param[out] idx handle index +/// \param handles handles to wait for +/// \param n number of handles +/// \param write_bitmask bitmask of indexes of handles opened for writing +/// \param timeout_ms timeout value in milliseconds +/// \return error code. on timeout, returns 0 and sets idx to -1 + +idaman THREAD_SAFE int ida_export qwait_for_handles( + int *idx, + const qhandle_t *handles, + int n, + uint32 write_bitmask, + int timeout_ms); + +/// Get the user name for the current desktop session +/// \param[out] out output buffer +/// \return success + +idaman THREAD_SAFE bool ida_export get_login_name(qstring *out); + +/// Get the total CPU physical core count +/// \return the physical core count, or -1 on error + +idaman THREAD_SAFE int ida_export get_physical_core_count(); + +/// Get the total CPU logical core count +/// \return the logical core count, or -1 on error + +idaman THREAD_SAFE int ida_export get_logical_core_count(); + +/// Get the number of logical CPU cores available to the current process +/// if supported by the OS. +/// \return the logical core count available for the process, or -1 on error + +idaman THREAD_SAFE int ida_export get_available_core_count(); + + +#endif /* _PRO_H */ + +``` + +`IdaSDK/problems.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _PROBLEMS_HPP +#define _PROBLEMS_HPP + +/*! \file problems.hpp + + \brief Functions that deal with the list of problems. + + There are several problem lists. An address may be inserted to any list. + The kernel simply maintains these lists, no additional processing + is done. + + The problem lists are accessible for the user + from the View->Subviews->Problems menu item. + + Addresses in the lists are kept sorted. In general IDA just maintains + these lists without using them during analysis (except PR_ROLLED). + +*/ + +typedef uchar problist_id_t; ///< see \ref PR_ + +/// \defgroup PR_ Problem types +///@{ +const problist_id_t + PR_NOBASE = 1, ///< Can't find offset base + PR_NONAME = 2, ///< Can't find name + PR_NOFOP = 3, ///< Can't find forced op (not used anymore) + PR_NOCMT = 4, ///< Can't find comment (not used anymore) + PR_NOXREFS = 5, ///< Can't find references + PR_JUMP = 6, ///< Jump by table !!!! ignored + PR_DISASM = 7, ///< Can't disasm + PR_HEAD = 8, ///< Already head + PR_ILLADDR = 9, ///< Exec flows beyond limits + PR_MANYLINES = 10, ///< Too many lines + PR_BADSTACK = 11, ///< Failed to trace the value of the stack pointer + PR_ATTN = 12, ///< Attention! Probably erroneous situation. + PR_FINAL = 13, ///< Decision to convert to instruction/data is made by IDA + PR_ROLLED = 14, ///< The decision made by IDA was wrong and rolled back + PR_COLLISION = 15, ///< FLAIR collision: the function with the given name already exists + PR_DECIMP = 16, ///< FLAIR match indecision: the patterns matched, but not the function(s) being referenced + PR_END = 17; ///< Number of problem types +///@} + +/// Get the human-friendly description of the problem, +/// if one was provided to remember_problem. +/// \param buf a buffer to store the message into. +/// \param t problem list type. +/// \param ea linear address. +/// \return the message length or -1 if none + +idaman ssize_t ida_export get_problem_desc(qstring *buf, problist_id_t t, ea_t ea); + + +/// Insert an address to a list of problems. +/// Display a message saying about the problem (except of ::PR_ATTN,::PR_FINAL) +/// ::PR_JUMP is temporarily ignored. +/// \param type problem list type +/// \param ea linear address +/// \param msg a user-friendly message to be displayed instead of +/// the default more generic one associated with +/// the type of problem. Defaults to nullptr. + +idaman void ida_export remember_problem(problist_id_t type, ea_t ea, const char *msg = nullptr); + + +/// Get an address from the specified problem list. +/// The address is not removed from the list. +/// \param type problem list type +/// \param lowea the returned address will be higher or equal +/// than the specified address +/// \return linear address or #BADADDR + +idaman ea_t ida_export get_problem(problist_id_t type, ea_t lowea); + + +/// Remove an address from a problem list +/// \param type problem list type +/// \param ea linear address +/// \return success + +idaman bool ida_export forget_problem(problist_id_t type, ea_t ea); + + +/// Get problem list description + +idaman const char *ida_export get_problem_name(problist_id_t type, bool longname=true); + + +/// Check if the specified address is present in the problem list + +idaman bool ida_export is_problem_present(problist_id_t t, ea_t ea); + + +inline bool was_ida_decision(ea_t ea) { return is_problem_present(PR_FINAL, ea); } + + +#endif // _PROBLEMS_HPP + +``` + +`IdaSDK/prodir.h`: + +```h +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _PRODIR_H +#define _PRODIR_H + +/*! \file prodir.h + + \brief Unified interface to qfindfirst(),qfindnext(),qfindclose() functions. + + These are low level functions, it is better to use enumerate_files(). +*/ + +/// \def{DIRCHAR, Path separator} +/// \def{SDIRCHAR, Path separator as a string} +/// \def{DRVCHAR, Windows drive separator} +#ifdef __NT__ +#define __FAT__ +#define SDIRCHAR "\\" +#define DIRCHAR '\\' +#define DRVCHAR ':' +#else +#define SDIRCHAR "/" +#define DIRCHAR '/' +#endif + +/// Extension character is '.' for all systems +#define EXTCHAR '.' + +//---------------------------------------------------------------------------- +/// \struct{qffblk_t, Various file statistics returned by qfind()-like functions} +/// \var{qffblk_t::ff_name, file path} +/// \var{qffblk_t::ff_fsize, file size} +/// \var{qffblk_t::ff_attrib, file attribute} +/// \var{qffblk_t::ff_ftime, file time stamp (ms dos fat format)} +/// \var{qffblk_t::ff_fdate, file date stamp (ms dos fat format)} +/// \def{FA_RDONLY, File cannot be opened for writing} +/// \def{FA_DIREC, Directory} +/// \def{FA_ARCH, File has not been backed up} +/// \def{MAXPATH, Size limit of qffblk_t::ff_name} +#if defined(__UNIX__) + #define MAXPATH QMAXPATH + struct qffblk_t // Unix + { + // user fields: + int ff_attrib; + #define FA_DIREC S_IFDIR + #define FA_ARCH 0 + #define FA_RDONLY 0 + char ff_name[QMAXPATH]; + uint32 ff_fsize; + uint16 ff_fdate; + uint16 ff_ftime; + // private fields: + void *filelist; + int fileidx, fileqty; + char dirpath[QMAXPATH]; + char pattern[QMAXPATH]; + int attr; + qffblk_t(void) : filelist(nullptr), fileqty(0) {} + }; +#elif !defined(__X86__) + // Win64 - use Visual Studio's ffblk + #define MAXPATH _MAX_PATH + struct qffblk_t + { + // inlined __finddata64_t from Visual Studio (for compatibility with IDA 7.0 API) + unsigned attrib; + // Time values are stored in UTC format. + __time64_t time_create; // -1 for FAT file systems + __time64_t time_access; // -1 for FAT file systems + __time64_t time_write; + __int64 size; + char name[260]; // NB: file name is assumed to be UTF-8 + intptr_t handle; // handle returned by _wfindfirst64 + int attr; // attr value passed to qfindfirst. only FA_DIREC is checked + #define FA_RDONLY 0x01 + #define FA_DIREC 0x10 + #define FA_ARCH 0x20 + #define ff_name name + #define ff_attrib attrib + #define ff_fsize size + unsigned short ff_ftime; // FAT/DOS format modification time + unsigned short ff_fdate; // FAT/DOS format modification date + qffblk_t(void) : handle(-1) {} + }; + CASSERT(sizeof(qffblk_t) == 0x140); +#else + #define MAXPATH 260 + struct qffblk_t // Win32 - use Borland's ffblk (IDA 6.95 API compatibility) + { + long ff_reserved; + long ff_fsize; + unsigned long ff_attrib; + #define FA_RDONLY 0x01 + #define FA_DIREC 0x10 + #define FA_ARCH 0x20 + unsigned short ff_ftime; + unsigned short ff_fdate; + char ff_name[MAXPATH]; + qffblk_t(void) : ff_reserved(0) {} + }; +#endif + +/// \def{MAXDRIVE, Max drive name size} +/// \def{MAXDIR, Max directory name size} +/// \def{MAXFILE, Max file name size} +/// \def{MAXEXT, Max file extension size} +#if defined(__UNIX__) + #define MAXDRIVE QMAXPATH + #define MAXDIR QMAXPATH + #define MAXFILE QMAXPATH + #define MAXEXT QMAXPATH +#else + #define MAXDRIVE _MAX_DRIVE + #define MAXDIR _MAX_DIR + #define MAXFILE _MAX_FNAME + #define MAXEXT _MAX_EXT +#endif + +/// Find first file that matches the pattern. +/// \param pattern file name pattern, usually with * and ? wildcards +/// \param blk structure that will hold the answer. +/// blk->ff_name will hold the file name, for example. +/// \param attr the desired file types (#FA_DIREC for directories only or 0 for both directories and files) +/// \return 0 if found a file, other values mean error (check qerrno) + +idaman THREAD_SAFE int ida_export qfindfirst( + const char *pattern, + struct qffblk64_t *blk, + int attr); + + +/// Find next file that matches the pattern. +/// \param blk structure that holds the current state. +/// blk->ff_name will hold the next file name upon return. +/// \return 0 if found the next file, other values mean error (check qerrno) + +idaman THREAD_SAFE int ida_export qfindnext(struct qffblk64_t *blk); + +/// Stop the file enumeration and free internal structures. +/// \note usually there is no need to call this function manually, it is called +/// from the ::qffblk64_t destructor. +/// \param blk file enumeration structure + +idaman THREAD_SAFE void ida_export qfindclose(struct qffblk64_t *blk); + + +/// Common structure with 64-bit ff_fsize - see ::qffblk_t. +struct qffblk64_t +{ + int ff_attrib; + char ff_name[QMAXPATH]; + uint64 ff_fsize; + uint16 ff_fdate; + uint16 ff_ftime; + // private field + struct qffblk_t base; + + qffblk64_t(void) {} + ~qffblk64_t(void) { qfindclose(this); } +}; + +#endif // _PRODIR_H + +``` + +`IdaSDK/pronet.h`: + +```h +// socket API wrappers + +#ifndef __PRONET_H__ +#define __PRONET_H__ + +#ifdef __NT__ +# define WIN32_LEAN_AND_MEAN +# pragma pack(push) +# include <winsock2.h> // may change structure packing?! +# pragma pack(pop) +#else // __NT__ +# include <errno.h> +# include <netdb.h> +# include <poll.h> +# include <sys/socket.h> +# include <sys/select.h> +#endif + +#ifdef __NT__ +#pragma comment(lib, "WS2_32.lib") +#endif + +/*! \file pronet.h + + \brief Network related functions + + Each of the following functions work just like their C standard equivalent, only + they are safer and system independent. +*/ + +//--------------------------------------------------------------------------- +#ifdef __NT__ +# define SIG_SAFE_CALL(expr) return expr +# define SOCKLEN_T int +# define SOCKBUF_T char * +#else +# define SIG_SAFE_CALL(expr) \ + do \ + { \ + long rc = expr; \ + if ( rc != -1 || errno != EINTR ) \ + return rc; \ + } \ + while ( true ) +# define SOCKLEN_T socklen_t +# define SOCKBUF_T void * +#endif + +//--------------------------------------------------------------------------- +inline ssize_t qsendto(int socket, const SOCKBUF_T buf, size_t size, int flags, const struct sockaddr *dest_addr, SOCKLEN_T addrlen) +{ + SIG_SAFE_CALL(::sendto(socket, buf, size, flags, dest_addr, addrlen)); +} + +//--------------------------------------------------------------------------- +inline ssize_t qrecvfrom(int socket, SOCKBUF_T buf, size_t size, int flags, struct sockaddr *src_addr, SOCKLEN_T *addrlen) +{ + SIG_SAFE_CALL(::recvfrom(socket, buf, size, flags, src_addr, addrlen)); +} + +//--------------------------------------------------------------------------- +inline ssize_t qsend(int socket, const void *buf, size_t size) +{ +#ifdef __NT__ + return qsendto(socket, (SOCKBUF_T)buf, size, 0, nullptr, 0); +#else + SIG_SAFE_CALL(::send(socket, buf, size, 0)); +#endif +} + +//--------------------------------------------------------------------------- +inline ssize_t qrecv(int socket, void *buf, size_t size) +{ +#ifdef __NT__ + return qrecvfrom(socket, (SOCKBUF_T)buf, size, 0, nullptr, nullptr); +#else + SIG_SAFE_CALL(::recv(socket, buf, size, 0)); +#endif +} + +//--------------------------------------------------------------------------- +inline int qselect(int nflds, fd_set *rds, fd_set *wds, fd_set *eds, struct timeval *timeout) +{ + SIG_SAFE_CALL(::select(nflds, rds, wds, eds, timeout)); +} + +//------------------------------------------------------------------------- +inline int qpoll(pollfd *fds, uint32 nfds, int timeout_ms) +{ +#ifdef __NT__ + return WSAPoll(fds, nfds, timeout_ms); +#else + SIG_SAFE_CALL(::poll(fds, nfds, timeout_ms)); +#endif +} + +//--------------------------------------------------------------------------- +// Prevent using of the socket functions directly +// (compiler diagnostics: call of overloaded ... is ambiguous) +namespace DONT_USE_FUNCS +{ + inline ssize_t sendto(int, const SOCKBUF_T, size_t, int, const struct sockaddr *, SOCKLEN_T) { return 0; } + inline ssize_t recvfrom(int, SOCKBUF_T, size_t, int, struct sockaddr *, SOCKLEN_T *) { return 0; } + inline ssize_t send(int, const SOCKBUF_T, size_t, int) { return 0; } + inline ssize_t recv(int, SOCKBUF_T, size_t, int) { return 0; } + inline int select(int, fd_set *, fd_set *, fd_set *, struct timeval *) { return 0; } +} +using namespace DONT_USE_FUNCS; + +//------------------------------------------------------------------------- +/// Get the IPv4 or IPv6 address corresponding to the given host. +/// +/// \param out should be of type 'sockaddr_in' or 'sockaddr_in6', depending +/// on the value of 'family'. +/// \param name the host name. +/// \param family either AF_INET or AF_INET6. +/// \param port a port number, or 0 for none. +/// \return true on success, false otherwise +idaman bool ida_export qhost2addr_( + void *out, + const char *name, + ushort family, + ushort port = 0); + +//------------------------------------------------------------------------- +inline bool qhost2addr(struct sockaddr_in *out, const char *name, ushort port = 0) +{ + return qhost2addr_(out, name, AF_INET, port); +} + +//------------------------------------------------------------------------- +inline bool qhost2addr(struct sockaddr_in6 *out, const char *name, ushort port = 0) +{ + return qhost2addr_(out, name, AF_INET6, port); +} + +//------------------------------------------------------------------------- +// Get the local host IP +bool get_my_ip(char out[NI_MAXHOST], const ushort family = AF_INET); + +//------------------------------------------------------------------------- +// Get the local host name (utf-8) +idaman bool ida_export qgethostname(qstring *out); + + +#undef SIG_SAFE_CALL +#undef SOCKLEN_T +#undef SOCKBUF_T + +#endif // __PRONET_H__ + +``` + +`IdaSDK/range.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _RANGE_HPP +#define _RANGE_HPP + +/*! \file range.hpp + + \brief Contains the definition of ::range_t. + + A range is a non-empty continuous range of addresses (specified by + its start and end addresses, the end address is excluded from the + range). + + Ranges are stored in the Btree part of the IDA database. + To learn more about Btrees (Balanced Trees): + http://www.bluerwhite.org/btree/ +*/ + +#ifndef SWIG +struct range_t; +/// Helper function. Should not be called directly! +idaman size_t ida_export range_t_print(const range_t *, char *buf, size_t bufsize); +#endif + +//-------------------------------------------------------------------------- +/// Base class for an range. This class is used as a base class for +/// a class with real information - see segment.hpp for example. +/// The end address is excluded, it points past the range end. +struct range_t +{ + friend size_t ida_export range_t_print(const range_t *cb, char *buf, size_t bufsize); + ea_t start_ea; ///< start_ea included + ea_t end_ea; ///< end_ea excluded + range_t(ea_t ea1=0, ea_t ea2=0) : start_ea(ea1), end_ea(ea2) {} + + /// Compare two range_t instances, based on the start_ea + DECLARE_COMPARISONS(range_t) + { + return start_ea > r.start_ea ? 1 : start_ea < r.start_ea ? -1 : end_ea > r.end_ea ? 1 : end_ea < r.end_ea ? -1 : 0; + } + + /// Is 'ea' in the address range? + bool contains(ea_t ea) const { return start_ea <= ea && end_ea > ea; } + + /// Is every ea in 'r' also in this range_t? + bool contains(const range_t &r) const { return r.start_ea >= start_ea && r.end_ea <= end_ea; } + + /// Is there an ea in 'r' that is also in this range_t? + bool overlaps(const range_t &r) const { return r.start_ea < end_ea && start_ea < r.end_ea; } + + /// Set #start_ea, #end_ea to 0 + void clear() { start_ea = end_ea = 0; } + + /// Is the size of the range_t <= 0? + bool empty() const { return start_ea >= end_ea; } + + /// Get #end_ea - #start_ea + asize_t size() const { return end_ea - start_ea; } + + /// Assign the range_t to the intersection between the range_t and 'r' + void intersect(const range_t &r) + { + if ( start_ea < r.start_ea ) + start_ea = r.start_ea; + if ( end_ea > r.end_ea ) + end_ea = r.end_ea; + if ( end_ea < start_ea ) + end_ea = start_ea; + } + + /// Ensure that the range_t includes 'ea' + void extend(ea_t ea) + { + if ( start_ea > ea ) + start_ea = ea; + if ( end_ea < ea ) + end_ea = ea; + } + + /// Print the range_t. + /// \param buf the output buffer + /// \param bufsize the size of the buffer + size_t print(char *buf, size_t bufsize) const { return range_t_print(this, buf, bufsize); } +}; +DECLARE_TYPE_AS_MOVABLE(range_t); +typedef qvector<range_t> rangevec_base_t; +struct rangevec_t : public rangevec_base_t /// Vector of range_t instances +{ +}; + +//-------------------------------------------------------------------------- +// Various kinds of ranges, see +// \ref idb_event::changing_range_cmt +// \ref idb_event::range_cmt_changed +enum range_kind_t +{ + RANGE_KIND_UNKNOWN, + RANGE_KIND_FUNC, ///< \ref func_t + RANGE_KIND_SEGMENT, ///< \ref segment_t + RANGE_KIND_HIDDEN_RANGE, ///< \ref hidden_range_t +}; + +//-------------------------------------------------------------------------- +/// Helper functions. Should not be called directly! +#ifndef SWIG +#define RANGESET_HELPER_DEFINITIONS(decl) \ +decl bool ida_export rangeset_t_add(rangeset_t *, const range_t &range);\ +decl bool ida_export rangeset_t_sub(rangeset_t *, const range_t &range);\ +decl bool ida_export rangeset_t_add2(rangeset_t *, const rangeset_t &aset);\ +decl bool ida_export rangeset_t_sub2(rangeset_t *, const rangeset_t &aset);\ +decl bool ida_export rangeset_t_has_common(const rangeset_t *, const range_t &range, bool strict);\ +decl bool ida_export rangeset_t_has_common2(const rangeset_t *, const rangeset_t &aset);\ +decl bool ida_export rangeset_t_contains(const rangeset_t *, const rangeset_t &aset);\ +decl size_t ida_export rangeset_t_print(const rangeset_t *, char *buf, size_t bufsize);\ +decl bool ida_export rangeset_t_intersect(rangeset_t *, const rangeset_t &aset);\ +decl const range_t *ida_export rangeset_t_find_range(const rangeset_t *, ea_t ea);\ +decl ea_t ida_export rangeset_t_next_addr(const rangeset_t *, ea_t ea);\ +decl ea_t ida_export rangeset_t_prev_addr(const rangeset_t *, ea_t ea);\ +decl ea_t ida_export rangeset_t_next_range(const rangeset_t *, ea_t ea);\ +decl ea_t ida_export rangeset_t_prev_range(const rangeset_t *, ea_t ea);\ +decl rangevec_t::const_iterator ida_export rangeset_t_lower_bound(const rangeset_t *, ea_t ea);\ +decl rangevec_t::const_iterator ida_export rangeset_t_upper_bound(const rangeset_t *, ea_t ea);\ +decl void ida_export rangeset_t_swap(rangeset_t *, rangeset_t &r); +#else +#define RANGESET_HELPER_DEFINITIONS(decl) +#endif // SWIG + +class rangeset_t; + +RANGESET_HELPER_DEFINITIONS(idaman) + +/// An ordered set of non-overlapping address ranges +class rangeset_t +{ + rangevec_t bag; + mutable const range_t *cache; + int undo_code = -1; + + RANGESET_HELPER_DEFINITIONS(friend) + bool verify(void) const; +public: + DEFINE_MEMORY_ALLOCATION_FUNCS() + /// Constructor + rangeset_t(void) : cache(nullptr) {} + /// Constructor - Initialize set with 'range' + rangeset_t(const range_t &range): cache(nullptr) { if ( !range.empty() ) bag.push_back(range); } + /// Constructor - Initialize set with 'ivs' + rangeset_t(const rangeset_t &ivs) : bag(ivs.bag), cache(nullptr) {} + rangeset_t &operator=(const rangeset_t &ivs) { bag = ivs.bag; cache = nullptr; return *this; } + /// Set this = 'r' and 'r' = this. See qvector::swap() + void swap(rangeset_t &r) { rangeset_t_swap(this, r); } + + /// Add an address range to the set. + /// If 'range' intersects an existing element e, then e is extended + /// to include 'range', and any superfluous elements (subsets of e) are removed. + /// \param range address range to add. cannot be empty + /// \return false if 'range' was not added (the set was unchanged) + bool add(const range_t &range) { return rangeset_t_add(this, range); } + + /// Create a new range_t from 'start' and 'end' and add it to the set + bool add(ea_t start, ea_t _end) { return add(range_t(start, _end)); } + + /// Add each element of 'aset' to the set. + /// \return false if no elements were added (the set was unchanged) + bool add(const rangeset_t &aset) { return rangeset_t_add2(this, aset); } + + /// Subtract an address range from the set. + /// All subsets of 'range' will be removed, and all elements that intersect + /// 'range' will be truncated/split so they do not include 'range'. + /// \param range address range to subtract. cannot be empty. + /// \return false if 'range' was not subtracted (the set was unchanged) + bool sub(const range_t &range) { return rangeset_t_sub(this, range); } + + /// Subtract an ea (an range of size 1) from the set. See sub(const range_t &) + bool sub(ea_t ea) { return sub(range_t(ea, ea+1)); } + + /// Subtract each range in 'aset' from the set + /// \return false if nothing was subtracted (the set was unchanged) + bool sub(const rangeset_t &aset) { return rangeset_t_sub2(this, aset); } + + /// Is there an ea in 'range' that is also in the rangeset? + bool has_common(const range_t &range) const + { return rangeset_t_has_common(this, range, false); } + + /// Is every ea in 'range' contained in the rangeset? + bool includes(const range_t &range) const + { return rangeset_t_has_common(this, range, true); } + + /// Print each range_t in the rangeset + size_t print(char *buf, size_t bufsize) const + { return rangeset_t_print(this, buf, bufsize); } + + /// Size in bytes + asize_t count(void) const; + + /// Get the range_t at index 'idx' + const range_t &getrange(int idx) const { return bag[idx]; } + + /// Get the last range_t in the set + const range_t &lastrange(void) const { return bag.back(); } + + /// Get the number of range_t elements in the set + size_t nranges(void) const { return bag.size(); } + + /// Does the set have zero elements + bool empty(void) const { return bag.empty(); } + + /// Delete all elements from the set. See qvector::clear() + void clear(void) { bag.clear(); cache = nullptr; } + + /// Does any element of 'aset' overlap with an element in this rangeset?. See range_t::overlaps() + bool has_common(const rangeset_t &aset) const + { return rangeset_t_has_common2(this, aset); } + + /// Does an element of the rangeset contain 'ea'? See range_t::contains(ea_t) + bool contains(ea_t ea) const { return !empty() && find_range(ea) != nullptr; } + + /// Is every element in 'aset' contained in an element of this rangeset?. See range_t::contains(range_t) + bool contains(const rangeset_t &aset) const + { return rangeset_t_contains(this, aset); } + + /// Set the rangeset to its intersection with 'aset'. + /// \return false if the set was unchanged + bool intersect(const rangeset_t &aset) + { return rangeset_t_intersect(this, aset); } + + /// Is every element in the rangeset contained in an element of 'aset'? + bool is_subset_of(const rangeset_t &aset) const { return aset.contains(*this); } + + /// Do this rangeset and 'aset' have identical elements? + bool is_equal(const rangeset_t &aset) const { return bag == aset.bag; } + + bool operator==(const rangeset_t &aset) const { return is_equal(aset); } ///< Compare two rangesets with '==' + bool operator!=(const rangeset_t &aset) const { return !is_equal(aset); } ///< Compare two rangesets with '!=' + + typedef rangevec_t::iterator iterator; ///< Iterator for rangesets + typedef rangevec_t::const_iterator const_iterator; ///< Const iterator for rangesets + const_iterator begin(void) const { return bag.begin(); } ///< Get an iterator that points to the first element in the set + const_iterator end(void) const { return bag.end(); } ///< Get an iterator that points to the end of the set. (This is NOT the last element) + iterator begin(void) { return bag.begin(); } ///< \copydoc begin + iterator end(void) { return bag.end(); } ///< \copydoc end + + /// Get the first range that contains at least one ea_t value greater than 'ea' + const_iterator lower_bound(ea_t ea) const { return rangeset_t_lower_bound(this, ea); } + + /// Get the first range such that every ea_t value in this range is strictly greater than 'ea' + const_iterator upper_bound(ea_t ea) const { return rangeset_t_upper_bound(this, ea); } + + /// Get the element from the set that contains 'ea'. + /// \return nullptr if there is no such element + const range_t *find_range(ea_t ea) const + { return rangeset_t_find_range(this, ea); } + + /// When searching the rangeset, we keep a cached element to help speed up searches. + /// \return a pointer to the cached element + const range_t *cached_range(void) const { return cache; } + + /// Get the smallest ea_t value greater than 'ea' contained in the rangeset + ea_t next_addr(ea_t ea) const { return rangeset_t_next_addr(this, ea); } + + /// Get the largest ea_t value less than 'ea' contained in the rangeset + ea_t prev_addr(ea_t ea) const { return rangeset_t_prev_addr(this, ea); } + + /// Get the smallest ea_t value greater than 'ea' that is not in the same range as 'ea' + ea_t next_range(ea_t ea) const { return rangeset_t_next_range(this, ea); } + + /// Get the largest ea_t value less than 'ea' that is not in the same range as 'ea' + ea_t prev_range(ea_t ea) const { return rangeset_t_prev_range(this, ea); } + + /// Subtract the address range (from, from+size) and add the range (to, to+size) + int move_chunk(ea_t from, ea_t to, asize_t size); + + /// Check if the intended move_chunk() arguments are correct. + int check_move_args(ea_t from, ea_t to, asize_t size); // returns VAMOVE_... + + /// Return underlying rangevec_t object + const rangevec_t &as_rangevec() const + { return bag; } +}; +DECLARE_TYPE_AS_MOVABLE(rangeset_t); +typedef qvector<rangeset_t> array_of_rangesets; ///< Array of rangeset_t objects +typedef qvector<const rangeset_t*> rangeset_crefvec_t; + +//-------------------------------------------------------------------------- +struct range64_t +{ + uint64 start; + uint64 end; + + range64_t(uint64 _start=0, uint64 _end=0) : start(_start), end(_end) {} + DECLARE_COMPARISONS(range64_t) { return start > r.start ? 1 : start < r.start ? -1 : end > r.end ? 1 : end < r.end ? -1 : 0; } + bool contains(uint64 x) const { return start <= x && end > x; } + bool contains(const range64_t &r) const { return r.start >= start && r.end <= end; } + bool overlaps(const range64_t &r) const { return r.start < end && start < r.end; } + void clear() { start = end = 0; } + bool empty() const { return start >= end; } + uint64 size() const { return end - start; } + void intersect(const range64_t &r) + { + if ( start < r.start ) + start = r.start; + if ( end > r.end ) + end = r.end; + if ( end < start ) + end = start; + } + void extend(uint64 ea) + { + if ( start > ea ) + start = ea; + if ( end < ea ) + end = ea; + } +}; +DECLARE_TYPE_AS_MOVABLE(range64_t); + +struct range64vec_t : public qvector<range64_t> +{ + const range64_t *find_range(uint64 off) const + { + for ( const range64_t &r : *this ) + if ( r.contains(off) ) + return &r; + return nullptr; + } +}; + +#endif // _RANGE_HPP + +``` + +`IdaSDK/regex.h`: + +```h +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE2 is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Original API code Copyright (c) 1997-2012 University of Cambridge + New API code Copyright (c) 2016 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +#ifndef _REGEX_H_ +#define _REGEX_H_ +#ifdef __cplusplus +#include <kernwin.hpp> +#endif + +#include <pro.h> + +#ifdef __GNUC__ +#undef __P +#endif + +typedef off_t regoff_t; +/* The structure representing a compiled regular expression. */ + +struct regex_t +{ + int re_magic; + size_t re_nsub; /* number of parenthesized subexpressions */ + const char *re_endp; /* end pointer for REG_PEND */ + void *re_g; /* none of your business :-) */ +}; + +/* The structure in which a captured offset is returned. */ + +struct regmatch_t +{ + regoff_t rm_so; /* start of match */ + regoff_t rm_eo; /* end of match */ +}; +#ifndef REG_ICASE +/* Options, mostly defined by POSIX, but with some extras. */ + +#define REG_ICASE 0x0001 /* Maps to PCRE2_CASELESS */ +#define REG_NEWLINE 0x0002 /* Maps to PCRE2_MULTILINE */ +#define REG_NOTBOL 0x0004 /* Maps to PCRE2_NOTBOL */ +#define REG_NOTEOL 0x0008 /* Maps to PCRE2_NOTEOL */ +#define REG_DOTALL 0x0010 /* NOT defined by POSIX; maps to PCRE2_DOTALL */ +#define REG_NOSUB 0x0020 /* Maps to PCRE2_NO_AUTO_CAPTURE */ +#define REG_UTF 0x0040 /* NOT defined by POSIX; maps to PCRE2_UTF */ +#define REG_STARTEND 0x0080 /* BSD feature: pass subject string by so,eo */ +#define REG_NOTEMPTY 0x0100 /* NOT defined by POSIX; maps to PCRE2_NOTEMPTY */ +#define REG_UNGREEDY 0x0200 /* NOT defined by POSIX; maps to PCRE2_UNGREEDY */ +#define REG_UCP 0x0400 /* NOT defined by POSIX; maps to PCRE2_UCP */ +#define REG_PEND 0x0800 /* GNU feature: pass end pattern by re_endp */ +#define REG_NOSPEC 0x1000 /* Maps to PCRE2_LITERAL */ + +/* This is not used by PCRE2, but by defining it we make it easier +to slot PCRE2 into existing programs that make POSIX calls. */ + +#define REG_EXTENDED 0 +#define REG_TRACE 0 // unsupported by PCRE2 + +/* Error values. Not all these are relevant or used by the wrapper. */ + + +enum +{ + REG_ASSERT = 1, /* internal error ? */ + REG_BADBR, /* invalid repeat counts in {} */ + REG_BADPAT, /* pattern error */ + REG_BADRPT, /* ? * + invalid */ + REG_EBRACE, /* unbalanced {} */ + REG_EBRACK, /* unbalanced [] */ + REG_ECOLLATE, /* collation error - not relevant */ + REG_ECTYPE, /* bad class */ + REG_EESCAPE, /* bad escape sequence */ + REG_EMPTY, /* empty expression */ + REG_EPAREN, /* unbalanced () */ + REG_ERANGE, /* bad range inside [] */ + REG_ESIZE, /* expression too big */ + REG_ESPACE, /* failed to get memory */ + REG_ESUBREG, /* bad back reference */ + REG_INVARG, /* bad argument */ + REG_NOMATCH /* match failed */ +}; +#endif //REG_ICASE + +/* The functions */ + +// compile the regular expression +idaman THREAD_SAFE int ida_export qregcomp( + struct regex_t *preg, + const char *pattern, + int cflags); + +// mapping from error codes returned by qregcomp() and qregexec() to a string +idaman THREAD_SAFE size_t ida_export qregerror( + int errcode, + const struct regex_t *preg, + char *errbuf, + size_t errbuf_size); + +// match regex against a string +idaman THREAD_SAFE int ida_export qregexec( + const struct regex_t *preg, + const char *str, + size_t nmatch, + struct regmatch_t pmatch[], + int eflags); + +// free any memory allocated by qregcomp +idaman THREAD_SAFE void ida_export qregfree(struct regex_t *preg); + + +#ifdef __cplusplus + +//------------------------------------------------------------------------- +class refcnted_regex_t : public qrefcnt_obj_t +{ + regex_t regex; + + refcnted_regex_t() + { + regex = {}; + } + virtual ~refcnted_regex_t() + { + qregfree(®ex); + } +public: + virtual void idaapi release(void) override + { + delete this; + } + int exec(const char *string, size_t nmatch, regmatch_t pmatch[], int eflags) + { + return qregexec(®ex, string, nmatch, pmatch, eflags); + } + int process_errors(int code, qstring *errmsg) + { + if ( code != 0 && errmsg != nullptr ) + { + char errbuf[MAXSTR]; + qregerror(code, ®ex, errbuf, sizeof(errbuf)); + *errmsg = errbuf; + } + return code; + } + static refcnted_regex_t *create( + const qstring &text, + bool case_insensitive, + qstring *errmsg) + { + if ( text.empty() ) + return nullptr; + refcnted_regex_t *p = new refcnted_regex_t(); + int rflags = REG_EXTENDED; + if ( case_insensitive ) + rflags |= REG_ICASE; + int code = qregcomp(&p->regex, text.begin(), rflags); + if ( p->process_errors(code, errmsg) != 0 ) + { + // It is unnecessary to qregfree() here: the deletion of 'p' will + // call qregfree (but anyway, even that is unnecessary, because + // if we end up here, it means qregcomp() failed, and when that + // happens, qregcomp() frees the regex itself.) + delete p; + p = nullptr; + } + return p; + } + size_t nsub(void) + { + /* number of parenthesized subexpressions */ + return regex.re_nsub; + } + DECLARE_UNCOPYABLE(refcnted_regex_t); +}; +typedef qrefcnt_t<refcnted_regex_t> regex_ptr_t; + +//--------------------------------------------------------------------------- +struct regex_cache_t +{ + bool _find_or_create(regex_ptr_t **out, const qstring &str, qstring *errbuf=nullptr) + { + regex_cache_map_t::iterator it = cache.find(str); + if ( it == cache.end() ) + { + qstring errmsg; + regex_ptr_t rx = regex_ptr_t(refcnted_regex_t::create(str, false, errbuf)); + if ( rx == nullptr ) + return false; + it = cache.insert(regex_cache_map_t::value_type(str, rx)).first; + } + *out = &it->second; + return true; + } + regex_ptr_t &find_or_create(const qstring &str) + { + regex_ptr_t *ptr; + qstring errbuf; + if ( !_find_or_create(&ptr, str, &errbuf) ) + error("%s", errbuf.c_str()); + return *ptr; + } + +private: + typedef std::map<qstring, regex_ptr_t> regex_cache_map_t; + regex_cache_map_t cache; +}; + +#endif //__cplusplus +#endif /* !_REGEX_H_ */ + +``` + +`IdaSDK/regfinder.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#pragma once + +#include <pro.h> +#include <idp.hpp> +#include <ua.hpp> +#include <memory> // std::unique_ptr +#include <algorithm> // std::sort + +//------------------------------------------------------------------------- +/* A chain is a set of addresses where a register has the same value. + * Inside basic blocks, we collect these addresses by going backward until + * the register changes or may change its value. The instruction that + * changes the register value is not included in the chain. + * These addresses are stored in REG_USES and the value in CHAINS. + * If the result of an instruction that changes a value we are looking for + * depends on another value, we start a new chain. + * Example: + * a:=1 + * | + * *<----\ + * | | + * | a:=a+1 + * | | + * +=====/ + * | + * a? + * + * Chains are constructed in the depth-first way. + * This is the listing for the above example: + * 00 li $v0, 1 # a:=1 + * 04 loc_4: # (*) + * 04 sw $v0, 0($a0) # <body> + * 08 sltiu $v1, $v0, 0xA + * 0C bnezl $v1, loc_4 # (+) + * 10 addiu $v0, 1 # a:=a+1 (the delay slot of a likely branch) + * 14 sw $a0, 0($v0) # a? + * + * after calling find_const($v0, 0x14) we get the following chains: + * 0=>free + * 1=>1@0(li) (no addresses) + * 2=><UNK> (04, 08, 0C, 10, 14) + */ + +//------------------------------------------------------------------------- +struct reg_value_def_t; +#define DECLARE_REG_VALUE_DEF_HELPERS(decl)\ +decl void ida_export reg_value_def_dstr(const reg_value_def_t *_this, qstring *vout, int how, const procmod_t *pm); + +DECLARE_REG_VALUE_DEF_HELPERS(idaman) + +//------------------------------------------------------------------------- +/// the register value and its defining instruction +/// \note A simple add/sub instruction (like PUSH for SP) is not considered +/// as the defining instruction. +struct reg_value_def_t +{ + uint64 val = BADADDR; ///< the value + ea_t def_ea = BADADDR; ///< the instruction address + uint16 def_itype = 0; ///< the instruction code (processor specific) + uint16 flags = 0; ///< additional info about the value + +#define DEF_BIT static constexpr uint16 + DEF_BIT SHORT_INSN = 0x0001; ///< like 'addi reg, imm' + DEF_BIT PC_BASED = 0x0010; ///< the value depends on DEF_EA + ///< only for numbers \sa is_num() + DEF_BIT LIKE_GOT = 0x0020; ///< the value is like GOT + ///< only for numbers \sa is_num() +#undef DEF_BIT + static bool is_short_insn(const insn_t &insn) + { + return insn.Op2.type == o_imm && insn.Op3.type == o_void; + } + + reg_value_def_t() {} + reg_value_def_t(uint64 _val, ea_t ea, uint16 _flags = 0) + : val(_val), def_ea(ea), flags(_flags) {} + reg_value_def_t(uint64 _val, const insn_t &insn, uint16 _flags = 0) + : val(_val), + def_ea(insn.ea), + def_itype(insn.itype), + flags(_flags | (is_short_insn(insn) ? SHORT_INSN : 0)) {} + + bool is_short_insn() const { return (flags & SHORT_INSN) != 0; } + bool is_pc_based() const { return (flags & PC_BASED) != 0; } + bool is_like_got() const { return (flags & LIKE_GOT) != 0; } + + bool operator==(const reg_value_def_t &r) const + { + return def_ea == r.def_ea && val == r.val; + } + bool operator<(const reg_value_def_t &r) const + { + if ( def_ea < r.def_ea ) + return true; + if ( def_ea > r.def_ea ) + return false; + return val < r.val; + } + + /// How to print reg_value_def_t? + enum dstr_val_t + { + NOVAL, ///< without a value + UVAL, ///< as a number + SPVAL, ///< as a SP delta + ABORTED, ///< as an ABORTED value + }; + /// Return the string representation. + qstring dstr(dstr_val_t how, const procmod_t *pm = nullptr) const + { + qstring out; + reg_value_def_dstr(this, &out, how, pm); + return out; + } + +protected: + DECLARE_REG_VALUE_DEF_HELPERS(friend) + + qstring dstr_impl(dstr_val_t how, const procmod_t *pm) const; +}; +DECLARE_TYPE_AS_MOVABLE(reg_value_def_t); + +//------------------------------------------------------------------------- +struct reg_value_info_t; +#define DECLARE_REG_VALUE_INFO_HELPERS(decl)\ +decl int ida_export reg_value_info_vals_union(reg_value_info_t *_this, const reg_value_info_t *r);\ +decl void ida_export reg_value_info_dstr(const reg_value_info_t *_this, qstring *vout, const procmod_t *pm); + +DECLARE_REG_VALUE_INFO_HELPERS(idaman) + +//------------------------------------------------------------------------- +/// the value in a register after emulating instructions +struct reg_value_info_t +{ +protected: + using val_def_t = reg_value_def_t; + friend struct reg_finder_block_t; // for join_values() + + enum state_t : uint8 + { + UNDEF, // we know nothing about a value + DEADEND, // no execution flow to the insn + ABORTED, // the tracking process was aborted because the maximal + // tracking depth is not enough to find a value or it found + // the huge basic block + BADINSN, // the insn cannot be decoded + UNKINSN, // the result of the insn execution is unknown + UNKFUNC, // the register comes from the function start + UNKLOOP, // the register is changed in a loop + UNKMULT, // the register has incompatible values + // (a number and SP delta) + UNKXREF, // too many xrefs to the address + UNKVALS, // the register has too many values at the address + NUMINSN, // the value is a number after executing the insn + NUMADDR, // the value is a number before the address + SPDINSN, // the value is a SP delta after executing the insn + SPDADDR, // the value is a SP delta before the address + }; + // the SP delta is the value of SP minus the initial value of SP at the + // function start. + // each value (except UNDEF) may be set either before or after executing + // an insn at vals[i].def_ea. in the table below, these options are + // labeled B and A, respectively. The table shows what address is in + // vals[i].def_ea. + // the states below allow a single value + // DEADEND B the address of the dead end + // ABORTED B the address where it was aborted + // BADINSN A the insn address (ITYPE is not set) + // UNKINSN A the insn address + // UNKFUNC B the function start address + // UNKLOOP B the address of the changing insn + // UNKMULT B the address of the joint point + // UNKXREF B the address with a lot of xrefs + // UNKVALS B the address with a lot of values + // the states below allow multiple values + // NUMINSN A the address of the defining insn + // NUMADDR B the address after which the value became known + // SPDINSN A the address of the defining insn + // SPDADDR B the address after which the value became known + + qvector<val_def_t> vals; // sorted + state_t state = UNDEF; + + explicit reg_value_info_t( + state_t _state, + ea_t def_ea, + uval_t _val = BADADDR, + uint16 val_flags = 0) + : state(_state) + { + vals.push_back(val_def_t(_val, def_ea, val_flags)); + } + explicit reg_value_info_t( + state_t _state, + const insn_t &insn, + uval_t _val = BADADDR, + uint16 val_flags = 0) + : state(_state) + { + vals.push_back(val_def_t(_val, insn, val_flags)); + } + +public: + reg_value_info_t() {} + /// Undefine the value. + void clear() + { + state = UNDEF; + vals.qclear(); + } + /// Return 'true' if we know nothing about a value. + bool empty() const { return state == UNDEF; } + + void swap(reg_value_info_t &r) noexcept + { + std::swap(state, r.state); + vals.swap(r.vals); + } + + /// Return the undefined value because of a dead end. + /// \sa is_dead_end() + static reg_value_info_t make_dead_end(ea_t dead_end_ea) + { + return reg_value_info_t(DEADEND, dead_end_ea); + } + + /// Return the value after aborting. + /// \sa aborted() + static reg_value_info_t make_aborted( + ea_t bblk_ea, + int aborting_depth = -1) + { + return reg_value_info_t(ABORTED, bblk_ea, uint32(aborting_depth)); + } + + /// Return the unknown value after a bad insn. + /// \sa is_badinsn() + static reg_value_info_t make_badinsn(ea_t insn_ea) + { + return reg_value_info_t(BADINSN, insn_ea); + } + + /// Return the unknown value after executing the insn. + /// \sa is_unkinsn() + static reg_value_info_t make_unkinsn(const insn_t &insn) + { + return reg_value_info_t(UNKINSN, insn); + } + + /// Return the unknown value from the function start. + /// \sa is_unkfunc() + static reg_value_info_t make_unkfunc(ea_t func_ea) + { + return reg_value_info_t(UNKFUNC, func_ea); + } + + /// Return the unknown value if it changes in a loop. + /// \sa is_unkloop() + static reg_value_info_t make_unkloop(ea_t bblk_ea) + { + return reg_value_info_t(UNKLOOP, bblk_ea); + } + + /// Return the unknown value if the register has incompatible values. + /// \sa is_unkmult() + static reg_value_info_t make_unkmult(ea_t bblk_ea) + { + return reg_value_info_t(UNKMULT, bblk_ea); + } + + /// Return the unknown value if there are too many xrefs. + /// \sa is_unkxref() + static reg_value_info_t make_unkxref(ea_t bblk_ea) + { + return reg_value_info_t(UNKXREF, bblk_ea); + } + + /// Return the unknown value if the register has too many values. + /// \sa is_unkvals() + static reg_value_info_t make_unkvals(ea_t bblk_ea) + { + return reg_value_info_t(UNKVALS, bblk_ea); + } + + /// Return the value that is the RVAL number. + /// \sa is_num() + static reg_value_info_t make_num( + uval_t rval, + const insn_t &insn, + uint16 val_flags = 0) + { + return reg_value_info_t(NUMINSN, insn, rval, val_flags); + } + /// Return the value that is the RVAL number. + /// \sa is_num() + static reg_value_info_t make_num( + uval_t rval, + ea_t val_ea, + uint16 val_flags = 0) + { + return reg_value_info_t(NUMADDR, val_ea, rval, val_flags); + } + + /// Return the value that is the initial stack pointer. + /// \sa is_spd() + static reg_value_info_t make_initial_sp(ea_t func_ea) + { + return reg_value_info_t(SPDADDR, func_ea, 0); + } + + //----------------------------------------------------------------------- + // access methods + + /// Return 'true' if the value is undefined because of a dead end. + bool is_dead_end() const { return state == DEADEND; } + /// Return 'true' if the tracking process was aborted. + bool aborted() const { return state == ABORTED; } + /// Return 'true' if the value requires special handling. + bool is_special() const { return is_dead_end() || aborted(); } + + /// Return 'true' if the value is unknown because of a bad insn. + bool is_badinsn() const { return state == BADINSN; } + /// Return 'true' if the value is unknown after executing the insn. + bool is_unkinsn() const { return state == UNKINSN; } + /// Return 'true' if the value is unknown from the function start. + bool is_unkfunc() const { return state == UNKFUNC; } + /// Return 'true' if the value is unknown because it changes in a loop. + bool is_unkloop() const { return state == UNKLOOP; } + /// Return 'true' if the value is unknown because the register has + /// incompatible values (a number and SP delta). + bool is_unkmult() const { return state == UNKMULT; } + /// Return 'true' if the value is unknown because there are too many xrefs. + bool is_unkxref() const { return state == UNKXREF; } + /// Return 'true' if the value is unknown because the register has too + /// many values. + bool is_unkvals() const { return state == UNKVALS; } + /// Return 'true' if the value is unknown. + bool is_unknown() const + { + return state == BADINSN + || state == UNKINSN + || state == UNKFUNC + || state == UNKLOOP + || state == UNKMULT + || state == UNKXREF + || state == UNKVALS; + } + + /// Return 'true' if the value is a constant. + bool is_num() const { return state == NUMINSN || state == NUMADDR; } + /// Return 'true' if the value depends on the stack pointer. + bool is_spd() const { return state == SPDINSN || state == SPDADDR; } + /// Return 'true' if the value is known (i.e. it is a number or SP delta). + bool is_known() const { return is_num() || is_spd(); } + + /// Return the number if the value is a constant. + /// \sa is_num() + bool get_num(uval_t *uval) const + { + if ( !is_num() || !is_value_unique() ) + return false; + *uval = vals.begin()->val; + return true; + } + /// Return the SP delta if the value depends on the stack pointer. + /// \sa is_spd() + /// \param [out] sval the value of SP minus the initial value of SP at + /// the function start. + bool get_spd(sval_t *sval) const + { + if ( !is_spd() || !is_value_unique() ) + return false; + *sval = int64(vals.begin()->val); + return true; + } + /// Return the defining address. + ea_t get_def_ea() const + { + return !is_value_unique() ? BADADDR : vals.begin()->def_ea; + } + /// Return the defining instruction code (processor specific). + uint16 get_def_itype() const + { + return !is_value_unique() ? 0 : vals.begin()->def_itype; + } + /// Return the aborting depth if the value is ABORTED + int get_aborting_depth() const + { + if ( !aborted() || !is_value_unique() ) + return -1; + return int32(vals.begin()->val); + } + + /// Return a const iterator to the first value. + const reg_value_def_t *vals_begin() const { return vals.begin(); } + /// Return a const iterator right after the last value. + const reg_value_def_t *vals_end() const { return vals.end(); } + /// Return the number of values. + size_t vals_size() const { return vals.size(); } + + /// Check that the value is unique. + bool is_value_unique() const + { + if ( empty() ) + return false; + if ( vals.size() == 1 ) + return true; + auto p = vals.begin(); + uint64 v = p->val; + for ( ++p; p != vals.end(); ++p ) + if ( p->val != v ) + return false; + return true; + } + + /// Check the given flag for each value. + bool have_all_vals_flag(uint16 val_flags) const + { + for ( auto p : vals ) + if ( (p.flags & val_flags) == 0 ) + return false; + return true; + } + bool has_any_vals_flag(uint16 val_flags) const + { + for ( auto p : vals ) + if ( (p.flags & val_flags) != 0 ) + return true; + return false; + } + bool is_all_vals_pc_based() const + { + return have_all_vals_flag(val_def_t::PC_BASED); + } + bool is_any_vals_pc_based() const + { + return has_any_vals_flag(val_def_t::PC_BASED); + } + bool is_all_vals_like_got() const + { + return have_all_vals_flag(val_def_t::LIKE_GOT); + } + bool is_any_vals_like_got() const + { + return has_any_vals_flag(val_def_t::LIKE_GOT); + } + + /// Set the given flag for each value. + void set_all_vals_flag(uint16 val_flags) + { + for ( auto &p : vals ) + p.flags |= val_flags; + } + void set_all_vals_pc_based() + { + return set_all_vals_flag(val_def_t::PC_BASED); + } + void set_all_vals_got_based() + { + return set_all_vals_flag(val_def_t::LIKE_GOT); + } + + //----------------------------------------------------------------------- + // modification methods + + /// Set the value to be undefined because of a dead end. + /// \sa is_dead_end() + void set_dead_end(ea_t dead_end_ea) + { + state = DEADEND; + vals.qclear(); + vals.push_back(val_def_t(BADADDR, dead_end_ea)); + } + + /// Set the value to be unknown after a bad insn. + /// \sa is_badinsn() + void set_badinsn(ea_t insn_ea) + { + state = BADINSN; + vals.qclear(); + vals.push_back(val_def_t(BADADDR, insn_ea)); + } + + /// Set the value to be unknown after executing the insn. + /// \sa is_unkinsn() + void set_unkinsn(const insn_t &insn) + { + state = UNKINSN; + vals.qclear(); + vals.push_back(val_def_t(BADADDR, insn)); + } + + /// Set the value to be unknown from the function start. + /// \sa is_unkfunc() + void set_unkfunc(ea_t func_ea) + { + state = UNKFUNC; + vals.qclear(); + vals.push_back(val_def_t(BADADDR, func_ea)); + } + + /// Set the value to be unknown because it changes in a loop. + /// \sa is_unkloop() + void set_unkloop(ea_t bblk_ea) + { + state = UNKLOOP; + vals.qclear(); + vals.push_back(val_def_t(BADADDR, bblk_ea)); + } + + /// Set the value to be unknown because the register has incompatible values. + /// \sa is_unkmult() + void set_unkmult(ea_t bblk_ea) + { + state = UNKMULT; + vals.qclear(); + vals.push_back(val_def_t(BADADDR, bblk_ea)); + } + + /// Set the value to be unknown because there are too many xrefs. + /// \sa is_unkxref() + void set_unkxref(ea_t bblk_ea) + { + state = UNKXREF; + vals.qclear(); + vals.push_back(val_def_t(BADADDR, bblk_ea)); + } + + /// Set the value to be unknown because the register has too many values. + /// \sa is_unkvals() + void set_unkvals(ea_t bblk_ea) + { + state = UNKVALS; + vals.qclear(); + vals.push_back(val_def_t(BADADDR, bblk_ea)); + } + + /// Set the value after aborting. + /// \sa aborted() + void set_aborted(ea_t bblk_ea, int aborting_depth = -1) + { + state = ABORTED; + vals.qclear(); + vals.push_back(val_def_t(uint32(aborting_depth), bblk_ea)); + } + + /// Set the value to be a number after executing an insn. + /// \sa is_num() + void set_num(uval_t rval, const insn_t &insn, uint16 val_flags = 0) + { + state = NUMINSN; + vals.qclear(); + vals.push_back(val_def_t(rval, insn, val_flags)); + } + /// Set the value to be numbers after executing an insn. + /// \note This method spoils RVALS. + /// \sa is_num() + void set_num(uvalvec_t *rvals, const insn_t &insn) + { + state = NUMINSN; + set_multivals(rvals, insn); + } + /// Set the value to be a number before an address. + /// \sa is_num() + void set_num(uval_t rval, ea_t val_ea, uint16 val_flags = 0) + { + state = NUMADDR; + vals.qclear(); + vals.push_back(val_def_t(rval, val_ea, val_flags)); + } + + /// The result of comparison of 2 value sets. + enum set_compare_res_t + { + EQUAL, ///< L==R + CONTAINS, ///< L contains R (i.e. R\L is empty) + CONTAINED, ///< L is contained in R (i.e. L\R is empty) + NOT_COMPARABLE, ///< L\R is not empty and R\L is not empty + }; + /// Add values from R into THIS ignoring duplicates. + /// \note This method is the only way to get multiple values. + /// \retval EQUAL THIS is not changed + /// \retval CONTAINS THIS is not changed + /// \retval CONTAINED THIS is a copy of R + /// \retval NOT_COMPARABLE values from R are added to THIS + set_compare_res_t vals_union(const reg_value_info_t &r) + { + return set_compare_res_t(reg_value_info_vals_union(this, &r)); + } + + /// Sign-, or zero-extend the number or SP delta value to full size. + /// The initial value is considered to be of size WIDTH. + /// \note This method do nothing for unknown values. + inline void extend(const procmod_t &pm, int width, bool is_signed); + + /// Truncate the number to the application bitness. + /// \note This method do nothing for non-number values. + inline void trunc_uval(const procmod_t &pm); + + // arithmetic operations + enum arith_op_t + { + ADD, SUB, + OR, AND, XOR, AND_NOT, + SLL, SLR, SAR, MOVT, + NEG, NOT, + }; + + /// Add R to the value, save INSN as a defining instruction. + /// \note Either THIS or R must have a single value. + inline void add(const reg_value_info_t &r, const insn_t &insn); + /// Subtract R from the value, save INSN as a defining instruction. + /// \note Either THIS or R must have a single value. + inline void sub(const reg_value_info_t &r, const insn_t &insn); + /// Make bitwise OR of R to the value, + /// save INSN as a defining instruction. + /// \note Either THIS or R must have a single value. + inline void bor(const reg_value_info_t &r, const insn_t &insn); + /// Make bitwise AND of R to the value, + /// save INSN as a defining instruction. + /// \note Either THIS or R must have a single value. + inline void band(const reg_value_info_t &r, const insn_t &insn); + /// Make bitwise eXclusive OR of R to the value, + /// save INSN as a defining instruction. + /// \note Either THIS or R must have a single value. + inline void bxor(const reg_value_info_t &r, const insn_t &insn); + /// Make bitwise AND of the inverse of R to the value, + /// save INSN as a defining instruction. + /// \note Either THIS or R must have a single value. + inline void bandnot(const reg_value_info_t &r, const insn_t &insn); + /// Shift the value left by R, save INSN as a defining instruction. + /// \note Either THIS or R must have a single value. + inline void sll(const reg_value_info_t &r, const insn_t &insn); + /// Shift logically the value right by R, + /// save INSN as a defining instruction. + /// \note Either THIS or R must have a single value. + inline void slr(const reg_value_info_t &r, const insn_t &insn); + /// Shift arithmetically the value right by R, + /// save INSN as a defining instruction. + /// \note Either THIS or R must have a single value. + inline void sar(const reg_value_info_t &r, const insn_t &insn); + /// Replace the top 16 bits with bottom 16 bits of R, leaving the bottom + /// 16 bits untouched, save INSN as a defining instruction. + /// \note Either THIS or R must have a single value. + inline void movt(const reg_value_info_t &r, const insn_t &insn); + /// Negate the value, save INSN as a defining instruction. + inline void neg(const insn_t &insn); + /// Make bitwise inverse of the value, + /// save INSN as a defining instruction. + inline void bnot(const insn_t &insn); + /// Add R to the value, save INSN as a defining instruction. + /// \note This method do nothing for unknown values. + inline void add_num(uval_t r, const insn_t &insn); + + /// Add R to the value, do not change the defining instructions. + /// \note This method do nothing for unknown values. + inline void add_num(uval_t r); + /// Shift the value left by R, do not change the defining instructions. + /// \note This method do nothing for unknown values. + inline void shift_left(uval_t r); + /// Shift the value right by R, do not change the defining instructions. + /// \note This method do nothing for unknown values. + inline void shift_right(uval_t r); + + /// Return the string representation. + qstring dstr(const procmod_t *pm = nullptr) const + { + qstring out; + reg_value_info_dstr(this, &out, pm); + return out; + } + +protected: + DECLARE_REG_VALUE_INFO_HELPERS(friend) + + // helper implementation + set_compare_res_t vals_union_impl(const reg_value_info_t &r); + qstring dstr_impl(const procmod_t *pm) const; + + // perform a binary operation + // \note Either THIS or R must have a single value. + inline bool perform_binary_op( + const reg_value_info_t &r, + arith_op_t aop, + const insn_t &insn); + // fix the sorting order and set VALS + template<class V> + inline void set_multivals(V *rvals, const insn_t &insn); + // fix the sorting order + inline void sort_multivals(); +}; + +//------------------------------------------------------------------------- +// what operand are we going to track? +struct reg_finder_op_t +{ +private: + // |31 |30-29|28 |... + // |reg or stkvar|width|signness|... + // for registers: + // ...|27-16 |15-0 | + // ...|reserved|register number| + // for stkvars: + // ...|27 |26-0 | + // ...|stkoff sign|stkoff absolute value| + static constexpr uint32 REG = 0; + static constexpr uint32 STKVAR = 1 << 31; + static constexpr int WIDTH_SHIFT = 29; + static constexpr uint32 WIDTH_MASK = 0x3 << WIDTH_SHIFT; + static constexpr uint32 SIGNED = 1 << 28; + static constexpr int STKOFF_SIGNBIT = 1 << 27; + static constexpr uint32 STKOFF_MASK = STKOFF_SIGNBIT - 1; + // the impossible combination of bits + static constexpr uint32 BADREG = 0x10000; + + uint32 packed = BADREG; + + explicit reg_finder_op_t(uint32 _packed) : packed(_packed) {} + +public: + using rfop_t = reg_finder_op_t; + reg_finder_op_t() {} + reg_finder_op_t(const rfop_t &r) = default; + rfop_t &operator=(const rfop_t &r) = default; + + // if after constructors empty() returns 'true' + // that means that arguments are bad + bool empty() const { return packed == BADREG; } + void clear() { packed = BADREG; } + + static bool is_valid_reg(int reg) + { + return reg >= 0 && reg < BADREG; + } + inline static reg_finder_op_t make_reg(int reg, int width); + static reg_finder_op_t make_reg(const procmod_t &pm, int reg) + { + return make_reg(reg, pm.eah().ea_size); // 4 or 8 + } + + static bool is_valid_stkoff(sval_t stkoff) + { + return stkoff >= -sval_t(STKOFF_MASK) && stkoff <= sval_t(STKOFF_MASK); + } + inline static reg_finder_op_t make_stkoff(sval_t stkoff, int width); + inline static int get_op_width(const op_t &op); + + inline void set_width(int width); + inline void set_signness(bool is_signed); + inline void set_width_signness(int width, bool is_signed); + + bool is_reg() const { return (packed & STKVAR) == 0; } + bool is_stkvar() const { return (packed & STKVAR) != 0; } + bool is_signed() const { return (packed & SIGNED) != 0; } + int get_width() const + { + return 1 << ((packed & WIDTH_MASK) >> WIDTH_SHIFT); + } + + uint16 get_reg() const { return uint16(packed); } + sval_t get_stkoff() const + { + sval_t stkoff = packed & STKOFF_MASK; + return (packed & STKOFF_SIGNBIT) == 0 ? stkoff : -stkoff; + } + + bool is_reg(int reg) const { return is_reg() && get_reg() == reg; } + + DECLARE_COMPARISONS(reg_finder_op_t) + { + // the empty object is less than any other object + if ( empty() ) + return r.empty() ? 0 : -1; + if ( r.empty() ) + return 1; + return ::compare(packed, r.packed); + } + + +protected: + inline static uint32 pack_width(int width); +}; + +//------------------------------------------------------------------------- +struct reg_finder_t; +struct reg_value_ud_chain_t; +typedef void (*reg_finder_binary_ops_adjust_fun)( + reg_value_info_t *v1, + reg_value_info_t *v2, + const insn_t &insn, + void *ud); + +#define DECLARE_REG_FINDER_HELPERS(decl)\ +decl void ida_export reg_finder_invalidate_cache(reg_finder_t *_this, ea_t to, ea_t from, cref_t cref);\ +decl void ida_export reg_finder_invalidate_xrefs_cache(reg_finder_t *_this, ea_t ea, dref_t dref);\ +decl void ida_export reg_finder_find(reg_finder_t *_this, reg_value_info_t *out, ea_t ea, ea_t ds, reg_finder_op_t op, int max_depth, size_t linear_insns);\ +decl void ida_export reg_finder_make_rfop(reg_finder_t *_this, reg_finder_op_t *rfop, const op_t *op, const insn_t *insn, func_t *pfn);\ +decl bool ida_export reg_finder_calc_op_addr(reg_finder_t *_this, reg_value_info_t *addr, const op_t *memop, const insn_t *insn, ea_t ea, ea_t ds, int max_depth);\ +decl bool ida_export reg_finder_emulate_mem_read(reg_finder_t *_this, reg_value_info_t *value, const reg_value_info_t *addr, int width, bool is_signed, const insn_t *insn);\ +decl void ida_export reg_finder_emulate_binary_op(reg_finder_t *_this, reg_value_info_t *value, int aop, const op_t *op1, const op_t *op2, const insn_t *insn, ea_t ea, ea_t ds, reg_finder_binary_ops_adjust_fun adjust, void *ud);\ +decl void ida_export reg_finder_emulate_unary_op(reg_finder_t *_this, reg_value_info_t *value, int aop, int reg, const insn_t *insn, ea_t ea, ea_t ds);\ +decl bool ida_export reg_finder_may_modify_stkvar(reg_finder_t *_this, reg_value_info_t *value, reg_finder_op_t op, const insn_t *insn);\ +decl bool ida_export reg_finder_can_resolve_mem(const reg_finder_t *_this, ea_t ea);\ +decl void ida_export reg_finder_ctr(reg_finder_t *_this);\ +decl void ida_export reg_finder_dtr(reg_finder_t *_this); + +DECLARE_REG_FINDER_HELPERS(idaman) + +//------------------------------------------------------------------------- +//lint -e{958} padding needed +struct reg_finder_block_t; +struct reg_finder_pred_t; +struct reg_finder_t +{ + const procmod_t ± + const int proc_maxop; // max number of operands in insns + + uint32 flags; // a set of RF_... bits + + // a call insn may modify stkvars (via aliased stkvars passed as args). + // this bit indicates how to answer this question. + static constexpr uint32 RF_DOES_CALL_SPOIL_STKVARS = 0x0001; + // this bit allows to use the write-xrefs cache. if it is set then the + // proc module has to call invalidate_xrefs_cache() on the events: + // - ev_add_dref + // - ev_del_dref + static constexpr uint32 RF_ALLOW_XREFS_CACHE = 0x0002; + + bool does_call_spoil_stkvars() const + { + return (flags & RF_DOES_CALL_SPOIL_STKVARS) != 0; + } + bool allow_xrefs_cache() const + { + return (flags & RF_ALLOW_XREFS_CACHE) != 0; + } + +protected: + using rvi_t = reg_value_info_t; + using rfop_t = reg_finder_op_t; + using block_t = reg_finder_block_t; + using pred_t = reg_finder_pred_t; + friend struct reg_finder_block_t; + friend struct reg_finder_pred_t; + + // the data members below are set by the each call of find() + func_t *cur_func = nullptr; // function to search in + size_t initial_block_idx = 0; + bool fixed_max_depth = false; // is the initial MAX_DEPTH specified by the + // caller (not taken from ida.cfg) ? + int cur_max_depth = 0; // maximum search depth + int cur_call_depth = 0; // the current depth of recursive calls + size_t linear_flow_cnt = 0; // the number of insn to search in the + // linear flow (if != 0) + bool only_linear_flow() const { return linear_flow_cnt != 0; } + size_t bblk_cnt = 0; // the number of insn in a basic block + ea_t aborting_ea = BADADDR; // to make tracking aborting easier + rvi_t standalone_value; // to return a value for the initial block + // without addresses + + static constexpr size_t NO_CHAIN = size_t(-1); + + // a temporary storage to return from is_move_insn() + op_t fake_op1; + op_t fake_op2; + + // the condition under which the instruction is executed, + // and some additional instruction features + struct cond_t + { + private: + uint32 packed; + static constexpr uint32 COND_MASK = 0x0F; + static constexpr int KIND_SHIFT = 4; + static constexpr uint32 KIND_MASK = 0xF; + + public: + // got from arm.hpp + enum : uchar + { + EQ, // 0000 Z Equal + NE, // 0001 !Z Not equal + CS, // 0010 C Unsigned higher or same + CC, // 0011 !C Unsigned lower + MI, // 0100 N Negative + PL, // 0101 !N Positive or Zero + VS, // 0110 V Overflow + VC, // 0111 !V No overflow + HI, // 1000 C & !Z Unsigned higher + LS, // 1001 !C | Z Unsigned lower or same + GE, // 1010 (N & V) | (!N & !V) Greater or equal + LT, // 1011 (N & !V) | (!N & V) Less than + GT, // 1100 !Z & ((N & V)|(!N & !V)) Greater than + LE, // 1101 Z | (N & !V) | (!N & V) Less than or equal + AL, // 1110 Always + NV, // 1111 Never + }; + + enum : uchar + { + NONE, + MODIFIES_CC, + JUMPS, + }; + + cond_t(uchar cond = AL, uchar kind = NONE) + : packed((cond & COND_MASK) | ((kind & KIND_MASK) << KIND_SHIFT)) {} + + uchar get_cond() const { return uchar(packed & COND_MASK); } + // e.g. GE includes GE, GT, EQ + // MOVGE ... this insn is executed if the branch is taken + // BGT away + bool is_included_in(cond_t r) const + { + // TODO implement non-trivial cases + uchar cnd = get_cond(); + uchar rcnd = r.get_cond(); + // AL includes all other conditions + return cnd == rcnd || rcnd == AL; + } + + uchar get_kind() const + { + return uchar((packed >> KIND_SHIFT) & KIND_MASK); + } + bool modifies_cond_codes() const { return get_kind() == MODIFIES_CC; } + bool jumps() const { return get_kind() == JUMPS; } + }; + +private: + using udc_t = reg_value_ud_chain_t; + + friend struct reg_finder_chainvec_t; + struct reg_finder_chainvec_t *chains = nullptr; // the chain values + + struct addr_t + { + ea_t ea; + rfop_t rfop; + addr_t(ea_t _ea, rfop_t _rfop = rfop_t()) : ea(_ea), rfop(_rfop) {} + DECLARE_COMPARISONS(addr_t) + { + int code = ::compare(ea, r.ea); + if ( code == 0 ) + code = rfop.compare(r.rfop); + return code; + } + }; + // where does the value come from? + struct vref_t + { + size_t chain_num; // the chain number + sval_t delta; // the addend to the chain value + explicit vref_t( + size_t _chain_num = reg_finder_t::NO_CHAIN, + sval_t _delta = 0) + : chain_num(_chain_num), delta(_delta) {} + bool empty() const { return chain_num == reg_finder_t::NO_CHAIN; } + void clear() { chain_num = reg_finder_t::NO_CHAIN; delta = 0; } + bool operator==(const vref_t &r) + { + return chain_num == r.chain_num && delta == r.delta; + } + }; + + // key - an operand and an address, + // value - the reference to the value of the register at this address + // (the chain value + DELTA) + friend struct reg_finder_rfop_chains_t; // the opaque type + struct reg_finder_rfop_chains_t *rfop_chains = nullptr; + + // the path of depth-first search + qvector<vref_t> path; + + // the cache of the write xrefs of addresses in read-only segments. + // this cache is used only if the regfinder is created with the + // RF_ALLOW_XREFS_CACHE bit. + friend struct reg_finder_xrefs_cache_t; // the opaque type + struct reg_finder_xrefs_cache_t *xrefs_cache = nullptr; + + bool in_invalidate = false; // the guard for invalidate_regfinder_cache() + bool debug_on = true; + +public: + reg_finder_t( + const procmod_t &_pm, + int _proc_maxop = 3, + uint32 _flags = RF_DOES_CALL_SPOIL_STKVARS) + : pm(_pm), + proc_maxop(_proc_maxop), + flags(_flags) + { + reg_finder_ctr(this); + } + virtual ~reg_finder_t() { reg_finder_dtr(this); } + + // the code xref from FROM to TO was added or deleted. + // if we have TO address in the cache we should invalidate the value at + // this address and the dependent values. + void invalidate_cache(ea_t to, ea_t from, cref_t cref) + { + reg_finder_invalidate_cache(this, to, from, cref); + } + // clear the entire cache. + void invalidate_cache() + { + reg_finder_invalidate_cache(this, BADADDR, BADADDR, fl_U); + } + + // a new data xref to EA was added or deleted. + // \sa RF_ALLOW_XREFS_CACHE + void invalidate_xrefs_cache(ea_t ea, dref_t dref) + { + reg_finder_invalidate_xrefs_cache(this, ea, dref); + } + + // find a value of OP before EA + // \param max_depth the maximum search depth. + // 0 means the value of REGTRACK_MAX_DEPTH or + // REGTRACK_FUNC_MAX_DEPTH from ida.cfg depending on the + // register, + // -1 means always the value of REGTRACK_FUNC_MAX_DEPTH. + // A value greater than 1 cannot be used in recursive + // calls of find() (e.g. from emulate_insn()). + // \param linear_insns a non-zero value causes switching to the linear + // flow search mode. In this mode the search stops at + // the first xref or when LINEAR_INSNS insns are + // tracked. The number of instructions tracked is + // counted from the address of the first call with a + // non-zero argument, i.e. recursive calls to find() + // after switching to this mode ignore this argument. + rvi_t find( + ea_t ea, + rfop_t rfop, + int max_depth = 0, + size_t linear_insns = 0) + { + rvi_t ret; + flow_t flow = process_delay_slot(pm.trunc_uval(ea), fl_U); + reg_finder_find(this, &ret, + flow.ea, flow.ds, rfop, + max_depth, linear_insns); + return ret; + } + + // find the value of any of the two registers + int find_nearest( + reg_value_info_t *rvi, + ea_t ea, + const int reg[2], + size_t linear_insns = 10) + { + if ( reg[0] == reg[1] ) + return -1; + *rvi = find(ea, rfop_t::make_reg(pm, reg[0]), 0, linear_insns); + if ( rvi->is_known() ) + return 0; + *rvi = find(ea, rfop_t::make_reg(pm, reg[1]), 0, linear_insns); + if ( rvi->is_known() ) + return 1; + *rvi = find(ea, rfop_t::make_reg(pm, reg[0]), 0); + if ( rvi->is_known() ) + return 0; + *rvi = find(ea, rfop_t::make_reg(pm, reg[1]), 0); + if ( rvi->is_known() ) + return 1; + return -1; + } + + // find a value of non-SP based register before EA + bool find_const(uval_t *val, ea_t ea, rfop_t rfop, int max_depth = 0) + { + return find(ea, rfop, max_depth).get_num(val); + } + + // find a value of SP based register before EA + // by default it uses the SP register and REGTRACK_FUNC_MAX_DEPTH + bool find_spd(sval_t *spval, ea_t ea, int reg = -1, int max_depth = -1) + { + if ( reg == -1 ) + { + reg = get_sp_reg(ea); + if ( reg == -1 ) + return false; + } + return find(ea, rfop_t::make_reg(pm, reg), max_depth).get_spd(spval); + } + + // make the regfinder operand from the insn operand. + // if OP is unsupported this function returns an empty operand. + rfop_t make_rfop(const op_t &_op, const insn_t &insn, func_t *pfn) + { + op_t op = _op; // make a copy + if ( !can_track_op(&op, insn, pfn) ) + return rfop_t(); + rfop_t res; + reg_finder_make_rfop(this, &res, &op, &insn, pfn); + aborting_ea = BADADDR; // ignore for this call + return res; + } + + // find the operand addresses (o_displ or o_phrase or o_mem) + // \li in the case of 'o_displ', the address is formed by adding + // memop.addr to the content of memop.phrase + // \li in the case of 'o_phrase', the address is formed by adding + // memop.addr to the sum of the content of memop.phrase and the content of + // the second register (memop.value), \sa procmod_t::make_op_phrase() + // \li in the case of 'o_mem', the address is just memop.addr + // \param memop the operand + // \param insn the emulated insn + // \param max_depth the maximum search depth. + rvi_t find_op_addr( + const op_t &memop, + const insn_t &insn, + int max_depth = 0) + { + rvi_t ret; + flow_t flow = process_delay_slot(pm.trunc_uval(insn.ea), fl_U); + reg_finder_calc_op_addr(this, &ret, + &memop, + &insn, flow.ea, flow.ds, max_depth); + return ret; + } + + // handle a memory read + // \note this method checks that the address belongs to the readonly + // segment, if not it calls is_mem_readonly() to make an additional + // check. + // \param addr the address to read from. + // this parameter may be the same as VALUE. + // \param width the data width. it must be 1/2/4/8. + // \sa get_data_value() + // \param is_signed if 'true' the result should be sign-extended + // \param insn the emulated insn + void emulate_mem_read( + rvi_t *value, + const rvi_t &addr, + int width, + bool is_signed, + const insn_t &insn) + { + reg_finder_emulate_mem_read(this, value, &addr, + width, is_signed, + &insn); + } + + // is memory at EA read-only? + bool can_resolve_mem(ea_t ea) const + { + return reg_finder_can_resolve_mem(this, ea); + } + + +protected: + // the address of the emulated instruction, taking into account a possible + // delay slot + struct flow_t + { + ea_t ea; + ea_t ds; // to handle a delay slot + bool has_delay_slot() const { return ds != BADADDR; } + // pre-condidition: has_delay_slot() + bool is_ea_handled() const { return ds > ea; } + // pre-condidition: is_ea_handled() + void handle_delay_slot() { qswap(ea, ds); } + + flow_t(ea_t _ea, ea_t _ds = BADADDR) : ea(_ea), ds(_ds) {} + + // the address of the actually emulated insn + // when handling a delay slot we are actually emulating the main insn + // 00 func: + // 00 beq ..., @away # $t9 is not 00 because it is modified in the + // # delay slot + // 04 addui $t9, ... # $t9 is 00 before this insn + ea_t actual_ea() const { return has_delay_slot() ? ds : ea; } + operator ea_t() const { return actual_ea(); } + + bool operator<(const flow_t &r) const { return ea < r.ea; } + + }; + + // processor specific methods + + // we reached EA using REF. + // For processors with delay slots we can process another insn first. + // 00 jal main + // 04 addiu $a1, $v0, 4 <-- EA but we should start with 00 + // \param ref how did we reach EA? + // - fl_F: by the ordinary flow, + // - fl_JN, fl_JF: by the jump, + // - fl_U: from the find() method. + virtual flow_t process_delay_slot(ea_t ea, cref_t /*ref*/) const + { + return flow_t(ea); // no delay slots + } + + // an instruction may be executed under a condition + virtual cond_t get_cond(ea_t ea) const + { + qnotused(ea); + return cond_t(); // no conditional instructions + } + + // we may know the value of some registers + // \retval empty() we know nothing + // \retval is_unkfunc() there may be any value (e.g. a func argument) + // \retval is_known() we found the value (e.g. the GOT register) + virtual rvi_t handle_well_known_regs( + flow_t flow, + rfop_t rfop, + bool is_func_start) const + { + qnotused(rfop); + qnotused(flow); + qnotused(is_func_start); + return rvi_t(); // know nothing about registers + } + + // is the content of memory at EA a constant? + // \note this method is called from emulate_mem_read() if it cannot itself + // determine that this memory is read-only. + virtual bool is_mem_readonly(ea_t /*ea*/) const + { + return false; + } + + // get the SP register + virtual int get_sp_reg(ea_t ea) const + { + qnotused(ea); + return -1; + } + + // is REG used throughout the function? + virtual bool is_funcwide_reg(ea_t ea, int reg) const + { + return reg == get_sp_reg(ea); + } + + // we can track only registers and stkvars (including BP-based) + virtual bool can_track_op(op_t *op, const insn_t &insn, func_t *pfn) const + { + qnotused(op); + qnotused(insn); + qnotused(pfn); + return false; + } + + // is INSN a 'move' instruction? (or a simple add/sub instruction) + struct move_desc_t + { + // the first case (if !new_rfop.empty()): + // is_move_insn() already knows the new tracked operand + rfop_t new_rfop; + + // the second case (if new_rfop.empty()): + // the operand types should be byte/word/dword/qword + // both DST_OP and SRC_OP cannot be memory operands + // the pointer to the source operand + const op_t *dst_op = nullptr; + // the pointer to the destination operand + const op_t *src_op = nullptr; + // if 'true' and DST_OP is wider than SRC_OP then the source operand + // will be sign extended + bool is_signed = false; + + // if non-zero then the instruction is a simple 'add/sub' insn with an + // immediate value. the operands must have the same size. + // \note this member is used by both cases. + // DELTA is positive for the 'add' insn. + sval_t delta = 0; + }; + // \param [out] move_desc the decription of the 'move' insn + // \param [in] rfop the operand to find a value of + // \param insn the emulating insn + // \retval false if INSN is not a 'move' insn + // \retval true INSN is a 'move' insn + virtual bool is_move_insn( + move_desc_t *move_desc, + const rfop_t &rfop, + const insn_t &insn) + { + qnotused(move_desc); + qnotused(rfop); + qnotused(insn); + return false; + } + + // emulate INSN and find the value of OP + // to get values of the source registers this method may call find() + // (this call will be recursive) + // \param [out] value only if the method returns 'true' + // is_dead_end(): there is no flow to this insn + // aborted(): the tracking process was aborted + // is_unkinsn(): INSN spoils OP in an unknown way + // is_known(): the found value + // \param [in] rfop the operand to find a value of + // \param [in] insn the instruction to emulate + // \param [in] flow the control flow to get a preceding insn. + // it will be passed to find(). + // \retval true the found value is in VALUE + // \retval false INSN does not modify OP + virtual bool emulate_insn( + rvi_t *value, + const rfop_t &rfop, + const insn_t &insn, + flow_t flow) + { + qnotused(rfop); + qnotused(flow); + value->set_unkinsn(insn); // do not support any instruction + return true; + } + + // helper methods for emulate_insn() + + // get values of the operand from emulate_insn() + // \param flow the control flow to get a starting insn + // \param rfop the operand to find a value of + rvi_t find(flow_t flow, rfop_t rfop) + { + rvi_t ret; + reg_finder_find(this, &ret, flow.ea, flow.ds, rfop, 0, 0); + return ret; + } + + // get values of the register from emulate_insn() + // \param flow the control flow to get a starting insn + // \param reg the register to find a value of + rvi_t find(flow_t flow, int reg) + { + rvi_t ret; + rfop_t rfop = rfop_t::make_reg(pm, reg); + reg_finder_find(this, &ret, flow.ea, flow.ds, rfop, 0, 0); + return ret; + } + + // get operand addresses (o_displ or o_phrase or o_mem) + // \sa find_op_addr() + void calc_op_addr( + rvi_t *addr, + const op_t &memop, + const insn_t &insn, + flow_t flow) + { + reg_finder_calc_op_addr(this, addr, + &memop, + &insn, flow.ea, flow.ds, 0); + } + + void emulate_binary_op( + rvi_t *value, + rvi_t::arith_op_t aop, // not NEG, NOT + const op_t &op1, + const op_t &op2, + const insn_t &insn, + flow_t flow, + reg_finder_binary_ops_adjust_fun adjust = nullptr, + void *ud = nullptr) + { + reg_finder_emulate_binary_op(this, value, + aop, &op1, &op2, + &insn, flow.ea, flow.ds, + adjust, ud); + } + void emulate_unary_op( + rvi_t *value, + rvi_t::arith_op_t aop, // only NEG, NOT + int reg, + const insn_t &insn, + flow_t flow) + { + reg_finder_emulate_unary_op(this, value, + aop, reg, + &insn, flow.ea, flow.ds); + } + + // this method returns 'true' and sets VALUE to UNKINSN if there is the + // slightest possibility that INSN changes OP (it is a stkvar). + // in the current implementation we assume that any store instruction with + // an unknown address or any call instruction may modify stkvars. + bool may_modify_stkvar(rvi_t *value, rfop_t rfop, const insn_t &insn) + { + return reg_finder_may_modify_stkvar(this, value, rfop, &insn); + } + +private: + // implementation methods + rvi_t find_chain(const flow_t flow, const rfop_t rfop); + void create_initial_block(const flow_t flow, const rfop_t rfop); + // these 2 methods may set ABORTING_EA to abort tracking + bool create_new_block(vref_t *res_vref, const pred_t &pred); + bool handle_block_pred(size_t chain_num, vref_t pred_vref); + vref_t finalize_block(); + + // for create_new_block() + bool collect_predecessors(qvector<flow_t> *pred_addrs, flow_t flow) const; + bool analyze_linear_flow( + qvector<flow_t> *pred_addrs, + eavec_t *to_eas, + ea_t initial_ea) const; + bool merge_loop_blocks(const block_t *loop_block, sval_t delta); + bool decode_and_emulate_insn( + rvi_t *value, + rfop_t *rfop, + sval_t *delta, + flow_t flow); + bool is_same_func(ea_t ea) const + { + return cur_func != nullptr + ? func_contains(cur_func, ea) + : get_fchunk(ea) == nullptr; + } + rvi_t make_aborted(); + bool set_aborted_or_unkinsn(rvi_t *value, const insn_t &insn); + + // what to do after move handling? + enum handle_move_res_t + { + HANDLED, // the move is handled, we get a new tracked operand or + // we are sure that it is not spoiled + UNSUPPORTED, // the move does not touch the tracked operand + SPOILED, // a partial modification of the tracked operand is + // detected + }; + // handle a move to a tracked register + // \note the operand types should be byte/word/dword/qword. + // \note it may set ABORTING_EA if it returned SPOILED. + // \param [inout] rfop the tracked operand + // \param move_desc the decription of the 'move' insn + // (move_desc_t::delta is not used in this function) + // \param insn the move instruction + handle_move_res_t handle_move( + rfop_t *rfop, + const move_desc_t &move_desc, + const insn_t &insn); + + // for SAME the operand widths may be different. + // if it failed to calculate the stkvar offset it returns OVERLAPS. + // \note it may set ABORTING_EA if it returned OVERLAPS. + enum overlap_res_t { SAME, OVERLAPS, DIFFERENT }; + overlap_res_t does_rfop_overlap_with_op( + rfop_t rfop, + const op_t &op, + const insn_t &insn); + + // it returns success + // \note it may set ABORTING_EA if it returned 'false'. + bool calc_stkvar_off( + sval_t *stkoff, + const op_t &op, + const insn_t &insn, + func_t *pfn); + + // to work with chains + reg_value_ud_chain_t &get_chain(size_t chain_num); + const reg_value_ud_chain_t &get_chain(size_t chain_num) const; + void erase_block(size_t chain_num); + // move addresses to the block of TO_CHAIN_NUM adjusting deltas + void move_addrs( + size_t from_chain_num, + size_t to_chain_num, + sval_t delta); + void adjust_deltas(const qvector<addr_t> &addrs, sval_t delta); + void trim_cache(ea_t ea, int max_cache_size); + // does RFOP has at FLOW a value different from CHAIN_NUM? + bool is_rfop_changed( + const flow_t flow, + const rfop_t rfop, + size_t chain_num); + + DECLARE_REG_FINDER_HELPERS(friend) + + // helper implementation + void invalidate_cache_impl(ea_t to, ea_t from, cref_t cref); + void invalidate_xrefs_cache_impl(ea_t ea, dref_t dref); + rvi_t find_impl(flow_t flow, rfop_t rfop, int max_depth, size_t linear_insns); + rvi_t find_impl2(flow_t flow, rfop_t rfop, int max_depth, size_t linear_insns); + rvi_t find_impl(flow_t flow, const op_t &op); + rvi_t find_impl(flow_t flow, int reg, int max_depth = 0) + { + return find_impl(flow, rfop_t::make_reg(pm, reg), max_depth, 0); + } + rfop_t make_rfop_impl(const op_t &op, const insn_t &insn, func_t *pfn); + bool calc_op_addr_impl( + rvi_t *addr, + const op_t &memop, + const insn_t &insn, + flow_t flow, + int max_depth); + bool emulate_mem_read_impl( + rvi_t *value, + const rvi_t &addr, + int width, + bool is_signed, + const insn_t &insn); + void emulate_binary_op_impl( + rvi_t *value, + rvi_t::arith_op_t aop, // not NEG, NOT + const op_t &op1, + const op_t &op2, + const insn_t &insn, + flow_t flow, + reg_finder_binary_ops_adjust_fun adjust = nullptr, + void *ud = nullptr); + void emulate_unary_op_impl( + rvi_t *value, + rvi_t::arith_op_t aop, // only NEG, NOT + int reg, + const insn_t &insn, + flow_t flow); + bool may_modify_stkvar_impl( + rvi_t *value, + rfop_t rfop, + const insn_t &insn); + bool can_resolve_mem_impl(ea_t ea) const; + +}; + +//------------------------------------------------------------------------- +// convenience functions +//------------------------------------------------------------------------- +/// Find register value using the register tracker. +/// \note The returned value is valid *before* executing the instruction. +/// \param [out] uval the found value +/// \param ea the address to find a value at +/// \param reg the register to find +/// \retval 0 no value (the value is varying or the find depth is +/// not enough to find a value) +/// \retval 1 the found value is in VAL +/// \retval -1 the processor module does not support a register +/// tracker +idaman int ida_export find_reg_value(uval_t *uval, ea_t ea, int reg); + +//------------------------------------------------------------------------- +/// Find a value of the SP based register using the register tracker. +/// \note The returned value is valid *before* executing the instruction. +/// \param [out] sval the found value +/// \param ea the address to find a value at +/// \param reg the register to find. +/// by default the SP register is used. +/// \retval 0 no value (the value is varying or the find depth is +/// not enough to find a value) +/// \retval 1 the found value is in VAL +/// \retval -1 the processor module does not support a register +/// tracker +idaman int ida_export find_sp_value(sval_t *sval, ea_t ea, int reg = -1); + +//------------------------------------------------------------------------- +/// Find register value using the register tracker. +/// \note The returned value is valid *before* executing the instruction. +/// \note The _undefined_ value means that there is no execution flow to EA, +/// e.g. we try to find a value after the call of NORET function. +/// \note The _unknown_ value means that the value is: +/// \li a result of unsupported instruction, e.g. the result of a call, +/// \li a function argument, +/// \li is varying, e.g. it is a loop counter. +/// \param [out] rvi the found value with additional attributes +/// \param ea the address to find a value at +/// \param reg the register to find +/// \param max_depth the number of basic blocks to look before aborting the +/// search and returning the unknown value. +/// 0 means the value of REGTRACK_MAX_DEPTH from ida.cfg +/// for ordinal registers or REGTRACK_FUNC_MAX_DEPTH +/// for the function-wide registers, +/// -1 means the value of REGTRACK_FUNC_MAX_DEPTH from +/// ida.cfg. +/// \retval 'false' the processor module does not support a register +/// tracker +/// \retval 'true' the found value is in RVI +idaman bool ida_export find_reg_value_info( + reg_value_info_t *rvi, + ea_t ea, + int reg, + int max_depth = 0); + +//------------------------------------------------------------------------- +/// Find the value of any of the two registers using the register tracker. +/// First, this function tries to find the registers in the basic block of +/// EA, and if it could not do this, then it tries to find in the entire +/// function. +/// \param [out] rvi the found value with additional attributes +/// \param ea the address to find a value at +/// \param reg the registers to find +/// \return the index of the found register or -1 +idaman int ida_export find_nearest_rvi( + reg_value_info_t *rvi, + ea_t ea, + const int reg[2]); + +//------------------------------------------------------------------------- +/// The control flow from FROM to TO has removed (CREF==fl_U) or added +/// (CREF!=fl_U). +/// Try to update the register tracker cache after this change. +/// If TO == BADADDR then clear the entire cache. +idaman void ida_export invalidate_regfinder_cache( + ea_t to = BADADDR, + ea_t from = BADADDR, + cref_t cref = fl_U); + +//------------------------------------------------------------------------- +/// The data reference to TO has added (DREF!=dr_O) or removed (DREF==dr_O). +/// Update the regtracker xrefs cache after this change. +/// If TO == BADADDR then clear the entire xrefs cache. +idaman void ida_export invalidate_regfinder_xrefs_cache( + ea_t to = BADADDR, + dref_t dref = dr_O); + +//------------------------------------------------------------------------- +// inline methods +//------------------------------------------------------------------------- +inline void reg_value_info_t::extend( + const procmod_t &pm, + int width, + bool is_signed) +{ + if ( !is_known() ) + return; + for ( auto &p : vals ) + { + p.val = extend_sign(p.val, width, is_signed); + if ( !is_spd() ) // SP delta is signed + p.val = pm.trunc_uval(p.val); + else + p.val = pm.ea2sval(p.val); + } + sort_multivals(); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::trunc_uval(const procmod_t &pm) +{ + if ( !is_num() ) + return; + for ( auto &p : vals ) + p.val = pm.trunc_uval(p.val); + sort_multivals(); +} + +//------------------------------------------------------------------------- +inline bool reg_value_info_t::perform_binary_op( + const reg_value_info_t &r, + arith_op_t aop, + const insn_t &insn) +{ + const reg_value_info_t *mv; + uint64 sv; + if ( r.is_value_unique() ) + { + mv = this; + sv = r.vals.begin()->val; + } + else if ( is_value_unique() ) + { + mv = &r; + sv = vals.begin()->val; + } + else + { + return false; // both THIS and R have multiple values + } + qvector<uint64> rvals; + rvals.reserve(mv->vals.size()); + for ( const auto &p : mv->vals ) + { + uint64 res; + switch ( aop ) + { + case ADD: res = p.val + sv; break; + case SUB: res = p.val - sv; break; + case OR: res = p.val | sv; break; + case AND: res = p.val & sv; break; + case XOR: res = p.val ^ sv; break; + case AND_NOT: res = p.val & ~sv; break; + case SLL: res = p.val << sv; break; + case SLR: res = p.val >> sv; break; + case SAR: res = int64(p.val) >> sv; break; + case MOVT: res = (p.val & 0xFFFF) | ((sv & 0xFFFF) << 16); break; + default: return false; + } + rvals.push_back(res); + } + set_multivals(&rvals, insn); + return true; +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::add( + const reg_value_info_t &r, + const insn_t &insn) +{ + if ( is_spd() && r.is_num() // spd + num -> spd + || is_num() && r.is_spd() // num + spd -> spd + || is_num() && r.is_num() ) // num + num -> num + { + if ( perform_binary_op(r, ADD, insn) ) + { + if ( is_spd() || r.is_spd() ) + state = SPDINSN; + return; + } + } + // spd + spd or unknown or both THIS and R have multiple values + set_unkinsn(insn); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::sub( + const reg_value_info_t &r, + const insn_t &insn) +{ + if ( is_spd() && r.is_num() // spd - num -> spd + || is_spd() && r.is_spd() // spd - spd -> num + || is_num() && r.is_num() ) // num - num -> num + { + if ( perform_binary_op(r, SUB, insn) ) + { + if ( is_spd() ) + state = r.is_spd() ? NUMINSN : SPDINSN; + return; + } + } + // num - spd or unknown or both THIS and R have multiple values + set_unkinsn(insn); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::bor( + const reg_value_info_t &r, + const insn_t &insn) +{ + if ( is_num() && r.is_num() ) + if ( perform_binary_op(r, OR, insn) ) + return; + // not numbers or both THIS and R have multiple values + set_unkinsn(insn); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::band( + const reg_value_info_t &r, + const insn_t &insn) +{ + uval_t mask; + if ( is_spd() && r.get_num(&mask) && is_pow2(mask + 1) && mask <= 31 ) + { + if ( perform_binary_op(r, AND, insn) ) + { + state = NUMINSN; + return; + } + } + if ( is_num() && r.is_num() ) + { + if ( perform_binary_op(r, AND, insn) ) + return; + } + // not numbers or not mask of SPD or both THIS and R have multiple values + set_unkinsn(insn); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::bxor( + const reg_value_info_t &r, + const insn_t &insn) +{ + if ( is_num() && r.is_num() ) + if ( perform_binary_op(r, XOR, insn) ) + return; + // not numbers or both THIS and R have multiple values + set_unkinsn(insn); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::bandnot( + const reg_value_info_t &r, + const insn_t &insn) +{ + uval_t mask; + if ( is_spd() && r.get_num(&mask) && is_pow2(mask + 1) && mask <= 31 ) + if ( perform_binary_op(r, AND_NOT, insn) ) + return; + if ( is_num() && r.is_num() ) + if ( perform_binary_op(r, AND_NOT, insn) ) + return; + // not numbers or not SPD aligning or both THIS and R have multiple values + set_unkinsn(insn); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::sll( + const reg_value_info_t &r, + const insn_t &insn) +{ + if ( is_num() && r.is_num() && perform_binary_op(r, SLL, insn) ) + ; + else // not numbers + set_unkinsn(insn); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::slr( + const reg_value_info_t &r, + const insn_t &insn) +{ + if ( is_num() && r.is_num() && perform_binary_op(r, SLR, insn) ) + ; + else // not numbers + set_unkinsn(insn); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::sar( + const reg_value_info_t &r, + const insn_t &insn) +{ + if ( is_num() && r.is_num() && perform_binary_op(r, SAR, insn) ) + ; + else // not numbers + set_unkinsn(insn); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::movt( + const reg_value_info_t &r, + const insn_t &insn) +{ + if ( is_num() && r.is_num() && perform_binary_op(r, MOVT, insn) ) + ; + else // not numbers + set_unkinsn(insn); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::neg(const insn_t &insn) +{ + if ( is_num() ) + { + qvector<uint64> rvals; + rvals.reserve(vals.size()); + for ( auto &p : vals ) + rvals.push_back(0-p.val); + set_multivals(&rvals, insn); + } + else // not a number + { + set_unkinsn(insn); + } +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::bnot(const insn_t &insn) +{ + if ( is_num() ) + { + qvector<uint64> rvals; + rvals.reserve(vals.size()); + for ( auto &p : vals ) + rvals.push_back(~p.val); + set_multivals(&rvals, insn); + } + else // not a number + { + set_unkinsn(insn); + } +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::add_num(uval_t r, const insn_t &insn) +{ + if ( !is_known() || r == 0 ) + return; + qvector<uint64> rvals; + rvals.reserve(vals.size()); + for ( auto &p : vals ) + rvals.push_back(p.val + r); + set_multivals(&rvals, insn); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::add_num(uval_t r) +{ + if ( !is_known() || r == 0 ) + return; + for ( auto &p : vals ) + p.val += r; + sort_multivals(); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::shift_left(uval_t r) +{ + if ( !is_known() || r == 0 ) + return; + for ( auto &p : vals ) + p.val <<= r; + sort_multivals(); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::shift_right(uval_t r) +{ + if ( !is_known() || r == 0 ) + return; + for ( auto &p : vals ) + p.val >>= r; + sort_multivals(); +} + +//------------------------------------------------------------------------- +template<class V> +inline void reg_value_info_t::set_multivals( + V *rvals, + const insn_t &insn) +{ + // DEF_EA will be the same so it is enough to sort only VAL + std::sort(rvals->begin(), rvals->end()); + size_t newsz = std::unique(rvals->begin(), rvals->end()) - rvals->begin(); + vals.resize(newsz); + for ( size_t i = 0; i < newsz; ++i ) + vals[i] = val_def_t(rvals->at(i), insn); +} + +//------------------------------------------------------------------------- +inline void reg_value_info_t::sort_multivals() +{ + // at this point we use reg_value_def_t::operator<() + std::sort(vals.begin(), vals.end()); + // at this point we use reg_value_def_t::operator==() + size_t new_size = std::unique(vals.begin(), vals.end()) - vals.begin(); + vals.resize(new_size); +} + +//------------------------------------------------------------------------- +inline reg_finder_op_t reg_finder_op_t::make_reg(int reg, int width) +{ + uint32 packed_width = pack_width(width); + if ( packed_width == BADREG || !is_valid_reg(reg) ) + return rfop_t(); + return rfop_t(REG | packed_width | uint16(reg)); +} + +//------------------------------------------------------------------------- +inline reg_finder_op_t reg_finder_op_t::make_stkoff( + sval_t stkoff, + int width) +{ + uint32 packed_width = pack_width(width); + if ( packed_width == BADREG || !is_valid_stkoff(stkoff) ) + return rfop_t(); + bool negative = stkoff < 0; + return rfop_t(STKVAR + | packed_width + | (negative ? STKOFF_SIGNBIT : 0) + | uint32(negative ? -stkoff : stkoff)); +} + +//------------------------------------------------------------------------- +inline void reg_finder_op_t::set_width(int width) +{ + uint32 packed_width = pack_width(width); + if ( packed_width == BADREG ) + { + clear(); + return; + } + packed &= ~WIDTH_MASK; + packed |= packed_width; +} + +//------------------------------------------------------------------------- +inline void reg_finder_op_t::set_signness(bool is_signed) +{ + packed &= ~SIGNED; + if ( is_signed ) + packed |= SIGNED; +} + +//------------------------------------------------------------------------- +inline void reg_finder_op_t::set_width_signness(int width, bool is_signed) +{ + uint32 packed_width = pack_width(width); + if ( packed_width == BADREG ) + { + clear(); + return; + } + packed &= ~WIDTH_MASK; + packed |= packed_width; + packed &= ~SIGNED; + if ( is_signed ) + packed |= SIGNED; +} + +//------------------------------------------------------------------------- +inline uint32 reg_finder_op_t::pack_width(int width) +{ + switch ( width ) + { + case 1: return 0 << WIDTH_SHIFT; break; + case 2: return 1 << WIDTH_SHIFT; break; + case 4: return 2 << WIDTH_SHIFT; break; + case 8: return 3 << WIDTH_SHIFT; break; + default: return BADREG; + } +} + +//------------------------------------------------------------------------- +inline int reg_finder_op_t::get_op_width(const op_t &op) +{ + return get_dtype_size(op.dtype); +} + +``` + +`IdaSDK/registry.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef __REGISTRY_HPP +#define __REGISTRY_HPP + +/*! \file registry.hpp + + \brief Registry related functions + + IDA uses the registry to store global configuration options that must + persist after IDA has been closed. + + On Windows, IDA uses the Windows registry directly. On Unix systems, the registry + is stored in a file (typically ~/.idapro/ida.reg). + + The root key for accessing IDA settings in the registry is defined by #ROOT_KEY_NAME. +*/ + +// Unix systems use a file in the user directory (usually $HOME/.idapro/ida.reg) +#if !defined(LOCAL_REGISTRY) && !defined(__NT__) +# define LOCAL_REGISTRY +#endif +//#define LOCAL_REGISTRY // for testing local registry on windows, uncomment + +//--------------------------------------------------------------------------- +// Each application has its own registry file +#ifdef LOCAL_REGISTRY +# define IDA_REGISTRY_NAME "ida" +#else +# define IDA_REGISTRY_NAME "IDA" +#endif +#define HVUI_REGISTRY_NAME "hvui" + +// note: root_name[] in register.cpp +/// Default key used to store IDA settings in registry (Windows version). +/// \note this name is automatically prepended to all +/// key names passed to functions in this file. +#define ROOT_KEY_NAME "Software\\Hex-Rays\\IDA" + +/// \cond +// Low level functions. DO NOT USE THEM. See the wrappers below. +// 'mode' is: +// - 0: data is a raw buffer, and its datalen must be able to hold the entire binary contents +// - 1: data is a raw buffer, and its datalen doesn't need to be able to hold the entire binary contents +// - 2: data is a ::bytevec_t*, datalen is ignored. +idaman THREAD_SAFE bool ida_export reg_bin_op( + const char *name, + bool save, + void *data, + size_t datalen, + const char *subkey, + int mode = 0); +idaman THREAD_SAFE bool ida_export reg_str_get(qstring *buf, const char *name, const char *subkey); +idaman THREAD_SAFE void ida_export reg_str_set(const char *name, const char *subkey, const char *buf); +idaman THREAD_SAFE int ida_export reg_int_op( + const char *name, + bool save, + int value, + const char *subkey = nullptr); +/// \endcond + +/// Types of values stored in the registry +enum regval_type_t +{ + reg_unknown = 0, ///< unknown + reg_sz = 1, ///< utf8 string + reg_binary = 3, ///< binary data + reg_dword = 4 ///< 32-bit number +}; + + +/// Delete a key from the registry +idaman THREAD_SAFE bool ida_export reg_delete_subkey(const char *name); + +/// Delete a subtree from the registry +idaman THREAD_SAFE bool ida_export reg_delete_tree(const char *name); + +/// Delete a value from the registry. +/// \param name value name +/// \param subkey parent key +/// \return success + +idaman THREAD_SAFE bool ida_export reg_delete(const char *name, const char *subkey = nullptr); + + +/// Is there already a key with the given name? + +idaman THREAD_SAFE bool ida_export reg_subkey_exists(const char *name); + + +/// Is there already a value with the given name? +/// \param name value name +/// \param subkey parent key + +idaman THREAD_SAFE bool ida_export reg_exists(const char *name, const char *subkey = nullptr); + + +/// Retrieve the child names of the given key. +/// \param out result +/// \param name key name +/// \param subkeys if true, collect subkey names. if false, collect value names. +/// \return false if the given key does not exist + +idaman THREAD_SAFE bool ida_export reg_subkey_children(qstrvec_t *out, const char *name, bool subkeys); + + +/// Get data type of a given value. +/// \param out result +/// \param name value name +/// \param subkey key name +/// \return false if the [key+]value doesn't exist + +idaman THREAD_SAFE bool ida_export reg_data_type(regval_type_t *out, const char *name, const char *subkey = nullptr); + + +/// Retrieve all string values associated with the given key. +/// Also see reg_update_strlist(), reg_write_strlist() + +idaman THREAD_SAFE void ida_export reg_read_strlist(qstrvec_t *list, const char *subkey); + + +/// Write string values associated with the given key. +/// Also see reg_read_strlist(), reg_update_strlist() + +idaman THREAD_SAFE void ida_export reg_write_strlist(const qstrvec_t &in, const char *subkey); + + +/// Update list of strings associated with given key. +/// \param subkey key name +/// \param add string to be added to list, can be nullptr +/// \param maxrecs limit list to this size +/// \param rem string to be removed from list, can be nullptr +/// \param ignorecase ignore case for 'add' and 'rem' + +idaman THREAD_SAFE void ida_export reg_update_strlist( + const char *subkey, + const char *add, + size_t maxrecs, + const char *rem = nullptr, + bool ignorecase = false); + + +/// Write binary data to the registry. +/// \param name value name +/// \param data input, must not be nullptr +/// \param datalen length of input in bytes +/// \param subkey key name + +inline THREAD_SAFE void reg_write_binary( + const char *name, + const void *data, + size_t datalen, + const char *subkey = nullptr) +{ + reg_bin_op(name, true, CONST_CAST(void *)(data), datalen, subkey); +} + + +/// Write binary data to the registry. +/// \param name value name +/// \param data input data (as byte vector) +/// \param subkey key name +inline THREAD_SAFE void reg_write_binary( + const char *name, + const bytevec_t &data, + const char *subkey = nullptr) +{ + return reg_write_binary(name, data.begin(), data.size(), subkey); +} + + +/// Read binary data from the registry. +/// \param name value name +/// \param[out] data result, must not be nullptr +/// \param datalen length of out buffer in bytes +/// \param subkey key name +/// \return false if 'data' is not large enough to hold all data present. +/// in this case 'data' is left untouched. + +inline THREAD_SAFE bool reg_read_binary( + const char *name, + void *data, + size_t datalen, + const char *subkey = nullptr) +{ + return reg_bin_op(name, false, data, datalen, subkey); +} + + +/// Read a chunk of binary data from the registry. +/// This function succeeds even in the case of a partial read. +/// \param name value name +/// \param[out] data result, must not be nullptr +/// \param datalen length of output buffer in bytes +/// \param subkey key name +/// \return success + +inline THREAD_SAFE bool reg_read_binary_part( + const char *name, + void *data, + size_t datalen, + const char *subkey = nullptr) +{ + return reg_bin_op(name, false, data, datalen, subkey, 1); +} + + +/// Read binary data from the registry. +/// \param name value name +/// \param[out] data output buffer, must not be nullptr +/// \param subkey key name +/// \return success + +inline THREAD_SAFE bool reg_read_binary( + const char *name, + bytevec_t *data, + const char *subkey = nullptr) +{ + return reg_bin_op(name, false, data, 0, subkey, 2); +} + + +/// Write a string to the registry. +/// \param name value name +/// \param utf8 utf8-encoded string +/// \param subkey key name + +inline THREAD_SAFE void reg_write_string( + const char *name, + const char *utf8, + const char *subkey = nullptr) +{ + reg_str_set(name, subkey, utf8); +} + + +/// Read a string from the registry. +/// \param[out] utf8 output buffer +/// \param name value name +/// \param subkey key name +/// \return success + +inline THREAD_SAFE bool reg_read_string( + qstring *utf8, + const char *name, + const char *subkey = nullptr) +{ + return reg_str_get(utf8, name, subkey); +} + + +/// Read integer value from the registry. +/// \param name value name +/// \param defval default value +/// \param subkey key name +/// \return the value read from the registry, or 'defval' if the read failed + +inline THREAD_SAFE int reg_read_int(const char *name, int defval, const char *subkey = nullptr) +{ + return reg_int_op(name, false, defval, subkey); +} + + +/// Write integer value to the registry. +/// \param name value name +/// \param value value to write +/// \param subkey key name + +inline THREAD_SAFE void reg_write_int(const char *name, int value, const char *subkey = nullptr) +{ + reg_int_op(name, true, value, subkey); +} + + +/// Read boolean value from the registry. +/// \param name value name +/// \param defval default value +/// \param subkey key name +/// \return boolean read from registry, or 'defval' if the read failed + +inline THREAD_SAFE bool reg_read_bool(const char *name, bool defval, const char *subkey = nullptr) +{ + return reg_int_op(name, false, int(defval), subkey) != 0; +} + + +/// Write boolean value to the registry. +/// \param name value name +/// \param value boolean to write (nonzero = true) +/// \param subkey key name + +inline THREAD_SAFE void reg_write_bool(const char *name, int value, const char *subkey = nullptr) +{ + reg_int_op(name, true, value != 0, subkey); +} + + +/// Get all subkey names of given key + +inline THREAD_SAFE bool reg_subkey_subkeys(qstrvec_t *out, const char *name) +{ + return reg_subkey_children(out, name, true); +} + + +/// Get all value names under given key + +inline THREAD_SAFE bool reg_subkey_values(qstrvec_t *out, const char *name) +{ + return reg_subkey_children(out, name, false); +} + + +/// Update registry with a file list. +/// Case sensitivity will vary depending on the target OS. +/// \note 'add' and 'rem' must be UTF-8, just like for regular string operations. + +inline THREAD_SAFE void reg_update_filestrlist( + const char *subkey, + const char *add, + size_t maxrecs, + const char *rem = nullptr) +{ + reg_update_strlist( + subkey, add, maxrecs, rem, + #ifdef __NT__ // Ignore case in Windows + true + #else + false + #endif + ); +} + +//----------------------------------------------------------------------------- +// INTERNALS + +/// \cond +#define _RVN_(f) regname_ ## f + +#ifndef __DEFINE_REG_NAMES__ +#define REG_VAL_NAME(n,s) \ + extern const char _RVN_(n)[] +#else +#define REG_VAL_NAME(n,s) \ + extern const char _RVN_(n)[]; \ + const char _RVN_(n)[] = s +#endif + +#define REG_BOOL_FUNC(func, valname) \ +REG_VAL_NAME(func, valname); \ +inline THREAD_SAFE void regset_ ## func(bool value) \ + { reg_write_bool(_RVN_(func), value); } \ +inline THREAD_SAFE bool regget_ ## func(bool def) \ + { return reg_read_bool(_RVN_(func), def); } + +#define REG_INT_FUNC(func, valname) \ +REG_VAL_NAME(func,valname); \ +inline THREAD_SAFE void regset_ ## func(int value) \ +{ \ + reg_int_op(_RVN_(func), true, value); \ +} \ +inline THREAD_SAFE int regget_ ## func(int def=0) \ +{ \ + return reg_int_op(_RVN_(func), false, def); \ +} + +/// Define the Windows registry subkey (under HKCU\\Software\\Hex-Rays) +/// acting as the root of the IDA registry. Under Linux/Mac, it is used as +/// a filename with extension ".cfg". Defaults to "IDA" (lowercase in Linux/Mac). +idaman THREAD_SAFE bool ida_export set_registry_name(const char *name); + +// if using history functions below, you have to define the following two variables +extern const char regkey_history[]; +extern int max_history_files; // max number of files in the file menu + // and in the welcome box +#define MAX_HISTORY_FILES_DEF 10 // default value + +inline THREAD_SAFE void regget_history(qstrvec_t *list) +{ + reg_read_strlist(list, regkey_history); +} + +inline THREAD_SAFE void reg_update_history(const char *addfile, const char *removefile = nullptr) +{ + // On Windows avoid duplicate upper/lower-case entries + // by using reg_update_filestrlist() which takes care of case sensitivity + reg_update_filestrlist(regkey_history, addfile, max_history_files, removefile); +} + +inline THREAD_SAFE void reg_history_size_truncate(void) +{ + reg_update_strlist(regkey_history, nullptr, max_history_files, nullptr); +} +/// \endcond + + +#endif // __REGISTRY_HPP + +``` + +`IdaSDK/search.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * ALL RIGHTS RESERVED. + * Copyright (c) 1990-2025 Hex-Rays + * + */ + +#ifndef __SEARCH_HPP +#define __SEARCH_HPP + +/*! \file search.hpp + + \brief Middle-level search functions + + They all are controlled by \ref SEARCH_ +*/ + +/// \defgroup SEARCH_ Search flags +///@{ +#define SEARCH_UP 0x000 ///< search towards lower addresses +#define SEARCH_DOWN 0x001 ///< search towards higher addresses +#define SEARCH_NEXT 0x002 ///< skip the starting address when searching. + ///< this bit is useful only for search(), bin_search(), find_reg_access(). + ///< find_.. functions skip the starting address automatically. +#define SEARCH_CASE 0x004 ///< case-sensitive search (case-insensitive otherwise) +#define SEARCH_REGEX 0x008 ///< regular expressions in search string (supported only for the text search) +#define SEARCH_NOBRK 0x010 ///< do not test if the user clicked cancel to interrupt the search +#define SEARCH_NOSHOW 0x020 ///< do not display the search progress/refresh screen +#define SEARCH_IDENT 0x080 ///< search for an identifier (text search). + ///< it means that the characters before + ///< and after the match cannot be is_visible_char(). +#define SEARCH_BRK 0x100 ///< return #BADADDR if the search was cancelled. +#define SEARCH_USE 0x200 ///< find_reg_access: search for a use (read access) +#define SEARCH_DEF 0x400 ///< find_reg_access: search for a definition (write access) +#define SEARCH_USESEL 0x800 ///< query the UI for a possible current + ///< selection to limit the search to +///@} + + +/// Is the #SEARCH_DOWN bit set? + +inline THREAD_SAFE bool search_down(int sflag) { return (sflag & SEARCH_DOWN) != 0; } + + +/// \name find_... functions +/// \param ea start ea +/// \param sflag combination of \ref SEARCH_ +/// \param[out] opnum filled with operand number whenever relevant +/// \return first ea at which the search criteria is met +///@{ + + +/// Find next error or problem + +idaman ea_t ida_export find_error(ea_t ea, int sflag, int *opnum=nullptr); + + +/// Find next operand without any type info + +idaman ea_t ida_export find_notype(ea_t ea, int sflag, int *opnum=nullptr); + + +/// Find next unexplored address + +idaman ea_t ida_export find_unknown(ea_t ea, int sflag); + + +/// Find next ea that is the start of an instruction or data + +idaman ea_t ida_export find_defined(ea_t ea, int sflag); + + +/// Find next suspicious operand + +idaman ea_t ida_export find_suspop(ea_t ea, int sflag, int *opnum=nullptr); + + +/// Find next data address + +idaman ea_t ida_export find_data(ea_t ea, int sflag); + + +/// Find next code address + +idaman ea_t ida_export find_code(ea_t ea, int sflag); + + +/// Find next code address that does not belong to a function + +idaman ea_t ida_export find_not_func(ea_t ea, int sflag); + + +/// Find next immediate operand with the given value + +idaman ea_t ida_export find_imm(ea_t ea, int sflag, uval_t search_value, int *opnum=nullptr); + + +/// See search() + +idaman ea_t ida_export find_text(ea_t start_ea, int y, int x, const char *ustr, int sflag); + + +/// Find access to a register. +/// \param out pointer to the output buffer. must be non-null. +/// upon success contains info about the found register. +/// upon failed search for a read access out->range contains +/// the info about the non-redefined parts of the register. +/// \param start_ea starting address +/// \param end_ea ending address. BADADDR means that the end limit is missing. +/// otherwise, if the search direction is SEARCH_UP, +/// END_EA must be lower than START_EA. +/// \param regname the register to search for. +/// \param sflag combination of \ref SEARCH_ bits. +/// \note This function does not care about the control flow and +/// probes all instructions in the specified range, starting from START_EA. +/// Only direct references to registers are detected. Function calls and +/// system traps are ignored. +/// \return the found address. BADADDR if not found or error. +idaman ea_t ida_export find_reg_access( + struct reg_access_t *out, + ea_t start_ea, + ea_t end_ea, + const char *regname, + int sflag); + +///@} + +class place_t; + + +/// Search for a text substring (low level function). +/// \param ud line array parameter +/// \param[in,out] start pointer to starting place: +/// - start->ea: starting address +/// - start->lnnum: starting Y coordinate +/// \param end pointer to ending place: +/// - end->ea: ending address +/// - end->lnnum: ending Y coordinate +/// \param[in,out] startx pointer to starting X coordinate +/// \param str substring to search for. +/// \param sflag \ref SEARCH_ +/// \retval 0 substring not found +/// \retval 1 substring found. The matching position is returned in: +/// - start->ea: address +/// - start->lnnum: Y coordinate +/// - *startx: X coordinate +/// \retval 2 search was cancelled by ctrl-break. +/// The farthest searched address is +/// returned in the same manner as in the successful return (1). +/// \retval 3 the input regular expression is bad. +/// The error message was displayed. + +idaman int ida_export search( + void *ud, + place_t *start, + const place_t *end, + int *startx, + const char *str, + int sflag); + +#endif // __SEARCH_HPP + +``` + +`IdaSDK/segment.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _SEGMENT_HPP +#define _SEGMENT_HPP + +#include <ida.hpp> +#include <range.hpp> // segments are range of addresses + // with characteristics + +/*! \file segment.hpp + + \brief Functions that deal with segments. + + IDA requires that all program addresses belong to segments + (each address must belong to exactly one segment). + The situation when an address doesn't belong to any segment + is allowed as a temporary situation only when the user changes program + segmentation. Bytes outside a segment can't be converted to + instructions, have names, comments, etc. + Each segment has its start address, ending address and represents + a contiguous range of addresses. There might be unused holes between + segments. + + Each segment has its unique segment selector. This selector is used + to distinguish the segment from other segments. For 16-bit programs + the selector is equal to the segment base paragraph. For 32-bit + programs there is special array to translate the selectors to + the segment base paragraphs. A selector is a 32/64 bit value. + + The segment base paragraph determines the offsets in the segment. + If the start address of the segment == (base << 4) then the first + offset in the segment will be 0. The start address should be + higher or equal to (base << 4). + We will call the offsets in the segment 'virtual addresses'. + So, the virtual address of the first byte of the segment is + + (start address of segment - segment base linear address) + + For IBM PC, the virtual address corresponds to the offset part of the address. + For other processors (Z80, for example), virtual addresses correspond + to Z80 addresses and linear addresses are used only internally. + For MS Windows programs the segment base paragraph is 0 and therefore + the segment virtual addresses are equal to linear addresses. +*/ + +/// \defgroup seg Segments +/// \copybrief segment.hpp + + +/// Maximum number of segment registers is 16 (see segregs.hpp) +#define SREG_NUM 16 + + +//------------------------------------------------------------------------- +// D E F I N I T O N O F S E G M E N T S T R U C T U R E +//------------------------------------------------------------------------- +/// \defgroup seg_t Segment structure +/// Definition of ::segment_t and related functions +/// \ingroup seg +///@{ + +/// Describes a program segment +class segment_t : public range_t +{ +public: +/* 8 */ uval_t name; ///< use get/set_segm_name() functions +/* 12 */ uval_t sclass; ///< use get/set_segm_class() functions +/* 16 */ uval_t orgbase; ///< this field is IDP dependent. + ///< you may keep your information about + ///< the segment here + +/* 20 */ uchar align; ///< \ref sa_ +/// \defgroup sa_ Segment alignment codes +/// \ingroup seg_t +/// Used by segment_t::align +///@{ +#define saAbs 0 ///< Absolute segment. +#define saRelByte 1 ///< Relocatable, byte aligned. +#define saRelWord 2 ///< Relocatable, word (2-byte) aligned. +#define saRelPara 3 ///< Relocatable, paragraph (16-byte) aligned. +#define saRelPage 4 ///< Relocatable, aligned on 256-byte boundary +#define saRelDble 5 ///< Relocatable, aligned on a double word (4-byte) + ///< boundary. +#define saRel4K 6 ///< This value is used by the PharLap OMF for page (4K) + ///< alignment. It is not supported by LINK. +#define saGroup 7 ///< Segment group +#define saRel32Bytes 8 ///< 32 bytes +#define saRel64Bytes 9 ///< 64 bytes +#define saRelQword 10 ///< 8 bytes +#define saRel128Bytes 11 ///< 128 bytes +#define saRel512Bytes 12 ///< 512 bytes +#define saRel1024Bytes 13 ///< 1024 bytes +#define saRel2048Bytes 14 ///< 2048 bytes +#define saRel_MAX_ALIGN_CODE saRel2048Bytes +///@} + +/* 21 */ uchar comb; ///< \ref sc_ +/// \defgroup sc_ Segment combination codes +/// \ingroup seg_t +/// Used by segment_t::comb +///@{ +#define scPriv 0 ///< Private. Do not combine with any other program + ///< segment. +#define scGroup 1 ///< Segment group +#define scPub 2 ///< Public. Combine by appending at an offset that meets + ///< the alignment requirement. +#define scPub2 4 ///< As defined by Microsoft, same as C=2 (public). +#define scStack 5 ///< Stack. Combine as for C=2. This combine type forces + ///< byte alignment. +#define scCommon 6 ///< Common. Combine by overlay using maximum size. +#define scPub3 7 ///< As defined by Microsoft, same as C=2 (public). +#define sc_MAX_COMB_CODE scPub3 +///@} + +/* 22 */ uchar perm; ///< \ref SEGPERM_ (0 means no information) +/// \defgroup SEGPERM_ Segment permissions +/// \ingroup seg_t +/// Used by segment_t::perm +///@{ +#define SEGPERM_EXEC 1 ///< Execute +#define SEGPERM_WRITE 2 ///< Write +#define SEGPERM_READ 4 ///< Read +#define SEGPERM_MAXVAL (SEGPERM_EXEC + SEGPERM_WRITE + SEGPERM_READ) ///< Execute + Write + Read +///@} + +/* 23 */ uchar bitness; ///< Number of bits in the segment addressing + ///< - 0: 16 bits + ///< - 1: 32 bits + ///< - 2: 64 bits +#define SEG_MAX_BITNESS_CODE 2 ///< Maximum segment bitness value + + /// Is a 16-bit segment? + bool is_16bit(void) const { return bitness == 0; } + /// Is a 32-bit segment? + bool is_32bit(void) const { return bitness == 1; } + /// Is a 64-bit segment? + bool is_64bit(void) const { return bitness == 2; } + /// Get number of address bits + int abits(void) const { return 1<<(bitness+4); } + /// Get number of address bytes + int abytes(void) const { return abits() / 8; } + +/// \defgroup SFL_ Segment flags +/// \ingroup seg_t +/// Used by segment_t::flags + +/* 24 */ ushort flags; ///< \ref SFL_ + +/// IDP dependent field (IBM PC: if set, ORG directive is not commented out) +/// \ingroup SFL_ +#define SFL_COMORG 0x01 + + /// \name Segment flag: ORG directive + /// See #SFL_COMORG + ///@{ + bool comorg(void) const { return (flags & SFL_COMORG) != 0; } + void set_comorg(void) { flags |= SFL_COMORG; } + void clr_comorg(void) { flags &= ~SFL_COMORG; } + ///@} + +/// Orgbase is present? (IDP dependent field) +/// \ingroup SFL_ +#define SFL_OBOK 0x02 + + /// \name Segment flag: orgbase + /// See #SFL_OBOK + ///@{ + bool ob_ok(void) const { return (flags & SFL_OBOK) != 0; } + void set_ob_ok(void) { flags |= SFL_OBOK; } + void clr_ob_ok(void) { flags &= ~SFL_OBOK; } + ///@} + +/// Is the segment hidden? +/// \ingroup SFL_ +#define SFL_HIDDEN 0x04 + + /// \name Segment flag: hidden + /// See #SFL_HIDDEN + ///@{ + bool is_visible_segm(void) const { return (flags & SFL_HIDDEN) == 0; } + void set_visible_segm(bool visible) { setflag(flags, SFL_HIDDEN, !visible); } + ///@} + +/// Is the segment created for the debugger?. +/// Such segments are temporary and do not have permanent flags. +/// \ingroup SFL_ +#define SFL_DEBUG 0x08 + + /// \name Segment flag: debugger segment + /// See #SFL_DEBUG + ///@{ + bool is_debugger_segm(void) const { return (flags & SFL_DEBUG) != 0; } + // use ::change_segment_status() to change the segment state during debugging + void set_debugger_segm(bool debseg) { setflag(flags, SFL_DEBUG, debseg); } + ///@} + +/// Is the segment created by the loader? +/// \ingroup SFL_ +#define SFL_LOADER 0x10 + + /// \name Segment flag: loader segment + /// See #SFL_LOADER + ///@{ + bool is_loader_segm(void) const { return (flags & SFL_LOADER) != 0; } + void set_loader_segm(bool ldrseg) { setflag(flags, SFL_LOADER, ldrseg); } + ///@} + +/// Hide segment type (do not print it in the listing) +/// \ingroup SFL_ +#define SFL_HIDETYPE 0x20 + + /// \name Segment flag: hide segment type + /// See #SFL_HIDETYPE + ///@{ + bool is_hidden_segtype(void) const { return (flags & SFL_HIDETYPE) != 0; } + void set_hidden_segtype(bool hide) { setflag(flags, SFL_HIDETYPE, hide); } + ///@} + +/// Header segment (do not create offsets to it in the disassembly) +/// \ingroup SFL_ +#define SFL_HEADER 0x40 + + /// \name Segment flag: header segment + /// See #SFL_HEADER + ///@{ + bool is_header_segm(void) const { return (flags & SFL_HEADER) != 0; } + void set_header_segm(bool on) { setflag(flags, SFL_HEADER, on); } + ///@} + + /// Ephemeral segments are not analyzed automatically + /// (no flirt, no functions unless required, etc). + /// Most likely these segments will be destroyed at the end of the + /// debugging session unless the user changes their status. + bool is_ephemeral_segm(void) const + { return (flags & (SFL_DEBUG|SFL_LOADER)) == SFL_DEBUG; } + +/* 28 */ sel_t sel; ///< segment selector - should be unique. You can't + ///< change this field after creating the segment. + ///< Exception: 16bit OMF files may have several + ///< segments with the same selector, but this is not + ///< good (no way to denote a segment exactly) + ///< so it should be fixed in the future. + +/* 32 */ sel_t defsr[SREG_NUM];///< default segment register values. + ///< first element of this array keeps information + ///< about value of \ph{reg_first_sreg} + +/* 96 */ uchar type; ///< segment type (see \ref SEG_). + ///< The kernel treats different segment types differently. + ///< Segments marked with '*' contain no instructions + ///< or data and are not declared as 'segments' in + ///< the disassembly. + +/// \defgroup SEG_ Segment types +/// \ingroup seg_t +/// Used by segment_t::type +///@{ +#define SEG_NORM 0 ///< unknown type, no assumptions +#define SEG_XTRN 1 ///< * segment with 'extern' definitions. + ///< no instructions are allowed +#define SEG_CODE 2 ///< code segment +#define SEG_DATA 3 ///< data segment +#define SEG_IMP 4 ///< java: implementation segment +#define SEG_GRP 6 ///< * group of segments +#define SEG_NULL 7 ///< zero-length segment +#define SEG_UNDF 8 ///< undefined segment type (not used) +#define SEG_BSS 9 ///< uninitialized segment +#define SEG_ABSSYM 10 ///< * segment with definitions of absolute symbols +#define SEG_COMM 11 ///< * segment with communal definitions +#define SEG_IMEM 12 ///< internal processor memory & sfr (8051) +#define SEG_MAX_SEGTYPE_CODE SEG_IMEM ///< maximum value segment type can take +///@} + +/* 100 */ bgcolor_t color; ///< the segment color + + /// Update segment information. You must call this function after modification + /// of segment characteristics. Note that not all fields of segment structure + /// may be modified directly, there are special functions to modify some fields. + /// \return success + inline bool update(void); + + /// Constructor + segment_t(void) + : name(0), + sclass(0), + orgbase(0), + align(0), + comb(0), + perm(0), + bitness(0), + flags(0), + sel(0), + type(SEG_NORM), + color(DEFCOLOR) + { + memset(defsr, 0, sizeof(defsr)); + } + +#ifndef SWIG + DECLARE_COMPARISONS(segment_t); +#endif +}; // total 104/192 bytes + +#ifdef __EA64__ +CASSERT(sizeof(segment_t) == 192); +#else +CASSERT(sizeof(segment_t) == 104); +#endif + +/// See #SFL_HIDDEN +inline bool is_visible_segm(segment_t *s) { return s != nullptr && s->is_visible_segm(); } +/// See #SFL_HIDDEN, #SCF_SHHID_SEGM +inline bool is_finally_visible_segm(segment_t *s) +{ + return (inf_get_cmtflg() & SCF_SHHID_SEGM) != 0 || is_visible_segm(s); +} +/// See #SFL_HIDDEN +idaman void ida_export set_visible_segm(segment_t *s, bool visible); + + +/// Has segment a special type?. +/// (#SEG_XTRN, #SEG_GRP, #SEG_ABSSYM, #SEG_COMM) + +idaman bool ida_export is_spec_segm(uchar seg_type); + + +/// Does the address belong to a segment with a special type?. +/// (#SEG_XTRN, #SEG_GRP, #SEG_ABSSYM, #SEG_COMM) +/// \param ea linear address + +idaman bool ida_export is_spec_ea(ea_t ea); + + +/// Lock segment pointer +/// Locked pointers are guaranteed to remain valid until they are unlocked. +/// Ranges with locked pointers cannot be deleted or moved. + +idaman void ida_export lock_segm(const segment_t *segm, bool lock); + +/// Helper class to lock a segment pointer so it stays valid +class lock_segment +{ + const segment_t *segm; +public: + lock_segment(const segment_t *_segm) : segm(_segm) + { + lock_segm(segm, true); + } + ~lock_segment(void) + { + lock_segm(segm, false); + } +}; + +/// Is a segment pointer locked? +idaman bool ida_export is_segm_locked(const segment_t *segm); + +///@} seg_t + +//------------------------------------------------------------------------- +// S E G M E N T S E L E C T O R S +// +/// \defgroup seg_sel Segment selectors +/// \ingroup seg +/// The kernel maintains a table to translate selector values to +/// segment base paragraphs. A Paragraph is a 16byte quantity. +/// This table and translation is necessary because IBM PC uses +/// 16bit selectors in instructions but segments may reside anywhere +/// in the linear addressing space. For example, if a segment with +/// selector 5 resides at 0x400000, we need to have selector translation +/// 5 -> 0x400000. +/// For 16bit programs the selector translation table is usually empty, +/// selector values are equal to segment base paragraphs. +///@{ +//------------------------------------------------------------------------- + +/// Get description of selector (0..get_selector_qty()-1) + +idaman bool ida_export getn_selector(sel_t *sel, ea_t *base, int n); + + +/// Get number of defined selectors + +idaman size_t ida_export get_selector_qty(void); + + +/// Allocate a selector for a segment if necessary. +/// You must call this function before calling add_segm_ex(). +/// add_segm() calls this function itself, so you don't need to +/// allocate a selector. +/// This function will allocate a selector if 'segbase' requires more than +/// 16 bits and the current processor is IBM PC. +/// Otherwise it will return the segbase value. +/// \param segbase a new segment base paragraph +/// \return the allocated selector number + +idaman sel_t ida_export setup_selector(ea_t segbase); + + +/// Allocate a selector for a segment unconditionally. +/// You must call this function before calling add_segm_ex(). +/// add_segm() calls this function itself, so you don't need to +/// allocate a selector. +/// This function will allocate a new free selector and setup its mapping +/// using find_free_selector() and set_selector() functions. +/// \param segbase a new segment base paragraph +/// \return the allocated selector number + +idaman sel_t ida_export allocate_selector(ea_t segbase); + + +/// Find first unused selector. +/// \return a number >= 1 + +idaman sel_t ida_export find_free_selector(void); + + +/// Set mapping of selector to a paragraph. +/// You should call this function _before_ creating a segment +/// which uses the selector, otherwise the creation of the segment will fail. +/// \param selector number of selector to map +/// - if selector == #BADSEL, then return 0 (fail) +/// - if the selector has had a mapping, old mapping is destroyed +/// - if the selector number is equal to paragraph value, then the mapping is +/// destroyed because we don't need to keep trivial mappings. +/// \param paragraph paragraph to map selector +/// \retval 1 ok +/// \retval 0 failure (bad selector or too many mappings) + +idaman int ida_export set_selector(sel_t selector, ea_t paragraph); + + +/// Delete mapping of a selector. +/// Be wary of deleting selectors that are being used in the program, this +/// can make a mess in the segments. +/// \param selector number of selector to remove from the translation table + +idaman void ida_export del_selector(sel_t selector); + + +/// Get mapping of a selector. +/// \param selector number of selector to translate +/// \return paragraph the specified selector is mapped to. +/// if there is no mapping, returns 'selector'. + +idaman ea_t ida_export sel2para(sel_t selector); + + +/// Get mapping of a selector as a linear address. +/// \param selector number of selector to translate to linear address +/// \return linear address the specified selector is mapped to. +/// if there is no mapping, returns to_ea(selector,0); + +inline ea_t idaapi sel2ea(sel_t selector) +{ + if ( selector == BADSEL ) + return BADADDR; + return to_ea(sel2para(selector), 0); +} + + +/// Find a selector that has mapping to the specified paragraph. +/// \param base paragraph to search in the translation table +/// \return selector value or base + +idaman sel_t ida_export find_selector(ea_t base); + + +/// Enumerate all selectors from the translation table. +/// This function calls 'func' for each selector in the translation table. +/// If 'func' returns non-zero code, enumeration is stopped and this code +/// is returned. +/// \param func callback function +/// - sel: selector number +/// - para: selector mapping +/// \return 0 or code returned by 'func'. + +idaman int ida_export enumerate_selectors(int (idaapi *func)(sel_t sel,ea_t para)); + + +/// Enumerate all segments with the specified selector. +/// This function will call the callback function 'func' for each +/// segment that has the specified selector. Enumeration starts +/// from the last segment and stops at the first segment (reverse order). +/// If the callback function 'func' returns a value != #BADADDR, the +/// enumeration is stopped and this value is returned to the caller. +/// \param selector segments that have this selector are enumerated +/// \param func callback function +/// - s: pointer to segment structure +/// - ud: user data +/// \param ud pointer to user data. this pointer will be passed +/// to the callback function +/// \return #BADADDR or the value returned by the callback function 'func' + +idaman ea_t ida_export enumerate_segments_with_selector( + sel_t selector, + ea_t (idaapi *func)(segment_t *s, void *ud), + void *ud=nullptr); + + +/// Get pointer to segment structure. +/// This function finds a segment by its selector. If there are several +/// segments with the same selectors, the last one will be returned. +/// \param selector a segment with the specified selector will be returned +/// \return pointer to segment or nullptr + +idaman segment_t *ida_export get_segm_by_sel(sel_t selector); + +///@} seg_sel + +//------------------------------------------------------------------------- +// S E G M E N T M A N I P U L A T I O N F U N C T I O N S +//------------------------------------------------------------------------- +/// \defgroup seg_man Segment manipulation functions +/// Add/Delete/Modify segments +/// \ingroup seg +///@{ + +/// Add a new segment. +/// If a segment already exists at the specified range of addresses, +/// this segment will be truncated. Instructions and data in the old +/// segment will be deleted if the new segment has another addressing +/// mode or another segment base address. +/// \param s pointer to filled segment structure. +/// segment selector should have proper mapping (see set_selector()). +/// - if s.start_ea==#BADADDR then s.start_ea <- get_segm_base(&s) +/// - if s.end_ea==#BADADDR, then a segment up to the next segment +/// will be created (if the next segment doesn't exist, then +/// 1 byte segment will be created). +/// - if the s.end_ea < s.start_ea, then fail. +/// - if s.end_ea is too high and the new segment would overlap +/// the next segment, s.end_ea is adjusted properly. +/// \param name name of new segment. may be nullptr. +/// if specified, the segment is immediately renamed +/// \param sclass class of the segment. may be nullptr. +/// if specified, the segment class is immediately changed +/// \param flags \ref ADDSEG_ +/// \retval 1 ok +/// \retval 0 failed, a warning message is displayed + +idaman bool ida_export add_segm_ex( + segment_t *NONNULL s, + const char *name, + const char *sclass, + int flags); +/// \defgroup ADDSEG_ Add segment flags +/// Passed as 'flags' parameter to add_segm_ex() +///@{ +#define ADDSEG_NOSREG 0x0001 ///< set all default segment register values to #BADSEL + ///< (undefine all default segment registers) +#define ADDSEG_OR_DIE 0x0002 ///< qexit() if can't add a segment +#define ADDSEG_NOTRUNC 0x0004 ///< don't truncate the new segment at the beginning of the next segment if they overlap. + ///< destroy/truncate old segments instead. +#define ADDSEG_QUIET 0x0008 ///< silent mode, no "Adding segment..." in the messages window +#define ADDSEG_FILLGAP 0x0010 ///< fill gap between new segment and previous one. + ///< i.e. if such a gap exists, and this gap is less + ///< than 64K, then fill the gap by extending the + ///< previous segment and adding .align directive + ///< to it. This way we avoid gaps between segments. + ///< too many gaps lead to a virtual array failure. + ///< it cannot hold more than ~1000 gaps. +#define ADDSEG_SPARSE 0x0020 ///< use sparse storage method for the new ranges + ///< of the created segment. please note that the + ///< ranges that were already enabled before + ///< creating the segment will not change their + ///< storage type. +#define ADDSEG_NOAA 0x0040 ///< do not mark new segment for auto-analysis +#define ADDSEG_IDBENC 0x0080 ///< 'name' and 'sclass' are given in the IDB encoding; + ///< non-ASCII bytes will be decoded accordingly +///@} + + +/// Add a new segment, second form. +/// Segment alignment is set to #saRelByte. +/// Segment combination is "public" or "stack" (if segment class is "STACK"). +/// Addressing mode of segment is taken as default (16bit or 32bit). +/// Default segment registers are set to #BADSEL. +/// If a segment already exists at the specified range of addresses, +/// this segment will be truncated. Instructions and data in the old +/// segment will be deleted if the new segment has another addressing +/// mode or another segment base address. +/// \param para segment base paragraph. +/// if paragraph can't fit in 16bit, then a new selector is +/// allocated and mapped to the paragraph. +/// \param start start address of the segment. +/// if start==#BADADDR then start <- to_ea(para,0). +/// \param end end address of the segment. end address should be higher than +/// start address. For emulate empty segments, use #SEG_NULL segment +/// type. If the end address is lower than start address, then fail. +/// If end==#BADADDR, then a segment up to the next segment +/// will be created (if the next segment doesn't exist, then +/// 1 byte segment will be created). +/// If 'end' is too high and the new segment would overlap +/// the next segment, 'end' is adjusted properly. +/// \param name name of new segment. may be nullptr +/// \param sclass class of the segment. may be nullptr. +/// type of the new segment is modified if class is one of +/// predefined names: +/// - "CODE" -> #SEG_CODE +/// - "DATA" -> #SEG_DATA +/// - "CONST" -> #SEG_DATA +/// - "STACK" -> #SEG_BSS +/// - "BSS" -> #SEG_BSS +/// - "XTRN" -> #SEG_XTRN +/// - "COMM" -> #SEG_COMM +/// - "ABS" -> #SEG_ABSSYM +/// \param flags \ref ADDSEG_ +/// \retval 1 ok +/// \retval 0 failed, a warning message is displayed + +idaman bool ida_export add_segm( + ea_t para, + ea_t start, + ea_t end, + const char *name, + const char *sclass, + int flags=0); + + +/// Delete a segment. +/// \param ea any address belonging to the segment +/// \param flags \ref SEGMOD_ +/// \retval 1 ok +/// \retval 0 failed, no segment at 'ea'. + +idaman bool ida_export del_segm(ea_t ea, int flags); + +/// \defgroup SEGMOD_ Segment modification flags +/// Used by functions in \ref seg_man +///@{ +#define SEGMOD_KILL 0x0001 ///< disable addresses if segment gets shrinked or deleted +#define SEGMOD_KEEP 0x0002 ///< keep information (code & data, etc) +#define SEGMOD_SILENT 0x0004 ///< be silent +#define SEGMOD_KEEP0 0x0008 ///< flag for internal use, don't set +#define SEGMOD_KEEPSEL 0x0010 ///< do not try to delete unused selector +#define SEGMOD_NOMOVE 0x0020 ///< don't move info from the start of segment to the new start address + ///< (for set_segm_start()) +#define SEGMOD_SPARSE 0x0040 ///< use sparse storage if extending the segment + ///< (for set_segm_start(), set_segm_end()) +///@} + + +/// Get number of segments + +idaman int ida_export get_segm_qty(void); + + +/// Get pointer to segment by linear address. +/// \param ea linear address belonging to the segment +/// \return nullptr or pointer to segment structure + +idaman segment_t *ida_export getseg(ea_t ea); + + +/// Get pointer to segment by its number. +/// \warning Obsoleted because it can slow down the debugger (it has to refresh the whole +/// memory segmentation to calculate the correct answer) +/// \param n segment number in the range (0..get_segm_qty()-1) +/// \return nullptr or pointer to segment structure + +idaman segment_t *ida_export getnseg(int n); + + +/// Get number of segment by address. +/// \param ea linear address belonging to the segment +/// \return -1 if no segment occupies the specified address. +/// otherwise returns number of the specified segment (0..get_segm_qty()-1) +idaman int ida_export get_segm_num(ea_t ea); + + +/// Get pointer to the next segment +idaman segment_t *ida_export get_next_seg(ea_t ea); +/// Get pointer to the previous segment +idaman segment_t *ida_export get_prev_seg(ea_t ea); + +/// Get pointer to the first segment +idaman segment_t *ida_export get_first_seg(void); +/// Get pointer to the last segment +idaman segment_t *ida_export get_last_seg(void); + + +/// Get pointer to segment by its name. +/// If there are several segments with the same name, returns the first of them. +/// \param name segment name. may be nullptr. +/// \return nullptr or pointer to segment structure + +idaman segment_t *ida_export get_segm_by_name(const char *name); + + +/// Set segment end address. +/// The next segment is shrinked to allow expansion of the specified segment. +/// The kernel might even delete the next segment if necessary. +/// The kernel will ask the user for a permission to destroy instructions +/// or data going out of segment scope if such instructions exist. +/// \param ea any address belonging to the segment +/// \param newend new end address of the segment +/// \param flags \ref SEGMOD_ +/// \retval 1 ok +/// \retval 0 failed, a warning message is displayed + +idaman bool ida_export set_segm_end(ea_t ea, ea_t newend, int flags); + + +/// Set segment start address. +/// The previous segment is trimmed to allow expansion of the specified segment. +/// The kernel might even delete the previous segment if necessary. +/// The kernel will ask the user for a permission to destroy instructions +/// or data going out of segment scope if such instructions exist. +/// \param ea any address belonging to the segment +/// \param newstart new start address of the segment +/// note that segment start address should be higher than +/// segment base linear address. +/// \param flags \ref SEGMOD_ +/// \retval 1 ok +/// \retval 0 failed, a warning message is displayed + +idaman bool ida_export set_segm_start(ea_t ea, ea_t newstart, int flags); + + +/// Move segment start. +/// The main difference between this function and set_segm_start() is +/// that this function may expand the previous segment while set_segm_start() +/// never does it. So, this function allows to change bounds of two segments +/// simultaneously. If the previous segment and the specified segment +/// have the same addressing mode and segment base, then instructions +/// and data are not destroyed - they simply move from one segment +/// to another. Otherwise all instructions/data which migrate +/// from one segment to another are destroyed. +/// \note this function never disables addresses. +/// \param ea any address belonging to the segment +/// \param newstart new start address of the segment +/// note that segment start address should be higher than +/// segment base linear address. +/// \param mode policy for destroying defined items +/// - 0: if it is necessary to destroy defined items, +/// display a dialog box and ask confirmation +/// - 1: if it is necessary to destroy defined items, +/// just destroy them without asking the user +/// - -1: if it is necessary to destroy defined items, +/// don't destroy them (i.e. function will fail) +/// - -2: don't destroy defined items (function will succeed) +/// \retval 1 ok +/// \retval 0 failed, a warning message is displayed + +idaman bool ida_export move_segm_start(ea_t ea, ea_t newstart, int mode); + + +/// \defgroup MOVE_SEGM_ Move segment result codes +/// Return values for move_segm() add rebase_program() +///@{ +enum move_segm_code_t +{ + MOVE_SEGM_OK = 0, ///< all ok + MOVE_SEGM_PARAM = -1, ///< The specified segment does not exist + MOVE_SEGM_ROOM = -2, ///< Not enough free room at the target address + MOVE_SEGM_IDP = -3, ///< IDP module forbids moving the segment + MOVE_SEGM_CHUNK = -4, ///< Too many chunks are defined, can't move + MOVE_SEGM_LOADER = -5, ///< The segment has been moved but the loader complained + MOVE_SEGM_ODD = -6, ///< Cannot move segments by an odd number of bytes + MOVE_SEGM_ORPHAN = -7, ///< Orphan bytes hinder segment movement + MOVE_SEGM_DEBUG = -8, ///< Debugger segments cannot be moved + MOVE_SEGM_SOURCEFILES = -9, ///< Source files ranges of addresses hinder segment movement + MOVE_SEGM_MAPPING = -10, ///< Memory mapping ranges of addresses hinder segment movement + MOVE_SEGM_INVAL = -11, ///< Invalid argument (delta/target does not fit the address space) +}; +///@} + + +/// Return string describing error MOVE_SEGM_... code +idaman const char *ida_export move_segm_strerror(move_segm_code_t code); + + +/// This function moves all information to the new address. +/// It fixes up address sensitive information in the kernel. +/// The total effect is equal to reloading the segment to the target address. +/// For the file format dependent address sensitive information, loader_t::move_segm is called. +/// Also IDB notification event idb_event::segm_moved is called. +/// \param s segment to move +/// \param to new segment start address +/// \param flags \ref MSF_ +/// \return \ref MOVE_SEGM_ + +idaman move_segm_code_t ida_export move_segm(segment_t *s, ea_t to, int flags=0); + +/// \defgroup MSF_ Move segment flags +/// Passed as 'flags' parameter to move_segm() and rebase_program() +///@{ +#define MSF_SILENT 0x0001 ///< don't display a "please wait" box on the screen +#define MSF_NOFIX 0x0002 ///< don't call the loader to fix relocations +#define MSF_LDKEEP 0x0004 ///< keep the loader in the memory (optimization) +#define MSF_FIXONCE 0x0008 ///< call loader only once with the special calling method. + ///< valid for rebase_program(). see loader_t::move_segm. +#define MSF_PRIORITY 0x0020 ///< loader segments will overwrite any existing debugger segments when moved. + ///< valid for move_segm() +#define MSF_NETNODES 0x0080 ///< move netnodes instead of changing inf.netdelta (this is slower); + ///< valid for rebase_program() +///@} + +/// Rebase the whole program by 'delta' bytes. +/// \param delta number of bytes to move the program +/// \param flags \ref MSF_ +/// it is recommended to use #MSF_FIXONCE so that the loader takes +/// care of global variables it stored in the database +/// \return \ref MOVE_SEGM_ + +idaman move_segm_code_t ida_export rebase_program(adiff_t delta, int flags); + + +/// Convert a debugger segment to a regular segment and vice versa. +/// When converting debug->regular, the memory contents will be copied +/// to the database. +/// \param s segment to modify +/// \param is_deb_segm new status of the segment +/// \return \ref CSS_ + +idaman int ida_export change_segment_status(segment_t *s, bool is_deb_segm); + +/// \defgroup CSS_ Change segment status result codes +/// Return values for change_segment_status() +///@{ +#define CSS_OK 0 ///< ok +#define CSS_NODBG -1 ///< debugger is not running +#define CSS_NORANGE -2 ///< could not find corresponding memory range +#define CSS_NOMEM -3 ///< not enough memory (might be because the segment + ///< is too big) +#define CSS_BREAK -4 ///< memory reading process stopped by user +///@} + + +/// \defgroup SNAP_ Snapshot types +/// Specifies which segments should be included in the snapshot. +/// Used by take_memory_snapshot +///@{ +#define SNAP_ALL_SEG 0 ///< Take a snapshot of all segments +#define SNAP_LOAD_SEG 1 ///< Take a snapshot of loader segments +#define SNAP_CUR_SEG 2 ///< Take a snapshot of current segment +///@} + +/// Take a memory snapshot of the running process. +/// \param type specifies which snapshot we want (see SNAP_ Snapshot types) +/// \return success + +idaman bool ida_export take_memory_snapshot(int type); + +/// Is the database a miniidb created by the debugger?. +/// \return true if the database contains no segments +/// or only debugger segments + +idaman bool ida_export is_miniidb(void); + + +/// Internal function + +idaman bool ida_export set_segm_base(segment_t *s, ea_t newbase); + +///@} seg_man + +//------------------------------------------------------------------------- +// S E G M E N T G R O U P S +//------------------------------------------------------------------------- +/// \defgroup seg_grp Segment groups +/// \ingroup seg +///@{ + + +/// Create a new group of segments (used OMF files). +/// \param grp selector of group segment (segment type is #SEG_GRP) +/// You should create an 'empty' (1 byte) group segment +/// It won't contain anything and will be used to +/// redirect references to the group of segments to the +/// common selector. +/// \param sel common selector of all segments belonging to the segment +/// You should create all segments within the group with the +/// same selector value. +/// \retval 1 ok +/// \retval 0 too many groups (see #MAX_GROUPS) + +idaman int ida_export set_group_selector(sel_t grp, sel_t sel); + +#define MAX_GROUPS 8 ///< max number of segment groups + + +/// Get common selector for a group of segments. +/// \param grpsel selector of group segment +/// \return common selector of the group or 'grpsel' if no such group is found + +idaman sel_t ida_export get_group_selector(sel_t grpsel); + +///@} seg_grp + +//------------------------------------------------------------------------- +// S E G M E N T T R A N S L A T I O N S +/// +/// \defgroup seg_trans Segment translations +/// \ingroup seg +/// +/// Used to represent overlayed memory banks. +/// Segment translations are used to redirect access to overlayed segments +/// so that the correct overlay is accessed. Each segment has its own +/// translation list. For example, suppose we have +/// four segments: +/// <pre> +/// A 1000-2000 +/// B 1000-2000 +/// C 2000-3000 +/// D 2000-3000 +/// </pre> +/// A and B occupy the same virtual addresses. The same with C and D. +/// Segment A works with segment C, segment B works with segment D. +/// +/// So all references from A to 2000-3000 should go to C. For this +/// we add translation C for segment A. The same with B,D: add +/// translation D for segment B. Also, we need to specify the correct +/// segment to be accessed from C, thus we add translation A for segment C. +/// And we add translation B for segment D. +/// +/// After this, all references to virtual addresses 2000-3000 made from A +/// go to segment C (even if segment A would be large and occupy 1000-3000) +/// So, we need the following translations: +/// <pre> +/// A: C +/// B: D +/// C: A +/// D: B +/// </pre> +/// With translations, the segments may reside at any linear addresses, +/// all references will pass through the translation mechanism and go to the +/// correct segment. +/// +/// Segment translation works only for code segments (see map_code_ea()) +///@{ +//------------------------------------------------------------------------- + +/// Add segment translation. +/// \param segstart start address of the segment to add translation to +/// \param mappedseg start address of the overlayed segment +/// \retval 1 ok +/// \retval 0 too many translations or bad segstart + +idaman bool ida_export add_segment_translation(ea_t segstart, ea_t mappedseg); + +#define MAX_SEGM_TRANSLATIONS 64 ///< max number of segment translations + + +/// Set new translation list. +/// \param segstart start address of the segment to add translation to +/// \param transmap vector of segment start addresses for the translation list. +/// If transmap is empty, the translation list is deleted. +/// \retval 1 ok +/// \retval 0 too many translations or bad segstart + +idaman bool ida_export set_segment_translations(ea_t segstart, const eavec_t &transmap); + + +/// Delete the translation list +/// \param segstart start address of the segment to delete translation list + +idaman void ida_export del_segment_translations(ea_t segstart); + + +/// Get segment translation list. +/// \param transmap vector of segment start addresses for the translation list +/// \param segstart start address of the segment to get information about +/// \return -1 if no translation list or bad segstart. +/// otherwise returns size of translation list. + +idaman ssize_t ida_export get_segment_translations(eavec_t *transmap, ea_t segstart); +///@} seg_trans + +//------------------------------------------------------------------------- +// S E G M E N T C O M M E N T S +// +/// \defgroup seg_cmt Segment comments +/// \ingroup seg +/// +/// Segment comments are rarely used yet. +/// The user may define a segment comment by pressing ':' +/// while standing on the segment name at the segment start. +/// +/// The main advantage of segment comments compared to anterior +/// lines (see lines.hpp) is that they are attached to a segment, +/// not to an address and they will move with the start of segment +/// if the segment boundaries change. +/// +/// You may set segment comments in your LDR module to describe +/// characteristics of a segment in comments. +/// +/// Repeatable segment comments are not used at all, because I don't +/// know where they should be repeated. +///@{ +//------------------------------------------------------------------------- + +/// Get segment comment. +/// \param buf buffer for the comment +/// \param s pointer to segment structure +/// \param repeatable 0: get regular comment. +/// 1: get repeatable comment. +/// \return size of comment or -1 + +idaman ssize_t ida_export get_segment_cmt(qstring *buf, const segment_t *s, bool repeatable); + + +/// Set segment comment. +/// \param s pointer to segment structure +/// \param cmt comment string, may be multiline (with '\n'). +/// maximal size is 4096 bytes. +/// Use empty str ("") to delete comment +/// \param repeatable 0: set regular comment. +/// 1: set repeatable comment. +/// + +idaman void ida_export set_segment_cmt(const segment_t *s, const char *cmt, bool repeatable); + + +/// Generate segment footer line as a comment line. +/// This function may be used in IDP modules to generate segment footer +/// if the target assembler doesn't have 'ends' directive. + +idaman void ida_export std_out_segm_footer(struct outctx_t &ctx, segment_t *seg); + +///@} seg_cmt + +//------------------------------------------------------------------------- +// S E G M E N T N A M E S +//------------------------------------------------------------------------- +/// \defgroup seg_name Segment names +/// \ingroup seg +/// Various ways to retrieve the name of a segment +///@{ + +/// Rename segment. +/// The new name is validated (see validate_name). +/// A segment always has a name. If you hadn't specified a name, +/// the kernel will assign it "seg###" name where ### is segment number. +/// \param s pointer to segment (may be nullptr) +/// \param name new segment name +/// \param flags ADDSEG_IDBENC or 0 +/// \retval 1 ok, name is good and segment is renamed +/// \retval 0 failure, name is bad or segment is nullptr + +idaman int ida_export set_segm_name( + segment_t *s, + const char *name, + int flags=0); + + +/// Get true segment name by pointer to segment. +/// \param buf output buffer. cannot be nullptr +/// \param s pointer to segment +/// \param flags 0-return name as is; 1-substitute bad symbols with _ +/// 1 corresponds to GN_VISIBLE +/// \return size of segment name (-1 if s==nullptr) + +idaman ssize_t ida_export get_segm_name(qstring *buf, const segment_t *s, int flags=0); + + +/// Get segment name by pointer to segment. +/// \param buf output buffer. cannot be nullptr +/// \param s pointer to segment +/// \return size of segment name (-1 if s==nullptr) + +inline ssize_t idaapi get_visible_segm_name(qstring *buf, const segment_t *s) +{ + return get_segm_name(buf, s, 1); +} + + +/// Get colored segment name expression in the form (segname + displacement). +/// \param buf output buffer to hold segment expression +/// \param from linear address of instruction operand or data referring to +/// the name. This address will be used to get fixup information, +/// so it should point to exact position of operand in the +/// instruction. +/// \param sel value to convert to segment expression +/// \return size of segment expression or -1 + +ssize_t get_segm_expr(qstring *buf, ea_t from, sel_t sel); + +///@} seg_name + +//------------------------------------------------------------------------- +// S E G M E N T C L A S S E S A N D T Y P E S +//------------------------------------------------------------------------- +/// \defgroup seg_type Segment classes and types +/// \ingroup seg +/// See \ref SEG_ +///@{ + +/// Get segment class. +/// Segment class is arbitrary text (max 8 characters). +/// \param buf output buffer. cannot be nullptr. +/// \param s pointer to segment +/// \return size of segment class (-1 if s==nullptr or bufsize<=0) + +idaman ssize_t ida_export get_segm_class(qstring *buf, const segment_t *s); + + +/// Set segment class. +/// \param s pointer to segment (may be nullptr) +/// \param sclass segment class (may be nullptr). +/// If segment type is #SEG_NORM and segment class is one of predefined +/// names, then segment type is changed to: +/// - "CODE" -> #SEG_CODE +/// - "DATA" -> #SEG_DATA +/// - "STACK" -> #SEG_BSS +/// - "BSS" -> #SEG_BSS +/// - if "UNK" then segment type is reset to #SEG_NORM. +/// \param flags \ref ADDSEG_ +/// \retval 1 ok, name is good and segment is renamed +/// \retval 0 failure, name is nullptr or bad or segment is nullptr + +idaman int ida_export set_segm_class(segment_t *s, const char *sclass, int flags=0); + + +/// Get segment type. +/// \param ea any linear address within the segment +/// \return \ref SEG_, #SEG_UNDF if no segment found at 'ea' + +idaman uchar ida_export segtype(ea_t ea); + +///@} seg_type + +//------------------------------------------------------------------------- +// S E G M E N T A L I G N M E N T A N D C O M B I N A T I O N +//------------------------------------------------------------------------- +/// \defgroup seg_align Segment alignment and combination +/// \ingroup seg +///@{ + +/// Get text representation of segment alignment code. +/// \return text digestable by IBM PC assembler. + +idaman const char *ida_export get_segment_alignment(uchar align); + + +/// Get text representation of segment combination code. +/// \return text digestable by IBM PC assembler. + +idaman const char *ida_export get_segment_combination(uchar comb); + +///@} seg_align + +//------------------------------------------------------------------------- +// S E G M E N T A D D R E S S I N G +//------------------------------------------------------------------------- +/// \defgroup seg_addr Segment addressing +/// \ingroup seg +///@{ + +/// Get segment base paragraph. +/// Segment base paragraph may be converted to segment base linear address +/// using to_ea() function. +/// In fact, to_ea(get_segm_para(s), 0) == get_segm_base(s). +/// \param s pointer to segment +/// \return 0 if s == nullptr, +/// the segment base paragraph + +idaman ea_t ida_export get_segm_para(const segment_t *s); + + +/// Get segment base linear address. +/// Segment base linear address is used to calculate virtual addresses. +/// The virtual address of the first byte of the segment will be +/// (start address of segment - segment base linear address) +/// \param s pointer to segment +/// \return 0 if s == nullptr, +/// otherwise segment base linear address + +idaman ea_t ida_export get_segm_base(const segment_t *s); + + +/// Change segment addressing mode (16, 32, 64 bits). +/// You must use this function to change segment addressing, never change +/// the 'bitness' field directly. +/// This function will delete all instructions, comments and names in the segment +/// \param s pointer to segment +/// \param bitness new addressing mode of segment +/// - 2: 64bit segment +/// - 1: 32bit segment +/// - 0: 16bit segment +/// \return success + +idaman bool ida_export set_segm_addressing(segment_t *s, size_t bitness); + +///@} seg_addr + +//----------------------------------------------------------------------- + +/// Does the address belong to a debug segment? + +inline bool is_debugger_segm(ea_t ea) +{ + segment_t *s = getseg(ea); + return s != nullptr && s->is_debugger_segm(); +} + +/// Does the address belong to an ephemeral segment? + +inline bool is_ephemeral_segm(ea_t ea) +{ + segment_t *s = getseg(ea); + return s != nullptr && s->is_ephemeral_segm(); +} + +//------------------------------------------------------------------------- +idaman ea_t ida_export correct_address(ea_t ea, ea_t from, ea_t to, ea_t size, bool skip_check=false); + +//------------------------------------------------------------------------- +idaman bool ida_export update_segm(segment_t *s); + +inline bool segment_t::update(void) +{ + return update_segm(this); +} + +/// Truncate and sign extend a delta depending on the segment +idaman adiff_t ida_export segm_adjust_diff(const segment_t *s, adiff_t delta); + +/// Truncate an address depending on the segment +idaman ea_t ida_export segm_adjust_ea(const segment_t *s, ea_t ea); + +#endif // _SEGMENT_HPP + +``` + +`IdaSDK/segregs.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _SRAREA_HPP +#define _SRAREA_HPP + +#include <range.hpp> +#include <segment.hpp> + +/*! \file segregs.hpp + + \brief Functions that deal with the segment registers. + + If your processor doesn't use segment registers, then these functions + are of no use for you. However, you should define + two virtual segment registers - CS and DS (for code segment and + data segment) and specify their internal numbers in the LPH structure + (processor_t::reg_code_sreg and processor_t::reg_data_sreg). +*/ + +//------------------------------------------------------------------------- +/// The values of the segment registers are kept as address ranges. The segment +/// register does not change its value within one address range. +/// The processor module finds segment register change points and splits +/// ::sreg_range_t ranges so that a new sreg_range_t range is started at +/// each segment register change point. The kernel deletes sreg_range_t +/// if an instruction is converted back to unexplored bytes. +/// +/// So, we have information about a segment register by keeping information +/// about the range of addresses where segment register does not change the value. +/// +/// Note that each segment has information about the default values of +/// the segment registers. This information is used if the value of a segment +/// register could not be determined. +struct sreg_range_t : public range_t +{ + sel_t val; ///< segment register value + uchar tag; ///< \ref SR_ + +/// \defgroup SR_ Segment register range tags +/// Used by sreg_range_t::tag +///@{ +#define SR_inherit 1 ///< the value is inherited from the previous range +#define SR_user 2 ///< the value is specified by the user +#define SR_auto 3 ///< the value is determined by IDA +#define SR_autostart 4 ///< used as #SR_auto for segment starting address +///@} +}; +DECLARE_TYPE_AS_MOVABLE(sreg_range_t); + + +/// Get value of a segment register. +/// This function uses segment register range and default segment register +/// values stored in the segment structure. +/// \param ea linear address in the program +/// \param rg number of the segment register +/// \return value of the segment register, #BADSEL if value is unknown or rg is not a segment register. + +idaman sel_t ida_export get_sreg(ea_t ea, int rg); + + +/// Create a new segment register range. +/// This function is used when the IDP emulator detects that a segment +/// register changes its value. +/// \param ea linear address where the segment register will +/// have a new value. if ea==#BADADDR, nothing to do. +/// \param rg the number of the segment register +/// \param v the new value of the segment register. If the value is +/// unknown, you should specify #BADSEL. +/// \param tag the register info tag. see \ref SR_ +/// \param silent if false, display a warning() in the case of failure +/// \return success + +idaman bool ida_export split_sreg_range( + ea_t ea, + int rg, + sel_t v, + uchar tag, + bool silent=false); + + +/// Set default value of a segment register for a segment. +/// \param sg pointer to segment structure +/// if nullptr, then set the register for all segments +/// \param rg number of segment register +/// \param value its default value. this value will be used by get_sreg() +/// if value of the register is unknown at the specified address. +/// \return success + +idaman bool ida_export set_default_sreg_value(segment_t *sg, int rg, sel_t value); + + +/// Set the segment register value at the next instruction. +/// This function is designed to be called from idb_event::sgr_changed handler +/// in order to contain the effect of changing a segment +/// register value only until the next instruction. +/// +/// It is useful, for example, in the ARM module: the modification +/// of the T register does not affect existing instructions later in the code. +/// \param ea1 address to start to search for an instruction +/// \param ea2 the maximal address +/// \param rg the segment register number +/// \param value the segment register value + +idaman void ida_export set_sreg_at_next_code(ea_t ea1, ea_t ea2, int rg, sel_t value); + + +/// Get segment register range by linear address. +/// \param out segment register range +/// \param ea any linear address in the program +/// \param rg the segment register number +/// \return success + +idaman bool ida_export get_sreg_range(sreg_range_t *out, ea_t ea, int rg); + + +/// Get segment register range previous to one with address. +/// \note more efficient then get_sreg_range(reg, ea-1) +/// \param out segment register range +/// \param ea any linear address in the program +/// \param rg the segment register number +/// \return success + +idaman bool ida_export get_prev_sreg_range(sreg_range_t *out, ea_t ea, int rg); + + +/// Set default value of DS register for all segments + +idaman void ida_export set_default_dataseg(sel_t ds_sel); + + +/// Get number of segment register ranges. +/// \param rg the segment register number + +idaman size_t ida_export get_sreg_ranges_qty(int rg); + + +/// Get segment register range by its number. +/// \param out segment register range +/// \param rg the segment register number +/// \param n number of range (0..qty()-1) +/// \return success + +idaman bool ida_export getn_sreg_range(sreg_range_t *out, int rg, int n); + + +/// Get number of segment register range by address. +/// \param ea any address in the range +/// \param rg the segment register number +/// \return -1 if no range occupies the specified address. +/// otherwise returns number of +/// the specified range (0..get_srranges_qty()-1) + +idaman int ida_export get_sreg_range_num(ea_t ea, int rg); + + +/// Delete segment register range started at ea. +/// When a segment register range is deleted, +/// the previous range is extended to cover the empty space. +/// The segment register range at the beginning of a segment cannot be deleted. +/// \param ea start_ea of the deleted range +/// \param rg the segment register number +/// \return success + +idaman bool ida_export del_sreg_range(ea_t ea, int rg); + + +/// Duplicate segment register ranges. +/// \param dst_rg number of destination segment register +/// \param src_rg copy ranges from +/// \param map_selector map selectors to linear addresses using sel2ea() + +idaman void ida_export copy_sreg_ranges(int dst_rg, int src_rg, bool map_selector=false); + + +#endif // _SRAREA_HPP + +``` + +`IdaSDK/srclang.hpp`: + +```hpp +#ifndef SRCLANG_H +#define SRCLANG_H + +#include <typeinf.hpp> + +/*! \file srclang.hpp + + \brief Third-party compiler support +*/ + +/// \defgroup SRCLANG_ Source language IDs +///@{ +#define SRCLANG_C 0x01 ///< C +#define SRCLANG_CPP 0x02 ///< C++ +#define SRCLANG_OBJC 0x04 ///< Objective-C +#define SRCLANG_SWIFT 0x08 ///< Swift (not supported yet) +#define SRCLANG_GO 0x10 ///< Golang (not supported yet) +///@} + +/// Bitmask that describes all source languages supported by a compiler. Combination of \ref SRCLANG_ values +typedef int srclang_t; + + +/// Set the parser with the given name as the current parser. +/// Pass nullptr or an empty string to select the default parser. +/// \return false if no parser was found with the given name + +idaman bool ida_export select_parser_by_name(const char *name); + + +/// Set the parser that supports the given language(s) as the current parser. +/// The selected parser must support all languages specified by the given ::srclang_t. +/// \return false if no such parser was found + +idaman bool ida_export select_parser_by_srclang(srclang_t lang); + + +/// Get current parser name. +/// \param[out] out the current (selected) parser name, +/// is empty for default parser +/// \return success + +idaman bool ida_export get_selected_parser_name(qstring *out); + + +/// Set the command-line args to use for invocations of the parser with the given name +/// \param parser_name name of the target parser +/// \param argv argument list +/// \retval -1 no parser was found with the given name +/// \retval -2 the operation is not supported by the given parser +/// \retval 0 success + +idaman int ida_export set_parser_argv(const char *parser_name, const char *argv); + + +/// Parse type declarations in the specified language +/// \param lang the source language(s) expected in the input +/// \param til type library to store the types +/// \param input input source. can be a file path or decl string +/// \param is_path true if input parameter is a path to a source file, false if the input is an in-memory source snippet +/// \retval -1 no parser was found that supports the given source language(s) +/// \retval else the number of errors encountered in the input source + +idaman int ida_export parse_decls_for_srclang( + srclang_t lang, + til_t *til, + const char *input, + bool is_path); + + +/// Parse type declarations using the parser with the specified name +/// \param parser_name name of the target parser +/// \param til type library to store the types +/// \param input input source. can be a file path or decl string +/// \param hti_flags combination of \ref HTI_ +/// \retval -1 no parser was found with the given name +/// \retval else the number of errors encountered in the input source + +idaman int ida_export parse_decls_with_parser_ext( + const char *parser_name, + til_t *til, + const char *input, + int hti_flags); + + +/// Get option for the parser with the specified name +/// \param[out] out parser option value +/// \param parser_name name of the target parser +/// \param option_name parser option name +/// \return success + +idaman bool ida_export get_parser_option( + qstring *out, + const char *parser_name, + const char *option_name); + + +/// Set option for the parser with the specified name +/// \param parser_name name of the target parser +/// \param option_name parser option name +/// \param option_value parser option value +/// \return success +/// \note For boolean option use "on"/"off" + +idaman bool ida_export set_parser_option( + const char *parser_name, + const char *option_name, + const char *option_value); + + +/// Parse type declarations using the parser with the specified name +/// \param parser_name name of the target parser +/// \param til type library to store the types +/// \param input input source. can be a file path or decl string +/// \param is_path true if input parameter is a path to a source file, false if the input is an in-memory source snippet +/// \retval -1 no parser was found with the given name +/// \retval else the number of errors encountered in the input source + +idaman int ida_export parse_decls_with_parser( + const char *parser_name, + til_t *til, + const char *input, + bool is_path); + + + +#endif // !SRCLANG_H + +``` + +`IdaSDK/strlist.hpp`: + +```hpp +/* + * The Interactive Disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _STRLIST_HPP +#define _STRLIST_HPP + +/*! \file strlist.hpp + + \brief Functions that deal with the string list + + While the kernel keeps the string list, it does not update it. + The string list is not used by the kernel because + keeping it up-to-date would slow down IDA without any benefit. + If the string list is not cleared using clear_strlist(), the list will be + saved to the database and restored on the next startup. + + The users of this list should call build_strlist() if they need an + up-to-date version. +*/ + +/// Structure to keep string list parameters +struct strwinsetup_t +{ + bytevec_t strtypes; // set of allowed string types + sval_t minlen = -1; + uchar display_only_existing_strings = 0; + uchar only_7bit = 1; + uchar ignore_heads = 0; +}; + +/// Information about one string from the string list +struct string_info_t +{ + ea_t ea; + int length = 0; // in octets + int type = 0; + string_info_t(ea_t _ea=BADADDR) : ea(_ea) {} + bool operator<(const string_info_t &r) const { return ea < r.ea; } +}; +DECLARE_TYPE_AS_MOVABLE(string_info_t); + + +/// Get the static string list options + +idaman const strwinsetup_t *ida_export get_strlist_options(); + + +/// Rebuild the string list. + +idaman void ida_export build_strlist(); + + +/// Clear the string list. + +idaman void ida_export clear_strlist(); + + +/// Get number of elements in the string list. +/// The list will be loaded from the database (if saved) or +/// built from scratch. + +idaman size_t ida_export get_strlist_qty(void); + + +/// Get nth element of the string list (n=0..get_strlist_qty()-1) + +idaman bool ida_export get_strlist_item(string_info_t *si, size_t n); + + +#endif // _STRLIST_HPP + +``` + +`IdaSDK/tryblks.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 2016-2025 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: + * - A 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<catch_t> 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 || size() == 0; } + 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<tryblk_t> 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 nullptr +/// \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); + + +/// \defgroup TBEA_ flags for is_ea_tryblks() +///@{ +#define TBEA_TRY 0x01 ///< is EA within a c++ try block? +#define TBEA_CATCH 0x02 ///< is EA the start of a c++ catch/cleanup block? +#define TBEA_SEHTRY 0x04 ///< is EA within a seh try block +#define TBEA_SEHLPAD 0x08 ///< is EA the start of a seh finally/except block? +#define TBEA_SEHFILT 0x10 ///< is EA the start of a seh filter? +#define TBEA_ANY 0x1f +#define TBEA_FALLTHRU 0x20 ///< is there a fall through into provided ea from an unwind region +///@} + +/// Check if the given address ea is part of tryblks description. +/// \param ea address to check +/// \param flags combination of \ref TBEA_ +idaman bool ida_export is_ea_tryblks(ea_t ea, uint32 flags); + + +#endif // TRYBLKS_HPP + +``` + +`IdaSDK/typeinf.hpp`: + +```hpp +/* + * Interactive disassembler (IDA) + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + */ + +#ifndef _TYPEINF_HPP +#define _TYPEINF_HPP +#include <functional> +#include <idp.hpp> +#include <name.hpp> + +/*! \file typeinf.hpp + + \brief Type information in IDA. + + In IDA, types are represented by and manipulated through ::tinfo_t objects. + + A ::tinfo_t can represent a simple type (e.g., `int`, `float`), + a complex type (a structure, enum, union, typedef), or even + an array, or a function prototype. + + The key types in this file are: + + - ::til_t - a type info library. Holds type information in serialized form. + - ::tinfo_t - information about a type (simple, complex, ...) + + # Glossary + + All throughout this file, there are certain terms that will keep + appearing: + + - udt: "user-defined type": a structure or union - but not enums. See ::udt_type_data_t + - udm: "udt member": i.e., a structure or union member. See ::udm_t + - edm: "enum member": i.e., an enumeration member - i.e., an enumerator. See ::edm_t + + # Under the hood + + The ::tinfo_t type provides a lot of useful methods already, + but it's possible to achieve even more by retrieving its + contents into the container classes: + + - ::udt_type_data_t - for structures & unions. See ::tinfo_t::get_udt_details . Essentially, a vector of ::udm_t + - ::enum_type_data_t - for enumerations. See ::tinfo_t::get_enum_details . Essentially, a vector of ::edm_t + - ::ptr_type_data_t - for pointers. See ::tinfo_t::get_ptr_details + - ::array_type_data_t - for arrays. See ::tinfo_t::get_array_details + - ::func_type_data_t - for function prototypes. See ::tinfo_t::get_func_details + - ::bitfield_type_data_t - for bitfields. See ::tinfo_t::get_bitfield_details + + # Attached & detached tinfo_t objects + + ::tinfo_t objects can be attached to a ::til_t library, or can be + created without using any til_t. + + Here is an example, assigning a function prototype: + + func_type_data_t func_info; + + funcarg_t argc; + argc.name = "argc"; + argc.type = tinfo_t(BT_INT); + func_info.push_back(argc); + + funcarg_t argv; + argc.name = "argv"; + argc.type = tinfo_t("const char **"); + func_info.push_back(argv) + + tinfo_t tif; + if ( tif.create_func(func_info) ) + { + ea_t ea = // get address of "main" + apply_tinfo(ea, tif, TINFO_DEFINITE); + } + + This code manipulates a "detached" ::tinfo_t object, which does not depend on + any ::til_t file. However, any complex type will require a ::til_t file. + In IDA, there is always a default til_t file for each idb file. + This til_t file can be specified by nullptr. + + On the other hand, the following code manipulates an "attached" + ::tinfo_t object, and any operation that modifies it, will + also modify it in the hosting ::til_t: + + tinfo_t tif; + // Load type from the "Local Types" til_t. + // Note: we could have used `get_idati()` instead of nullptr + if ( tif.get_named_type(nullptr, "my_struct_t") ) + tif.add_udm("extra_field", "unsigned long long"); + + You can check if a ::tinfo_t instance is attached to a type in a til_t file + by calling ::tinfo_t::is_typeref +*/ + +/// In serialized form, a type is represented by a byte sequence. (see \ref tf) +/// We call it a type string. +typedef uchar type_t; +/// Inside a type string, a pascal-like string: dt length, characters +typedef uchar p_string; +/// several ::p_string's +typedef uchar p_list; +/// unsigned value that describes a bitmask +typedef uint64 bmask64_t; +#define DEFMASK64 bmask64_t(-1) ///< default bitmask 64bits + +struct til_t; // type information library +class lexer_t; // lexical analyzer +class argloc_t; // argument location +class tinfo_t; // type info object +class func_t; // function + +struct func_type_data_t; +struct til_bucket_t; +struct til_stream_t; +struct value_repr_t; +struct udm_t; +struct edm_t; +struct tinfo_changes_t; + +//------------------------------------------------------------------------ +#define RESERVED_BYTE 0xFF ///< multifunctional purpose +//------------------------------------------------------------------------ +/// \defgroup tf Type flags +/// Here we describe the byte arrays used to describe type information +///@{ + +/// \defgroup tf_mask Masks +///@{ +const type_t TYPE_BASE_MASK = 0x0F; ///< the low 4 bits define the basic type +const type_t TYPE_FLAGS_MASK = 0x30; ///< type flags - they have different + ///< meaning depending on the basic type +const type_t TYPE_MODIF_MASK = 0xC0; ///< modifiers. + ///< - for ::BT_ARRAY see \ref tf_array + ///< - ::BT_VOID can have them ONLY in 'void *' + +const type_t TYPE_FULL_MASK = (TYPE_BASE_MASK | TYPE_FLAGS_MASK); ///< basic type with type flags +///@} + +/*! \defgroup tf_unk Basic type: unknown & void + ::BT_UNK and ::BT_VOID with non-zero type flags can be used in function + (and struct) declarations to describe the function arguments or structure + fields if only their size is known. They may be used in ida to describe + the user input. + + In general BT_... bits should not be used alone to describe types. + Use BTF_... constants instead. + + For struct used also as 'single-field-alignment-suffix' + [__declspec(align(x))] with ::TYPE_MODIF_MASK == ::TYPE_FULL_MASK. +*/ +///@{ +const type_t BT_UNK = 0x00; ///< unknown +const type_t BT_VOID = 0x01; ///< void +const type_t BTMT_SIZE0 = 0x00; ///< ::BT_VOID - normal void; ::BT_UNK - don't use +const type_t BTMT_SIZE12 = 0x10; ///< size = 1 byte if ::BT_VOID; 2 if ::BT_UNK +const type_t BTMT_SIZE48 = 0x20; ///< size = 4 bytes if ::BT_VOID; 8 if ::BT_UNK +const type_t BTMT_SIZE128 = 0x30; ///< size = 16 bytes if ::BT_VOID; unknown if ::BT_UNK + ///< (IN struct alignment - see below) +///@} + +/// \defgroup tf_int Basic type: integer +///@{ +const type_t BT_INT8 = 0x02; ///< __int8 +const type_t BT_INT16 = 0x03; ///< __int16 +const type_t BT_INT32 = 0x04; ///< __int32 +const type_t BT_INT64 = 0x05; ///< __int64 +const type_t BT_INT128 = 0x06; ///< __int128 (for alpha & future use) +const type_t BT_INT = 0x07; ///< natural int. (size provided by idp module) +const type_t BTMT_UNKSIGN = 0x00; ///< unknown signedness +const type_t BTMT_SIGNED = 0x10; ///< signed +const type_t BTMT_USIGNED = 0x20; ///< unsigned +const type_t BTMT_UNSIGNED = BTMT_USIGNED; +const type_t BTMT_CHAR = 0x30; ///< specify char or segment register + ///< - ::BT_INT8 - char + ///< - ::BT_INT - segment register + ///< - other BT_INT... - don't use +///@} + +/// \defgroup tf_bool Basic type: bool +///@{ +const type_t BT_BOOL = 0x08; ///< bool +const type_t BTMT_DEFBOOL = 0x00; ///< size is model specific or unknown(?) +const type_t BTMT_BOOL1 = 0x10; ///< size 1byte +const type_t BTMT_BOOL2 = 0x20; ///< size 2bytes - !inf_is_64bit() +const type_t BTMT_BOOL8 = 0x20; ///< size 8bytes - inf_is_64bit() +const type_t BTMT_BOOL4 = 0x30; ///< size 4bytes +///@} + +/// \defgroup tf_float Basic type: float +///@{ +const type_t BT_FLOAT = 0x09; ///< float +const type_t BTMT_FLOAT = 0x00; ///< float (4 bytes) +const type_t BTMT_DOUBLE = 0x10; ///< double (8 bytes) +const type_t BTMT_LNGDBL = 0x20; ///< long double (compiler specific) +const type_t BTMT_SPECFLT = 0x30; ///< float (variable size). + ///< if \ph{use_tbyte()} then use \ph{tbyte_size}, + ///< otherwise 2 bytes +///@} + +/// \defgroup tf_last_basic Basic type: last +///@{ +const type_t _BT_LAST_BASIC = BT_FLOAT; ///< the last basic type, + ///< all basic types may be followed by + ///< [tah-typeattrs] +///@} + +/*! \defgroup tf_ptr Derived type: pointer + Pointers to undeclared yet ::BT_COMPLEX types are prohibited +*/ +///@{ +const type_t BT_PTR = 0x0A; ///< pointer. + ///< has the following format: + ///< [db sizeof(ptr)]; [tah-typeattrs]; type_t... +const type_t BTMT_DEFPTR = 0x00; ///< default for model +const type_t BTMT_NEAR = 0x10; ///< near +const type_t BTMT_FAR = 0x20; ///< far +const type_t BTMT_CLOSURE = 0x30; ///< closure. + ///< - if ptr to ::BT_FUNC - __closure. + ///< in this case next byte MUST be + ///< #RESERVED_BYTE, and after it ::BT_FUNC + ///< - else the next byte contains sizeof(ptr) + ///< allowed values are 1 - \varmem{ph,processor_t,max_ptr_size} + ///< - if value is bigger than \varmem{ph,processor_t,max_ptr_size}, + ///< based_ptr_name_and_size() is called to + ///< find out the typeinfo +///@} + +/*! \defgroup tf_array Derived type: array + For ::BT_ARRAY, the BTMT_... flags must be equivalent to the BTMT_... flags of its elements +*/ +///@{ +const type_t BT_ARRAY = 0x0B; ///< array +const type_t BTMT_NONBASED = 0x10; ///< \code + /// if set + /// array base==0 + /// format: dt num_elem; [tah-typeattrs]; type_t... + /// if num_elem==0 then the array size is unknown + /// else + /// format: da num_elem, base; [tah-typeattrs]; type_t... \endcode + /// used only for serialization +const type_t BTMT_ARRESERV = 0x20; ///< reserved bit +///@} + +/*! \defgroup tf_func Derived type: function + Ellipsis is not taken into account in the number of parameters + The return type cannot be ::BT_ARRAY or ::BT_FUNC. +*/ +///@{ +const type_t BT_FUNC = 0x0C; ///< function. + ///< format: <pre> + /// optional: ::CM_CC_SPOILED | num_of_spoiled_regs + /// if num_of_spoiled_reg == BFA_FUNC_MARKER: + /// ::bfa_byte + /// if (bfa_byte & BFA_FUNC_EXT_FORMAT) != 0 + /// ::fti_bits (only low bits: FTI_SPOILED,...,FTI_VIRTUAL) + /// num_of_spoiled_reg times: spoiled reg info (see extract_spoiledreg) + /// else + /// bfa_byte is function attribute byte (see \ref BFA_...) + /// else: + /// num_of_spoiled_reg times: spoiled reg info (see extract_spoiledreg) + /// ::cm_t ... calling convention and memory model + /// [tah-typeattrs]; + /// ::type_t ... return type; + /// [serialized argloc_t of returned value (if ::CM_CC_SPECIAL{PE} && !return void); + /// if !::CM_CC_VOIDARG: + /// dt N (N=number of parameters) + /// if ( N == 0 ) + /// if ::CM_CC_ELLIPSIS or ::CM_CC_SPECIALE + /// func(...) + /// else + /// parameters are unknown + /// else + /// N records: + /// ::type_t ... (i.e. type of each parameter) + /// [serialized argloc_t (if ::CM_CC_SPECIAL{PE})] (i.e. place of each parameter) + /// [#FAH_BYTE + de( \ref funcarg_t::flags )] </pre> + +const type_t BTMT_DEFCALL = 0x00; ///< call method - default for model or unknown +const type_t BTMT_NEARCALL = 0x10; ///< function returns by retn +const type_t BTMT_FARCALL = 0x20; ///< function returns by retf +const type_t BTMT_INTCALL = 0x30; ///< function returns by iret + ///< in this case cc MUST be 'unknown' +///@} + +/// \defgroup tf_complex Derived type: complex +///@{ +const type_t BT_COMPLEX = 0x0D; ///< struct/union/enum/typedef. + ///< format: <pre> + /// [dt N (N=field count) if !::BTMT_TYPEDEF] + /// if N == 0: + /// p_string name (unnamed types have names "anon_...") + /// [sdacl-typeattrs]; + /// else, for struct & union: + /// if N == 0x7FFE // Support for high (i.e., > 4095) members count + /// N = deserialize_de() + /// ALPOW = N & 0x7 + /// MCNT = N >> 3 + /// if MCNT == 0 + /// empty struct + /// if ALPOW == 0 + /// ALIGN = get_default_align() + /// else + /// ALIGN = (1 << (ALPOW - 1)) + /// [sdacl-typeattrs]; + /// else, for enums: + /// if N == 0x7FFE // Support for high enum entries count. + /// N = deserialize_de() + /// [tah-typeattrs]; </pre> + /// +const type_t BTMT_STRUCT = 0x00; ///< struct: + ///< MCNT records: type_t; [sdacl-typeattrs]; +const type_t BTMT_UNION = 0x10; ///< union: + ///< MCNT records: type_t... +const type_t BTMT_ENUM = 0x20; ///< enum: + ///< next byte bte_t (see below) + ///< N records: de delta(s) + ///< OR + ///< blocks (see below) +const type_t BTMT_TYPEDEF = 0x30; ///< named reference + ///< always p_string name + +const type_t BT_BITFIELD = 0x0E; ///< bitfield (only in struct) + ///< ['bitmasked' enum see below] + ///< next byte is dt + ///< ((size in bits << 1) | (unsigned ? 1 : 0)) +const type_t BTMT_BFLDI8 = 0x00; ///< __int8 +const type_t BTMT_BFLDI16 = 0x10; ///< __int16 +const type_t BTMT_BFLDI32 = 0x20; ///< __int32 +const type_t BTMT_BFLDI64 = 0x30; ///< __int64 +///@} + +const type_t BT_RESERVED = 0x0F; ///< RESERVED + + +//------------------------------------------------------------------------ +/*! \defgroup tf_modifiers Type modifiers + "const volatile" types are forbidden +*/ +///@{ +const type_t BTM_CONST = 0x40; ///< const +const type_t BTM_VOLATILE = 0x80; ///< volatile +///@} + +//------------------------------------------------------------------------ +/// \defgroup tf_enum Special enum definitions +///@{ +typedef uchar bte_t; ///< Enum type flags + +const bte_t BTE_SIZE_MASK = 0x07; ///< storage size. + ///< - if == 0 then inf_get_cc_size_e() + ///< - else 1 << (n -1) = 1,2,4,8 + ///< - n == 5,6,7 are reserved +const bte_t BTE_RESERVED = 0x08; ///< must be 0, in order to distinguish + ///< from a tah-byte +const bte_t BTE_BITMASK = 0x10; ///< 'subarrays'. In this case ANY record + ///< has the following format: + ///< - 'de' mask (has name) + ///< - 'dt' cnt + ///< - cnt records of 'de' values + ///< (cnt CAN be 0) + ///< + ///< \note delta for ALL subsegment is ONE +const bte_t BTE_OUT_MASK = 0x60; ///< output style mask +const bte_t BTE_HEX = 0x00; ///< hex +const bte_t BTE_CHAR = 0x20; ///< char or hex +const bte_t BTE_SDEC = 0x40; ///< signed decimal +const bte_t BTE_UDEC = 0x60; ///< unsigned decimal +const bte_t BTE_ALWAYS = 0x80; ///< this bit MUST be present +///@} + +/// \defgroup tf_conv_segreg Convenience definitions: segment register +///@{ +const type_t BT_SEGREG = (BT_INT | BTMT_CHAR); ///< segment register +///@} + +/// \defgroup tf_conv_unk Convenience definitions: unknown types +///@{ +const type_t BT_UNK_BYTE = (BT_VOID | BTMT_SIZE12); ///< 1 byte +const type_t BT_UNK_WORD = (BT_UNK | BTMT_SIZE12); ///< 2 bytes +const type_t BT_UNK_DWORD = (BT_VOID | BTMT_SIZE48); ///< 4 bytes +const type_t BT_UNK_QWORD = (BT_UNK | BTMT_SIZE48); ///< 8 bytes +const type_t BT_UNK_OWORD = (BT_VOID | BTMT_SIZE128); ///< 16 bytes +const type_t BT_UNKNOWN = (BT_UNK | BTMT_SIZE128); ///< unknown size - for parameters +///@} + +//------------------------------------------------------------------------ +/// \defgroup tf_shortcuts Convenience definitions: shortcuts +///@{ +const type_t BTF_BYTE = BT_UNK_BYTE; ///< byte +const type_t BTF_UNK = BT_UNKNOWN; ///< unknown +const type_t BTF_VOID = BT_VOID | BTMT_SIZE0; ///< void + +const type_t BTF_INT8 = BT_INT8 | BTMT_SIGNED; ///< signed byte +const type_t BTF_CHAR = BT_INT8 | BTMT_CHAR; ///< signed char +const type_t BTF_UCHAR = BT_INT8 | BTMT_USIGNED; ///< unsigned char +const type_t BTF_UINT8 = BT_INT8 | BTMT_USIGNED; ///< unsigned byte + +const type_t BTF_INT16 = BT_INT16 | BTMT_SIGNED; ///< signed short +const type_t BTF_UINT16 = BT_INT16 | BTMT_USIGNED; ///< unsigned short + +const type_t BTF_INT32 = BT_INT32 | BTMT_SIGNED; ///< signed int +const type_t BTF_UINT32 = BT_INT32 | BTMT_USIGNED; ///< unsigned int + +const type_t BTF_INT64 = BT_INT64 | BTMT_SIGNED; ///< signed long +const type_t BTF_UINT64 = BT_INT64 | BTMT_USIGNED; ///< unsigned long + +const type_t BTF_INT128 = BT_INT128 | BTMT_SIGNED; ///< signed 128-bit value +const type_t BTF_UINT128 = BT_INT128 | BTMT_USIGNED; ///< unsigned 128-bit value + +const type_t BTF_INT = BT_INT | BTMT_UNKSIGN; ///< int, unknown signedness +const type_t BTF_UINT = BT_INT | BTMT_USIGNED; ///< unsigned int +const type_t BTF_SINT = BT_INT | BTMT_SIGNED; ///< singed int + +const type_t BTF_BOOL = BT_BOOL; ///< boolean + +const type_t BTF_FLOAT = BT_FLOAT | BTMT_FLOAT; ///< float +const type_t BTF_DOUBLE = BT_FLOAT | BTMT_DOUBLE; ///< double +const type_t BTF_LDOUBLE = BT_FLOAT | BTMT_LNGDBL; ///< long double +const type_t BTF_TBYTE = BT_FLOAT | BTMT_SPECFLT; ///< see ::BTMT_SPECFLT + +const type_t BTF_STRUCT = BT_COMPLEX | BTMT_STRUCT; ///< struct +const type_t BTF_UNION = BT_COMPLEX | BTMT_UNION; ///< union +const type_t BTF_ENUM = BT_COMPLEX | BTMT_ENUM; ///< enum +const type_t BTF_TYPEDEF = BT_COMPLEX | BTMT_TYPEDEF; ///< typedef +///@} + +///@} tf + +//------------------------------------------------------------------------ +// convenience functions: + +inline THREAD_SAFE bool is_type_const(type_t t) { return (t & BTM_CONST) != 0; } ///< See ::BTM_CONST +inline THREAD_SAFE bool is_type_volatile(type_t t) { return (t & BTM_VOLATILE) != 0; } ///< See ::BTM_VOLATILE + +inline THREAD_SAFE type_t get_base_type(type_t t) { return (t & TYPE_BASE_MASK); } ///< Get get basic type bits (::TYPE_BASE_MASK) +inline THREAD_SAFE type_t get_type_flags(type_t t) { return (t & TYPE_FLAGS_MASK); } ///< Get type flags (::TYPE_FLAGS_MASK) +inline THREAD_SAFE type_t get_full_type(type_t t) { return (t & TYPE_FULL_MASK); } ///< Get basic type bits + type flags (::TYPE_FULL_MASK) + +/// Is the type_t the last byte of type declaration? +/// (there are no additional bytes after a basic type, see ::_BT_LAST_BASIC) +inline THREAD_SAFE bool is_typeid_last(type_t t) { return(get_base_type(t) <= _BT_LAST_BASIC); } + +/// Identifies an unknown or void type with a known size (see \ref tf_unk) +inline THREAD_SAFE bool is_type_partial(type_t t) { return(get_base_type(t) <= BT_VOID) && get_type_flags(t) != 0; } + +inline THREAD_SAFE bool is_type_void(type_t t) { return(get_full_type(t) == BTF_VOID); } ///< See ::BTF_VOID +inline THREAD_SAFE bool is_type_unknown(type_t t) { return(get_full_type(t) == BT_UNKNOWN); } ///< See ::BT_UNKNOWN + +inline THREAD_SAFE bool is_type_ptr(type_t t) { return(get_base_type(t) == BT_PTR); } ///< See ::BT_PTR +inline THREAD_SAFE bool is_type_complex(type_t t) { return(get_base_type(t) == BT_COMPLEX); } ///< See ::BT_COMPLEX +inline THREAD_SAFE bool is_type_func(type_t t) { return(get_base_type(t) == BT_FUNC); } ///< See ::BT_FUNC +inline THREAD_SAFE bool is_type_array(type_t t) { return(get_base_type(t) == BT_ARRAY); } ///< See ::BT_ARRAY + +inline THREAD_SAFE bool is_type_typedef(type_t t) { return(get_full_type(t) == BTF_TYPEDEF); } ///< See ::BTF_TYPEDEF +inline THREAD_SAFE bool is_type_sue(type_t t) { return is_type_complex(t) && !is_type_typedef(t); } ///< Is the type a struct/union/enum? +inline THREAD_SAFE bool is_type_struct(type_t t) { return(get_full_type(t) == BTF_STRUCT); } ///< See ::BTF_STRUCT +inline THREAD_SAFE bool is_type_union(type_t t) { return(get_full_type(t) == BTF_UNION); } ///< See ::BTF_UNION +inline THREAD_SAFE bool is_type_struni(type_t t) { return(is_type_struct(t) || is_type_union(t)); } ///< Is the type a struct or union? +inline THREAD_SAFE bool is_type_enum(type_t t) { return(get_full_type(t) == BTF_ENUM); } ///< See ::BTF_ENUM + +inline THREAD_SAFE bool is_type_bitfld(type_t t) { return(get_base_type(t) == BT_BITFIELD); } ///< See ::BT_BITFIELD + + +/// Does the type_t specify one of the basic types in \ref tf_int? +inline THREAD_SAFE bool is_type_int(type_t bt) { bt = get_base_type(bt); return bt >= BT_INT8 && bt <= BT_INT; } + +/// Does the type specify a 128-bit value? (signed or unsigned, see \ref tf_int) +inline THREAD_SAFE bool is_type_int128(type_t t) +{ + return get_full_type(t) == (BT_INT128|BTMT_UNKSIGN) + || get_full_type(t) == (BT_INT128|BTMT_SIGNED); +} + +/// Does the type specify a 64-bit value? (signed or unsigned, see \ref tf_int) +inline THREAD_SAFE bool is_type_int64(type_t t) +{ + return get_full_type(t) == (BT_INT64|BTMT_UNKSIGN) + || get_full_type(t) == (BT_INT64|BTMT_SIGNED); +} + +/// Does the type specify a 32-bit value? (signed or unsigned, see \ref tf_int) +inline THREAD_SAFE bool is_type_int32(type_t t) +{ + return get_full_type(t) == (BT_INT32|BTMT_UNKSIGN) + || get_full_type(t) == (BT_INT32|BTMT_SIGNED); +} + +/// Does the type specify a 16-bit value? (signed or unsigned, see \ref tf_int) +inline THREAD_SAFE bool is_type_int16(type_t t) +{ + return get_full_type(t) == (BT_INT16|BTMT_UNKSIGN) + || get_full_type(t) == (BT_INT16|BTMT_SIGNED); +} + +/// Does the type specify a char value? (signed or unsigned, see \ref tf_int) +inline THREAD_SAFE bool is_type_char(type_t t) // chars are signed by default(?) +{ + return get_full_type(t) == (BT_INT8|BTMT_CHAR) + || get_full_type(t) == (BT_INT8|BTMT_SIGNED); +} + +/// Is the type a pointer, array, or function type? +inline THREAD_SAFE bool is_type_paf(type_t t) +{ + t = get_base_type(t); + return t >= BT_PTR && t <= BT_FUNC; +} + +/// Is the type a pointer or array type? +inline THREAD_SAFE bool is_type_ptr_or_array(type_t t) { t = get_base_type(t); return t == BT_PTR || t == BT_ARRAY; } +/// Is the type a floating point type? +inline THREAD_SAFE bool is_type_floating(type_t t) { return get_base_type(t) == BT_FLOAT; } // any floating type +/// Is the type an integral type (char/short/int/long/bool)? +inline THREAD_SAFE bool is_type_integral(type_t t) { return get_full_type(t) > BT_VOID && get_base_type(t) <= BT_BOOL; } +/// Is the type an extended integral type? (integral or enum) +inline THREAD_SAFE bool is_type_ext_integral(type_t t) { return is_type_integral(t) || is_type_enum(t); } +/// Is the type an arithmetic type? (floating or integral) +inline THREAD_SAFE bool is_type_arithmetic(type_t t) { return get_full_type(t) > BT_VOID && get_base_type(t) <= BT_FLOAT; } +/// Is the type an extended arithmetic type? (arithmetic or enum) +inline THREAD_SAFE bool is_type_ext_arithmetic(type_t t) { return is_type_arithmetic(t) || is_type_enum(t); } + +inline THREAD_SAFE bool is_type_uint(type_t t) { return get_full_type(t) == BTF_UINT; } ///< See ::BTF_UINT +inline THREAD_SAFE bool is_type_uchar(type_t t) { return get_full_type(t) == BTF_UCHAR; } ///< See ::BTF_UCHAR +inline THREAD_SAFE bool is_type_uint16(type_t t) { return get_full_type(t) == BTF_UINT16; } ///< See ::BTF_UINT16 +inline THREAD_SAFE bool is_type_uint32(type_t t) { return get_full_type(t) == BTF_UINT32; } ///< See ::BTF_UINT32 +inline THREAD_SAFE bool is_type_uint64(type_t t) { return get_full_type(t) == BTF_UINT64; } ///< See ::BTF_UINT64 +inline THREAD_SAFE bool is_type_uint128(type_t t) { return get_full_type(t) == BTF_UINT128; } ///< See ::BTF_UINT128 +inline THREAD_SAFE bool is_type_ldouble(type_t t) { return get_full_type(t) == BTF_LDOUBLE; } ///< See ::BTF_LDOUBLE +inline THREAD_SAFE bool is_type_double(type_t t) { return get_full_type(t) == BTF_DOUBLE; } ///< See ::BTF_DOUBLE +inline THREAD_SAFE bool is_type_float(type_t t) { return get_full_type(t) == BTF_FLOAT; } ///< See ::BTF_FLOAT +inline THREAD_SAFE bool is_type_tbyte(type_t t) { return get_full_type(t) == BTF_TBYTE; } ///< See ::BTF_FLOAT +inline THREAD_SAFE bool is_type_bool(type_t t) { return get_base_type(t) == BT_BOOL; } ///< See ::BTF_BOOL + +/*! \defgroup tattr Type attributes + \ingroup tf + The type attributes start with the type attribute header byte (::TAH_BYTE), + followed by attribute bytes +*/ +///@{ +#define TAH_BYTE 0xFE ///< type attribute header byte +#define FAH_BYTE 0xFF ///< function argument attribute header byte + +#define MAX_DECL_ALIGN 0x000F + +/// \defgroup tattr_ext Extended type attributes +///@{ +#define TAH_HASATTRS 0x0010 ///< has extended attributes +///@} + +/// \defgroup tattr_udt Type attributes for udts +///@{ +#define TAUDT_UNALIGNED 0x0040 ///< struct: unaligned struct +#define TAUDT_MSSTRUCT 0x0020 ///< struct: gcc msstruct attribute +#define TAUDT_CPPOBJ 0x0080 ///< struct: a c++ object, not simple pod type +#define TAUDT_VFTABLE 0x0100 ///< struct: is virtual function table +#define TAUDT_FIXED 0x0400 ///< struct: fixed field offsets, stored in serialized form; + ///< cannot be set for unions +#define TAUDT_TUPLE 0x0800 ///< tuple: tuples are like structs but are + ///< returned differently from functions +///@} + +/// \defgroup tattr_field Type attributes for udt fields +///@{ +#define TAFLD_BASECLASS 0x0020 ///< field: do not include but inherit from the current field +#define TAFLD_UNALIGNED 0x0040 ///< field: unaligned field +#define TAFLD_VIRTBASE 0x0080 ///< field: virtual base (not supported yet) +#define TAFLD_VFTABLE 0x0100 ///< field: ptr to virtual function table +#define TAFLD_METHOD 0x0200 ///< denotes a udt member function +#define TAFLD_GAP 0x0400 ///< field: gap member (displayed as padding in type details) +#define TAFLD_REGCMT 0x0800 ///< field: the comment is regular (if not set, it is repeatable) +#define TAFLD_FRAME_R 0x1000 ///< frame: function return address frame slot +#define TAFLD_FRAME_S 0x2000 ///< frame: function saved registers frame slot +#define TAFLD_BYTIL 0x4000 ///< field: was the member created due to the type system +///@} + +/// \defgroup tattr_ptr Type attributes for pointers +///@{ +#define TAPTR_PTR32 0x0020 ///< ptr: __ptr32 +#define TAPTR_PTR64 0x0040 ///< ptr: __ptr64 +#define TAPTR_RESTRICT 0x0060 ///< ptr: __restrict +#define TAPTR_SHIFTED 0x0080 ///< ptr: __shifted(parent_struct, delta) +///@} + +/// \defgroup tattr_enum Type attributes for enums +///@{ +#define TAENUM_64BIT 0x0020 ///< enum: store 64-bit values +#define TAENUM_UNSIGNED 0x0040 ///< enum: unsigned +#define TAENUM_SIGNED 0x0080 ///< enum: signed +#define TAENUM_OCT 0x0100 ///< enum: octal representation, if BTE_HEX +#define TAENUM_BIN 0x0200 ///< enum: binary representation, if BTE_HEX + ///< only one of OCT/BIN bits can be set. they + ///< are meaningful only if BTE_HEX is used. +#define TAENUM_NUMSIGN 0x0400 ///< enum: signed representation, if BTE_HEX +#define TAENUM_LZERO 0x0800 ///< enum: print numbers with leading zeroes (only for HEX/OCT/BIN) +///@} + +#define TAH_ALL 0x7FF0 ///< all defined bits + +///@} tattr + + +/// The TAH byte (type attribute header byte) denotes the start of type attributes. +/// (see "tah-typeattrs" in the type bit definitions) + +inline THREAD_SAFE bool is_tah_byte(type_t t) +{ + return t == TAH_BYTE; +} + + +/// Identify an sdacl byte. +/// The first sdacl byte has the following format: 11xx000x. +/// The sdacl bytes are appended to udt fields. They indicate the start of type +/// attributes (as the tah-bytes do). The sdacl bytes are used in the udt +/// headers instead of the tah-byte. This is done for compatibility with old +/// databases, they were already using sdacl bytes in udt headers and as udt +/// field postfixes. +/// (see "sdacl-typeattrs" in the type bit definitions) + +inline THREAD_SAFE bool is_sdacl_byte(type_t t) +{ + return ((t & ~TYPE_FLAGS_MASK) ^ TYPE_MODIF_MASK) <= BT_VOID; +} + +#ifndef SWIG +/// Compare two bytevecs with '<'. +/// v1 is considered less than v2 if either: +/// - v1.size() < v2.size() +/// - there is some i such that v1[i] < v2[i] + +inline THREAD_SAFE bool operator <(const bytevec_t &v1, const bytevec_t &v2) +{ + size_t n = qmin(v1.size(), v2.size()); + for ( size_t i=0; i < n; i++ ) + { + uchar k1 = v1[i]; + uchar k2 = v2[i]; + if ( k1 < k2 ) + return true; + if ( k1 > k2 ) + return false; + } + return v1.size() < v2.size(); +} +#endif + +/// \addtogroup tattr_ext Extended type attributes +///@{ +/// Extended type attributes. +struct type_attr_t +{ + qstring key; ///< one symbol keys are reserved to be used by the kernel + ///< the ones starting with an underscore are reserved too +#define TA_ORG_TYPEDEF "__org_typedef" ///< the original typedef name (simple string) +#define TA_ORG_ARRDIM "__org_arrdim" ///< the original array dimension (pack_dd) +#define TA_FORMAT "format" ///< info about the 'format' argument. + ///< 3 times pack_dd: + ///< \ref format_functype_t, + ///< argument number of 'format', + ///< argument number of '...' +#define TA_VALUE_REPR "\x01" ///< serialized value_repr_t (used for scalars and arrays) + bytevec_t value; ///< attribute bytes + bool operator < (const type_attr_t &r) const { return key < r.key; } + bool operator >= (const type_attr_t &r) const { return !(*this < r); } +}; +DECLARE_TYPE_AS_MOVABLE(type_attr_t); + +/// this vector must be sorted by keys +typedef qvector<type_attr_t> type_attrs_t; + +typedef int type_sign_t; ///< type signedness +const type_sign_t + no_sign = 0, ///< no sign, or unknown + type_signed = 1, ///< signed type + type_unsigned = 2; ///< unsigned type +///@} + +//--------------------------------------------------------------------------- +idaman bool ida_export append_argloc(qtype *out, const argloc_t &vloc); ///< Serialize argument location +/// Deserialize an argument location. +/// Argument FORBID_STKOFF checks location type. +/// It can be used, for example, to check the return location of a function that cannot return a value in the stack +idaman bool ida_export extract_argloc(argloc_t *vloc, const type_t **ptype, bool forbid_stkoff); + +idaman const type_t *ida_export resolve_typedef(const til_t *til, const type_t *type); + +// low level functions to be used in predicate_t::should_display() +// in other places please use tinfo_t +inline bool is_restype_void(const til_t *til, const type_t *type) +{ + type = resolve_typedef(til, type); + return type != nullptr && is_type_void(*type); +} + +inline bool is_restype_enum(const til_t *til, const type_t *type) +{ + type = resolve_typedef(til, type); + return type != nullptr && is_type_enum(*type); +} + +inline bool is_restype_struni(const til_t *til, const type_t *type) +{ + type = resolve_typedef(til, type); + return type != nullptr && is_type_struni(*type); +} + +inline bool is_restype_struct(const til_t *til, const type_t *type) +{ + type = resolve_typedef(til, type); + return type != nullptr && is_type_struct(*type); +} + +// Get a base type for the specified size. +// This function prefers to return integer types +// \param size size in bytes; should be 1,2,4,8,16 or sizeof(floating point) +// \return BT_INT.. or BT_FLOAT... or BT_UNK + +idaman type_t ida_export get_scalar_bt(int size); + + +//------------------------------------------------------------------------ +/// Type Information Library +//------------------------------------------------------------------------ +struct til_t +{ + char *name = nullptr; ///< short file name (without path and extension) + char *desc = nullptr; ///< human readable til description + int nbases = 0; ///< number of base tils + til_t **base = nullptr; ///< tils that our til is based on + uint32 flags = 0; ///< \ref TIL_ +/// \defgroup TIL_ Type info library property bits +/// used by til_t::flags +///@{ +#define TIL_ZIP 0x0001 ///< pack buckets using zip +#define TIL_MAC 0x0002 ///< til has macro table +#define TIL_ESI 0x0004 ///< extended sizeof info (short, long, longlong) +#define TIL_UNI 0x0008 ///< universal til for any compiler +#define TIL_ORD 0x0010 ///< type ordinal numbers are present +#define TIL_ALI 0x0020 ///< type aliases are present (this bit is used only on the disk) +#define TIL_MOD 0x0040 ///< til has been modified, should be saved +#define TIL_STM 0x0080 ///< til has extra streams +#define TIL_SLD 0x0100 ///< sizeof(long double) +#define TIL_ECC 0x0200 ///< extended callcnv_t +///@} + /// Has the til been modified? (#TIL_MOD) + inline bool is_dirty() const { return (flags & TIL_MOD) != 0; } + /// Mark the til as modified (#TIL_MOD) + inline void set_dirty() { flags |= TIL_MOD; } + + /// Find the base til with the provided name + /// \param n the base til name + /// \return the found til_t, or nullptr + inline til_t *find_base(const char *n) + { + if ( n != nullptr ) + { + for ( int i = 0; i < nbases; ++i ) + if ( streq(base[i]->name, n) ) + return base[i]; + } + return nullptr; + } + + compiler_info_t cc; ///< information about the target compiler + til_bucket_t *syms = nullptr; ///< symbols + til_bucket_t *types = nullptr; ///< types + til_bucket_t *macros = nullptr; ///< macros + int nrefs = 0; ///< number of references to the til + int nstreams = 0; ///< number of extra streams + til_stream_t **streams = nullptr; ///< symbol stream storage +}; + + +/// Initialize a til + +idaman til_t *ida_export new_til(const char *name, const char *desc); + + +/// Add multiple base tils. +/// \param[out] errbuf error message +/// \param ti target til +/// \param tildir directory where specified tils can be found. +/// nullptr means all default til subdirectories. +/// \param bases comma separated list of til names +/// \param gen_events generate corresponding IDB events +/// \return one of \ref TIL_ADD_ + +idaman int ida_export add_base_tils(qstring *errbuf, til_t *ti, const char *tildir, const char *bases, bool gen_events); + +/// \defgroup TIL_ADD_ Add TIL result codes +/// returned by add_base_tils() +///@{ +#define TIL_ADD_FAILED 0 ///< see errbuf +#define TIL_ADD_OK 1 ///< some tils were added +#define TIL_ADD_ALREADY 2 ///< the base til was already added +///@} + + +/// Load til from a file without adding it to the database list (see also \ref add_til). +/// Failure to load base tils are reported into 'errbuf'. They do not prevent +/// loading of the main til. +/// \param name filename of the til. If it's an absolute path, tildir is ignored. +/// - NB: the file extension is forced to .til +/// \param[out] errbuf error message +/// \param tildir directory where to load the til from. +/// nullptr means default til subdirectories. +/// \return pointer to resulting til, nullptr if failed and error message is in errbuf + +idaman til_t *ida_export load_til(const char *name, qstring *errbuf, const char *tildir=nullptr); + + +/// Sort til (use after modifying it). +/// \return false if no memory or bad parameter + +idaman bool ida_export sort_til(til_t *ti); + + +/// Collect garbage in til. +/// Must be called before storing the til. +/// \return true if any memory was freed + +idaman bool ida_export compact_til(til_t *ti); + + +/// Store til to a file. +/// If the til contains garbage, it will be collected before storing the til. +/// Your plugin should call compact_til() before calling store_til(). +/// \param ti type library to store +/// \param tildir directory where to store the til. nullptr means current directory. +/// \param name filename of the til. If it's an absolute path, tildir is ignored. +/// - NB: the file extension is forced to .til +/// \return success + +idaman bool ida_export store_til(til_t *ti, const char *tildir, const char *name); + + +/// Free memory allocated by til + +idaman void ida_export free_til(til_t *ti); + + +/// Get human-readable til description + +idaman til_t *ida_export load_til_header(const char *tildir, const char *name, qstring *errbuf); + + +//------------------------------------------------------------------------ +/// \defgroup CM_ CM +/// Calling convention & Model +///@{ + +/// \defgroup CM_ptr Default pointer size +///@{ +const cm_t CM_MASK = 0x03; +const cm_t CM_UNKNOWN = 0x00; ///< unknown +const cm_t CM_N8_F16 = 0x01; ///< if sizeof(int)<=2: near 1 byte, far 2 bytes +const cm_t CM_N64 = 0x01; ///< if sizeof(int)>2: near 8 bytes, far 8 bytes +const cm_t CM_N16_F32 = 0x02; ///< near 2 bytes, far 4 bytes +const cm_t CM_N32_F48 = 0x03; ///< near 4 bytes, far 6 bytes +///@} +/// \defgroup CM_M_ Model +///@{ +const cm_t CM_M_MASK = 0x0C; +const cm_t CM_M_NN = 0x00; ///< small: code=near, data=near (or unknown if CM_UNKNOWN) +const cm_t CM_M_FF = 0x04; ///< large: code=far, data=far +const cm_t CM_M_NF = 0x08; ///< compact: code=near, data=far +const cm_t CM_M_FN = 0x0C; ///< medium: code=far, data=near + +/// Does the given model specify far code?. +inline THREAD_SAFE bool is_code_far(cm_t cm) { return((cm & 4) != 0); } +/// Does the given model specify far data?. +inline THREAD_SAFE bool is_data_far(cm_t cm) { return((cm &= CM_M_MASK) && cm != CM_M_FN); } +///@} + +/// \defgroup CM_CC_ Calling convention +///@{ +const callcnv_t CM_CC_MASK = 0xF0; +const callcnv_t CM_CC_INVALID = 0x00; ///< this value is invalid +const callcnv_t CM_CC_UNKNOWN = 0x10; ///< unknown calling convention +const callcnv_t CM_CC_VOIDARG = 0x20; ///< function without arguments + ///< if has other cc and argnum == 0, + ///< represent as f() - unknown list +const callcnv_t CM_CC_CDECL = 0x30; ///< stack +const callcnv_t CM_CC_ELLIPSIS = 0x40; ///< cdecl + ellipsis +const callcnv_t CM_CC_STDCALL = 0x50; ///< stack, purged +const callcnv_t CM_CC_PASCAL = 0x60; ///< stack, purged, reverse order of args +const callcnv_t CM_CC_FASTCALL = 0x70; ///< stack, purged (x86), first args are in regs (compiler-dependent) +const callcnv_t CM_CC_THISCALL = 0x80; ///< stack, purged (x86), first arg is in reg (compiler-dependent) +const callcnv_t CM_CC_SWIFT = 0x90; ///< (Swift) arguments and return values in registers (compiler-dependent) +const callcnv_t CM_CC_SPOILED = 0xA0; ///< This is NOT a cc! Mark of __spoil record + ///< the low nibble is count and after n {spoilreg_t} + ///< present real cm_t byte. if n == BFA_FUNC_MARKER, + ///< the next byte is the function attribute byte. +const callcnv_t CM_CC_GOLANG = 0xB0; ///< (Go) arguments and return value reg/stack depending on version +const callcnv_t CM_CC_RESERVE3 = 0xC0; ///< reserved; used for internal needs +const callcnv_t CM_CC_SPECIALE = 0xD0; ///< ::CM_CC_SPECIAL with ellipsis +const callcnv_t CM_CC_SPECIALP = 0xE0; ///< Equal to ::CM_CC_SPECIAL, but with purged stack +const callcnv_t CM_CC_SPECIAL = 0xF0; ///< usercall: locations of all arguments + ///< and the return value are explicitly specified +const callcnv_t CM_CC_LAST_USERCALL = 0xFF; + +const callcnv_t CM_CC_GOSTK = 0x100; ///< (Go) arguments and return value in stack + +const callcnv_t CM_CC_FIRST_PLAIN_CUSTOM = 0x200; // please note that variadic or usercall + // codes have their own ranges +// recommendation: +// use 0x100 and higher for new calling convention codes. +// max calling convention code is MAX_DT. +// however, for calling conventions with special traits the following code may be used: +// - usercall variations may use values between 0xD0..0xFF. +// - ellipsis variations may use values 0x40..0x4F and 0xD0..0xDF. + +///@} CM_CC_ + +///@} CM_ + +/*! \defgroup BFA_ Function attribute byte + \ingroup tf_func + Zero attribute byte is forbidden. +*/ +///@{ +const type_t BFA_NORET = 0x01; ///< __noreturn +const type_t BFA_PURE = 0x02; ///< __pure +const type_t BFA_HIGH = 0x04; ///< high level prototype (with possibly hidden args) +const type_t BFA_STATIC = 0x08; ///< static +const type_t BFA_VIRTUAL = 0x10; ///< virtual + +const cm_t BFA_FUNC_MARKER = 0x0F; ///< This is NOT a cc! (used internally as a marker) +const type_t BFA_FUNC_EXT_FORMAT = 0x80; ///< This is NOT a real attribute (used internally as marker for extended format) +///@} + +#ifndef SWIG +/// Helper to declare common ::argloc_t related functions +#define ARGLOC_HELPER_DEFINITIONS(decl) \ +decl void ida_export copy_argloc(argloc_t *dst, const argloc_t *src); \ +decl void ida_export cleanup_argloc(argloc_t *vloc);\ +decl int ida_export compare_arglocs(const argloc_t &a, const argloc_t &b); +#else +#define ARGLOC_HELPER_DEFINITIONS(decl) +#endif // SWIG +ARGLOC_HELPER_DEFINITIONS(idaman) + +/// \defgroup argloc Argument locations +/// \ingroup CM_ +///@{ + +/// Specifies the location type of a function argument +/// or a debugger variable - see \ref ALOC_ +typedef int argloc_type_t; +/// \defgroup ALOC_ Argument location types +///@{ +const argloc_type_t + ALOC_NONE = 0, ///< none + ALOC_STACK = 1, ///< stack offset + ALOC_DIST = 2, ///< distributed (scattered) + ALOC_REG1 = 3, ///< one register (and offset within it) + ALOC_REG2 = 4, ///< register pair + ALOC_RREL = 5, ///< register relative + ALOC_STATIC = 6, ///< global address + ALOC_CUSTOM = 7; ///< custom argloc (7 or higher) +///@} + +/// Register-relative argument location +struct rrel_t +{ + sval_t off; ///< displacement from the address pointed by the register + int reg; ///< register index (into \varmem{ph,processor_t,reg_names}) +}; + +class scattered_aloc_t; + +/// Description of a custom argloc. Custom arglocs can be added by plugins in order +/// to describe the locations unsupported by the ida kernel. +struct custloc_desc_t +{ + size_t cbsize; ///< size of this structure + const char *name; ///< name of the custom argloc type. must be unique + + /// Copy src into empty_dst + void (idaapi *copy)(argloc_t *empty_dst, const argloc_t &src); + + /// Clear contents of loc before it is modified (may be nullptr) + void (idaapi *cleanup)(argloc_t *loc); + + /// May be nullptr + bool (idaapi *verify)( + const argloc_t &loc, + int size, + const rangeset_t *gaps, + bool part_of_scattered); + + /// Lexical comparison of two arglocs + int (idaapi *compare)(const argloc_t &a, const argloc_t &b); + + /// Get textual description of the location (not the value at the location!) + size_t (idaapi *print)( + char *buf, + size_t bufsize, + const argloc_t &loc, + asize_t size, + int praloc_flags); // PRALOC_... + + /// Dereference the struct/union pointed by 'strloc': take member at offset 'off' + /// (or use the field name), improve member 'tif' if necessary + bool (idaapi *deref_field)( + argloc_t *out, + tinfo_t *tif, + const argloc_t &strloc, + const tinfo_t &struct_tif, + asize_t off, + const qstring &name); + + /// Dereference the array pointed by 'arrloc': take member number 'n' + /// (element size is 'elsize'), improve member 'tif' if necessary + bool (idaapi *deref_array)( + argloc_t *out, + tinfo_t *tif, + const argloc_t &arrloc, + const tinfo_t &array_tif, + asize_t n, + asize_t elsize); + + /// Dereference the pointer at 'loc': retrieve location of the pointed object, + /// improve 'tif' of the pointed object if necessary + bool (idaapi *deref_ptr)( + argloc_t *out, + tinfo_t *tif, + const argloc_t &ptrloc); + + /// Read the pointer at 'loc': retrieve value of a simple object. + /// the object value must fit value_union_t. + bool (idaapi *read_value)( + value_union_t *value, + const argloc_t &loc, + int size, + const tinfo_t &tif); + + /// Update value at 'loc'. if idcv is VT_LONG/VT_INT64/VT_FLOAT, the value + /// in native format is copied to 'scalar_value' for your convenience. otherwise + /// please use 'idcv' and not 'scalar_value'. + bool (idaapi *write_value)( + const argloc_t &loc, + const idc_value_t &idcv, + const value_union_t &scalar_value, + int size, + qstring *errbuf); + + /// Calc max natural string length at 'loc' in the debugged process memory + asize_t (idaapi *calc_string_length)( + const argloc_t &loc, + const tinfo_t &string_tif); + + /// Retrieve string at 'loc' from the debugged process memory, + /// returns quoted string value + bool (idaapi *get_string)( + qstring *out, + tinfo_t *elem_tif, + const argloc_t &loc, + const tinfo_t &string_tif, + size_t len); + + /// Retrieve size of array at 'loc' (number of elements) + asize_t (idaapi *guess_array_size)( + const argloc_t &loc, + const tinfo_t &array_tif); + + /// Retrieve type of the object at 'loc' + bool (idaapi *get_tinfo)( + tinfo_t *out, + const argloc_t &loc); + + /// Calculate the number of children for the given location. + /// (arrays, structs, ptrs may have children and therefore be expanded) + int (idaapi *calc_number_of_children)(const argloc_t &loc, const tinfo_t &tif); + + /// Get string containing a printable representation of the pointer at 'loc'. + /// Returns the number of characters printed. + /// May be nullptr. + size_t (idaapi *print_ptr_value)( + char *buf, + size_t bufsize, + bool *is_valid_ptr, + const argloc_t &loc, + const tinfo_t &tif); +}; + + +/// Save a custom argloc +idaman int ida_export install_custom_argloc(const custloc_desc_t *custloc); +/// Delete the custom argloc at the given index +idaman bool ida_export remove_custom_argloc(int idx); +/// Retrieve the custom argloc at the given index +idaman const custloc_desc_t *ida_export retrieve_custom_argloc(int idx); + +/// Describes an argument location. +/// A typical argument is stored in one location, either a register or a stack slot. \n +/// However, some arguments can be stored in multiple locations, for example in a pair \n +/// of registers. In some really complex cases an argument can be located in multiple \n +/// registers and some stack slots. This class can describe all these cases. +class argloc_t // #argloc +{ +public: + typedef size_t biggest_t; + +private: + argloc_type_t type; + union + { + sval_t sval; // ::ALOC_STACK, ::ALOC_STATIC + uint32 reginfo; // ::ALOC_REG1, ::ALOC_REG2 + rrel_t *rrel; // ::ALOC_RREL + scattered_aloc_t *dist; // ::ALOC_DIST + void *custom; // ::ALOC_CUSTOM + biggest_t biggest; // to facilitate manipulation of this union + }; + ARGLOC_HELPER_DEFINITIONS(friend) + +public: + argloc_t() : type(ALOC_NONE), biggest(0) {} + argloc_t(const argloc_t &r) : type(ALOC_NONE) { copy_argloc(this, &r); } + ~argloc_t() { cleanup_argloc(this); } + argloc_t &operator=(const argloc_t &r) { copy_argloc(this, &r); return *this; } + DEFINE_MEMORY_ALLOCATION_FUNCS() + + /// Assign this == r and r == this + void swap(argloc_t &r) + { + biggest_t tmp = biggest; biggest = r.biggest; r.biggest = tmp; + argloc_type_t t = type; type = r.type; r.type = t; + } + + const char *dstr() const; + + argloc_type_t atype() const { return type; } ///< Get type (\ref ALOC_) + bool is_reg1() const { return type == ALOC_REG1; } ///< See ::ALOC_REG1 + bool is_reg2() const { return type == ALOC_REG2; } ///< See ::ALOC_REG2 + bool is_reg() const { return type == ALOC_REG1 || type == ALOC_REG2; } ///< is_reg1() || is_reg2() + bool is_rrel() const { return type == ALOC_RREL; } ///< See ::ALOC_RREL + bool is_ea() const { return type == ALOC_STATIC; } ///< See ::ALOC_STATIC + bool is_stkoff() const { return type == ALOC_STACK; } ///< See ::ALOC_STACK + bool is_scattered() const { return type == ALOC_DIST; } ///< See ::ALOC_DIST + inline bool has_reg() const; ///< TRUE if argloc has a register part + inline bool has_stkoff() const; ///< TRUE if argloc has a stack part + inline bool is_mixed_scattered() const; ///< mixed scattered: consists of register and stack parts + inline bool in_stack() const; ///< TRUE if argloc is in stack entirely + bool is_fragmented() const { return type == ALOC_DIST || type == ALOC_REG2; } ///< is_scattered() || is_reg2() + bool is_custom() const { return type >= ALOC_CUSTOM; } ///< See ::ALOC_CUSTOM + bool is_badloc() const { return type == ALOC_NONE; } ///< See ::ALOC_NONE + + /// Get the register info. + /// Use when atype() == ::ALOC_REG1 or ::ALOC_REG2 + int reg1() const { return uint16(reginfo); } + + /// Get offset from the beginning of the register in bytes. + /// Use when atype() == ::ALOC_REG1 + int regoff() const { return uint16(reginfo >> 16); } + + /// Get info for the second register. + /// Use when atype() == ::ALOC_REG2 + int reg2() const { return uint16(reginfo >> 16); } + + /// Get all register info. + /// Use when atype() == ::ALOC_REG1 or ::ALOC_REG2 + uint32 get_reginfo() const { return reginfo; } + + /// Get the stack offset. + /// Use if atype() == ::ALOC_STACK + sval_t stkoff() const { return sval; } + + /// Get the global address. + /// Use when atype() == ::ALOC_STATIC + ea_t get_ea() const { return sval; } + + /// Get scattered argument info. + /// Use when atype() == ::ALOC_DIST + scattered_aloc_t &scattered() { return *dist; } + const scattered_aloc_t &scattered() const { return *dist; } ///< \copydoc scattered() + + /// Get register-relative info. + /// Use when atype() == ::ALOC_RREL + rrel_t &get_rrel() { return *rrel; } + const rrel_t &get_rrel() const { return *rrel; } ///< \copydoc get_rrel() + + /// Get custom argloc info. + /// Use if atype() == ::ALOC_CUSTOM + void *get_custom() const { return custom; } + + /// Get largest element in internal union + biggest_t get_biggest() const { return biggest; } + + // be careful with these functions, they do not cleanup! + void _set_badloc() { type = ALOC_NONE; } ///< Use set_badloc() + void _set_reg1(int reg, int off=0) { type = ALOC_REG1; reginfo = reg | (off << 16); } ///< Use set_reg1() + void _set_reg2(int _reg1, int _reg2) { type = ALOC_REG2; reginfo = _reg1 | (_reg2 << 16); } ///< Use set_reg2() + void _set_stkoff(sval_t off) { type = ALOC_STACK; sval = off; } ///< Use set_stkoff() + void _set_ea(ea_t _ea) { type = ALOC_STATIC; sval = _ea; } ///< Use set_ea + /// Use consume_rrel() + bool _consume_rrel(rrel_t *p) //lint -sem(argloc_t::_consume_rrel, custodial(1)) + { + if ( p == nullptr ) + return false; + type = ALOC_RREL; + rrel = p; + return true; + } + /// Use consume_scattered() + bool _consume_scattered(scattered_aloc_t *p) + { + if ( p == nullptr ) + return false; + type = ALOC_DIST; + dist = p; + return true; + } + + /// Set custom argument location (careful - this function does not clean up!) + void _set_custom(argloc_type_t ct, void *pdata) { type = ct; custom = pdata; } + + /// Set biggest element in internal union (careful - this function does not clean up!) + void _set_biggest(argloc_type_t ct, biggest_t data) { type = ct; biggest = data; } + + /// Set register location + void set_reg1(int reg, int off=0) { cleanup_argloc(this); _set_reg1(reg, off); } + + /// Set secondary register location + void set_reg2(int _reg1, int _reg2) { cleanup_argloc(this); _set_reg2(_reg1, _reg2); } + + /// Set stack offset location + void set_stkoff(sval_t off) { cleanup_argloc(this); _set_stkoff(off); } + + /// Set static ea location + void set_ea(ea_t _ea) { cleanup_argloc(this); _set_ea(_ea); } + + /// Set register-relative location - can't be nullptr + void consume_rrel(rrel_t *p) { cleanup_argloc(this); _consume_rrel(p); } + + /// Set distributed argument location + void consume_scattered(scattered_aloc_t *p) { cleanup_argloc(this); _consume_scattered(p); } + + /// Set to invalid location + void set_badloc() { cleanup_argloc(this); } + + /// Calculate offset that can be used to compare 2 similar arglocs + sval_t calc_offset() const + { + switch ( type ) + { + default: + case ALOC_NONE: + case ALOC_DIST: + case ALOC_REG2: + return -1; + case ALOC_RREL: + return rrel->off; + case ALOC_STACK: + case ALOC_STATIC: + return sval; + case ALOC_REG1: + return reg1(); + } + } + + /// Move the location to point 'delta' bytes further + bool advance(int delta) + { + switch ( type ) + { + case ALOC_REG1: + _set_reg1(reg1()+delta, regoff()); + break; + case ALOC_STACK: + case ALOC_STATIC: + sval += delta; + break; + case ALOC_RREL: + rrel->off += delta; + break; + default: + return false; + } + return true; + } + + /// Set register offset to align it to the upper part of _SLOTSIZE + void align_reg_high(size_t size, size_t _slotsize) + { + if ( is_reg1() ) + _set_reg1(reg1(), size < _slotsize ? _slotsize - size : 0); + } + + /// Set stack offset to align to the upper part of _SLOTSIZE + void align_stkoff_high(size_t size, size_t _slotsize) + { + if ( is_stkoff() ) + { + sval_t off = align_down(stkoff(), _slotsize); + if ( size < _slotsize ) + off += _slotsize - size; + _set_stkoff(off); + } + } + + DECLARE_COMPARISONS(argloc_t) + { + return compare_arglocs(*this, r); + } +}; +DECLARE_TYPE_AS_MOVABLE(argloc_t); +typedef qvector<argloc_t> arglocs_t; ///< vector of argument locations + +/// Subsection of an argument location +struct argpart_t : public argloc_t +{ + ushort off; ///< offset from the beginning of the argument + ushort size; ///< the number of bytes + DEFINE_MEMORY_ALLOCATION_FUNCS() + argpart_t(const argloc_t &a) : argloc_t(a), off(0xFFFF), size(0) {} ///< Constructor + argpart_t() : off(0xFFFF), size(0) {} ///< Constructor + argpart_t ©_from(const argloc_t &a) { *(argloc_t*)this = a; return *this; } + + /// Does this argpart have a valid offset? + bool bad_offset() const { return off == 0xFFFF; } + + /// Does this argpart have a valid size? + bool bad_size() const { return size == 0; } + + /// Compare two argparts, based on their offset + bool operator < (const argpart_t &r) const { return off < r.off; } + + /// Assign this = r and r = this + void swap(argpart_t &r) + { + argloc_t::swap(r); + qswap(off, r.off); + qswap(size, r.size); + } +}; +DECLARE_TYPE_AS_MOVABLE(argpart_t); +typedef qvector<argpart_t> argpartvec_t; + +/// Used to manage arguments that are described by multiple locations (also see ::ALOC_DIST) +class scattered_aloc_t : public argpartvec_t +{ +public: + DEFINE_MEMORY_ALLOCATION_FUNCS() +}; +DECLARE_TYPE_AS_MOVABLE(scattered_aloc_t); + + +/// Verify argloc_t. +/// \param vloc argloc to verify +/// \param size total size of the variable +/// \param gaps if not nullptr, specifies gaps in structure definition. +/// these gaps should not map to any argloc, but everything else must be covered +/// \return 0 if ok, otherwise an interr code. + +idaman int ida_export verify_argloc(const argloc_t &vloc, int size, const rangeset_t *gaps); + + +/// Verify and optimize scattered argloc into simple form. +/// All new arglocs must be processed by this function. +/// \retval true success +/// \retval false the input argloc was illegal + +idaman bool ida_export optimize_argloc(argloc_t *vloc, int size, const rangeset_t *gaps); + + +/// Convert an argloc to human readable form + +idaman size_t ida_export print_argloc( + char *buf, + size_t bufsize, + const argloc_t &vloc, + int size=0, + int vflags=0); +#define PRALOC_VERIFY 0x01 ///< interr if illegal argloc +#define PRALOC_STKOFF 0x02 ///< print stack offsets + + +/// Visit all argument locations. The callback will not receive ::ALOC_DIST/::ALOC_REG2 types, +/// they will be converted into smaller argloc types (::ALOC_REG1 or other) +struct aloc_visitor_t +{ + virtual int idaapi visit_location(argloc_t &v, int off, int size) = 0; + virtual ~aloc_visitor_t() {} +}; + +/// Compress larger argloc types and initiate the aloc visitor +idaman int ida_export for_all_arglocs(aloc_visitor_t &vv, argloc_t &vloc, int size, int off=0); + +/// Same as ::aloc_visitor_t, but may not modify the argloc +struct const_aloc_visitor_t +{ + virtual int idaapi visit_location(const argloc_t &v, int off, int size) = 0; + virtual ~const_aloc_visitor_t() {} +}; + +/// See for_all_arglocs() +inline int idaapi for_all_const_arglocs(const_aloc_visitor_t &vv, const argloc_t &vloc, int size, int off=0) +{ + return for_all_arglocs(*(aloc_visitor_t*)(&vv), + CONST_CAST(argloc_t&)(vloc), + size, + off); +} + +//-------------------------------------------------------------------------- +/// \defgroup C_PC_ Standard C-language models for x86 +/// \ingroup CM_ +///@{ +const cm_t C_PC_TINY = (CM_N16_F32 | CM_M_NN); +const cm_t C_PC_SMALL = (CM_N16_F32 | CM_M_NN); +const cm_t C_PC_COMPACT = (CM_N16_F32 | CM_M_NF); +const cm_t C_PC_MEDIUM = (CM_N16_F32 | CM_M_FN); +const cm_t C_PC_LARGE = (CM_N16_F32 | CM_M_FF); +const cm_t C_PC_HUGE = (CM_N16_F32 | CM_M_FF); +const cm_t C_PC_FLAT = (CM_N32_F48 | CM_M_NN); +///@} + + +// Helper functions that maintain binary compatibility with old plugins +// (when possible) yet let us work with 32-bit calling convention codes. +inline callcnv_t helper_get_cc(cm_t cm, callcnv_t new_callcnv) +{ + cm_t cc = cm & CM_CC_MASK; + if ( cc == CM_CC_RESERVE3 ) + return new_callcnv; + else + return cc; +} + +inline void helper_set_cc(cm_t *cm, callcnv_t *new_callcnv, callcnv_t cc) +{ + *cm &= ~CM_CC_MASK; + if ( (cc & ~CM_CC_MASK) == 0 && cc != CM_CC_RESERVE3 ) + { + *cm |= cc; + *new_callcnv = 0; + } + else + { + *cm |= CM_CC_RESERVE3; + *new_callcnv = cc; + } +} + +inline callcnv_t compiler_info_t::get_cc() const { return helper_get_cc(cm, _new_callcnv); } +inline void compiler_info_t::set_cc(callcnv_t cc) { helper_set_cc(&cm, &_new_callcnv, cc); } + +/// Get effective calling convention (with respect to default CC) +inline callcnv_t get_effective_cc(callcnv_t cc) +{ + // if the calling convention is not specified, use the default one + if ( cc <= CM_CC_UNKNOWN ) + cc = inf_get_callcnv(); + return cc; +} + +/// Does the calling convention specify argument locations explicitly? +inline constexpr THREAD_SAFE bool is_user_cc(callcnv_t cc) +{ + return cc >= CM_CC_SPECIALE && cc <= CM_CC_LAST_USERCALL; +} + +/// Does the calling convention use ellipsis? +inline constexpr THREAD_SAFE bool is_vararg_cc(callcnv_t cc) +{ + cc &= ~0xF; // the range of vararg codes is 0x40..0x4F and 0xD0..0xDF. + return cc == CM_CC_ELLIPSIS || cc == CM_CC_SPECIALE; +} + +/// Does the calling convention clean the stack arguments upon return?. +/// \note this function is valid only for x86 code +inline constexpr THREAD_SAFE bool is_purging_cc(callcnv_t cc) +{ + cc &= ~0xF; // mask out the last 4 bits + return cc == CM_CC_STDCALL + || cc == CM_CC_PASCAL + || cc == CM_CC_SPECIALP + || cc == CM_CC_FASTCALL + || cc == CM_CC_THISCALL + || cc == CM_CC_SWIFT; +} + +/// GO language calling convention (return value in stack)? +inline constexpr bool is_golang_cc(callcnv_t cc) +{ + return cc == CM_CC_GOLANG || cc == CM_CC_GOSTK; +} + +/// Is custom calling convention? +inline bool is_custom_callcnv(callcnv_t cc) +{ + if ( cc >= CM_CC_FIRST_PLAIN_CUSTOM ) + return true; + if ( cc <= 0xFF && (cc & ~CM_CC_MASK) != 0 ) + return true; + return false; +} + +/// Swift calling convention (arguments and return values in registers)? + +inline constexpr bool is_swift_cc(callcnv_t cc) +{ + return cc == CM_CC_SWIFT; +} + + +//------------------------------------------------------------------------- +/// Some calling conventions foresee special areas on the stack for call arguments. +/// This structure lists their sizes. +/// +idaman bool ida_export get_stkarg_area_info( + stkarg_area_info_t *out, + callcnv_t cc); + +struct stkarg_area_info_t +{ + size_t cb = sizeof(stkarg_area_info_t); + + /// Offset from the SP to the first stack argument (can include linkage area) + /// examples: pc: 0, hppa: -0x34, ppc aix: 0x18 + sval_t stkarg_offset = 0; + + /// Size of the shadow area. + /// explanations at: https://stackoverflow.com/questions/30190132/what-is-the-shadow-space-in-x64-assembly + /// examples: x64 Visual Studio C++: 0x20, x64 gcc: 0, ppc aix: 0x20 + sval_t shadow_size = 0; + + /// Size of the linkage area. + /// explanations at: https://www.ibm.com/docs/en/xl-fortran-aix/16.1.0?topic=conventions-linkage-area + /// examples: pc: 0, hppa: 0, ppc aix: 0x18 (equal to stkarg_offset) + sval_t linkage_area = 0; + + stkarg_area_info_t() = default; + stkarg_area_info_t(callcnv_t cc) { get_info(cc); } + bool get_info(callcnv_t cc) { return get_stkarg_area_info(this, cc); } +}; + +//-------------------------------------------------------------------------- +/// Description of a custom calling convention +struct custom_callcnv_t +{ + int cbsize = sizeof(*this); + uint64 flags = 0; +#define CCI_VARARG 0x0001 ///< is variadic? +#define CCI_PURGE 0x0002 ///< purges arguments? +#define CCI_USER 0x0004 ///< is usercall? not tested + qstring name; ///< the name is used as a keyword in the function prototype + uint32 abibits = 0; ///< abibits to be used for the calling convention + + bool is_vararg() const { return (flags & CCI_VARARG) != 0; } + bool is_purging() const { return (flags & CCI_PURGE) != 0; } + bool is_usercall() const { return (flags & CCI_USER) != 0; } + + /// Validate a function prototype. + /// This function is used during parsing or deserializing a function prototype + /// to verify semantic limitations of the prototype (for example, returning + /// arrays is forbidden in C) + /// \param[in] fti function prototype + /// \param[out] reterr buffer for error message + virtual bool validate_func(const func_type_data_t &fti, qstring *reterr) const + { + qnotused(fti); + qnotused(reterr); + return true; + } + + /// Calculate the location of the return value. + /// This function must fill fti->retloc. + /// \param fti function prototype + /// \return success + virtual bool calc_retloc(func_type_data_t *fti) const = 0; + + /// Calculate the argument locations. + /// This function must fill all fti->at(i).argloc instances. + /// It may be called for variadic functions too, in calc_varglocs fails. + /// \param fti function prototype + /// \return success + virtual bool calc_arglocs(func_type_data_t *fti) const = 0; + + /// Discover variadic arguments. + /// This function is called only for variadic functions. + /// It is currently used by the decompiler. + /// \param fti function prototype. find_varargs() should append the discovered + /// variadic arguments to it. + /// \param call_ea address of the call instruction + /// \param blk microcode block with the call instruction + /// \return >0 - total number of arguments after the call + /// <0 - failure + /// ==0 - means to use the standard algorithm to discover variadic args + virtual ssize_t find_varargs( + func_type_data_t *fti, + ea_t call_ea, + class mblock_t *blk) const + { + qnotused(call_ea); + qnotused(fti); + qnotused(blk); + return 0; + } + + /// Calculate the argument locations for a variadic function. + /// This function must fill all fti->at(i).argloc instances and provide + /// more detailed info about registers and stkargs. + /// \param fti function prototype + /// \param[out] regs buffer for hidden register arguments, may be nullptr + /// \param[out] stkargs buffer for hidden stack arguments, may be nullptr + /// \param nfixed number of fixed arguments + /// \return success + virtual bool calc_varglocs( + func_type_data_t *fti, + regobjs_t *regs, + relobj_t *stkargs, + int nfixed) const + { + qnotused(fti); + qnotused(regs); + qnotused(stkargs); + qnotused(nfixed); + return false; + } + + /// Retrieve generic information about call registers + virtual bool get_cc_regs(callregs_t *out) const + { + qnotused(out); + return false; + } + + /// Retrieve generic information about stack arguments + virtual bool get_stkarg_area_info(stkarg_area_info_t *out) const + { + qnotused(out); + return false; + } + + /// Calculate the number of purged bytes + /// \param fti function prototype + /// \param call_ea address of the call instruction (not used yet) + virtual int calc_purged_bytes( + const func_type_data_t &fti, + ea_t call_ea=BADADDR) const + { + qnotused(fti); + qnotused(call_ea); + return 0; + } + + /// Decorate a function name. + /// Some compilers decorate names depending on the calling convention. + /// This function provides the means to handle it for custom callcnvs. + /// Please note that this is about name decoration (C), not name mangling (C++). + inline virtual bool decorate_name( + qstring *outbuf, + const char *name, + bool should_decorate, + callcnv_t cc, + const tinfo_t &type) const; + + /// Lower a function type. + /// See lower_type() for more explanations. + /// \param fti function prototype + /// \return <0-failure, >=0-ok, 2-made substantial changes + virtual int lower_func_type(func_type_data_t *fti) const + { + qnotused(fti); + return 0; + } + + custom_callcnv_t() = default; + custom_callcnv_t(const char *_name, uint64 f, uint32 _abibits) + : flags(f), name(_name), abibits(_abibits) + {} + virtual ~custom_callcnv_t() {} +#ifndef SWIG + DECLARE_COMPARISONS(custom_callcnv_t); +#endif +}; +DECLARE_TYPE_AS_MOVABLE(custom_callcnv_t); + +/// Register a calling convention +/// \param ccinf description of the custom calling convention. +/// this object must be alive until unregister_custom_callcnv(), +/// after that it can destroyed +/// \return CM_CC_INVALID means failure: +/// - bad ccinf.name +/// - ccinf.name already exists +/// - the calling convention is special (usercall, purging, vararg) +/// and there are too many of them already +idaman callcnv_t ida_export register_custom_callcnv(const custom_callcnv_t &ccinf); + +/// Unregister a calling convention +/// \return true if successfully unregistered the custom calling convention +idaman bool ida_export unregister_custom_callcnv(callcnv_t callcnv); + +/// Retrieve custom calling convention details +idaman const custom_callcnv_t *ida_export get_custom_callcnv(callcnv_t callcnv); + +/// Find a calling convention by its name +/// \return CM_CC_INVALID is not found +idaman callcnv_t ida_export find_custom_callcnv(const char *name); + +/// Get all custom calling conventions +/// \param names output buffer for the convention names +/// \param codes output buffer for the convention codes +/// The two output buffers correspond to each other. +/// \return number of the calling conventions added to the output buffers +idaman size_t ida_export get_custom_callcnvs(qstrvec_t *names, callcnvs_t *codes); + +//-------------------------------------------------------------------------- +/// Function argument passing: how GP & FP registers cooperate with each other +enum argreg_policy_t +{ + ARGREGS_POLICY_UNDEFINED, + ARGREGS_GP_ONLY, ///< GP registers used for all arguments + ARGREGS_INDEPENDENT, ///< FP/GP registers used separately (like gcc64) + ARGREGS_BY_SLOTS, ///< fixed FP/GP register per each slot (like vc64) + ARGREGS_FP_MASKS_GP, ///< FP register also consumes one or more GP regs but not vice versa (aix ppc ABI) + ARGREGS_MIPS_O32, ///< MIPS ABI o32 + ARGREGS_RISCV, ///< Risc-V API + ///< FP arguments are passed in GP registers if FP + ///< registers are exhausted and GP ones are not. + ///< Wide FP arguments are passed in GP registers. + ///< Variadic FP arguments are passed in GP registers. +}; + +///@} argloc + +/// Register allocation calling convention. +/// (allocation policy, arrays of GP and FP registers) +class callregs_t +{ + bool set_inds(int *p_ind1, int *p_ind2, int ind) const + { + if ( ind == -1 ) + return false; + *p_ind1 = ind; + *p_ind2 = by_slots() ? ind : -1; + return true; + } + + // copy -1-terminated array to a vector + static void set_regarray(intvec_t *regvec, const int *regarray) + { + regvec->clear(); + if ( regarray != nullptr ) + while ( *regarray != -1 ) + regvec->push_back(*regarray++); + } + void calc_nregs() + { + nregs = gpregs.size(); + if ( policy == ARGREGS_INDEPENDENT + || policy == ARGREGS_FP_MASKS_GP + || policy == ARGREGS_RISCV ) + { + nregs += int(fpregs.size()); + } + } + +public: + argreg_policy_t policy = ARGREGS_POLICY_UNDEFINED; ///< argument policy + int nregs = 0; ///< max number of registers that can be used in a call + intvec_t gpregs; ///< array of gp registers (general purpose) + intvec_t fpregs; ///< array of fp registers (floating point) + + /// Constructor + callregs_t() {} + + /// Constructor - initialize with the given request (see init_regs()) + callregs_t(callcnv_t cc) { init_regs(cc); } + + /// swap two instances + void swap(callregs_t &r) + { + std::swap(policy, r.policy); + std::swap(nregs, r.nregs); + gpregs.swap(r.gpregs); + fpregs.swap(r.fpregs); + } + + /// Init policy & registers for given CC. + bool init_regs(callcnv_t cc) + { + if ( is_custom_callcnv(cc) ) + { + const custom_callcnv_t *ccc = get_custom_callcnv(cc); + if ( ccc == nullptr ) + return false; + return ccc->get_cc_regs(this); + } + return processor_t::get_cc_regs(this, cc) > 0; + } + + // policy-specific options + bool by_slots() const { return policy == ARGREGS_BY_SLOTS; } + + /// Init policy & registers (arrays are -1-terminated) + void set(argreg_policy_t _policy, const int *gprs, const int *fprs) + { + policy = _policy; + set_regarray(&gpregs, gprs); + set_regarray(&fpregs, fprs); + calc_nregs(); + } + + /// Init registers (sequential) + enum reg_kind_t { GPREGS, FPREGS }; + void append_registers(reg_kind_t kind, int first_reg, int last_reg) + { + intvec_t ®vec = kind == FPREGS ? fpregs : gpregs; + size_t n = regvec.size(); + regvec.resize(n + last_reg - first_reg + 1); + for ( int i = first_reg; i <= last_reg; ++i ) + regvec[n + i - first_reg] = i; + } + void set_registers(reg_kind_t kind, int first_reg, int last_reg) + { + intvec_t ®vec = kind == FPREGS ? fpregs : gpregs; + regvec.clear(); + append_registers(kind, first_reg, last_reg); + } + + /// Set policy and registers to invalid values + void reset() + { + set(ARGREGS_POLICY_UNDEFINED, nullptr, nullptr); + } + + /// Get max number of registers may be used in a function call. + static int regcount(callcnv_t cc) + { + callregs_t vr(cc); + return vr.nregs; + } + + // return index of register, -1 else + static int findreg(const intvec_t ®s, int r) + { + intvec_t::const_iterator p = regs.find(r); + return p == regs.end() ? -1 : (p-regs.begin()); + } + + /// Get register indexes within GP/FP arrays. + /// (-1 -> is not present in the corresponding array) + bool reginds(int *gp_ind, int *fp_ind, int r) const + { + return findregs(gp_ind, fp_ind, r, gpregs, fpregs); + } + +protected: + /// Search for register r in gprs and fprs. + /// If found, fill gp_ind and fp_ind based on #policy + bool findregs(int *gp_ind, int *fp_ind, int r, const intvec_t &gprs, const intvec_t &fprs) const + { + *gp_ind = *fp_ind = -1; + return set_inds(gp_ind, fp_ind, findreg(gprs, r)) + || set_inds(fp_ind, gp_ind, findreg(fprs, r)); + } +}; + +//-------------------------------------------------------------------------- +/// \defgroup CC +/// Target compiler +///@{ + +/// \defgroup COMP_ Compiler IDs +///@{ +const comp_t COMP_MASK = 0x0F; +const comp_t COMP_UNK = 0x00; ///< Unknown +const comp_t COMP_MS = 0x01; ///< Visual C++ +const comp_t COMP_BC = 0x02; ///< Borland C++ +const comp_t COMP_WATCOM = 0x03; ///< Watcom C++ +// const comp_t COMP_ = 0x04 +// const comp_t COMP_ = 0x05 +const comp_t COMP_GNU = 0x06; ///< GNU C++ +const comp_t COMP_VISAGE = 0x07; ///< Visual Age C++ +const comp_t COMP_BP = 0x08; ///< Delphi +//---- +const comp_t COMP_UNSURE = 0x80; ///< uncertain compiler id +///@} + + +/// \defgroup CC_funcs Functions: work with compiler IDs +///@{ + +/// Get compiler bits + +inline THREAD_SAFE comp_t get_comp(comp_t comp) { return(comp & COMP_MASK); } + + +/// Get full compiler name + +idaman const char *ida_export get_compiler_name(comp_t id); + + +/// Get abbreviated compiler name + +idaman const char *ida_export get_compiler_abbr(comp_t id); + +/// Collection of compiler descriptions +typedef qvector<comp_t> compvec_t; + + +/// Get names of all built-in compilers + +idaman void ida_export get_compilers(compvec_t *ids, qstrvec_t *names, qstrvec_t *abbrs); + + +/// See ::COMP_UNSURE + +inline THREAD_SAFE comp_t is_comp_unsure(comp_t comp) { return (comp & COMP_UNSURE); } + + +/// Get compiler specified by \varmem{inf,idainfo,cc} + +inline comp_t default_compiler() { return get_comp(inf_get_cc_id()); } + + +/// Is the target compiler ::COMP_GNU? + +inline bool is_gcc() { return default_compiler() == COMP_GNU; } + + +/// Is the target compiler 32 bit gcc? + +inline bool is_gcc32() { return is_gcc() && !inf_is_64bit(); } + + +/// Is the target compiler 64 bit gcc? + +inline bool is_gcc64() { return is_gcc() && inf_is_64bit(); } + + +/// Should use the struct/union layout as done by gcc? + +inline bool gcc_layout() { return is_gcc() || (inf_get_abibits() & ABI_GCC_LAYOUT) != 0; } + + +/// Change current compiler. +/// \param cc compiler to switch to +/// \param flags \ref SETCOMP_ +/// \param abiname ABI name +/// \return success + +idaman bool ida_export set_compiler( + const compiler_info_t &cc, + int flags, + const char *abiname=nullptr); + +/// \defgroup SETCOMP_ Set compiler flags +///@{ +#define SETCOMP_OVERRIDE 0x0001 ///< may override old compiler info +#define SETCOMP_ONLY_ID 0x0002 ///< cc has only 'id' field; + ///< the rest will be set to defaults + ///< corresponding to the program bitness +#define SETCOMP_ONLY_ABI 0x0004 ///< ignore cc field complete, use only abiname +#define SETCOMP_BY_USER 0x0008 ///< invoked by user, cannot be replaced by module/loader +///@} + + +/// Set the compiler id (see \ref COMP_) + +inline bool idaapi set_compiler_id(comp_t id, const char *abiname=nullptr) +{ + compiler_info_t cc; + cc.id = id; + return set_compiler(cc, SETCOMP_ONLY_ID, abiname); +} + +/// Set abi name (see \ref COMP_) + +inline bool idaapi set_abi_name(const char *abiname, bool user_level = false) +{ + compiler_info_t cc; + cc.id = 0; + int flags = SETCOMP_ONLY_ABI | (user_level ? SETCOMP_BY_USER : 0); + return set_compiler(cc, flags, abiname); +} + +/// Get ABI name. +/// \return length of the name (>=0) + +idaman ssize_t ida_export get_abi_name(qstring *out); + + +/// Add/remove/check ABI option +/// General form of full abi name: abiname-opt1-opt2-... or -opt1-opt2-... +/// \param abi_opts - ABI options to add/remove in form opt1-opt2-... +/// \param user_level - initiated by user if TRUE (==SETCOMP_BY_USER) +/// \return success +idaman bool ida_export append_abi_opts(const char *abi_opts, bool user_level = false); +idaman bool ida_export remove_abi_opts(const char *abi_opts, bool user_level = false); + +/// \param compstr - compiler description in form <abbr>:<abiname> +/// \param user_level - initiated by user if TRUE +/// \return success +idaman bool ida_export set_compiler_string(const char *compstr, bool user_level); + + +/// is GOLANG calling convention used by default? +inline bool use_golang_cc() +{ + return is_golang_cc(inf_get_callcnv()); +} + + +/// switch to GOLANG calling convention (to be used as default CC) +inline void switch_to_golang() +{ + inf_set_callcnv(CM_CC_GOLANG); + if ( default_compiler() == COMP_UNK ) + set_compiler_id(COMP_GNU); +} + +///@} CC_funcs +///@} CC + +//-------------------------------------------------------------------------- +const size_t BADSIZE = size_t(-1); ///< bad type size +#define MAX_FUNC_ARGS 256 ///< max number of function arguments + +//-------------------------------------------------------------------------- +/// abstractness of declaration (see h2ti()) +enum abs_t +{ + ABS_UNK, + ABS_NO, + ABS_YES +}; +enum sclass_t ///< storage class +{ + SC_UNK = 0, ///< unknown + SC_TYPE = 1, ///< typedef + SC_EXT = 2, ///< extern + SC_STAT = 3, ///< static + SC_REG = 4, ///< register + SC_AUTO = 5, ///< auto + SC_FRIEND = 6, ///< friend + SC_VIRT = 7, ///< virtual +}; + +/// \defgroup parse_tinfo Type parsing +/// Format/Parse/Print type information +///@{ + +/// \defgroup HTI_ Type formatting flags +///@{ +#define HTI_CPP 0x00000001 ///< C++ mode (not implemented) +#define HTI_INT 0x00000002 ///< debug: print internal representation of types +#define HTI_EXT 0x00000004 ///< debug: print external representation of types +#define HTI_LEX 0x00000008 ///< debug: print tokens +#define HTI_UNP 0x00000010 ///< debug: check the result by unpacking it +#define HTI_TST 0x00000020 ///< test mode: discard the result +#define HTI_FIL 0x00000040 ///< "input" is file name, + ///< otherwise "input" contains a C declaration +#define HTI_MAC 0x00000080 ///< define macros from the base tils +#define HTI_NWR 0x00000100 ///< no warning messages +#define HTI_NER 0x00000200 ///< ignore all errors but display them +#define HTI_DCL 0x00000400 ///< don't complain about redeclarations +#define HTI_NDC 0x00000800 ///< don't decorate names +#define HTI_PAK 0x00007000 ///< explicit structure pack value (#pragma pack) +#define HTI_PAK_SHIFT 12 ///< shift for #HTI_PAK. This field should + ///< be used if you want to remember an explicit + ///< pack value for each structure/union type. + ///< See #HTI_PAK... definitions +#define HTI_PAKDEF 0x00000000 ///< default pack value +#define HTI_PAK1 0x00001000 ///< #pragma pack(1) +#define HTI_PAK2 0x00002000 ///< #pragma pack(2) +#define HTI_PAK4 0x00003000 ///< #pragma pack(4) +#define HTI_PAK8 0x00004000 ///< #pragma pack(8) +#define HTI_PAK16 0x00005000 ///< #pragma pack(16) +#define HTI_HIGH 0x00008000 ///< assume high level prototypes + ///< (with hidden args, etc) +#define HTI_LOWER 0x00010000 ///< lower the function prototypes +#define HTI_RAWARGS 0x00020000 ///< leave argument names unchanged (do not remove underscores) +#define HTI_RELAXED 0x00080000 ///< accept references to unknown namespaces +#define HTI_NOBASE 0x00100000 ///< do not inspect base tils +#define HTI_SEMICOLON 0x00200000 ///< do not complain if the terminating semicolon is absent +#define HTI_STANDALONE 0x00400000 ///< should parse standalone declaration, + ///< it may contain qualified name and type names, + ///< strictly speaking it is not a valid C++ code, + ///< IDA Pro specific +///@} + + +/// This callback will be called for each type/variable declaration. +/// \param name var/func/type name +/// \param tif type info +/// \param cmt main comment +/// \param value symbol value +/// \param cb_data data passed to callback +/// \retval T_CBBRKDEF the type declaration won't be saved in the til + +typedef int idaapi h2ti_type_cb( + const char *name, + const tinfo_t &tif, + const char *cmt, + const uint64 *value, + void *cb_data); + + +/// Specify a printing callback when parsing types. +/// See h2ti() and parse_decls(). +typedef AS_PRINTF(1, 2) int printer_t(const char *format, ...); + + +/// Convert declarations to type_t*. +/// This is a low level function - use parse_decls() or parse_decl() +/// \param ti type info library +/// \param lx input lexer. may be nullptr. always destroyed by h2ti() +/// \param input file name or C declaration +/// \param flags combination of \ref HTI_ +/// \param type_cb callback - for each type +/// \param var_cb callback - for each var +/// \param print_cb may pass msg() here +/// \param _cb_data data passed to callbacks +/// \param _isabs the expected abstracness of the type declaration(s) +/// \return number of errors (they are displayed using print_cb). zero means ok + +idaman int ida_export h2ti( + til_t *ti, + lexer_t *lx, + const char *input, + int flags=HTI_HIGH, + h2ti_type_cb *type_cb=nullptr, + h2ti_type_cb *var_cb=nullptr, + printer_t *print_cb=nullptr, + void *_cb_data=nullptr, + abs_t _isabs=ABS_UNK); + + +/// Convert \ref PT_ to \ref HTI_. +/// Type parsing flags lesser than 0x10 don't have stable meaning and will be ignored +/// (more on these flags can be seen in idc.idc) + +inline THREAD_SAFE int convert_pt_flags_to_hti(int pt_flags) +{ + return ((pt_flags >> 4) & 0x1f) << HTI_PAK_SHIFT; +} + + +/// Parse ONE declaration. +/// If the input string contains more than one declaration, the first complete +/// type declaration (#PT_TYP) or the last variable declaration (#PT_VAR) will be used. +/// \note name & tif may be empty after the call! +/// \param[out] out_tif type info +/// \param[out] out_name declared name +/// \param til type library to use. may be nullptr +/// \param decl C declaration to parse +/// \param pt_flags combination of \ref PT_ bits +/// \retval true ok +/// \retval false declaration is bad, the error message is displayed if !PT_SIL + +idaman bool ida_export parse_decl( + tinfo_t *out_tif, + qstring *out_name, + til_t *til, + const char *decl, + int pt_flags); + + +/// \defgroup PT_ Type parsing flags +///@{ +#define PT_SIL 0x0001 ///< silent, no messages +#define PT_NDC 0x0002 ///< don't decorate names +#define PT_TYP 0x0004 ///< return declared type information +#define PT_VAR 0x0008 ///< return declared object information +#define PT_PACKMASK 0x0070 ///< mask for pack alignment values +#define PT_HIGH 0x0080 ///< assume high level prototypes + ///< (with hidden args, etc) +#define PT_LOWER 0x0100 ///< lower the function prototypes +#define PT_REPLACE 0x0200 ///< replace the old type (used in idc) +#define PT_RAWARGS 0x0400 ///< leave argument names unchanged (do not remove underscores) +#define PT_RELAXED 0x1000 ///< accept references to unknown namespaces +#define PT_EMPTY 0x2000 ///< accept empty decl +#define PT_SEMICOLON 0x4000 ///< append the terminating semicolon +#define PT_SYMBOL 0x8000 ///< accept a symbol name and return its type. + ///< e.g. "LoadLibrary" will return its prototype +///@} + + +/// Parse many declarations and store them in a til. +/// If there are any errors, they will be printed using 'printer'. +/// This function uses default include path and predefined macros from the +/// database settings. It always uses the #HTI_DCL bit. +/// \param til type library to store the result +/// \param input input string or file name (see hti_flags) +/// \param printer function to output error messages (use msg or nullptr or your own callback) +/// \param hti_flags combination of \ref HTI_ +/// \return number of errors, 0 means ok. + +idaman int ida_export parse_decls( + til_t *til, + const char *input, + printer_t *printer, + int hti_flags); + + +/// Get type declaration for the specified address. +/// \param out output buffer +/// \param ea address +/// \param prtype_flags combination of \ref PRTYPE_ +/// \return success + +idaman bool ida_export print_type(qstring *out, ea_t ea, int prtype_flags); + + +/// \defgroup PRTYPE_ Type printing flags +///@{ +#define PRTYPE_1LINE 0x00000 ///< print to one line +#define PRTYPE_MULTI 0x00001 ///< print to many lines +#define PRTYPE_TYPE 0x00002 ///< print type declaration (not variable declaration) +#define PRTYPE_PRAGMA 0x00004 ///< print pragmas for alignment +#define PRTYPE_SEMI 0x00008 ///< append ; to the end +#define PRTYPE_CPP 0x00010 ///< use c++ name (only for print_type()) +#define PRTYPE_DEF 0x00020 ///< tinfo_t: print definition, if available +#define PRTYPE_NOARGS 0x00040 ///< tinfo_t: do not print function argument names +#define PRTYPE_NOARRS 0x00080 ///< tinfo_t: print arguments with #FAI_ARRAY as pointers +#define PRTYPE_NORES 0x00100 ///< tinfo_t: never resolve types (meaningful with PRTYPE_DEF) +#define PRTYPE_RESTORE 0x00200 ///< tinfo_t: print restored types for #FAI_ARRAY and #FAI_STRUCT +#define PRTYPE_NOREGEX 0x00400 ///< do not apply regular expressions to beautify name +#define PRTYPE_COLORED 0x00800 ///< add color tag COLOR_SYMBOL for any parentheses, commas and colons +#define PRTYPE_METHODS 0x01000 ///< tinfo_t: print udt methods +#define PRTYPE_1LINCMT 0x02000 ///< print comments even in the one line mode +#define PRTYPE_HEADER 0x04000 ///< print only type header (only for definitions) +#define PRTYPE_OFFSETS 0x08000 ///< print udt member offsets +#define PRTYPE_MAXSTR 0x10000 ///< limit the output length to 1024 bytes (the output may be slightly longer) +#define PRTYPE_TAIL 0x20000 ///< print only the definition tail (only for definitions, exclusive with PRTYPE_HEADER) +#define PRTYPE_ARGLOCS 0x40000 ///< print function arglocs (not only for usercall) +///@} + +///@} parse_tinfo + + +/// \defgroup named_types Named types +/// functions to work with named types +///@{ + + +/// Get named typeinfo. +/// The returned pointers are pointers to static storage. \n +/// They are valid until free_til(), set_named_type(), del_named_type(), \n +/// rename_named_type(), set_numbered_type(), del_numbered_type(), \n +/// and idb structure/enum manipulation (in other words, until ::til_t is changed). +/// \param ti pointer to type information library +/// \param name name of type +/// \param ntf_flags combination of \ref NTF_ +/// \param type ptr to ptr to output buffer for the type info +/// \param fields ptr to ptr to the field/args names. may be nullptr +/// \param cmt ptr to ptr to the main comment. may be nullptr +/// the comment may has TPOS_REGCMT as its first byte +/// \param fieldcmts ptr to ptr to the field/args comments. may be nullptr +/// \param sclass ptr to storage class +/// \param value ptr to symbol value. for types, ptr to the ordinal number +/// \retval 0 can't find the named type (or name==nullptr) +/// \retval 1 ok, the buffers are filled with information (if not nullptr) +/// \retval 2 ok, found it in a base til + +idaman int ida_export get_named_type( + const til_t *ti, + const char *name, + int ntf_flags, + const type_t **type=nullptr, + const p_list **fields=nullptr, + const char **cmt=nullptr, + const p_list **fieldcmts=nullptr, + sclass_t *sclass=nullptr, + uint32 *value=nullptr); + +/// \defgroup NTF_ Flags for named types +///@{ +#define NTF_TYPE 0x0001 ///< type name +#define NTF_SYMU 0x0008 ///< symbol, name is unmangled ('func') +#define NTF_SYMM 0x0000 ///< symbol, name is mangled ('_func'); + ///< only one of #NTF_TYPE and #NTF_SYMU, #NTF_SYMM can be used +#define NTF_NOBASE 0x0002 ///< don't inspect base tils (for get_named_type) +#define NTF_REPLACE 0x0004 ///< replace original type (for set_named_type) +#define NTF_UMANGLED 0x0008 ///< name is unmangled (don't use this flag) +#define NTF_NOCUR 0x0020 ///< don't inspect current til file (for get_named_type) +#define NTF_64BIT 0x0040 ///< value is 64bit +#define NTF_FIXNAME 0x0080 ///< force-validate the name of the type when setting + ///< (set_named_type, set_numbered_type only) +#define NTF_IDBENC 0x0100 ///< the name is given in the IDB encoding; + ///< non-ASCII bytes will be decoded accordingly + ///< (set_named_type, set_numbered_type only) +#define NTF_CHKSYNC 0x0200 ///< check that synchronization to IDB passed OK + ///< (set_numbered_type, set_named_type) +#define NTF_NO_NAMECHK 0x0400 ///< do not validate type name + ///< (set_numbered_type, set_named_type) +#define NTF_COPY 0x1000 ///< save a new type definition, not a typeref (tinfo_t::set_numbered_type, tinfo_t::set_named_type) +///@} + + +/// See get_named_type() above. +/// \note If the value in the 'ti' library is 32-bit, it will +/// be sign-extended before being stored in the 'value' pointer. + +inline int idaapi get_named_type64( + const til_t *ti, + const char *name, + int ntf_flags, + const type_t **type=nullptr, + const p_list **fields=nullptr, + const char **cmt=nullptr, + const p_list **fieldcmts=nullptr, + sclass_t *sclass=nullptr, + uint64 *value=nullptr) +{ + return get_named_type(ti, name, ntf_flags | NTF_64BIT, + type, fields, cmt, fieldcmts, sclass, (uint32 *)value); +} + + +/// Error codes various tinfo functions: +enum tinfo_code_t +{ + TERR_OK = 0, ///< ok + TERR_SAVE_ERROR = -1, ///< failed to save + TERR_SERIALIZE = -2, ///< failed to serialize + TERR_BAD_NAME = -3, ///< name %s is not acceptable + TERR_BAD_ARG = -4, ///< bad argument + TERR_BAD_TYPE = -5, ///< bad type + TERR_BAD_SIZE = -6, ///< bad size %d + TERR_BAD_INDEX = -7, ///< bad index %d + TERR_BAD_ARRAY = -8, ///< arrays are forbidden as function arguments + TERR_BAD_BF = -9, ///< bitfields are forbidden as function arguments + TERR_BAD_OFFSET = -10, ///< bad member offset %s + TERR_BAD_UNIVAR = -11, ///< unions cannot have variable sized members + TERR_BAD_VARLAST = -12, ///< variable sized member must be the last member in the structure + TERR_OVERLAP = -13, ///< the member overlaps with other members that cannot be deleted + TERR_BAD_SUBTYPE = -14, ///< recursive structure nesting is forbidden + TERR_BAD_VALUE = -15, ///< value 0x%I64X is not acceptable + TERR_NO_BMASK = -16, ///< bitmask 0x%I64X is not found + TERR_BAD_BMASK = -17, ///< Bad enum member mask 0x%I64X. The specified mask should not intersect with any existing mask in the enum. Zero masks are prohibited too + TERR_BAD_MSKVAL = -18, ///< bad bmask and value combination (value=0x%I64X; bitmask 0x%I64X) + TERR_BAD_REPR = -19, ///< bad or incompatible field representation + TERR_GRP_NOEMPTY = -20, ///< could not delete group mask for not empty group 0x%I64X + TERR_DUPNAME = -21, ///< duplicate name %s + TERR_UNION_BF = -22, ///< unions cannot have bitfields + TERR_BAD_TAH = -23, ///< bad bits in the type attributes (TAH bits) + TERR_BAD_BASE = -24, ///< bad base class + TERR_BAD_GAP = -25, ///< bad gap + TERR_NESTED = -26, ///< recursive structure nesting is forbidden + TERR_NOT_COMPAT = -27, ///< the new type is not compatible with the old type + TERR_BAD_LAYOUT = -28, ///< failed to calculate the structure/union layout + TERR_BAD_GROUPS = -29, ///< bad group sizes for bitmask enum + TERR_BAD_SERIAL = -30, ///< enum value has too many serials + TERR_ALIEN_NAME = -31, ///< enum member name is used in another enum + TERR_STOCK = -32, ///< stock type info cannot be modified + TERR_ENUM_SIZE = -33, ///< bad enum size + TERR_NOT_IMPL = -34, ///< not implemented + TERR_TYPE_WORSE = -35, ///< the new type is worse than the old type + TERR_BAD_FX_SIZE = -36, ///< cannot extend struct beyond fixed size + TERR_STRUCT_SIZE = -37, ///< bad fixed structure size + TERR_NOT_FOUND = -38, ///< member not found + TERR_COUNT = 39, +}; + +/// Helper function to convert an error code into a printable string. +/// Additional arguments are handled using the functions from err.h + +idaman const char *ida_export tinfo_errstr(tinfo_code_t code); + + +/// Delete information about a symbol. +/// \param ti type library +/// \param name name of symbol +/// \param ntf_flags combination of \ref NTF_ +/// \return success + +idaman bool ida_export del_named_type(til_t *ti, const char *name, int ntf_flags); + + +/// Enumerate types. +/// \param ti type library. nullptr means the local type library for the current database. +/// \param ntf_flags combination of \ref NTF_ +/// \return Type or symbol names, depending of ntf_flags. Returns mangled names. +/// Never returns anonymous types. To include them, enumerate types by ordinals. + +idaman const char *ida_export first_named_type(const til_t *ti, int ntf_flags); + + +/// \copydoc first_named_type() +/// \param name the current name. the name that follows this one will be returned. + +idaman const char *ida_export next_named_type( + const til_t *ti, + const char *name, + int ntf_flags); + + +/// Copy a named type from one til to another. +/// This function will copy the specified type and all dependent types +/// from the source type library to the destination library. +/// \param dsttil Destination til. It must have original types enabled +/// \param srctil Source til. +/// \param name name of the type to copy +/// \return ordinal number of the copied type. 0 means error +idaman uint32 ida_export copy_named_type( + til_t *dsttil, + const til_t *srctil, + const char *name); + +/// Decorate/undecorate a C symbol name. +/// \param out output buffer +/// \param name name of symbol +/// \param should_decorate true-decorate name, false-undecorate +/// \param cc calling convention +/// \param type name type (nullptr-unknown) +/// \return success +idaman bool ida_export decorate_name( + qstring *out, + const char *name, + bool should_decorate, + callcnv_t cc=CM_CC_UNKNOWN, + const tinfo_t *type = nullptr); + +/// Generic function for decorate_name() (may be used in IDP modules) +idaman bool ida_export gen_decorate_name( + qstring *out, + const char *name, + bool should_decorate, + callcnv_t cc, + const tinfo_t *type); + +/// Function to be overloaded for custom calling conventions +inline bool custom_callcnv_t::decorate_name( + qstring *out, + const char *_name, + bool should_decorate, + callcnv_t /*cc*/, + const tinfo_t &type) const +{ + return gen_decorate_name(out, _name, should_decorate, CM_CC_FASTCALL, &type); +} + +/// Get C or C++ form of the name. +/// \param out output buffer +/// \param name original (mangled or decorated) name +/// \param type name type if known, otherwise nullptr +/// \param ccn_flags one of \ref CCN_ + +idaman ssize_t ida_export calc_c_cpp_name( + qstring *out, + const char *name, + const tinfo_t *type, + int ccn_flags); +/// \defgroup CCN_ C/C++ naming flags +///@{ +#define CCN_C 0x00 // prepare C name +#define CCN_CPP 0x01 // prepare C++ name +///@} + +///@} named_types + +//-------------------------------------------------------------------------- +/// \defgroup numbered_types Numbered types +/// Functions to work with numbered (ordinal) types. +/// Numbered types may be named or anonymous. +/// They are referenced by their ordinal number. Access to them is faster because +/// there is no need to resolve their names. Also, they can stay anonymous +/// and be aliased. They can be used only in the local type library +/// created by IDA (in idati). +///@{ + +/// Enable the use of numbered types in til. +/// Currently it is impossible to disable numbered types once they are enabled + +idaman bool ida_export enable_numbered_types(til_t *ti, bool enable); + + +/// Retrieve a type by its ordinal number + +idaman bool ida_export get_numbered_type( + const til_t *ti, + uint32 ordinal, + const type_t **type=nullptr, + const p_list **fields=nullptr, + const char **cmt=nullptr, + const p_list **fieldcmts=nullptr, + sclass_t *sclass=nullptr); + + +/// Allocate a range of ordinal numbers for new types. +/// \param ti type library +/// \param qty number of ordinals to allocate +/// \return the first ordinal. 0 means failure. + +idaman uint32 ida_export alloc_type_ordinals(til_t *ti, int qty); + + +/// \call2{alloc_type_ordinals,ti,1} + +inline uint32 alloc_type_ordinal(til_t *ti) { return alloc_type_ordinals(ti, 1); } + + +/// Get number of allocated ordinals + 1. +/// If there are no allocated ordinals, return 0. +/// To enumerate all ordinals, use: for ( uint32 i = 1; i < limit; ++i ) +/// \param ti type library; nullptr means the local types for the current database. +/// \return uint32(-1) if ordinals have not been enabled for the til. +/// For local types (idati), ordinals are always enabled. + +idaman uint32 ida_export get_ordinal_limit(const til_t *ti=nullptr); + + +/// Get number of allocated ordinals. +/// \param ti type library; nullptr means the local types for the current database. +/// \return 0 if ordinals have not been enabled for the til. + +inline uint32 get_ordinal_count(const til_t *ti=nullptr) +{ + uint32 maxord = get_ordinal_limit(ti); + return maxord == 0 || maxord == uint32(-1) ? 0 : maxord - 1; +} + + +/// Delete a numbered type + +idaman bool ida_export del_numbered_type(til_t *ti, uint32 ordinal); + + +/// Create a type alias. +/// Redirects all references to source type to the destination type. +/// This is equivalent to instantaneous replacement all references to srctype by dsttype. + +idaman bool ida_export set_type_alias(til_t *ti, uint32 src_ordinal, uint32 dst_ordinal); + + +/// Find the final alias destination. +/// If the ordinal has not been aliased, return the specified ordinal itself +/// If failed, returns 0. + +idaman uint32 ida_export get_alias_target(const til_t *ti, uint32 ordinal); + + +/// Get type ordinal by its name + +idaman int32 ida_export get_type_ordinal(const til_t *ti, const char *name); + +/// Get type name (if exists) by its ordinal. +/// If the type is anonymous, returns "". If failed, returns nullptr + +idaman const char *ida_export get_numbered_type_name(const til_t *ti, uint32 ordinal); + + +/// Create anonymous name for numbered type. This name can be used +/// to reference a numbered type by its ordinal +/// Ordinal names have the following format: '#' + set_de(ord) +/// Returns: -1 if error, otherwise the name length + +idaman ssize_t ida_export create_numbered_type_name(qstring *buf, int32 ord); + + +/// Check if the name is an ordinal name. +/// Ordinal names have the following format: '#' + set_de(ord) + +idaman bool ida_export is_ordinal_name(const char *name, uint32 *ord=nullptr); + + +/// Generate a name like $hex_numbers based on the field types and names + +idaman void ida_export build_anon_type_name( + qstring *buf, + const type_t *type, + const p_list *fields); + + +/// Compact numbered types to get rid of empty slots. +/// \param ti type library to compact +/// \param min_ord minimal ordinal number to start to compact. lower +/// ordinals are not modified +/// \param p_ordmap the resulting mapping +/// (for example, the new ordinal of min_ord will be in ordmap[0]) +/// \param flags reserved +/// \return number of freed type slots + +idaman int ida_export compact_numbered_types( + til_t *ti, + uint32 min_ord=0, + intvec_t *p_ordmap=nullptr, + int flags=0); + + +/// Check if a struct/union type is choosable +/// \param ti type library +/// \param ordinal ordinal number of a UDT type +idaman bool ida_export is_type_choosable(const til_t *ti, uint32 ordinal); + +/// Enable/disable 'choosability' flag for a struct/union type +/// \param ti type library +/// \param ordinal ordinal number of a UDT type +/// \param value flag value +idaman void ida_export set_type_choosable(til_t *ti, uint32 ordinal, bool value); + +///@} numbered_types + +//-------------------------------------------------------------------------- +/// \defgroup vftable_types Link between vftable types and addresses +///@{ + +/// Get address of a virtual function table. +/// \param ordinal ordinal number of a vftable type. +/// \return address of the corresponding virtual function table in the current database. + +idaman ea_t ida_export get_vftable_ea(uint32 ordinal); + + +/// Get ordinal number of the virtual function table. +/// \param vftable_ea address of a virtual function table. +/// \return ordinal number of the corresponding vftable type. 0 - failure. + +idaman uint32 ida_export get_vftable_ordinal(ea_t vftable_ea); + + +/// Set the address of a vftable instance for a vftable type. +/// \param vftable_ea address of a virtual function table. +/// \param ordinal ordinal number of the corresponding vftable type. +/// \return success + +idaman bool ida_export set_vftable_ea(uint32 ordinal, ea_t vftable_ea); + + +/// Delete the address of a vftable instance for a vftable type. +/// \param ordinal ordinal number of a vftable type. +/// \return success + +inline bool del_vftable_ea(uint32 ordinal) { return set_vftable_ea(ordinal, BADADDR); } + + +///@} vftable_types + +//-------------------------------------------------------------------------- +// ALIGNMENT + +/// Get default alignment for structure fields. +/// \return one of 1,2,4,8,... + +inline size_t get_default_align() { return inf_get_cc_defalign(); } + + +/// Get alignment delta for the a structure field. +/// \param cur_tot_size the structure size calculated so far +/// \param elem_size size of the current field. +/// the whole structure should be calculated +/// \param algn the structure alignment (0,1,2,4,8...) + +inline THREAD_SAFE void align_size(size_t &cur_tot_size, size_t elem_size, size_t algn) +{ + size_t al = elem_size; + if ( algn != 0 && algn < al ) + al = algn; + cur_tot_size = align_up(cur_tot_size, al); +} + +/// Dereference a pointer. +/// \param[out] ptr_ea in/out parameter +/// - in: address of the pointer +/// - out: the pointed address +/// \param tif type of the pointer +/// \param[out] closure_obj closure object (not used yet) +/// \return success + +idaman bool ida_export deref_ptr( + ea_t *ptr_ea, + const tinfo_t &tif, + ea_t *closure_obj=nullptr); + + +/// Remove pointer of a type. +/// (i.e. convert "char *" into "char"). +/// Optionally remove the "lp" (or similar) prefix of the input name. +/// If the input type is not a pointer, then fail. + +idaman bool ida_export remove_tinfo_pointer(tinfo_t *tif, const char **pname, const til_t *til=nullptr); + +/// Load a til file and add it the database type libraries list. +/// IDA will also apply function prototypes for matching function names. +/// \param name til name +/// \param flags combination of \ref ADDTIL_F +/// \return one of \ref ADDTIL_R + +idaman int ida_export add_til(const char *name, int flags); + +/// \defgroup ADDTIL_F Load TIL flags +/// passed as 'flags' parameter to add_til() +///@{ +#define ADDTIL_DEFAULT 0x0000 ///< default behavior +#define ADDTIL_INCOMP 0x0001 ///< load incompatible tils +#define ADDTIL_SILENT 0x0002 ///< do not ask any questions +///@} + +/// \defgroup ADDTIL_R Load TIL result codes +/// return values for add_til() +///@{ +#define ADDTIL_FAILED 0 ///< something bad, the warning is displayed +#define ADDTIL_OK 1 ///< ok, til is loaded +#define ADDTIL_COMP 2 ///< ok, but til is not compatible with the current compiler +#define ADDTIL_ABORTED 3 ///< til was not loaded (incompatible til rejected by user) +///@} + + +/// Unload a til file + +idaman bool ida_export del_til(const char *name); + + +/// Apply the specified named type to the address. +/// \param ea linear address +/// \param name the type name, e.g. "FILE" +/// \return success + +idaman bool ida_export apply_named_type(ea_t ea, const char *name); + + +/// Apply the specified type to the specified address. +/// This function sets the type and tries to convert the item at the specified +/// address to conform the type. +/// \param ea linear address +/// \param tif new type +/// \param flags combination of \ref TINFO_ +/// \return success + +idaman bool ida_export apply_tinfo( + ea_t ea, + const tinfo_t &tif, + uint32 flags); + +/// \defgroup TINFO_ Apply tinfo flags +/// passed as 'flags' parameter to apply_tinfo() +///@{ +#define TINFO_GUESSED 0x0000 ///< this is a guessed type +#define TINFO_DEFINITE 0x0001 ///< this is a definite type +#define TINFO_DELAYFUNC 0x0002 ///< if type is a function and no function exists at ea, + ///< schedule its creation and argument renaming to auto-analysis, + ///< otherwise try to create it immediately +#define TINFO_STRICT 0x0004 ///< never convert given type to another one before applying +///@} + + +/// Apply the specified type to the address. +/// This function parses the declaration and calls apply_tinfo() +/// \param til type library +/// \param ea linear address +/// \param decl type declaration in C form +/// \param flags flags to pass to apply_tinfo (#TINFO_DEFINITE is always passed) +/// \return success + +idaman bool ida_export apply_cdecl(til_t *til, ea_t ea, const char *decl, int flags=0); + + +/// Apply the type of the called function to the calling instruction. +/// This function will append parameter comments and rename the local +/// variables of the calling function. It also stores information about +/// the instructions that initialize call arguments in the database. +/// Use get_arg_addrs() to retrieve it if necessary. Alternatively it is +/// possible to hook to processor_t::arg_addrs_ready event. +/// \param caller linear address of the calling instruction. +/// must belong to a function. +/// \param tif type info +/// \return success + +idaman bool ida_export apply_callee_tinfo(ea_t caller, const tinfo_t &tif); + + +/// Retrieve argument initialization addresses. +/// This function retrieves information about argument addresses. +/// This information is stored in the database by apply_callee_tinfo(). +/// \param out linear addresses of the instructions that load call arguments +/// \param caller address of the call instruction +/// \return success + +idaman bool ida_export get_arg_addrs(eavec_t *out, ea_t caller); + + +/// Apply the specified type and name to the address. +/// This function checks if the address already has a type. If the old type \n +/// does not exist or the new type is 'better' than the old type, then the \n +/// new type will be applied. A type is considered better if it has more \n +/// information (e.g. ::BTMT_STRUCT is better than ::BT_INT). \n +/// The same logic is with the name: if the address already have a meaningful \n +/// name, it will be preserved. Only if the old name does not exist or it \n +/// is a dummy name like byte_123, it will be replaced by the new name. +/// \param dea linear address +/// \param tif new type +/// \param name new name for the address +/// \return success + +idaman bool ida_export apply_once_tinfo_and_name( + ea_t dea, + const tinfo_t &tif, + const char *name); + + +// To retrieve the type information attach to an address, use get_tinfo() function +// (see nalt.hpp) + + +/// Generate a type information about the id from the disassembly. +/// id can be a structure/union/enum id or an address. +/// \return one of \ref GUESS_ + +idaman int ida_export guess_tinfo(tinfo_t *out, tid_t id); + +/// \defgroup GUESS_ Guess tinfo codes +/// return values for guess_tinfo() +///@{ +#define GUESS_FUNC_FAILED 0 ///< couldn't guess the function type +#define GUESS_FUNC_TRIVIAL 1 ///< the function type doesn't have interesting info +#define GUESS_FUNC_OK 2 ///< ok, some non-trivial information is gathered +///@} + + +// The following functions should eventually be replaced by exported functions +#ifndef __KERNEL__ +/// Set include directory path the target compiler +inline void set_c_header_path(const char *incdir) { setinf_buf(INF_H_PATH, incdir); } + +/// Get the include directory path of the target compiler +inline ssize_t get_c_header_path(qstring *buf) { return getinf_str(buf, INF_H_PATH); } + +/// Set predefined macros for the target compiler +inline void set_c_macros(const char *macros) { setinf_buf(INF_C_MACROS, macros); } + +/// Get predefined macros for the target compiler +inline ssize_t get_c_macros(qstring *buf) { return getinf_str(buf, INF_C_MACROS); } +#endif + +//------------------------------------------------------------------------ +// HIGH LEVEL FUNCTIONS TO SUPPORT TILS IN THE IDA KERNEL + +/// Pointer to the local type library - this til is private for each IDB file +/// Functions that accept til_t* default to `idati` when is nullptr provided. + +idaman til_t *ida_export get_idati(); + + +/// Extract information from a tinfo_t. +/// \param[out] out_size size of tif +/// \param[out] out_flags description of type using flags64_t +/// \param[out] out_mt info for non-scalar types +/// \param tif the type to inspect +/// \param[out] out_alsize alignment + +idaman bool ida_export get_idainfo_by_type( + size_t *out_size, + flags64_t *out_flags, + opinfo_t *out_mt, + const tinfo_t &tif, + size_t *out_alsize=nullptr); + + +/// Get tinfo object that corresponds to data flags +/// \param[out] out type info +/// \param flags simple flags (byte, word, ..., zword) + +idaman bool ida_export get_tinfo_by_flags(tinfo_t *out, flags64_t flags); + +//------------------------------------------------------------------------ +// Type information object: tinfo_t + +struct ptr_type_data_t; +struct udt_type_data_t; +struct enum_type_data_t; +struct array_type_data_t; +struct typedef_type_data_t; +struct bitfield_type_data_t; +struct udtmembervec_t; + +/// IDs for common types +enum stock_type_id_t +{ + STI_PCHAR, ///< char * + STI_PUCHAR, ///< uint8 * + STI_PCCHAR, ///< const char * + STI_PCUCHAR, ///< const uint8 * + STI_PBYTE, ///< _BYTE * + STI_PINT, ///< int * + STI_PUINT, ///< unsigned int * + STI_PVOID, ///< void * + STI_PPVOID, ///< void ** + STI_PCVOID, ///< const void * + STI_ACHAR, ///< char[] + STI_AUCHAR, ///< uint8[] + STI_ACCHAR, ///< const char[] + STI_ACUCHAR, ///< const uint8[] + STI_FPURGING, ///< void __userpurge(int) + STI_FDELOP, ///< void __cdecl(void *) + STI_MSGSEND, ///< void *(void *, const char *, ...) + STI_AEABI_LCMP, ///< int __fastcall __pure(int64 x, int64 y) + STI_AEABI_ULCMP, ///< int __fastcall __pure(uint64 x, uint64 y) + STI_DONT_USE, ///< unused stock type id; should not be used + STI_SIZE_T, ///< size_t + STI_SSIZE_T, ///< ssize_t + STI_AEABI_MEMCPY, ///< void __fastcall(void *, const void *, size_t) + STI_AEABI_MEMSET, ///< void __fastcall(void *, size_t, int) + STI_AEABI_MEMCLR, ///< void __fastcall(void *, size_t) + STI_RTC_CHECK_2, ///< int16 __fastcall(int16 x) + STI_RTC_CHECK_4, ///< int32 __fastcall(int32 x) + STI_RTC_CHECK_8, ///< int64 __fastcall(int64 x) + STI_COMPLEX64, ///< struct complex64_t { float real, imag; } + STI_COMPLEX128, ///< struct complex128_t { double real, imag; } + STI_PUNKNOWN, ///< _UNKNOWN * + STI_LAST +}; + +/// Constants to be used the editing methods +/// \defgroup ETF_ type changing flags +///@{ +enum etf_flag_t : uint +{ + ETF_NO_SAVE = 0x00000001, ///< don't save to til (normally typerefs are saved to til) + ///< A call with ETF_NO_SAVE must be followed by a call + ///< without it. Otherwise there may be inconsistencies + ///< between the memory and the type library. + ETF_NO_LAYOUT = 0x00000002, ///< don't calc type layout before editing + ETF_MAY_DESTROY = 0x00000004, ///< may destroy other members + ETF_COMPATIBLE = 0x00000008, ///< new type must be compatible with the old + ETF_FUNCARG = 0x00000010, ///< udm - member is a function argument (cannot create arrays) + ETF_FORCENAME = 0x00000020, ///< anyway use name, see below for more usage description + ETF_AUTONAME = 0x00000040, ///< udm - generate a member name if was not specified (add_udm, set_udm_type) + ETF_BYTIL = 0x00000080, ///< udm - new type was created by the type subsystem + ETF_NO_ARRAY = 0x00000100, ///< add_udm, set_udm_type - do not convert type to an array on the size mismatch +}; +///@} + +/// Constants to be used with get_udt_details() +enum gtd_udt_t +{ + GTD_CALC_LAYOUT = 0, ///< calculate udt layout + GTD_NO_LAYOUT = BTM_VOLATILE, ///< don't calculate udt layout + ///< please note that udt layout may have been + ///< calculated earlier + GTD_DEL_BITFLDS = BTM_CONST, ///< delete udt bitfields +}; + +/// Constants to be used with get_func_details() +enum gtd_func_t +{ + GTD_CALC_ARGLOCS = 0, ///< calculate func arg locations + GTD_NO_ARGLOCS = BTM_VOLATILE, ///< don't calculate func arg locations + ///< please note that the locations may have been + ///< calculated earlier +}; + +/// Constants to be used with get_size() +enum gts_code_t +{ + GTS_NESTED = 0x01, ///< nested type (embedded into a udt) + GTS_BASECLASS = 0x02, ///< is baseclass of a udt +}; + +/// \defgroup SUDT_ UDT serialization flags +/// passed as 'sudt_flags' parameter of helpers declared in #DECLARE_TINFO_HELPERS +///@{ +#define SUDT_SORT 0x0001 ///< fields are not sorted by offset, sort them first +#define SUDT_ALIGN 0x0002 ///< recalculate field alignments, struct packing, etc + ///< to match the offsets and size info +#define SUDT_GAPS 0x0004 ///< allow to fill gaps with additional members (_BYTE[]) +#define SUDT_UNEX 0x0008 ///< references to nonexistent member types are acceptable; + ///< in this case it is better to set the corresponding + ///< udm_t::fda field to the type alignment. If this + ///< field is not set, ida will try to guess the alignment. +#define SUDT_FAST 0x0010 ///< serialize without verifying offsets and alignments + +#define SUDT_CONST 0x0040 ///< only for serialize_udt: make type const +#define SUDT_VOLATILE 0x0080 ///< only for serialize_udt: make type volatile + +#define SUDT_TRUNC 0x0100 ///< serialize: truncate useless strings from fields, fldcmts +#define SUDT_SERDEF 0x0200 ///< serialize: if a typeref, serialize its definition +///@} + +typedef uint64 typid_t; + +/// Macro to declare common tinfo_t related functions +#define DECLARE_TINFO_HELPERS(decl)\ +decl void ida_export copy_tinfo_t(tinfo_t *_this, const tinfo_t &r); \ +decl bool ida_export detach_tinfo_t(tinfo_t *_this); \ +decl void ida_export clear_tinfo_t(tinfo_t *_this);\ +decl bool ida_export create_tinfo(tinfo_t *_this, type_t bt, type_t bt2, void *ptr);\ +decl int ida_export verify_tinfo(typid_t typid);\ +decl bool ida_export get_tinfo_details(typid_t typid, type_t bt2, void *buf);\ +decl size_t ida_export get_tinfo_size(uint32 *p_effalign, typid_t typid, int gts_code);\ +decl size_t ida_export get_tinfo_pdata(void *outptr, typid_t typid, int what);\ +decl size_t ida_export get_tinfo_property(typid_t typid, int gta_prop);\ +decl size_t ida_export get_tinfo_property4(typid_t typid, int gta_prop, size_t p1, size_t p2, size_t p3, size_t p4);\ +decl size_t ida_export set_tinfo_property(tinfo_t *tif, int sta_prop, size_t x);\ +decl size_t ida_export set_tinfo_property4(tinfo_t *tif, int sta_prop, size_t p1, size_t p2, size_t p3, size_t p4);\ +decl bool ida_export serialize_tinfo(qtype *type, qtype *fields, qtype *fldcmts, const tinfo_t *tif, int sudt_flags);\ +decl bool ida_export deserialize_tinfo(tinfo_t *tif, const til_t *til, const type_t **ptype, const p_list **pfields, const p_list **pfldcmts, const char *cmt);\ +decl int ida_export find_tinfo_udt_member(udm_t *udm, typid_t typid, int strmem_flags);\ +decl bool ida_export print_tinfo(qstring *result, const char *prefix, int indent, int cmtindent, int flags, const tinfo_t *tif, const char *name, const char *cmt);\ +decl const char *ida_export dstr_tinfo(const tinfo_t *tif);\ +decl int ida_export visit_subtypes(struct tinfo_visitor_t *visitor, struct type_mods_t *out, const tinfo_t &tif, const char *name, const char *cmt);\ +decl bool ida_export compare_tinfo(typid_t t1, typid_t t2, int tcflags);\ +decl int ida_export lexcompare_tinfo(typid_t t1, typid_t t2, int);\ +decl bool ida_export get_stock_tinfo(tinfo_t *tif, stock_type_id_t id);\ +decl uint64 ida_export read_tinfo_bitfield_value(typid_t typid, uint64 v, int bitoff);\ +decl uint64 ida_export write_tinfo_bitfield_value(typid_t typid, uint64 dst, uint64 v, int bitoff);\ +decl bool ida_export get_tinfo_attr(typid_t typid, const qstring &key, bytevec_t *bv, bool all_attrs);\ +decl bool ida_export set_tinfo_attr(tinfo_t *tif, const type_attr_t &ta, bool may_overwrite);\ +decl bool ida_export del_tinfo_attr(tinfo_t *tif, const qstring &key, bool make_copy);\ +decl bool ida_export get_tinfo_attrs(typid_t typid, type_attrs_t *tav, bool include_ref_attrs);\ +decl bool ida_export set_tinfo_attrs(tinfo_t *tif, type_attrs_t *ta);\ +decl uint32 ida_export score_tinfo(const tinfo_t *tif);\ +decl tinfo_code_t ida_export save_tinfo(tinfo_t *tif, til_t *til, size_t ord, const char *name, int ntf_flags);\ +decl bool ida_export append_tinfo_covered(rangeset_t *out, typid_t typid, uint64 offset);\ +decl bool ida_export calc_tinfo_gaps(rangeset_t *out, typid_t typid);\ +decl bool ida_export name_requires_qualifier(qstring *out, typid_t typid, const char *name, uint64 offset);\ +decl bool ida_export value_repr_t__from_opinfo(value_repr_t *_this, flags64_t flags, aflags_t afl, const opinfo_t *opinfo, const array_parameters_t *ap); \ +decl size_t ida_export value_repr_t__print_(const value_repr_t *_this, qstring *result, bool colored); \ +decl bool ida_export value_repr_t__parse_value_repr(value_repr_t *_this, const qstring &attr, type_t target_type); \ +decl ssize_t ida_export udt_type_data_t__find_member(const udt_type_data_t *_this, udm_t *udm, int strmem_flags); \ +decl ssize_t ida_export udt_type_data_t__get_best_fit_member(const udt_type_data_t *_this, asize_t disp); \ +decl uchar ida_export enum_type_data_t__get_max_serial(const enum_type_data_t *ei, uint64 value); \ +decl tinfo_code_t ida_export enum_type_data_t__set_value_repr(enum_type_data_t *ei, const value_repr_t &repr); \ +decl tinfo_code_t ida_export enum_type_data_t__get_value_repr(const enum_type_data_t *ei, value_repr_t *repr); \ +decl void ida_export tinfo_get_innermost_udm(tinfo_t *itif, const tinfo_t *tif, uint64 offset, size_t *udm_idx, uint64 *bit_offset, bool return_member_type); \ +decl ssize_t ida_export get_udm_by_tid(tinfo_t *tif, udm_t *udm, tid_t tid); \ +decl ssize_t ida_export get_edm_by_tid(tinfo_t *tif, edm_t *edm, tid_t tid); \ +decl bool ida_export get_type_by_tid(tinfo_t *tif, tid_t tid); \ +decl tid_t ida_export get_tinfo_tid(tinfo_t *tif, bool force_tid); \ +decl ssize_t ida_export get_tinfo_by_edm_name(tinfo_t *tif, const til_t *til, const char *mname); \ +decl ssize_t ida_export get_frame_var(tinfo_t *tif, sval_t *actval, const insn_t &insn, const op_t *x, sval_t v); \ +decl bool ida_export tinfo_get_func_frame(tinfo_t *tif, const func_t *pfn); \ + +DECLARE_TINFO_HELPERS(idaman) + +/*! \defgroup tf_nontrivial Nontrivial types + \ingroup tf + bits 0..5: base type \n + bits 6..7: const & volatile bits \n + bit 8: 'is_typeref' bit \n + bits 9..63: type detail idx +*/ +///@{ +const int FIRST_NONTRIVIAL_TYPID = 0x100; ///< Denotes the first bit describing a nontrivial type +const int TYPID_ISREF = 0x100; ///< Identifies that a type that is a typeref +const int TYPID_SHIFT = 9; ///< First type detail bit +///@} + +/// Primary mechanism for managing type information +class tinfo_t // #tinfo_t #tif +{ + typid_t typid; /// see \ref tf_nontrivial + bool create_type(type_t decl_type, type_t bt2, void *details) + { + return create_tinfo(this, decl_type, bt2, details); + } + /// Get the type details. + /// The information is copied to the user-supplied buffer. + /// Also check out convenience functions below (get_ptr_details, etc), they work faster because + /// they do not copy the entire type info but only the desired part of it. + bool get_type_details(type_t bt2, void *buf) const { return get_tinfo_details(typid, bt2, buf); } + void copy(const tinfo_t &r) { copy_tinfo_t(this, r); } + DECLARE_TINFO_HELPERS(friend) + friend struct type_detail_t; + friend tinfo_t remove_pointer(const tinfo_t &tif); + /// Various type properties (properties are 64-bit scalar values) + enum gta_prop_t + { + GTA_DECLALIGN, ///< declared alignment + GTA_RESOLVE, ///< real type (fully resolve eventual type references) + GTA_REALTYPE, ///< real type (do not fully resolve type refs) + GTA_TYPE_SIGN, ///< get type sign + GTA_FROM_SUBTIL, ///< is from a subtil (not from main til) + GTA_IS_FORWARD, ///< is forward declaration? + GTA_IS_FUNCPTR, ///< is a pointer to a function? + GTA_ORDINAL, ///< get initial type ordinal + GTA_FINAL_ORDINAL, ///< get final (resolved) type ordinal + GTA_PTR_OBJ, ///< ptr: pointed type + GTA_SAFE_PTR_OBJ, ///< ptr: pointed type or type itself + GTA_ARRAY_ELEM, ///< array: array element + GTA_ARRAY_NELEMS, ///< array: number of elements + GTA_PTRARR_SUBTIF, ///< ptr&array: pointed object or array element + GTA_PTRARR_SIZE, ///< ptr&array: get size of subtype + GTA_UNPADDED_SIZE, ///< udt: sizeof baseclass when embedded into a derived class + GTA_UDT_NMEMBERS, ///< udt: get number of udt members + GTA_IS_SMALL_UDT, ///< udt: is small udt (can be passed in regs) + GTA_ONEMEM_TYPE, ///< udt&array: object consisting of one member: type of the member + GTA_ENUM_BASE_TYPE, ///< enum: get enum base type + GTA_FUNC_CC, ///< func: calling convention + GTA_PURGED_BYTES, ///< func: number of purged bytes + GTA_IS_HIGH_TYPE, ///< func: is high type + GTA_FUNC_NARGS, ///< func: number of arguments + GTA_FUNC_RET, ///< func: get function return type + GTA_FUNC_ARG, ///< func: get type of function arg + GTA_LAST_FUNC_ARG = GTA_FUNC_ARG + 255, + GTA_IS_SSE_TYPE, ///< is a SSE vector type? + GTA_IS_ANON_UDT, ///< is anonymous struct/union? + GTA_OBSOLETE1, + GTA_HAS_VFTABLE, ///< has vftable? + GTA_IS_SHIFTED_PTR, ///< is a shifted pointer? + GTA_IS_VARSTRUCT, ///< is a variable-size structure? + GTA_IS_VARMEMBER, ///< is a variable member type? + GTA_IS_TYPEDEF, ///< is a typedef? + GTA_FINAL_ELEM, ///< if array, skip possible arrays, return a non-array type + GTA_FORWARD_TYPE, ///< if a forward declaration, return either BTMT_STRUCT/UNION/ENUM + GTA_BITMASK, ///< enum: is bitmask or regular enum \ref enum_type_data_t::is_bf() + GTA_ENUM_RADIX, ///< enum: get enum radix \ref enum_type_data_t::get_enum_radix() + GTA_EDM, ///< enum: get enum type member by index + GTA_EDM_BYVAL, ///< enum: find enum type member by value/serial/bmask + GTA_EDM_BYNAME, ///< enum: find enum type member by name + GTA_HAS_UNION, ///< has members of type "union"? + GTA_UDM_TID, ///< udt: get member TID + GTA_ALIAS, ///< get type alias + GTA_EDM_TID, ///< enum: get enum member tid + GTA_FRAME_FUNC, ///< frame: get function address for the frame + GTA_UDM_IS_BYTIL, ///< udm: was the member created due to the type system + GTA_OBSOLETE2, + GTA_EDT_NMEMBERS, ///< enum: get number of enum members + GTA_ENUM_WIDTH, ///< enum: get enum width \ref enum_type_data_t::calc_nbytes() + GTA_ENUM_REPR, ///< enum: get enum value representation + GTA_UDT_BITS, ///< udt: get udt_type_data_t::taudt_bits + }; + enum sta_prop_t ///< set type property + { + STA_DECLALIGN, ///< set declared alignment + STA_TYPE_SIGN, ///< set type sign + STA_UDT_ALIGN, ///< calculate udt field alignments + STA_UDT_METHODS, ///< set udt member functions + STA_RENAME, ///< set type name + STA_COMMENT, ///< set type comment + STA_CLR_MODIFS, ///< clear 'const/volatile' bits + STA_SET_SDA, ///< udt: set struct alignment + STA_SET_PACK, ///< udt: set struct packing + STA_ADD_UDM, ///< udt: add a struct member + STA_DEL_UDMS, ///< udt: del struct members + STA_UDM_NAME, ///< udt: rename a struct member + STA_UDM_TYPE, ///< udt: set type of a struct member + STA_UDM_CMT, ///< udt: set comment of a struct member + STA_UDM_REPR, ///< udt: set repr of a struct member + STA_EXPAND_UDT, ///< udt: expand/shrink struct + STA_ENUM_WIDTH, ///< enum: set the width of enum base type + STA_ENUM_SIGN, ///< enum: set enum sign + STA_BITMASK, ///< enum: make/unmake enum a bitmask + STA_ENUM_REPR, ///< enum: set enum base repr + STA_ADD_EDM, ///< enum: add enum member + STA_DEL_EDMS, ///< enum: del enum members + STA_EDM_NAME, ///< enum: rename enum member + STA_EDM_CMT, ///< enum: set comment of an enum member + STA_EDIT_EDM, ///< enum: change constant value and/or bitmask + STA_ALIAS, ///< set type alias + STA_ALIGNMENT, ///< set type alignment + STA_UDM_SET_BYTIL, ///< udm: the member is created due to the type system + STA_FIXED_STRUCT, ///< struct: use fixed member offsets + STA_STRUCT_SIZE, ///< struct: set struct size (only for fixed structs) + STA_FUNCARG_NAME, ///< func: rename a function argument + STA_FUNCARG_TYPE, ///< func: set type of a function argument + STA_FUNC_RETTYPE, ///< func: set function return type + STA_DEL_FUNCARGS, ///< func: del function arguments + STA_ADD_FUNCARG, ///< func: add function argument + STA_FUNC_CC, ///< func: set calling convention + STA_ENUM_RADIX, ///< enum: set enum radix + STA_FUNCARG_LOC, ///< func: set argument location + STA_FUNC_RETLOC, ///< func: set location of function return value + STA_TUPLE, ///< struct: set as tuple + }; + enum gta_pdata_t ///< get info returned by pointer + { + GTP_NAME, ///< get referenced name + GTP_NEXT_NAME, ///< get immediately next referenced name + GTP_FINAL_NAME, ///< get final referenced name + GTP_TIL, ///< get type library + GTP_UDT_METHODS, ///< get udt member functions + GTP_COMMENT, ///< get type comment + GTP_RPTCMT, ///< get repeatable type comment + GTP_BIT_BUCKETS, ///< get bit buckets + GTP_NICE_NAME, ///< get the referenced name and apply regular expressions to beautify the name + }; + +public: + /// Constructor + tinfo_t() : typid(BT_UNK) {} + /// Constructor - can only be used to initialize simple types! + explicit tinfo_t(type_t decl_type) : typid(decl_type) {} + /// Constructor - will attempt to parse the provided C declaration + explicit tinfo_t(const char *decl, til_t *til=nullptr, int pt_flags=0) + : typid(0) + { + parse(decl, til, pt_flags); + } + + /// Constructor + tinfo_t(const tinfo_t &r) : typid(0) { copy(r); } + /// Copy contents of given tinfo into this one + tinfo_t &operator=(const tinfo_t &r) { copy(r); return *this; } + /// Destructor + ~tinfo_t() { clear(); } + /// Clear contents of this tinfo, and remove from the type system + void clear() { clear_tinfo_t(this); } + /// Assign this = r and r = this + void swap(tinfo_t &r) { typid_t tmp = typid; typid = r.typid; r.typid = tmp; } + DEFINE_MEMORY_ALLOCATION_FUNCS() + + /// Create a tinfo_t object for an existing named type. + /// \param til type library to use + /// \param name name of the type to link to + /// \param decl_type if the reference was explicitly specified with the type tag + /// (::BTF_STRUCT/::BTF_UNION/::BTF_ENUM) you may specify it. + /// the kernel will accept only the specified tag after resolving + /// the type. If the resolved type does not correspond to the + /// explicitly specified tag, the type will be considered as undefined + /// \param resolve true: immediately resolve the type and return success code. + /// false: return true but do not immediately resolve the type + /// \param try_ordinal true: try to replace name reference by an ordinal reference + inline bool get_named_type( + const til_t *til, + const char *name, + type_t decl_type=BTF_TYPEDEF, + bool resolve=true, + bool try_ordinal=true); + + inline bool get_named_type(const char *name, type_t decl_type=BTF_TYPEDEF, bool resolve=true, bool try_ordinal=true) { return get_named_type(nullptr, name, decl_type, resolve, try_ordinal); } + + /// Create a tinfo_t object for an existing ordinal type. + /// \param til type library to use + /// \param ordinal number of the type to link to + /// \param decl_type if the reference was explicitly specified with the type tag + /// (BTF_STRUCT/BTF_UNION/BTF_ENUM) you may specify it. + /// the kernel will accept only the specified tag after resolving + /// the type. If the resolved type does not correspond to the + /// explicitly specified tag, the type will be considered as undefined + /// \param resolve true: immediately resolve the type and return success code + /// false: return true but do not immediately resolve the type + inline bool get_numbered_type( + const til_t *til, + uint32 ordinal, + type_t decl_type=BTF_TYPEDEF, + bool resolve=true); + + inline bool get_numbered_type(uint32 ordinal, type_t decl_type=BTF_TYPEDEF, bool resolve=true) { return get_numbered_type(nullptr, ordinal, decl_type, resolve); } + + /// Detach tinfo_t from the underlying type. + /// After calling this finction, tinfo_t will lose its link to the + /// underlying named or numbered type (if any) and will become a reference + /// to a unique type. After that, any modifications to tinfo_t will affect + /// only its type. + bool detach() { return detach_tinfo_t(this); } + + /// Serialize tinfo_t object into a type string. + bool serialize( + qtype *type, + qtype *fields=nullptr, + qtype *fldcmts=nullptr, + int sudt_flags=SUDT_FAST|SUDT_TRUNC) const + { + return serialize_tinfo(type, fields, fldcmts, this, sudt_flags); + } + + /// Deserialize a type string into a tinfo_t object + bool deserialize( + const til_t *til, + const type_t **ptype, + const p_list **pfields=nullptr, + const p_list **pfldcmts=nullptr, + const char *cmt=nullptr) + { + return deserialize_tinfo(this, til, ptype, pfields, pfldcmts, cmt); + } + /// \copydoc deserialize() + bool deserialize( + const til_t *til, + const qtype *ptype, + const qtype *pfields=nullptr, + const qtype *pfldcmts=nullptr, + const char *cmt=nullptr) + { + const type_t *tp = ptype->begin(); + const p_list *fp = pfields == nullptr ? nullptr : pfields->begin(); + const p_list *cp = pfldcmts == nullptr ? nullptr : pfldcmts->begin(); + const p_list **pfp = fp == nullptr ? nullptr : &fp; + const p_list **pcp = cp == nullptr ? nullptr : &cp; + return deserialize(til, &tp, pfp, pcp, cmt); + } + + /// Is the type object correct?. + /// It is possible to create incorrect types. For example, we can define a + /// function that returns an enum and then delete the enum type. + /// If this function returns false, the type should not be used in + /// disassembly. Please note that this function does not verify all + /// involved types: for example, pointers to undefined types are permitted. + bool is_correct() const { return verify_tinfo(typid) == 0; } + + /// Get the resolved base type. + /// Deserialization options: + /// - if full=true, the referenced type will be deserialized fully, + /// this may not always be desirable (slows down things) + /// - if full=false, we just return the base type, the referenced type will be + /// resolved again later if necessary + /// (this may lead to multiple resolvings of the same type) + /// imho full=false is a better approach because it does not perform + /// unnecessary actions just in case. however, in some cases the caller knows + /// that it is very likely that full type info will be required. in those cases + /// full=true makes sense + type_t get_realtype(bool full=false) const { return (type_t)get_tinfo_property(typid, full ? GTA_RESOLVE : GTA_REALTYPE); } + + /// Get declared type (without resolving type references; they are returned as is). + /// Obviously this is a very fast function and should be used instead of get_realtype() + /// if possible. + /// Please note that for typerefs this function will return BTF_TYPEDEF. + /// To determine if a typeref is a typedef, use is_typedef() + THREAD_SAFE type_t get_decltype() const { return type_t(typid); } + + /// Was tinfo_t initialized with some type info or not? + THREAD_SAFE bool empty() const { return get_decltype() == BT_UNK; } + + /// Is the type really present? (not a reference to a missing type, for example) + bool present() const { return get_realtype() != BT_UNK; } + + /// Get the type size in bytes. + /// \param p_effalign buffer for the alignment value + /// \param gts_code combination of GTS_... constants + /// \return ::BADSIZE in case of problems + size_t get_size(uint32 *p_effalign=nullptr, int gts_code=0) const { return get_tinfo_size(p_effalign, typid, gts_code); } + + /// Get the type size in bytes without the final padding, in bytes. + /// For some UDTs get_unpadded_size() != get_size() + size_t get_unpadded_size() const { return get_tinfo_property(typid, GTA_UNPADDED_SIZE); } + + /// Get type alignment + /// This function returns the effective type alignment. Zero means error. + uint32 get_alignment() const + { + uint32 alignment = 0; + get_size(&alignment); + return alignment; + } + + /// Get type sign + type_sign_t get_sign() const { return get_tinfo_property(typid, GTA_TYPE_SIGN); } + + /// Is this a signed type? + bool is_signed() const { return get_sign() == type_signed; } + + /// Is this an unsigned type? + bool is_unsigned() const { return get_sign() == type_unsigned; } + + /// Get declared alignment of the type + uchar get_declalign() const { return uchar(get_tinfo_property(typid, GTA_DECLALIGN)); } + + /// Is this type a type reference?. + THREAD_SAFE bool is_typeref() const { return (typid & TYPID_ISREF) != 0; } + + /// Does this type refer to a nontrivial type? + THREAD_SAFE bool has_details() const { return typid >= FIRST_NONTRIVIAL_TYPID; } + + /// Does a type refer to a name?. + /// If yes, fill the provided buffer with the type name and return true. + /// Names are returned for numbered types too: either a user-defined nice name + /// or, if a user-provided name does not exist, an ordinal name + /// (like #xx, see create_numbered_type_name()). + bool get_type_name(qstring *out) const { return is_typeref() && get_tinfo_pdata(out, typid, GTP_NAME); } + + /// Get the beautified type name. + /// Get the referenced name and apply regular expressions from goodname.cfg to beautify the name + bool get_nice_type_name(qstring *out) const { return is_typeref() && get_tinfo_pdata(out, typid, GTP_NICE_NAME); } + + /// Rename a type + /// \param name new type name + /// \param ntf_flags \ref NTF_ + /// \note The change is saved immediately + tinfo_code_t rename_type(const char *name, int ntf_flags=0) { return tinfo_code_t(set_tinfo_property4(this, STA_RENAME, size_t(name), size_t(NTF_TYPE|ntf_flags), 0, 0)); } + + /// Use in the case of typedef chain (TYPE1 -> TYPE2 -> TYPE3...TYPEn). + /// \return the name of the last type in the chain (TYPEn). + /// if there is no chain, returns TYPE1 + bool get_final_type_name(qstring *out) const { return is_typeref() && get_tinfo_pdata(out, typid, GTP_FINAL_NAME); } + + /// Use In the case of typedef chain (TYPE1 -> TYPE2 -> TYPE3...TYPEn). + /// \return the name of the next type in the chain (TYPE2). + /// if there is no chain, returns failure + bool get_next_type_name(qstring *out) const { return is_typeref() && get_tinfo_pdata(out, typid, GTP_NEXT_NAME); } + + /// Get the type tid + /// Each type in the local type library has a so-called `tid` associated with it. + /// The tid is used to collect xrefs to the type. The tid is created when + /// the type is created in the local type library and does not change + /// afterwards. It can be passed to xref-related functions + /// instead of the address. + /// \return tid or BADADDR + /// \note types that do not come from a type library (that exist only in the + /// memory) can not have a tid. + tid_t get_tid() const { return get_tinfo_tid((tinfo_t *)this, false); } + + /// Get the type tid. Create if it does not exist yet. + /// If the type comes from a base til, the type will be copied to the local + /// til and a new tid will be created for it. (if the type comes from a + /// base til, it does not have a tid yet). If the type comes from the local + /// til, this function is equivalent to get_tid() + /// \return tid or BADADDR + tid_t force_tid() { return get_tinfo_tid(this, true); } + + /// Get type ordinal (only if the type was created as a numbered type, 0 if none) + uint32 get_ordinal() const { return get_tinfo_property(typid, GTA_ORDINAL); } + + /// Get final type ordinal (0 if none) + uint32 get_final_ordinal() const { return get_tinfo_property(typid, GTA_FINAL_ORDINAL); } + + /// Get the type library for tinfo_t + til_t *get_til() const { til_t *til; get_tinfo_pdata(&til, typid, GTP_TIL); return til; } + + /// Was the named type found in some base type library (not the top level type library)?. + /// If yes, it usually means that the type comes from some loaded type library, + /// not the local type library for the database + bool is_from_subtil() const { return is_typeref() && get_tinfo_property(typid, GTA_FROM_SUBTIL); } + + /// Is this a forward declaration?. + /// Forward declarations are placeholders: the type definition does not exist + bool is_forward_decl() const { return bool(get_tinfo_property(typid, GTA_IS_FORWARD)); } + + /// Get type of a forward declaration. + /// For a forward declaration this function returns its base type. + /// In other cases it returns ::BT_UNK + type_t get_forward_type() const { return get_tinfo_property(typid, GTA_FORWARD_TYPE); } + bool is_forward_struct() const { return is_type_struct(get_forward_type()); } + bool is_forward_union() const { return is_type_union(get_forward_type()); } + bool is_forward_enum() const { return is_type_enum(get_forward_type()); } + + /// Is this a typedef?. + /// This function will return true for a reference to a local type that is + /// declared as a typedef. + bool is_typedef() const { return get_tinfo_property(typid, GTA_IS_TYPEDEF) != 0; } + + /// Get type comment + /// \return 0-failed, 1-returned regular comment, 2-returned repeatable comment + int get_type_cmt(qstring *out) const { return has_details() ? get_tinfo_pdata(out, typid, GTP_COMMENT) : 0; } + + /// Get type comment only if it is repeatable + bool get_type_rptcmt(qstring *out) const { return has_details() && get_tinfo_pdata(out, typid, GTP_RPTCMT); } + + THREAD_SAFE bool is_decl_const() const { return is_type_const(get_decltype()); } ///< \isdecl{is_type_const} + THREAD_SAFE bool is_decl_volatile() const { return is_type_volatile(get_decltype()); } ///< \isdecl{is_type_volatile} + THREAD_SAFE bool is_decl_void() const { return is_type_void(get_decltype()); } ///< \isdecl{is_type_void} + THREAD_SAFE bool is_decl_partial() const { return is_type_partial(get_decltype()); } ///< \isdecl{is_type_partial} + THREAD_SAFE bool is_decl_unknown() const { return is_type_unknown(get_decltype()); } ///< \isdecl{is_type_unknown} + THREAD_SAFE bool is_decl_last() const { return is_typeid_last(get_decltype()); } ///< \isdecl{is_typeid_last} + THREAD_SAFE bool is_decl_ptr() const { return is_type_ptr(get_decltype()); } ///< \isdecl{is_type_ptr} + THREAD_SAFE bool is_decl_array() const { return is_type_array(get_decltype()); } ///< \isdecl{is_type_array} + THREAD_SAFE bool is_decl_func() const { return is_type_func(get_decltype()); } ///< \isdecl{is_type_func} + THREAD_SAFE bool is_decl_complex() const { return is_type_complex(get_decltype()); } ///< \isdecl{is_type_complex} + THREAD_SAFE bool is_decl_typedef() const { return is_type_typedef(get_decltype()); } ///< \isdecl{is_type_typedef} + THREAD_SAFE bool is_decl_sue() const { return is_type_sue(get_decltype()); } ///< \isdecl{is_type_sue} + THREAD_SAFE bool is_decl_struct() const { return is_type_struct(get_decltype()); } ///< \isdecl{is_type_struct} + THREAD_SAFE bool is_decl_union() const { return is_type_union(get_decltype()); } ///< \isdecl{is_type_union} + THREAD_SAFE bool is_decl_udt() const { return is_type_struni(get_decltype()); } ///< \isdecl{is_type_struni} + THREAD_SAFE bool is_decl_enum() const { return is_type_enum(get_decltype()); } ///< \isdecl{is_type_enum} + THREAD_SAFE bool is_decl_bitfield() const { return is_type_bitfld(get_decltype()); } ///< \isdecl{is_type_bitfld} + THREAD_SAFE bool is_decl_int128() const { return is_type_int128(get_decltype()); } ///< \isdecl{is_type_int128} + THREAD_SAFE bool is_decl_int64() const { return is_type_int64(get_decltype()); } ///< \isdecl{is_type_int64} + THREAD_SAFE bool is_decl_int32() const { return is_type_int32(get_decltype()); } ///< \isdecl{is_type_int32} + THREAD_SAFE bool is_decl_int16() const { return is_type_int16(get_decltype()); } ///< \isdecl{is_type_int16} + THREAD_SAFE bool is_decl_int() const { return is_type_int(get_decltype()); } ///< \isdecl{is_type_int} + THREAD_SAFE bool is_decl_char() const { return is_type_char(get_decltype()); } ///< \isdecl{is_type_char} + THREAD_SAFE bool is_decl_uint() const { return is_type_uint(get_decltype()); } ///< \isdecl{is_type_uint} + THREAD_SAFE bool is_decl_uchar() const { return is_type_uchar(get_decltype()); } ///< \isdecl{is_type_uchar} + THREAD_SAFE bool is_decl_uint16() const { return is_type_uint16(get_decltype()); } ///< \isdecl{is_type_uint16} + THREAD_SAFE bool is_decl_uint32() const { return is_type_uint32(get_decltype()); } ///< \isdecl{is_type_uint32} + THREAD_SAFE bool is_decl_uint64() const { return is_type_uint64(get_decltype()); } ///< \isdecl{is_type_uint64} + THREAD_SAFE bool is_decl_uint128() const { return is_type_uint128(get_decltype()); } ///< \isdecl{is_type_uint128} + THREAD_SAFE bool is_decl_ldouble() const { return is_type_ldouble(get_decltype()); } ///< \isdecl{is_type_ldouble} + THREAD_SAFE bool is_decl_double() const { return is_type_double(get_decltype()); } ///< \isdecl{is_type_double} + THREAD_SAFE bool is_decl_float() const { return is_type_float(get_decltype()); } ///< \isdecl{is_type_float} + THREAD_SAFE bool is_decl_tbyte() const { return is_type_tbyte(get_decltype()); } ///< \isdecl{is_type_tbyte} + THREAD_SAFE bool is_decl_floating() const { return is_type_floating(get_decltype()); } ///< \isdecl{is_type_floating} + THREAD_SAFE bool is_decl_bool() const { return is_type_bool(get_decltype()); } ///< \isdecl{is_type_bool} + THREAD_SAFE bool is_decl_paf() const { return is_type_paf(get_decltype()); } ///< \isdecl{is_type_paf} + THREAD_SAFE bool is_well_defined() const { return !empty() && !is_decl_partial() && !is_punknown(); } ///< !(empty()) && !(is_decl_partial()) && !(is_punknown()) + + // Probe the resolved type for various attributes: + bool is_const() const { return is_type_const(get_realtype()); } ///< \isreal{is_type_const} + bool is_volatile() const { return is_type_volatile(get_realtype()); } ///< \isreal{is_type_volatile} + bool is_void() const { return is_type_void(get_realtype()); } ///< \isreal{is_type_void} + bool is_partial() const { return is_type_partial(get_realtype()); } ///< \isreal{is_type_partial} + bool is_unknown() const { return is_type_unknown(get_realtype()); } ///< \isreal{is_type_unknown} + bool is_ptr() const { return is_type_ptr(get_realtype()); } ///< \isreal{is_type_ptr} + bool is_array() const { return is_type_array(get_realtype()); } ///< \isreal{is_type_array} + bool is_func() const { return is_type_func(get_realtype()); } ///< \isreal{is_type_func} + bool is_complex() const { return is_type_complex(get_realtype()); } ///< \isreal{is_type_complex} + bool is_struct() const { return is_type_struct(get_realtype()); } ///< \isreal{is_type_struct} + bool is_union() const { return is_type_union(get_realtype()); } ///< \isreal{is_type_union} + bool is_udt() const { return is_type_struni(get_realtype()); } ///< \isreal{is_type_struni} + bool is_enum() const { return is_type_enum(get_realtype()); } ///< \isreal{is_type_enum} + bool is_sue() const { return is_type_sue(get_realtype()); } ///< \isreal{is_type_sue} + bool is_bitfield() const { return is_type_bitfld(get_realtype()); } ///< \isreal{is_type_bitfld} + bool is_int128() const { return is_type_int128(get_realtype()); } ///< \isreal{is_type_int128} + bool is_int64() const { return is_type_int64(get_realtype()); } ///< \isreal{is_type_int64} + bool is_int32() const { return is_type_int32(get_realtype()); } ///< \isreal{is_type_int32} + bool is_int16() const { return is_type_int16(get_realtype()); } ///< \isreal{is_type_int16} + bool is_int() const { return is_type_int(get_realtype()); } ///< \isreal{is_type_int} + bool is_char() const { return is_type_char(get_realtype()); } ///< \isreal{is_type_char} + bool is_uint() const { return is_type_uint(get_realtype()); } ///< \isreal{is_type_uint} + bool is_uchar() const { return is_type_uchar(get_realtype()); } ///< \isreal{is_type_uchar} + bool is_uint16() const { return is_type_uint16(get_realtype()); } ///< \isreal{is_type_uint16} + bool is_uint32() const { return is_type_uint32(get_realtype()); } ///< \isreal{is_type_uint32} + bool is_uint64() const { return is_type_uint64(get_realtype()); } ///< \isreal{is_type_uint64} + bool is_uint128() const { return is_type_uint128(get_realtype()); } ///< \isreal{is_type_uint128} + bool is_ldouble() const { return is_type_ldouble(get_realtype()); } ///< \isreal{is_type_ldouble} + bool is_double() const { return is_type_double(get_realtype()); } ///< \isreal{is_type_double} + bool is_float() const { return is_type_float(get_realtype()); } ///< \isreal{is_type_float} + bool is_tbyte() const { return is_type_tbyte(get_realtype()); } ///< \isreal{is_type_tbyte} + bool is_bool() const { return is_type_bool(get_realtype()); } ///< \isreal{is_type_bool} + bool is_paf() const { return is_type_paf(get_realtype()); } ///< \isreal{is_type_paf} + bool is_ptr_or_array() const { return is_type_ptr_or_array(get_realtype()); } ///< \isreal{is_type_ptr_or_array} + bool is_integral() const { return is_type_integral(get_realtype()); } ///< \isreal{is_type_integral} + bool is_ext_integral() const { return is_type_ext_integral(get_realtype()); } ///< \isreal{is_type_ext_integral} + bool is_floating() const { return is_type_floating(get_realtype()); } ///< \isreal{is_type_floating} + bool is_arithmetic() const { return is_type_arithmetic(get_realtype()); } ///< \isreal{is_type_arithmetic} + bool is_ext_arithmetic() const { return is_type_ext_arithmetic(get_realtype()); } ///< \isreal{is_type_ext_arithmetic} + /// Does the type represent a single number? + bool is_scalar() const { type_t bt = get_realtype(); return get_base_type(bt) <= BT_PTR || is_type_enum(bt); } + + /// Get the pointer info. + bool get_ptr_details(ptr_type_data_t *pi) const + { + return get_type_details(BT_PTR, pi); + } + + /// Get the array specific info + bool get_array_details(array_type_data_t *ai) const + { + return get_type_details(BT_ARRAY, ai); + } + + /// Get the enum specific info + bool get_enum_details(enum_type_data_t *ei) const + { + return get_type_details(BTF_ENUM, ei); + } + + /// Get the bitfield specific info + bool get_bitfield_details(bitfield_type_data_t *bi) const + { + return get_type_details(BT_BITFIELD, bi); + } + + /// Get the udt specific info + bool get_udt_details(udt_type_data_t *udt, gtd_udt_t gtd=GTD_CALC_LAYOUT) const + { + return get_type_details(BTF_STRUCT|gtd, udt); + } + + /// Get only the function specific info for this tinfo_t + bool get_func_details(func_type_data_t *fi, gtd_func_t gtd=GTD_CALC_ARGLOCS) const + { + return get_type_details(BT_FUNC|gtd, fi); + } + + /// Is this pointer to a function? + bool is_funcptr() const { return get_tinfo_property(typid, GTA_IS_FUNCPTR) != 0; } + + /// Is a shifted pointer? + bool is_shifted_ptr() const { return get_tinfo_property(typid, GTA_IS_SHIFTED_PTR) != 0; } + + /// Is a variable-size structure? + bool is_varstruct() const { return get_tinfo_property(typid, GTA_IS_VARSTRUCT) != 0; } + + /// Can the type be of a variable struct member? + /// This function checks for: is_array() && array.nelems==0 + /// Such a member can be only the very last member of a structure + bool is_varmember() const { return get_tinfo_property(typid, GTA_IS_VARMEMBER) != 0; } + + /// ::BT_PTR & ::BT_ARRAY: get size of pointed object or array element. On error returns -1 + int get_ptrarr_objsize() const { return get_tinfo_property(typid, GTA_PTRARR_SIZE); } + + /// ::BT_PTR & ::BT_ARRAY: get the pointed object or array element. + /// If the current type is not a pointer or array, return empty type info. + tinfo_t get_ptrarr_object() const { tinfo_t r; r.typid = get_tinfo_property(typid, GTA_PTRARR_SUBTIF); return r; } + + /// ::BT_PTR: get type of pointed object. + /// If the current type is not a pointer, return empty type info. + /// See also get_ptrarr_object() and remove_pointer() + tinfo_t get_pointed_object() const { tinfo_t r; r.typid = get_tinfo_property(typid, GTA_PTR_OBJ); return r; } + + /// Is "void *"?. This function does not check the pointer attributes and type modifiers + bool is_pvoid() const { return get_pointed_object().is_void(); } + + /// Is "_UNKNOWN *"?. This function does not check the pointer attributes and type modifiers + bool is_punknown() const { return get_pointed_object().is_unknown(); } + + /// ::BT_ARRAY: get type of array element. See also get_ptrarr_object() + tinfo_t get_array_element() const { tinfo_t r; r.typid = get_tinfo_property(typid, GTA_ARRAY_ELEM); return r; } + + /// repeat recursively: if an array, return the type of its element; else return the type itself. + tinfo_t get_final_element() const { tinfo_t r; r.typid = get_tinfo_property(typid, GTA_FINAL_ELEM); return r; } + + /// ::BT_ARRAY: get number of elements (-1 means error) + int get_array_nelems() const { return get_tinfo_property(typid, GTA_ARRAY_NELEMS); } + + /// ::BT_FUNC or ::BT_PTR ::BT_FUNC: Get type of n-th arg (-1 means return type, see get_rettype()) + tinfo_t get_nth_arg(int n) const + { + tinfo_t r; + if ( n >= -1 && n < MAX_FUNC_ARGS ) + r.typid = get_tinfo_property(typid, GTA_FUNC_ARG+n); + return r; + } + + /// ::BT_FUNC or ::BT_PTR ::BT_FUNC: Get the function's return type + tinfo_t get_rettype() const { return get_nth_arg(-1); } + + /// ::BT_FUNC or ::BT_PTR ::BT_FUNC: Calculate number of arguments (-1 - error) + int get_nargs() const { return get_tinfo_property(typid, GTA_FUNC_NARGS); } + + /// ::BT_FUNC or ::BT_PTR ::BT_FUNC: Get calling convention + callcnv_t get_cc() const { return (callcnv_t)get_tinfo_property(typid, GTA_FUNC_CC); } + bool is_user_cc() const { return ::is_user_cc(get_cc()); } ///< \tinfocc{is_user_cc} + bool is_vararg_cc() const { return ::is_vararg_cc(get_cc()); } ///< \tinfocc{is_vararg_cc} + bool is_purging_cc() const { return ::is_purging_cc(get_cc()); } ///< \tinfocc{is_purging_cc} + + /// ::BT_FUNC: Calculate number of purged bytes + int calc_purged_bytes() const { return get_tinfo_property(typid, GTA_PURGED_BYTES); } + + /// ::BT_FUNC: Is high level type? + bool is_high_func() const { return get_tinfo_property(typid, GTA_IS_HIGH_TYPE) != 0; } + + /// ::BT_COMPLEX: get a list of member functions declared in this udt. + /// \return false if no member functions exist + bool get_methods(udtmembervec_t *methods) const { return get_tinfo_pdata(methods, typid, GTP_UDT_METHODS) != 0; } + + /// ::BT_STRUCT: get bit buckets + /// Bit buckets are used to layout bitfields + /// \return false if wrong type was passed + bool get_bit_buckets(range64vec_t *buckets) const { return get_tinfo_pdata(buckets, typid, GTP_BIT_BUCKETS) != 0; } + + /// ::BTF_STRUCT,::BTF_UNION: Find a udt member. + /// - at the specified offset (#STRMEM_OFFSET) + /// - with the specified index (#STRMEM_INDEX) + /// - with the specified type (#STRMEM_TYPE) + /// - with the specified name (#STRMEM_NAME) + /// + /// \return the index of the found member or -1 + int find_udm(udm_t *udm, int strmem_flags) const { return find_tinfo_udt_member(udm, typid, strmem_flags); } +/// \defgroup STRMEM_ Find UDT member flags +/// used by 'strmem_flags' parameter to find_udm() +///@{ +#define STRMEM_MASK 0x000F +#define STRMEM_OFFSET 0x0000 ///< get member by offset + ///< - in: udm->offset - is a member offset in bits +#define STRMEM_INDEX 0x0001 ///< get member by number + ///< - in: udm->offset - is a member number +#define STRMEM_AUTO 0x0002 ///< get member by offset if struct, or get member by index if union + ///< - nb: union: index is stored in the udm->offset field! + ///< - nb: struct: offset is in bytes (not in bits)! +#define STRMEM_NAME 0x0003 ///< get member by name + ///< - in: udm->name - the desired member name. +#define STRMEM_TYPE 0x0004 ///< get member by type. + ///< - in: udm->type - the desired member type. + ///< member types are compared with tinfo_t::equals_to() +#define STRMEM_SIZE 0x0005 ///< get member by size. + ///< - in: udm->size - the desired member size. +#define STRMEM_MINS 0x0006 ///< get smallest member by size. +#define STRMEM_MAXS 0x0007 ///< get biggest member by size. +#define STRMEM_LOWBND 0x0008 ///< get member by offset or the next member (lower bound) + ///< - in: udm->offset - is a member offset in bits +#define STRMEM_NEXT 0x0009 ///< get next member after the offset + ///< - in: udm->offset - is a member offset in bits + +#define STRMEM_VFTABLE 0x10000000 + ///< can be combined with #STRMEM_OFFSET, #STRMEM_AUTO + ///< get vftable instead of the base class +#define STRMEM_SKIP_EMPTY 0x20000000 + ///< can be combined with #STRMEM_OFFSET, #STRMEM_AUTO + ///< skip empty members (i.e. having zero size) + ///< only last empty member can be returned +#define STRMEM_CASTABLE_TO 0x40000000 + ///< can be combined with #STRMEM_TYPE: + ///< member type must be castable to the specified type +#define STRMEM_ANON 0x80000000 + ///< can be combined with #STRMEM_NAME: + ///< look inside anonymous members too. +#define STRMEM_SKIP_GAPS 0x01000000 + ///< can be combined with #STRMEM_OFFSET, #STRMEM_LOWBND + ///< skip gap members +///@} + + /// ::BTF_STRUCT,::BTF_UNION: Find an udt member at the specified offset + /// \return the index of the found member or -1 + inline int find_udm(uint64 offset, int strmem_flags=0) const; + + /// ::BTF_STRUCT,::BTF_UNION: Find an udt member by name + /// \return the index of the found member or -1 + inline int find_udm(const char *name, int strmem_flags=0) const; + + /// Retrieve a structure/union member with the specified name + /// \param[out] out udm_t storage. Must not be nullptr + /// \param name member name. Must not be nullptr + /// \return the index of the found member, or -1 + inline int get_udm(udm_t *out, const char *name) const; + + /// Retrieve a structure/union member with the specified index + /// \param[out] out udm_t storage. Must not be nullptr + /// \param index the member index + /// \return the index of the found member, or -1 + inline int get_udm(udm_t *out, size_t index) const; + + /// Retrieve a structure/union member with the specified offset + /// \param[out] out udm_t storage. Must not be nullptr + /// \param offset member bit offset + /// \return the index of the found member, or -1 + inline int get_udm_by_offset(udm_t *out, uint64 offset) const; + + /// Get number of udt members. -1-error + int get_udt_nmembers() const { return get_tinfo_property(typid, GTA_UDT_NMEMBERS); } + + /// Is an empty struct/union? (has no fields) + bool is_empty_udt() const { return get_udt_nmembers() == 0; } + + /// Is a small udt? (can fit a register or a pair of registers) + bool is_small_udt() const { return get_tinfo_property(typid, GTA_IS_SMALL_UDT) != 0; } + + /// Get udt_type_data_t::taudt_bits + uint32 get_udt_taudt_bits() const { return get_tinfo_property(typid, GTA_UDT_BITS); } + + /// Is an unaligned struct + bool is_unaligned_struct() const { return (get_udt_taudt_bits() & TAUDT_UNALIGNED) != 0; } + + /// Is gcc msstruct attribute applied + bool is_msstruct() const { return (get_udt_taudt_bits() & TAUDT_MSSTRUCT) != 0; } + + /// Is a c++ object, not simple pod type + bool is_cpp_struct() const { return (get_udt_taudt_bits() & TAUDT_CPPOBJ) != 0; } + + /// Is a vftable type? + bool is_vftable() const { return (get_udt_taudt_bits() & TAUDT_VFTABLE) != 0; } + + /// Is a structure with fixed offsets? + bool is_fixed_struct() const { return (get_udt_taudt_bits() & TAUDT_FIXED) != 0; } + + /// Is a tuple? + bool is_tuple() const { return (get_udt_taudt_bits() & TAUDT_TUPLE) != 0; } + + /// Requires full qualifier? (name is not unique) + /// \param[out] out qualifier. may be nullptr + /// \param name field name + /// \param offset field offset in bits + /// \return if the name is not unique, returns true + bool requires_qualifier(qstring *out, const char *name, uint64 offset) const { return name_requires_qualifier(out, typid, name, offset); } + + /// Calculate set of covered bytes for the type + /// \param out pointer to the output buffer. covered bytes will be appended to it. + /// \param offset delta in bytes to add to all calculations. used internally during recurion. + bool append_covered(rangeset_t *out, uint64 offset=0) const { return append_tinfo_covered(out, typid, offset); } + + /// Calculate set of padding bytes for the type + /// \param out pointer to the output buffer; old buffer contents will be lost. + bool calc_gaps(rangeset_t *out) const { return calc_tinfo_gaps(out, typid); } + + /// Floating value or an object consisting of one floating member entirely + bool is_one_fpval() const { return get_onemember_type().is_floating(); } + + /// Is a SSE vector type? + bool is_sse_type() const { return get_tinfo_property(typid, GTA_IS_SSE_TYPE) != 0; } + + /// Is an anonymous struct/union? + /// We assume that types with names are anonymous if the name starts with $ + bool is_anonymous_udt() const { return get_tinfo_property(typid, GTA_IS_ANON_UDT) != 0; } + + /// Has a vftable? + bool has_vftable() const { return get_tinfo_property(typid, GTA_HAS_VFTABLE) != 0; } + + /// Has a member of type "union"? + bool has_union() const { return get_tinfo_property(typid, GTA_HAS_UNION) != 0; } + + /// Get number of enum members. + /// \return BADSIZE if error + size_t get_enum_nmembers() const { return get_tinfo_property(typid, GTA_EDT_NMEMBERS); } + + /// Is an empty enum? (has no constants) + bool is_empty_enum() const { return get_enum_nmembers() == 0; } + + /// Get enum base type (convert enum to integer type) + /// Returns ::BT_UNK if failed to convert + type_t get_enum_base_type() const { return (type_t)get_tinfo_property(typid, GTA_ENUM_BASE_TYPE); } + + /// Is bitmask enum? + /// \return true for bitmask enum and false in other cases + /// \ref enum_type_data_t::is_bf() + bool is_bitmask_enum() const { return get_tinfo_property(typid, GTA_BITMASK) != 0; } + + /// Get enum constant radix + /// \return radix or 1 for BTE_CHAR + /// \ref enum_type_data_t::get_enum_radix() + int get_enum_radix() const { return get_tinfo_property(typid, GTA_ENUM_RADIX); } + + /// Set the representation of enum members. + /// \param repr \ref value_repr_t + tinfo_code_t get_enum_repr(value_repr_t *repr) const { return tinfo_code_t(get_tinfo_property4(typid, GTA_ENUM_REPR, size_t(repr), 0, 0, 0)); } + + /// Get enum width + /// \return width of enum base type in bytes, 0 - unspecified, or -1 + /// \ref enum_type_data_t::calc_nbytes() + int get_enum_width() const { return get_tinfo_property(typid, GTA_ENUM_WIDTH); } + uint64 calc_enum_mask() const { return make_mask<uint64>(get_enum_width()*8); } + + /// Get enum member by its name + /// \param[out] out enum type member, may be nullptr + /// \param name enum member name. Must not be nullptr + /// \return member index, otherwise returns -1. + ssize_t get_edm(edm_t *out, const char *name) const { return get_tinfo_property4(typid, GTA_EDM_BYNAME, size_t(out), size_t(name), 0, 0); } + + /// Get enum member by its index + /// \param[out] edm enum type member + /// \param idx enum member index + tinfo_code_t get_edm(edm_t *edm, size_t idx) const { return tinfo_code_t(get_tinfo_property4(typid, GTA_EDM, size_t(edm), idx, 0, 0)); } + + /// Get enum member by its value + /// \param[out] out enum type member, may be nullptr + /// \param value + /// \param serial + /// \param bmask, in case of DEFMASK64 the bitmask enum property is ignored + /// \return member index, otherwise returns -1. + ssize_t get_edm_by_value(edm_t *out, uint64 value, bmask64_t bmask=DEFMASK64, uchar serial=0) const { return get_tinfo_property4(typid, GTA_EDM_BYVAL, size_t(out), value, bmask, serial); } + + /// Get enum member TID + /// \param idx enum member index + /// \return tid or BADADDR + /// The tid is used to collect xrefs to the member, + /// it can be passed to xref-related functions instead of the address. + tid_t get_edm_tid(size_t idx) const { return get_tinfo_property4(typid, GTA_EDM_TID, idx, 0, 0, 0); } + + /// For objects consisting of one member entirely: return type of the member + tinfo_t get_onemember_type() const { tinfo_t r; r.typid = get_tinfo_property(typid, GTA_ONEMEM_TYPE); return r; } + + /// Get the innermost member at the given offset + /// \param bitoffset bit offset into the structure + /// \param[out] out_index innermost member index + /// \param[out] out_bitoffset remaining offset into the returned member + /// \retval udt with the innermost member + /// \retval empty type if it is not a struct type or OFFSET could not be found + tinfo_t get_innermost_udm(uint64 bitoffset, size_t *out_index=nullptr, uint64 *out_bitoffset=nullptr) const { tinfo_t itif; tinfo_get_innermost_udm(&itif, this, bitoffset, out_index, out_bitoffset, false); return itif; } + + + /// Get the innermost member type at the given offset + /// \param bitoffset bit offset into the structure + /// \param[out] out_bitoffset remaining offset + /// \retval the innermost member type + tinfo_t get_innermost_member_type(uint64 bitoffset, uint64 *out_bitoffset=nullptr) const { tinfo_t itif; tinfo_get_innermost_udm(&itif, this, bitoffset, nullptr, out_bitoffset, true); return itif; } + + /// Calculate the type score (the higher - the nicer is the type) + uint32 calc_score() const { return score_tinfo(this); } + + /// Get a C-like string representation of the type. + /// \param out output string + /// \param name name of type + /// \param prtype_flags \ref PRTYPE_ + /// \param indent structure level indent + /// \param cmtindent comment indent + /// \param prefix string prepended to each line + /// \param cmt comment text (if specified, overrides the type comment) + /// \return success + bool print( + qstring *out, + const char *name=nullptr, + int prtype_flags=PRTYPE_1LINE, + int indent=0, + int cmtindent=0, + const char *prefix=nullptr, + const char *cmt=nullptr) const + { + return print_tinfo(out, prefix, indent, cmtindent, prtype_flags, this, name, cmt); + } + + /// Function to facilitate debugging + const char *dstr() const { return dstr_tinfo(this); } + + /// Get type attributes (all_attrs: include attributes of referenced types, if any) + bool get_attrs(type_attrs_t *tav, bool all_attrs=false) const { return get_tinfo_attrs(typid, tav, all_attrs); } + + /// Get a type attribute + bool get_attr(const qstring &key, bytevec_t *bv, bool all_attrs=true) const { return get_tinfo_attr(typid, key, bv, all_attrs); } + + /// Set type attributes. If necessary, a new typid will be created. + /// this function modifies tav! (returns old attributes, if any) + /// \return false: bad attributes + bool set_attrs(type_attrs_t *tav) { return set_tinfo_attrs(this, tav); } + + /// Set a type attribute. If necessary, a new typid will be created. + bool set_attr(const type_attr_t &ta, bool may_overwrite=true) { return set_tinfo_attr(this, ta, may_overwrite); } + + /// Del all type attributes. typerefs cannot be modified by this function. + void del_attrs() { set_tinfo_attrs(this, nullptr); } + + /// Del a type attribute. typerefs cannot be modified by this function. + bool del_attr(const qstring &key, bool make_copy=true) { return del_tinfo_attr(this, key, make_copy); } + + bool create_simple_type(type_t decl_type) { return create_type(decl_type, BT_INT, nullptr); } + bool create_ptr(const ptr_type_data_t &p, type_t decl_type=BT_PTR) { return create_type(decl_type, BT_PTR, (void*)&p); } + bool create_array(const array_type_data_t &p, type_t decl_type=BT_ARRAY) { return create_type(decl_type, BT_ARRAY, (void*)&p); } + bool create_bitfield(const bitfield_type_data_t &p, type_t decl_type=BT_BITFIELD) { return create_type(decl_type, BT_BITFIELD, (void*)&p); } + bool create_typedef(const typedef_type_data_t &p, type_t decl_type=BTF_TYPEDEF, bool try_ordinal=true) + { + type_t bt2 = try_ordinal ? BTF_TYPEDEF : BTF_TYPEDEF|BTM_VOLATILE; + return create_type(decl_type, bt2, (void *)&p); + } + + /// \name Convenience functions + ///@{ + inline bool create_ptr(const tinfo_t &tif, uchar bps=0, type_t decl_type=BT_PTR); + inline bool create_array(const tinfo_t &tif, uint32 nelems=0, uint32 base=0, type_t decl_type=BT_ARRAY); + inline void create_typedef(const til_t *til, const char *name, type_t decl_type=BTF_TYPEDEF, bool try_ordinal=true) { get_named_type(til, name, decl_type, false, try_ordinal); } + inline void create_typedef(const til_t *til, uint ord, type_t decl_type=BTF_TYPEDEF) { get_numbered_type(til, ord, decl_type, false); } + inline bool create_bitfield(uchar nbytes, uchar width, bool is_unsigned=false, type_t decl_type=BT_BITFIELD); + ///@} + + /// Convenience function to parse a string with a type declaration + /// \param decl a type declaration + /// \param til type library to use + /// \param pt_flags combination of \ref PT_ bits + bool parse(const char *decl, til_t *til=nullptr, int pt_flags=0) + { + return ::parse_decl(this, nullptr, til, decl, pt_flags | PT_SEMICOLON); + } + + /// Create an empty structure/union + inline bool create_udt(bool is_union=false); + + /// Create an empty enum + inline bool create_enum(bte_t bte=BTE_ALWAYS|BTE_HEX); + + /// \name Warning + /// These functions consume 'p' (make it empty) + ///@{ + inline bool create_udt(udt_type_data_t &p); + bool create_udt(udt_type_data_t &p, type_t decl_type) { return create_type(decl_type, BTF_STRUCT, &p); } + bool create_enum(enum_type_data_t &p, type_t decl_type=BTF_ENUM) { return create_type(decl_type, BTF_ENUM, &p); } + bool create_func(func_type_data_t &p, type_t decl_type=BT_FUNC) { return create_type(decl_type, BT_FUNC, &p); } + ///@} + + /// Retrive tinfo using type TID or struct/enum member MID + /// \param tid tid can denote a type tid or a member tid. + /// \param udm[out] place to save the found member to, may be nullptr + /// \param edm[out] place to save the found member to, may be nullptr + /// \return if a member tid was specified, returns the member index, + /// otherwise returns -1. + /// if the function fails, THIS object becomes empty. + ///@{ + ssize_t get_udm_by_tid(udm_t *udm, tid_t tid) { return ::get_udm_by_tid(this, udm, tid); } + ssize_t get_edm_by_tid(edm_t *edm, tid_t tid) { return ::get_edm_by_tid(this, edm, tid); } + bool get_type_by_tid(tid_t tid) { return ::get_type_by_tid(this, tid); } + ///@} + + /// Retrieve enum tinfo using enum member name + /// \param til type library + /// \param mname enum type member name + /// \return member index, otherwise returns -1. + /// If the function fails, THIS object becomes empty. + ssize_t get_by_edm_name(const char *mname, const til_t *til=nullptr) { return ::get_tinfo_by_edm_name(this, til, mname); } + + /// \name Store type + /// Store the type info in the type library as a named or numbered type. + /// The tinfo_t object will be replaced by a reference to the created type. + /// Allowed bits for ntf_flags: #NTF_NOBASE, #NTF_REPLACE + /// \note These methods are not applicable for the function frame + ///@{ + tinfo_code_t set_named_type(til_t *til, const char *name, int ntf_flags=0) { return save_tinfo(this, til, 0, name, ntf_flags|NTF_TYPE); } + tinfo_code_t set_symbol_type(til_t *til, const char *name, int ntf_flags=0) { return save_tinfo(this, til, 0, name, ntf_flags); } // NTF_SYMM and NTF_SYMU are permitted + tinfo_code_t set_numbered_type(til_t *til, uint32 ord, int ntf_flags=0, const char *name=nullptr) { return save_tinfo(this, til, ord, name, ntf_flags); } + tinfo_code_t save_type(int ntf_flags=NTF_TYPE|NTF_REPLACE) { return save_tinfo(this, nullptr, 0, nullptr, ntf_flags); } + tinfo_code_t copy_type(til_t *til, const char *name, int ntf_flags=NTF_TYPE|NTF_COPY) { return save_tinfo(this, til, 0, name, ntf_flags); } + ///@} + + /// Create a forward declaration. + /// decl_type: ::BTF_STRUCT, ::BTF_UNION, or ::BTF_ENUM + tinfo_code_t create_forward_decl(til_t *til, type_t decl_type, const char *name, int ntf_flags=0) + { + create_typedef(til, "", decl_type, false); + return set_named_type(til, name, ntf_flags); + } + + /// Get stock type information. + /// This function can be used to get tinfo_t for some common types. + /// The same tinfo_t will be returned for the same id, thus saving memory + /// and increasing the speed + /// Please note that retrieving the STI_SIZE_T or STI_SSIZE_T stock type, + /// will also have the side-effect of adding that type to the 'idati' TIL, + /// under the well-known name 'size_t' or 'ssize_t' (respectively). + /// The same is valid for STI_COMPLEX64 and STI_COMPLEX64 stock types + /// with names 'complex64_t' and 'complex128_t' (respectively). + static tinfo_t get_stock(stock_type_id_t id) { tinfo_t t; get_stock_tinfo(&t, id); return t; } + + /// Convert an array into a pointer. + /// type[] => type * + inline bool convert_array_to_ptr(); + + /// Replace the current type with the ptr obj or array element. + /// This function performs one of the following conversions: + /// - type[] => type + /// - type* => type + /// If the conversion is performed successfully, return true + inline bool remove_ptr_or_array() + { + tinfo_t tif = get_ptrarr_object(); + if ( tif.empty() ) + return false; + swap(tif); + return true; + } + + /// \name Bitfields + /// Helper functions to store/extract bitfield values + ///@{ + uint64 read_bitfield_value(uint64 v, int bitoff) const { return read_tinfo_bitfield_value(typid, v, bitoff); } + uint64 write_bitfield_value(uint64 dst, uint64 v, int bitoff) const { return write_tinfo_bitfield_value(typid, dst, v, bitoff); } + ///@} + + /// \name Modifiers + /// Work with type modifiers: const and volatile + ///@{ + type_t get_modifiers() const { return typid & TYPE_MODIF_MASK; } + void set_modifiers(type_t mod) { if ( !empty() ) typid = (typid & ~TYPE_MODIF_MASK) | (mod & TYPE_MODIF_MASK); } + void set_const() { if ( !empty() ) typid |= BTM_CONST; } + void set_volatile() { if ( !empty() ) typid |= BTM_VOLATILE; } + // remove modifiers for trivial types, without resolving the type + void clr_decl_const_volatile() { typid &= ~TYPE_MODIF_MASK; } + // remove modifiers from non-trivial types. for example, if the type was defined + // as "const struct s { int field; }", the "const" modifier will be removed. + // however, these function cannot remove modifiers from typedefs (ex: typedef const int constint) + bool clr_const() { return bool(set_tinfo_property(this, STA_CLR_MODIFS, BTM_CONST)); } + bool clr_volatile() { return bool(set_tinfo_property(this, STA_CLR_MODIFS, BTM_VOLATILE)); } + bool clr_const_volatile() { return bool(set_tinfo_property(this, STA_CLR_MODIFS, BTM_CONST|BTM_VOLATILE)); } + ///@} + + /// Set type alignment + tinfo_code_t set_type_alignment(uchar declalign, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_ALIGNMENT, declalign, 0, 0, etf_flags)); + } +#ifndef NO_OBSOLETE_FUNCS + DEPRECATED bool set_declalign(uchar declalign) { return set_tinfo_property(this, STA_DECLALIGN, declalign) != 0; } +#endif + + /// Change the type sign. Works only for the types that may have sign + bool change_sign(type_sign_t sign) { return set_tinfo_property(this, STA_TYPE_SIGN, sign) != 0; } + + /// Calculate the udt alignments using the field offsets/sizes and the total udt size + /// This function does not work on typerefs + bool calc_udt_aligns(int sudt_flags=SUDT_GAPS) + { return set_tinfo_property(this, STA_UDT_ALIGN, sudt_flags) != 0; } + + /// ::BT_COMPLEX: set the list of member functions. This function consumes 'methods' (makes it empty). + /// \return false if this type is not a udt, or if the given list is empty + bool set_methods(udtmembervec_t &methods) + { + return set_tinfo_property(this, STA_UDT_METHODS, size_t(&methods)) != 0; + } + + /// Set type comment + /// This function works only for non-trivial types + tinfo_code_t set_type_cmt(const char *cmt, bool is_regcmt=false, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_COMMENT, size_t(cmt), is_regcmt, etf_flags, 0)); + } + + /// Get type alias + /// If the type has no alias, return 0. + uint32 get_alias_target() const + { + return get_tinfo_property(typid, GTA_ALIAS); + } + bool is_aliased() const { return get_alias_target() != 0; } + + /// Set type alias + /// Redirects all references to source type to the destination type. + /// This is equivalent to instantaneous replacement all references to srctype by dsttype. + bool set_type_alias(uint32 dest_ord) + { + return set_tinfo_property4(this, STA_ALIAS, dest_ord, 0, 0, 0) != 0; + } + + /// Set declared structure alignment (sda) + /// This alignment supersedes the alignment returned by get_declalign() + /// and is really used when calculating the struct layout. However, the effective + /// structure alignment may differ from `sda` because of packing. + /// The type editing functions (they accept etf_flags) may overwrite this attribute. + tinfo_code_t set_udt_alignment(int sda, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_SET_SDA, sda, 0, 0, etf_flags)); + } + + /// Set structure packing. + /// The value controls how little a structure member alignment can be. + /// Example: if pack=1, then it is possible to align a double to a byte. + /// __attribute__((aligned(1))) double x; + /// However, if pack=3, a double will be aligned to 8 (2**3) even + /// if requested to be aligned to a byte. pack==0 will have the same effect. + /// The type editing functions (they accept etf_flags) may overwrite this attribute. + tinfo_code_t set_udt_pack(int pack, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_SET_PACK, pack, 0, 0, etf_flags)); + } + + /// Get udt member TID + /// \param idx the index of udt the member + /// \return tid or BADADDR + /// The tid is used to collect xrefs to the member, + /// it can be passed to xref-related functions instead of the address. + tid_t get_udm_tid(size_t idx) const { return get_tinfo_property4(typid, GTA_UDM_TID, idx, 0, 0, 0); } + + /// Add a structure/union member. + /// \param udm member to add + /// \param etf_flags \ref ETF_ flags + /// \param times how many times to add. if times > 1, the member name will be + /// appended a suffix like "_2" and so on + /// \param idx the index in the udm array where the new udm should be placed. + /// if the specified index cannot be honored because it would spoil + /// the udm sorting order, it is silently ignored. + /// \note ETF_NO_SAVE is ignored + tinfo_code_t add_udm(const udm_t &udm, uint etf_flags=0, size_t times=1, ssize_t idx=-1) + { + return tinfo_code_t(set_tinfo_property4(this, STA_ADD_UDM, size_t(&udm), times, idx, etf_flags)); + } + + /// Add a structure/union member. + /// + /// The member's size will be computed automatically. + /// + /// \param name Member name. Must not be nullptr or empty. + /// \param type Member type. Must not be empty. Can be any valid + /// udt member type, like a pointer, array, etc. + /// \param offset Member offset in bits. It is the caller's + /// responsibility to specify correct offsets. + /// \param etf_flags \ref ETF_ flags + /// \param times how many times to add. if times > 1, the member name will be + /// appended a suffix like "_2" and so on + /// \param idx the index in the udm array where the new udm should be placed. + /// if the specified index cannot be honored because it would spoil + /// the udm sorting order, it is silently ignored. + /// \note ETF_NO_SAVE is ignored + inline tinfo_code_t add_udm( + const char *name, + const tinfo_t &type, + uint64 offset=0, + uint etf_flags=0, + size_t times=1, + ssize_t idx=-1); + + /// Add a structure/union member. + /// + /// The member's type will be created from 'type', and + /// its size will be computed automatically. + /// + /// \param name Member name. Must not be nullptr or empty. + /// \param type Member type. + /// Can be only a simple type (integral/floating/bool). + /// \param offset Member offset in bits. It is the caller's + /// responsibility to specify correct offsets. + /// \param etf_flags \ref ETF_ flags + /// \param times how many times to add. if times > 1, the member name will be + /// appended a suffix like "_2" and so on + /// \param idx the index in the udm array where the new udm should be placed. + /// if the specified index cannot be honored because it would spoil + /// the udm sorting order, it is silently ignored. + /// \note ETF_NO_SAVE is ignored + inline tinfo_code_t add_udm( + const char *name, + type_t type, + uint64 offset=0, + uint etf_flags=0, + size_t times=1, + ssize_t idx=-1); + + /// Add a structure/union member. + /// + /// The member's type will be created by parsing 'type', and + /// its size will be computed automatically. + /// + /// \param name Member name. Must not be nullptr or empty. + /// \param type Member type. A valid C type declaration. + /// \param offset Member offset in bits. It is the caller's + /// responsibility to specify correct offsets. + /// \param etf_flags \ref ETF_ flags + /// \param times how many times to add. if times > 1, the member name will be + /// appended a suffix like "_2" and so on + /// \param idx the index in the udm array where the new udm should be placed. + /// if the specified index cannot be honored because it would spoil + /// the udm sorting order, it is silently ignored. + /// \note ETF_NO_SAVE is ignored + inline tinfo_code_t add_udm( + const char *name, + const char *type, + uint64 offset=0, + uint etf_flags=0, + size_t times=1, + ssize_t idx=-1); + + /// Delete a structure/union member. + tinfo_code_t del_udm(size_t index, uint etf_flags=0) + { + return del_udms(index, index+1, etf_flags); + } + + /// Delete structure/union members in the range [idx1, idx2) + tinfo_code_t del_udms(size_t idx1, size_t idx2, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_DEL_UDMS, idx1, idx2, 0, etf_flags)); + } + + /// Rename a structure/union member. + /// The new name must be unique. + /// \note ETF_NO_SAVE is ignored + tinfo_code_t rename_udm(size_t index, const char *name, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_UDM_NAME, index, size_t(name), 0, etf_flags)); + } + + /// Set type of a structure/union member. + /// \param index member index in the udm array + /// \param tif new type for the member + /// \param etf_flags \ref etf_flag_t + /// \param repr new representation for the member (optional) + /// \return \ref tinfo_code_t + tinfo_code_t set_udm_type(size_t index, const tinfo_t &tif, uint etf_flags=0, const value_repr_t *repr=nullptr) + { + return tinfo_code_t(set_tinfo_property4(this, STA_UDM_TYPE, index, size_t(&tif), size_t(repr), etf_flags)); + } + + /// Set a comment for a structure/union member. + /// A member may have just one comment, and it is either repeatable or regular. + tinfo_code_t set_udm_cmt(size_t index, const char *cmt, bool is_regcmt=false, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_UDM_CMT, index, size_t(cmt), is_regcmt, etf_flags)); + } + + /// Set the representation of a structure/union member. + tinfo_code_t set_udm_repr(size_t index, const value_repr_t &repr, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_UDM_REPR, index, size_t(&repr), 0, etf_flags)); + } + + /// Was the member created due to the type system + /// \param idx index of the member + bool is_udm_by_til(size_t idx) const { return get_tinfo_property4(typid, GTA_UDM_IS_BYTIL, idx, 0, 0, 0) != 0; } + + /// The member is created due to the type system + /// \param idx index of the member + /// \param on + /// \param etf_flags \ref etf_flag_t + tinfo_code_t set_udm_by_til(size_t idx, bool on=true, uint etf_flags=0) { return tinfo_code_t(set_tinfo_property4(this, STA_UDM_SET_BYTIL, idx, on, 0, etf_flags)); } + + /// Declare struct member offsets as fixed. + /// For such structures, IDA will not recalculate the member offsets. + /// If a member does not fit into its place anymore, it will be deleted. + /// This function works only with structures (not unions). + /// \param on + tinfo_code_t set_fixed_struct(bool on=true) { return tinfo_code_t(set_tinfo_property4(this, STA_FIXED_STRUCT, on, 0, 0, 0)); } + + /// Explicitly specify the struct size. + /// This function works only with fixed structures. + /// The new struct size can be equal or higher the unpadded struct size + /// (IOW, all existing members should fit into the specified size). + /// \param new_size new structure size in bytes + tinfo_code_t set_struct_size(size_t new_size) { return tinfo_code_t(set_tinfo_property4(this, STA_STRUCT_SIZE, new_size, 0, 0, 0)); } + + /// Expand/shrink a structure by adding/removing a gap + /// before the specified member. + /// + /// For regular structures, either the gap can be accommodated by + /// aligning the next member with an alignment directive, or an + /// explicit "gap" member will be inserted. + /// Also note that it is impossible to add a gap at the + /// end of a regular structure. + /// + /// When it comes to fixed-layout structures, there is no need to + /// either add new "gap" members or align existing members, since + /// all members have a fixed offset. + /// It is possible to add a gap at the end of a fixed-layout + /// structure, by passing `-1` as index. + /// + /// \param idx index of the member + /// \param delta number of bytes to add or remove + /// \param etf_flags \ref etf_flag_t + /// + /// \note + /// This function can be used to remove gaps in the middle of a structure by + /// specifying a negative delta value. + tinfo_code_t expand_udt(size_t idx, adiff_t delta, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_EXPAND_UDT, idx, delta, 0, etf_flags)); + } + + /// Declare struct as a tuple. + /// Currently, tuples in IDA behave the same way as structures but they are + /// returned in a different manner from functions. Also, 2 different tuples + /// having the same members are considered to be equal. + /// This function works only with structures (not unions). + /// \param on + tinfo_code_t set_tuple(bool on=true) { return tinfo_code_t(set_tinfo_property4(this, STA_TUPLE, on, 0, 0, 0)); } + + /// Create a tinfo_t object for the function frame + /// \param pfn function + bool get_func_frame(const func_t *pfn) { return tinfo_get_func_frame(this, pfn); } + + /// Is a function frame? + bool is_frame() const { return get_frame_func() != BADADDR; } + + /// Get function address for the frame + ea_t get_frame_func() const { return get_tinfo_property4(typid, GTA_FRAME_FUNC, 0, 0, 0, 0); } + + /// Retrieve frame tinfo for a stack variable + /// \param actval[out] actual value used to fetch stack variable, + /// this pointer may point to 'v', + /// may be nullptr + /// \param insn the instruction + /// \param x reference to instruction operand, may be nullptr + /// \param v immediate value in the operand (usually x.addr) + /// \return returns the member index, + /// otherwise returns -1. + /// if the function fails, THIS object becomes empty. + ssize_t get_stkvar( + sval_t *actval, + const insn_t &insn, + const op_t *x, + sval_t v) + { + return ::get_frame_var(this, actval, insn, x, v); + } + + /// Set the width of enum base type + /// \param nbytes width of enum base type, allowed values: 0 (unspecified),1,2,4,8,16,32,64 + /// \param etf_flags \ref etf_flag_t + tinfo_code_t set_enum_width(int nbytes, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_ENUM_WIDTH, nbytes, 0, 0, etf_flags)); + } + + /// Set enum sign + /// \param sign \ref type_sign_t + /// \param etf_flags \ref etf_flag_t + tinfo_code_t set_enum_sign(type_sign_t sign, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_ENUM_SIGN, sign, 0, 0, etf_flags)); + } + + /// Set or clear the 'bitmask' attribute of an enum. + /// This attribute controls if the enum is considered as a collection of bits + /// or a plain enum. Enums having the 'bitmask' attribute can be used + /// to represent bitwise combination of the defined enum members. + /// \param stance \ref bitmask_cvt_stance_t + /// \param etf_flags \ref etf_flag_t + /// + /// Each group starts with a mask member. + /// Group size is the number of enum constants in it, including group mask. + /// GROUP_SIZES contains the group sizes. + /// Sum of GROUPS_SIZES is equal to number of enum constants. + /// If value is the only value in a group, + /// no need for additional mask value. + /// \note + /// 1. If mask candidate is equal to the next constant + /// then this is not a bitmask enum + /// 2. Constant 0 could not be a mask + enum bitmask_cvt_stance_t + { + ENUMBM_OFF = 0, ///< convert to ordinal enum + ENUMBM_ON = 1, ///< convert to bitmask enum + ENUMBM_AUTO = 2, ///< convert to bitmask if the outcome is nice and useful + }; + tinfo_code_t set_enum_is_bitmask(bitmask_cvt_stance_t stance=ENUMBM_ON, uint etf_flags=0) { return tinfo_code_t(set_tinfo_property4(this, STA_BITMASK, stance, 0, 0, etf_flags)); } + + /// Set the representation of enum members. + /// \param repr \ref value_repr_t + /// \param etf_flags \ref etf_flag_t + tinfo_code_t set_enum_repr(const value_repr_t &repr, uint etf_flags=0) { return tinfo_code_t(set_tinfo_property4(this, STA_ENUM_REPR, size_t(&repr), 0, 0, etf_flags)); } + + /// Set enum radix to display constants + /// \param radix radix 2, 4, 8, 16, with the special case 1 to display as character + /// \param sign display as signed or unsigned + /// \param etf_flags \ref etf_flag_t + tinfo_code_t set_enum_radix(int radix, bool sign, uint etf_flags=0) { return tinfo_code_t(set_tinfo_property4(this, STA_ENUM_RADIX, radix, sign, 0, etf_flags)); } + + /// Add a new enum member (a new symbolic constant) + /// \param edm the constant name, value, and comment + /// \param bmask bmask of the group to add the constant to + /// \param etf_flags \ref etf_flag_t + /// ETF_FORCENAME may be used in case of TERR_ALIEN_NAME + /// \param idx the index in the edm array where the new edm should be placed. + /// if the specified index cannot be honored because it would spoil + /// the edm sorting order, it is silently ignored. + /// \note + /// 1. For non-bitmask enums, `bmask` is ignored + /// 2. For bitmask enums: + /// - `edm.value` must be covered by `bmask` + /// - if `bmask` is equal to DEFMASK64 then `edm.value` is used as `bmask` + /// - if group with `bmask` exists, the member will be added to that group + /// - otherwise use constant as bitmask for a new group + /// \note ETF_NO_SAVE is ignored + tinfo_code_t add_edm(const edm_t &edm, bmask64_t bmask=DEFMASK64, uint etf_flags=0, ssize_t idx=-1) + { + return tinfo_code_t(set_tinfo_property4(this, STA_ADD_EDM, size_t(&edm), bmask, idx, etf_flags)); + } + + /// Add a new enum member (a new symbolic constant) + /// \param name the constant name + /// \param value the constant value + /// \param bmask bmask of the group to add the constant to + /// \param etf_flags \ref etf_flag_t + /// ETF_FORCENAME may be used in case of TERR_ALIEN_NAME + /// \param idx the index in the edm array where the new edm should be placed. + /// if the specified index cannot be honored because it would spoil + /// the edm sorting order, it is silently ignored. + inline tinfo_code_t add_edm( + const char *name, + uint64 value, + bmask64_t bmask=DEFMASK64, + uint etf_flags=0, + ssize_t idx=-1); + + /// Delete enum members + /// \param idx1 index in edmvec_t + /// \param idx2 index in edmvec_t or size_t(-1) + /// \param etf_flags \ref etf_flag_t + /// Delete enum members in [idx1, idx2) + /// \note + /// For bitmask enum, the first member of a non-trivial group (having 2 or + /// more members) is considered as a group mask. It is impossible to delete + /// the group mask of a non-trivial group, other members of the group must be + /// deleted first. Empty groups are automatically deleted. + tinfo_code_t del_edms(size_t idx1, size_t idx2, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_DEL_EDMS, idx1, idx2, 0, etf_flags)); + } + tinfo_code_t del_edm(size_t idx, uint etf_flags=0) { return del_edms(idx, idx+1, etf_flags); } + + /// Delete enum member by its name + /// + /// \param name the enumerator name (cannot be nullptr) + /// \param etf_flags \ref etf_flag_t + /// \return \ref tinfo_code_t + inline tinfo_code_t del_edm(const char *name, uint etf_flags=0); + + /// Delete enum member by its value + /// + /// \param value the enumerator value + /// \param etf_flags \ref etf_flag_t + /// \param bmask, in case of DEFMASK64 the bitmask enum property is ignored + /// \param serial + /// \return \ref tinfo_code_t + inline tinfo_code_t del_edm_by_value(uint64 value, uint etf_flags=0, bmask64_t bmask=DEFMASK64, uchar serial=0); + + /// Rename a enum member + /// \param idx index in edmvec_t + /// \param name new name + /// \param etf_flags \ref etf_flag_t + /// ETF_FORCENAME may be used in case of TERR_ALIEN_NAME + /// \note ETF_NO_SAVE is ignored + tinfo_code_t rename_edm(size_t idx, const char *name, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_EDM_NAME, idx, size_t(name), 0, etf_flags)); + } + + /// Set a comment for an enum member. + /// Such comments are always considered as repeatable. + /// \param idx index in edmvec_t + /// \param cmt comment + /// \param etf_flags \ref etf_flag_t + tinfo_code_t set_edm_cmt(size_t idx, const char *cmt, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_EDM_CMT, idx, size_t(cmt), 0, etf_flags)); + } + + /// Change constant value and/or bitmask + /// \param idx index in edmvec_t + /// \param value old or new value + /// \param bmask old or new bitmask + /// \param etf_flags \ref etf_flag_t + /// \note if new bitmask is specified the index of constant may be changed + tinfo_code_t edit_edm(size_t idx, uint64 value, bmask64_t bmask=DEFMASK64, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_EDIT_EDM, idx, value, bmask, etf_flags)); + } + + /// Rename a function argument. + /// The new name must be unique. + /// \param index argument index in the function array + /// \param name new name + /// \param etf_flags \ref etf_flag_t + /// \note ETF_NO_SAVE is ignored + tinfo_code_t rename_funcarg(size_t index, const char *name, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_FUNCARG_NAME, index, size_t(name), 0, etf_flags)); + } + + /// Set type of a function argument. + /// \param index argument index in the function array + /// \param tif new type for the argument + /// \param etf_flags \ref etf_flag_t + /// \return \ref tinfo_code_t + tinfo_code_t set_funcarg_type(size_t index, const tinfo_t &tif, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_FUNCARG_TYPE, index, size_t(&tif), 0, etf_flags)); + } + + /// Set function return type . + /// \param tif new type for the return type + /// \param etf_flags \ref etf_flag_t + /// \return \ref tinfo_code_t + tinfo_code_t set_func_rettype(const tinfo_t &tif, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_FUNC_RETTYPE, size_t(&tif), 0, 0, etf_flags)); + } + + /// Delete function arguments + /// \param idx1 index in funcargvec_t + /// \param idx2 index in funcargvec_t or size_t(-1) + /// \param etf_flags \ref etf_flag_t + /// Delete function arguments in [idx1, idx2) + tinfo_code_t del_funcargs(size_t idx1, size_t idx2, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_DEL_FUNCARGS, idx1, idx2, 0, etf_flags)); + } + tinfo_code_t del_funcarg(size_t idx, uint etf_flags=0) { return del_funcargs(idx, idx+1, etf_flags); } + + /// Add a function argument. + /// \param farg argument to add + /// \param etf_flags \ref ETF_ flags + /// \param idx the index in the funcarg array where the new funcarg should be placed. + /// if the specified index cannot be honored because it would spoil + /// the funcarg sorting order, it is silently ignored. + /// \note ETF_NO_SAVE is ignored + tinfo_code_t add_funcarg(const funcarg_t &farg, uint etf_flags=0, ssize_t idx=-1) + { + return tinfo_code_t(set_tinfo_property4(this, STA_ADD_FUNCARG, size_t(&farg), idx, 0, etf_flags)); + } + + /// Set function calling convention + tinfo_code_t set_func_cc(callcnv_t cc, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_FUNC_CC, cc, 0, 0, etf_flags)); + } + + /// Set location of a function argument. + /// \param index argument index in the function array + /// \param argloc new location for the argument + /// \param etf_flags \ref etf_flag_t + /// \return \ref tinfo_code_t + tinfo_code_t set_funcarg_loc(size_t index, const argloc_t &argloc, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_FUNCARG_LOC, index, size_t(&argloc), 0, etf_flags)); + } + + /// Set location of function return value. + /// \param argloc new location for the return value + /// \param etf_flags \ref etf_flag_t + /// \return \ref tinfo_code_t + tinfo_code_t set_func_retloc(const argloc_t &argloc, uint etf_flags=0) + { + return tinfo_code_t(set_tinfo_property4(this, STA_FUNC_RETLOC, size_t(&argloc), 0, 0, etf_flags)); + } + + DECLARE_COMPARISONS(tinfo_t) + { // simple comparison: good enough to organize std::map, etc + // for this function "unsigned char" and "uchar" are different + // for deeper comparison see compare_with() + return lexcompare_tinfo(typid, r.typid, 0); + } +/// \defgroup TCMP_ tinfo_t comparison flags +/// passed as 'tcflags' parameter to tinfo_t::compare_with() +///@{ +#define TCMP_EQUAL 0x0000 ///< are types equal? +#define TCMP_IGNMODS 0x0001 ///< ignore const/volatile modifiers +#define TCMP_AUTOCAST 0x0002 ///< can t1 be cast into t2 automatically? +#define TCMP_MANCAST 0x0004 ///< can t1 be cast into t2 manually? +#define TCMP_CALL 0x0008 ///< can t1 be called with t2 type? +#define TCMP_DELPTR 0x0010 ///< remove pointer from types before comparing +#define TCMP_DECL 0x0020 ///< compare declarations without resolving them +#define TCMP_ANYBASE 0x0040 ///< accept any base class when casting +#define TCMP_SKIPTHIS 0x0080 ///< skip the first function argument in comparison +#define TCMP_DEEP_UDT 0x0100 ///< compare udt by member/attributes +///@} + /// Compare two types, based on given flags (see \ref TCMP_) + bool compare_with(const tinfo_t &r, int tcflags=0) const { return compare_tinfo(typid, r.typid, tcflags); } + bool equals_to(const tinfo_t &r) const { return compare_with(r, 0); } + bool is_castable_to(const tinfo_t &target) const { return compare_with(target, TCMP_AUTOCAST); } + bool is_manually_castable_to(const tinfo_t &target) const { return compare_with(target, TCMP_MANCAST); } +}; +DECLARE_TYPE_AS_MOVABLE(tinfo_t); +typedef qvector<tinfo_t> tinfovec_t; ///< vector of tinfo objects + +//------------------------------------------------------------------------ +/// SIMD type info +struct simd_info_t +{ + const char *name; ///< name of SIMD type (nullptr-undefined) + tinfo_t tif; ///< SIMD type (empty-undefined) + uint16 size; ///< SIMD type size in bytes (0-undefined) + type_t memtype; ///< member type + ///< BTF_INT8/16/32/64/128, BTF_UINT8/16/32/64/128 + ///< BTF_INT - integrals of any size/sign + ///< BTF_FLOAT, BTF_DOUBLE + ///< BTF_TBYTE - floatings of any size + ///< BTF_UNION - union of integral and floating types + ///< BTF_UNK - undefined + + simd_info_t(const char *nm = nullptr, uint16 sz = 0, type_t memt = BTF_UNK) + : name(nm), size(sz), memtype(memt) {} + + bool match_pattern(const simd_info_t *pattern) + { + if ( pattern == nullptr ) + return true; + if ( pattern->size != 0 && pattern->size != size + || pattern->name != nullptr && !streq(pattern->name, name) + || !pattern->tif.empty() && !pattern->tif.compare_with(tif) ) + { + return false; + } + if ( pattern->memtype == BTF_UNK || pattern->memtype == memtype ) + return true; + return pattern->memtype == BTF_TBYTE && is_type_float(memtype) + || pattern->memtype == BTF_INT && is_type_int(memtype); + } +}; +DECLARE_TYPE_AS_MOVABLE(simd_info_t); +typedef qvector<simd_info_t> simd_info_vec_t; + +//------------------------------------------------------------------------ +/// Use func_type_data_t::guess_cc() +idaman callcnv_t ida_export guess_func_cc( + const func_type_data_t &fti, + int npurged, + int cc_flags); +/// Use func_type_data_t::dump() +idaman bool ida_export dump_func_type_data( + qstring *out, + const func_type_data_t &fti, + int praloc_bits); +idaman bool ida_export calc_retloc(func_type_data_t *fti); +idaman bool ida_export calc_arglocs(func_type_data_t *fti); +idaman bool ida_export calc_varglocs( + func_type_data_t *fti, + regobjs_t *regs, + relobj_t *stkargs, + int nfixed); + +//------------------------------------------------------------------------ +/// Pointer type information (see tinfo_t::get_ptr_details()) +struct ptr_type_data_t // #ptr +{ + tinfo_t obj_type; ///< pointed object type + tinfo_t closure; ///< cannot have both closure and based_ptr_size + tinfo_t parent; ///< Parent struct + int32 delta; ///< Offset from the beginning of the parent struct + uchar based_ptr_size; + uchar taptr_bits = 0; ///< TAH bits + ptr_type_data_t( + tinfo_t c=tinfo_t(), + uchar bps=0, + tinfo_t p=tinfo_t(), + int32 d=0) + : closure(c), parent(p), delta(d), based_ptr_size(bps) {} + DEFINE_MEMORY_ALLOCATION_FUNCS() + void swap(ptr_type_data_t &r) { qswap(*this, r); } ///< Set this = r and r = this + bool operator == (const ptr_type_data_t &r) const + { + return obj_type == r.obj_type + && closure == r.closure + && based_ptr_size == r.based_ptr_size; + } + bool operator != (const ptr_type_data_t &r) const { return !(*this == r); } + bool is_code_ptr() const { return obj_type.is_func(); } ///< Are we pointing to code? + bool is_shifted() const { return delta != 0; } +}; +DECLARE_TYPE_AS_MOVABLE(ptr_type_data_t); + +//------------------------------------------------------------------------ +/// Array type information (see tinfo_t::get_array_details()) +struct array_type_data_t // #array +{ + tinfo_t elem_type; ///< element type + uint32 base; ///< array base + uint32 nelems; ///< number of elements + array_type_data_t(size_t b=0, size_t n=0) : base(b), nelems(n) {} ///< Constructor + DEFINE_MEMORY_ALLOCATION_FUNCS() + void swap(array_type_data_t &r) { qswap(*this, r); } ///< set this = r and r = this +}; +DECLARE_TYPE_AS_MOVABLE(array_type_data_t); + +//------------------------------------------------------------------------- +/// Information about a single function argument +struct funcarg_t +{ + argloc_t argloc; ///< argument location + qstring name; ///< argument name (may be empty) + qstring cmt; ///< argument comment (may be empty) + tinfo_t type; ///< argument type + uint32 flags = 0; ///< \ref FAI_ +/// \defgroup FAI_ Function argument property bits +/// used by funcarg_t::flags +///@{ +#define FAI_HIDDEN 0x0001 ///< hidden argument +#define FAI_RETPTR 0x0002 ///< pointer to return value. implies hidden +#define FAI_STRUCT 0x0004 ///< was initially a structure +#define FAI_ARRAY 0x0008 ///< was initially an array; + ///< see "__org_typedef" or "__org_arrdim" type attributes + ///< to determine the original type +#define FAI_UNUSED 0x0010 ///< argument is not used by the function +///@} + + funcarg_t() = default; + + /// Create a function argument, with the specified name and arbitrary type. + /// + /// \param _name Argument name. May be empty + /// \param _type Argument type. Must not be empty. + /// \param _argloc Argument location. Can be empty. + funcarg_t(const char *_name, const tinfo_t &_type, const argloc_t &_argloc=argloc_t()) + : argloc(_argloc), name(_name), type(_type) + { + } + + /// Create a function argument, with the specified name and simple type. + /// + /// \param _name Argument name. May be empty + /// \param _type Argument type. + /// Can be only a simple type (integral/floating/bool). + /// \param _argloc Argument location. Can be empty. + funcarg_t(const char *_name, const type_t _type, const argloc_t &_argloc=argloc_t()) + : argloc(_argloc), name(_name), type(_type) + { + } + + /// Create a function argument, with the specified name and type. + /// + /// The 'type' object will be created by parsing the '_type' type + /// declaration. + /// + /// \param _name Argument name. May be empty + /// \param _type Member type. Must not a valid C type declaration. + /// \param _argloc Argument location. Can be empty. + funcarg_t(const char *_name, const char *_type, const argloc_t &_argloc=argloc_t()) + : argloc(_argloc), name(_name) + { + type.parse(_type, nullptr, PT_SEMICOLON); + } + + bool operator == (const funcarg_t &r) const + { + return argloc == r.argloc + && name == r.name +// && cmt == r.cmt + && type == r.type; + } + bool operator != (const funcarg_t &r) const { return !(*this == r); } + +}; +DECLARE_TYPE_AS_MOVABLE(funcarg_t); +typedef qvector<funcarg_t> funcargvec_t; ///< vector of function argument objects + +/// Function type information (see tinfo_t::get_func_details()) +struct func_type_data_t : public funcargvec_t // #func +{ + int flags = 0; ///< \ref FTI_ +/// \defgroup FTI_ Function type data property bits +/// used by func_type_data_t::flags +///@{ +#define FTI_SPOILED 0x0001 ///< information about spoiled registers is present +#define FTI_NORET 0x0002 ///< noreturn +#define FTI_PURE 0x0004 ///< __pure +#define FTI_HIGH 0x0008 ///< high level prototype (with possibly hidden args) +#define FTI_STATIC 0x0010 ///< static +#define FTI_VIRTUAL 0x0020 ///< virtual +#define FTI_CALLTYPE 0x00C0 ///< mask for FTI_*CALL +#define FTI_DEFCALL 0x0000 ///< default call +#define FTI_NEARCALL 0x0040 ///< near call +#define FTI_FARCALL 0x0080 ///< far call +#define FTI_INTCALL 0x00C0 ///< interrupt call +#define FTI_ARGLOCS 0x0100 ///< info about argument locations has been calculated + ///< (stkargs and retloc too) +#define FTI_EXPLOCS 0x0200 ///< all arglocs are specified explicitly +#define FTI_CONST 0x0400 ///< const member function +#define FTI_CTOR 0x0800 ///< constructor +#define FTI_DTOR 0x1000 ///< destructor +#define FTI_ALL 0x1FFF ///< all defined bits +///@} + callcnv_t _new_callcnv = 0;// Do not access directly, use get_cc/set_cc + tinfo_t rettype; ///< return type + argloc_t retloc; ///< return location + uval_t stkargs = 0; ///< size of stack arguments (not used in build_func_type) + reginfovec_t spoiled; ///< spoiled register information. + ///< if spoiled register info is present, it overrides + ///< the standard spoil info (eax, edx, ecx for x86) + cm_t _old_cc = CM_CC_UNKNOWN; // calling convention, legacy codes. do not access + callcnv_t get_explicit_cc() const { return helper_get_cc(_old_cc, _new_callcnv); } + callcnv_t get_cc() const { return get_effective_cc(get_explicit_cc()); } + void set_cc(callcnv_t cc) { helper_set_cc(&_old_cc, &_new_callcnv, cc); } + void swap(func_type_data_t &r) { qswap(*this, r); } + bool is_high() const { return (flags & FTI_HIGH) != 0; } + bool is_noret() const { return (flags & FTI_NORET) != 0; } + bool is_pure() const { return (flags & FTI_PURE) != 0; } + bool is_static() const { return (flags & FTI_STATIC) != 0; } + bool is_virtual() const { return (flags & FTI_VIRTUAL) != 0; } + bool is_const() const { return (flags & FTI_CONST) != 0; } + bool is_ctor() const { return (flags & FTI_CTOR) != 0; } + bool is_dtor() const { return (flags & FTI_DTOR) != 0; } + int get_call_method() const { return flags & FTI_CALLTYPE; } + bool is_vararg_cc() const { return ::is_vararg_cc(get_cc()); } + bool is_golang_cc() const { return ::is_golang_cc(get_cc()); } + bool is_swift_cc() const { return ::is_swift_cc(get_cc()); } + bool is_user_cc() const { return ::is_user_cc(get_explicit_cc()); } + + /// Guess function calling convention + /// use the following info: argument locations and 'stkargs' + callcnv_t guess_cc(int purged, int cc_flags) const + { + return guess_func_cc(*this, purged, cc_flags); + } +#define CC_CDECL_OK 0x01 ///< can use __cdecl calling convention? +#define CC_ALLOW_ARGPERM 0x02 ///< disregard argument order? +#define CC_ALLOW_REGHOLES 0x04 ///< allow holes in register argument list? +#define CC_HAS_ELLIPSIS 0x08 ///< function has a variable list of arguments? +#define CC_GOLANG_OK 0x10 ///< can use __golang calling convention + /// Dump information that is not always visible in the function prototype. + /// (argument locations, return location, total stkarg size) + bool dump(qstring *out, int praloc_bits=PRALOC_STKOFF) const + { + return dump_func_type_data(out, *this, praloc_bits); + } + + /// find argument by name + ssize_t find_argument(const char *name, size_t from=0, size_t to=size_t(-1)) const + { + if ( from < size() ) + { + const_iterator e = begin() + qmin(size(), to); + for ( const_iterator p=begin()+from; p != e; ++p ) + if ( p->name == name ) + return p - begin(); + } + return -1; + } + +}; + +//------------------------------------------------------------------------- +/// Function index for the 'format' attribute. +enum format_functype_t +{ + FMTFUNC_PRINTF, + FMTFUNC_SCANF, + FMTFUNC_STRFTIME, + FMTFUNC_STRFMON, +}; + +//------------------------------------------------------------------------- +/// This structure describes an enum value +struct edm_t // #edm +{ + qstring name; + qstring cmt; // repeatable comment + uint64 value; + + edm_t() = default; + + /// Create an enumeration value with the specified name and value + /// + /// \param _name Enumerator name. Must not be empty. + /// \param _value Enumerator value + /// \param _cmt Enumerator repeatable comment. May be empty. + edm_t(const char *_name, uint64 _value, const char *_cmt=nullptr) + : name(_name), cmt(_cmt), value(_value) {} + + // a enum constant must at least have a name + bool empty() const { return name.empty(); } + + bool operator == (const edm_t &r) const + { + return name == r.name +// && cmt == r.cmt + && value == r.value; + } + bool operator != (const edm_t &r) const { return !(*this == r); } + void swap(edm_t &r) { qswap(*this, r); } + inline tid_t get_tid() const; + + + DEFINE_MEMORY_ALLOCATION_FUNCS() +}; +DECLARE_TYPE_AS_MOVABLE(edm_t); +/// vector of enum values. for regular enums, no sorting order is defined. +/// for bitmasks, the vector consists of bitmask groups. each non-trivial group +/// (having more than one member or more than one bit in the value) starts with +/// a mask member, the rest of the group has no defined sorting order. +typedef qvector<edm_t> edmvec_t; + +/// Enum type information (see tinfo_t::get_enum_details()) +struct enum_type_data_t : public edmvec_t // #enum +{ + intvec_t group_sizes; ///< if present, specifies bitmask group sizes + ///< each non-trivial group starts with a mask member + uint32 taenum_bits = 0; ///< \ref tattr_enum + bte_t bte; ///< enum member sizes (shift amount) and style. + ///< do not manually set BTE_BITMASK, use set_enum_is_bitmask() + enum_type_data_t(bte_t _bte=BTE_ALWAYS|BTE_HEX) : bte(_bte) {} + DEFINE_MEMORY_ALLOCATION_FUNCS() + + // How the enum members should be printed in the enum definition + /// Get enum constant radix + /// \return radix or 1 for BTE_CHAR + int get_enum_radix() const + { + switch ( bte & BTE_OUT_MASK ) + { + case BTE_CHAR: + return 1; + case BTE_HEX: + if ( (taenum_bits & TAENUM_BIN) != 0 ) + return 2; + if ( (taenum_bits & TAENUM_OCT) != 0 ) + return 8; + break; + case BTE_SDEC: + case BTE_UDEC: + return 10; + } + return 16; + } + bool is_number_signed() const + { + return (bte & BTE_OUT_MASK) == BTE_SDEC + || (bte & BTE_OUT_MASK) == BTE_HEX && (taenum_bits & TAENUM_NUMSIGN) != 0; + } + /// Set radix to display constants + /// \param radix radix with the special case 1 to display as character + /// \param sign + void set_enum_radix(int radix, bool sign) + { + bte = (bte & ~BTE_OUT_MASK) | BTE_HEX; + taenum_bits &= ~(TAENUM_BIN|TAENUM_OCT); + setflag(taenum_bits, TAENUM_NUMSIGN, sign); + switch ( radix ) + { + case 1: bte |= BTE_CHAR; break; + case 2: taenum_bits |= TAENUM_BIN; break; + case 8: taenum_bits |= TAENUM_OCT; break; + case 10: bte |= sign ? BTE_SDEC : BTE_UDEC; break; + } + } + bool is_char() const { return (bte & BTE_OUT_MASK) == BTE_CHAR; } + bool is_dec() const { return (bte & BTE_OUT_MASK) == BTE_SDEC; } + bool is_hex() const { return get_enum_radix() == 16; } + bool is_oct() const { return get_enum_radix() == 8; } + bool is_bin() const { return get_enum_radix() == 2; } + bool is_udec() const { return (bte & BTE_OUT_MASK) == BTE_UDEC; } + bool is_shex() const { return is_number_signed() && is_hex(); } + bool is_soct() const { return is_number_signed() && is_oct(); } + bool is_sbin() const { return is_number_signed() && is_bin(); } + + bool has_lzero() const { return (taenum_bits & TAENUM_LZERO) != 0; } + void set_lzero(bool on) { setflag(taenum_bits, TAENUM_LZERO, on); } + + uint64 calc_mask() const { return make_mask<uint64>(calc_nbytes()*8); } + bool store_64bit_values() const { return (taenum_bits & TAENUM_64BIT) != 0; } + + /// is bitmask or ordinary enum? + bool is_bf() const { return (bte & BTE_BITMASK) != 0; } + + /// get the width of enum in bytes + int calc_nbytes() const + { + int emsize = bte & BTE_SIZE_MASK; + return emsize != 0 ? 1 << (emsize-1) : inf_get_cc_size_e(); + } + /// set enum width (nbytes) + bool set_nbytes(int nbytes) + { + if ( nbytes < 0 || nbytes > 8 || !is_pow2(nbytes) ) + return false; // bad width + int idb_width = 0; + if ( nbytes != 0 ) + idb_width = log2ceil(nbytes) + 1; + bte = (bte & ~BTE_SIZE_MASK) | idb_width; + return true; + } + + /// get group parameters for the constant, valid for bitmask enum + /// \param[out] group_start_index index of the group mask + /// \param[out] group_size group size (>=1) + /// \param idx constant index + /// \return success + bool get_constant_group(size_t *group_start_index, size_t *group_size, size_t idx) const + { + if ( !group_sizes.empty() ) + { + size_t grp_start = 0; + for ( auto grp_size : group_sizes ) + { + if ( grp_start + grp_size > idx ) + { + if ( group_start_index != nullptr ) + *group_start_index = grp_start; + if ( group_size != nullptr ) + *group_size = grp_size; + return true; + } + grp_start += grp_size; + } + } + return false; + } + + /// is the enum member at IDX a non-trivial group mask? + /// a trivial group consist of one bit and has just one member, which can be + /// considered as a mask or a bitfield constant + /// \param idx index + /// \return success + bool is_group_mask_at(size_t idx) const + { + size_t grp_start; + size_t grp_size; + return get_constant_group(&grp_start, &grp_size, idx) + && grp_start == idx && grp_size > 1; + } + + /// is valid group sizes + bool is_valid_group_sizes() const + { + if ( !group_sizes.empty() ) + { + size_t sum = 0; + for ( int s : group_sizes ) + { + if ( s == 0 ) + return false; + sum += s; + } + return sum == size(); + } + return true; + } + + /// find member (constant or bmask) by name + ssize_t find_member(const char *name, size_t from=0, size_t to=size_t(-1)) const + { + if ( from < size() ) + { + const_iterator e = begin() + qmin(size(), to); + for ( const_iterator p=begin()+from; p != e; ++p ) + if ( p->name == name ) + return p - begin(); + } + return -1; + } + + /// find member (constant or bmask) by value + ssize_t find_member(uint64 value, uchar serial, size_t from=0, size_t to=size_t(-1), uint64 vmask=uint64(-1)) const + { + if ( from < size() ) + { + if ( vmask == uint64(-1) ) + vmask = calc_mask(); + value &= vmask; + uint64 value_signed = value | ~vmask; + const_iterator e = begin() + qmin(size(), to); + for ( const_iterator p=begin()+from; p != e; ++p ) + if ( (p->value == value || p->value == value_signed) && serial-- == 0 ) + return p - begin(); + } + return -1; + } + + /// swap two instances + void swap(enum_type_data_t &r) { qswap(*this, r); } + + /// add constant for regular enum + void add_constant(const char *name, uint64 value, const char *cmt=nullptr) + { + auto &c = push_back(); + c.name = name; + c.value = value; + c.cmt = cmt; + } + + /// get enum radix and other representation info + /// \param repr value display info + tinfo_code_t get_value_repr(value_repr_t *repr) const { return enum_type_data_t__get_value_repr(this, repr); } + + /// set enum radix and other representation info + /// \param repr value display info + tinfo_code_t set_value_repr(const value_repr_t &repr) { return enum_type_data_t__set_value_repr(this, repr); } + + /// returns serial for the constant + uchar get_serial(size_t index) const + { + uchar serial = 0; + if ( index < size() ) + { + uint64 value = at(index).value; + for ( size_t i=0; i < index; ++i ) + if ( at(i).value == value ) + serial++; + } + return serial; + } + + /// return the maximum serial for the value + uchar get_max_serial(uint64 value) const { return enum_type_data_t__get_max_serial(this, value); } + +#ifndef SWIG + /// visit all enum constants not bmasks + int for_all_constants(std::function<int(size_t idx, size_t grp_start, int grp_size)> v) const + { + if ( !group_sizes.empty() ) + { + auto gv = [v](size_t grp_start, int grp_size) + { + if ( grp_size == 1 ) + return v(grp_start, grp_start, grp_size); + size_t grp_end = grp_start + grp_size; + for ( size_t idx=grp_start+1; idx < grp_end; ++idx ) + { + int code = v(idx, grp_start, grp_size); + if ( code != 0 ) + return code; + } + return 0; + }; + return for_all_groups(gv); + } + else + { + size_t sz = size(); + for ( size_t idx=0; idx < sz; ++idx ) + { + int code = v(idx, 0, sz); + if ( code != 0 ) + return code; + } + } + return 0; + } + + /// visit all enum groups, for bitmask enum only + int for_all_groups(std::function<int(size_t grp_start, int grp_size)> v, bool skip_trivial=false) const + { + if ( !group_sizes.empty() && is_valid_group_sizes() ) + { + size_t grp_start = 0; + for ( auto grp_size : group_sizes ) + { + if ( !skip_trivial || grp_size != 1 ) + { + int code = v(grp_start, grp_size); + if ( code != 0 ) + return code; + } + grp_start += grp_size; + } + } + return 0; + } +#endif + +}; +DECLARE_TYPE_AS_MOVABLE(enum_type_data_t); + +//------------------------------------------------------------------------- +/// Max number of identical constants allowed for one enum type +const uchar MAX_ENUM_SERIAL = 255; + +//------------------------------------------------------------------------- +/// Type information for typedefs +struct typedef_type_data_t // #typedef +{ + const til_t *til; ///< type library to use when resolving + union + { + const char *name; ///< is_ordref=false: target type name. we do not own this pointer! + uint32 ordinal; ///< is_ordref=true: type ordinal number + }; + bool is_ordref; ///< is reference by ordinal? + bool resolve; ///< should resolve immediately? + typedef_type_data_t(const til_t *_til, const char *_name, bool _resolve=false) + : is_ordref(false), resolve(_resolve) + { + name = _name; + til = _til == nullptr ? get_idati() : _til; + } + typedef_type_data_t(const til_t *_til, uint32 ord, bool _resolve=false) + : is_ordref(true), resolve(_resolve) + { + name = nullptr; + ordinal = ord; + til = _til == nullptr ? get_idati() : _til; + } + DEFINE_MEMORY_ALLOCATION_FUNCS() + void swap(typedef_type_data_t &r) { qswap(*this, r); } +}; +DECLARE_TYPE_AS_MOVABLE(typedef_type_data_t); + +//------------------------------------------------------------------------- +// A high level variant of custom_data_type_ids_t +struct custom_data_type_info_t +{ + int16 dtid; ///< data type id + int16 fid; ///< data format ids +}; + +/// Visual representation of a member of a complex type (struct/union/enum) +struct value_repr_t // #repr +{ + uint64 bits = 0; +#define FRB_MASK 0xF ///< Mask for the value type (* means requires additional info): +#define FRB_UNK 0x0 ///< Unknown +#define FRB_NUMB 0x1 ///< Binary number +#define FRB_NUMO 0x2 ///< Octal number +#define FRB_NUMH 0x3 ///< Hexadecimal number +#define FRB_NUMD 0x4 ///< Decimal number +#define FRB_FLOAT 0x5 ///< Floating point number + ///< (for interpreting an integer type as a floating value) +#define FRB_CHAR 0x6 ///< Char +#define FRB_SEG 0x7 ///< Segment +#define FRB_ENUM 0x8 ///< *Enumeration +#define FRB_OFFSET 0x9 ///< *Offset +#define FRB_STRLIT 0xA ///< *String literal (used for arrays) +#define FRB_STROFF 0xB ///< *Struct offset +#define FRB_CUSTOM 0xC ///< *Custom data type +#define FRB_INVSIGN 0x0100 ///< Invert sign (0x01 is represented as -0xFF) +#define FRB_INVBITS 0x0200 ///< Invert bits (0x01 is represented as ~0xFE) +#define FRB_SIGNED 0x0400 ///< Force signed representation +#define FRB_LZERO 0x0800 ///< Toggle leading zeroes (used for integers) +#define FRB_TABFORM 0x1000 ///< has additional tabular parameters + /// Additional info + union + { + refinfo_t ri; ///< FRB_OFFSET + int32 strtype; ///< FRB_STRLIT + struct + { + adiff_t delta; ///< FRB_STROFF + uint32 type_ordinal; ///< FRB_STROFF, FRB_ENUM + }; + custom_data_type_info_t cd; ///< FRB_CUSTOM + }; + array_parameters_t ap; ///< FRB_TABFORM, + ///< AP_SIGNED is ignored, use FRB_SIGNED instead + +public: + void swap(value_repr_t &r) { ::qswap(*this, r); } + DEFINE_MEMORY_ALLOCATION_FUNCS() + + void clear() { bits = 0; } + + bool empty() const { return bits == 0; } + bool is_enum() const { return (bits & FRB_MASK) == FRB_ENUM; } + bool is_offset() const { return (bits & FRB_MASK) == FRB_OFFSET; } + bool is_strlit() const { return (bits & FRB_MASK) == FRB_STRLIT; } + bool is_custom() const { return (bits & FRB_MASK) == FRB_CUSTOM; } + bool is_stroff() const { return (bits & FRB_MASK) == FRB_STROFF; } + bool is_typref() const { return is_enum() || is_stroff(); } + bool is_signed() const { return (bits & FRB_SIGNED) != 0; } + bool has_tabform() const { return (bits & FRB_TABFORM) != 0; } + bool has_lzeroes() const { return (bits & FRB_LZERO) != 0; } + + uint64 get_vtype() const { return bits & FRB_MASK; } + void set_vtype(uint64 vt) { bits &= ~FRB_MASK; bits |= (vt & FRB_MASK); } + void set_signed(bool on) { setflag(bits, FRB_SIGNED, on); } + void set_tabform(bool on) { setflag(bits, FRB_TABFORM, on); } + void set_lzeroes(bool on) { setflag(bits, FRB_LZERO, on); } + + void set_ap(const array_parameters_t &_ap) + { + ap = _ap; + set_signed((ap.flags & AP_SIGNED) != 0); + ap.flags &= ~AP_SIGNED; + set_tabform(!ap.is_default()); + } + void init_ap(array_parameters_t *_ap) const + { + if ( _ap != nullptr ) + { + if ( has_tabform() ) + *_ap = ap; + setflag(_ap->flags, AP_SIGNED, is_signed()); + } + } + + bool from_opinfo(flags64_t flags, aflags_t afl, const opinfo_t *opinfo, const array_parameters_t *_ap) + { + return value_repr_t__from_opinfo(this, flags, afl, opinfo, _ap); + } + size_t print(qstring *result, bool colored=false) const + { + return value_repr_t__print_(this, result, colored); + } + bool parse_value_repr(const qstring &attr, type_t target_type=BTF_STRUCT) + { + return value_repr_t__parse_value_repr(this, attr, target_type); + } + +#ifndef SWIG + DECLARE_COMPARISONS(value_repr_t); +#endif +}; + +//------------------------------------------------------------------------- +/// An object to represent struct or union members +struct udm_t // #udm +{ + uint64 offset = 0; ///< member offset in bits + uint64 size = 0; ///< size in bits + qstring name; ///< member name + qstring cmt; ///< member comment + tinfo_t type; ///< member type + value_repr_t repr; ///< radix, refinfo, strpath, custom_id, strtype + int effalign = 0; ///< effective field alignment (in bytes) + uint32 tafld_bits = 0;///< TAH bits + uchar fda = 0; ///< field alignment (shift amount) + + udm_t() = default; + + /// Create a structure/union member, with the specified name and arbitrary type. + /// + /// The 'size' will be set automatically. + /// + /// \param _name Member name. Must not be empty. + /// \param _type Member type. Must not be empty. Can be any valid + /// udt member type, like a pointer, array, etc. + /// \param[in] _offset Member offset in bits. It is the caller's + /// responsibility to specify correct offsets. + udm_t(const char *_name, const tinfo_t &_type, uint64 _offset = 0) + : offset(_offset), name(_name), type(_type) + { + size = _type.get_size() * 8; + } + + /// Create a structure/union member, with the specified name and simple type. + /// + /// The 'type' will be created from type_t, and the 'size' will + /// be set automatically. + /// + /// \param _name Member name. Must not be empty. + /// \param _type Member type. Must not be empty. + /// Can be only a simple type (integral/floating/bool). + /// \param[in] _offset Member offset in bits. It is the caller's + /// responsibility to specify correct offsets. + udm_t(const char *_name, const type_t _type, uint64 _offset = 0) + : offset(_offset), name(_name) + { + if ( is_type_arithmetic(_type) ) + { + type = tinfo_t(_type); + size = type.get_size() * 8; + } + } + + /// Create a structure/union member, with the specified name and type. + /// + /// The 'type' object will be created by parsing the '_type' type + /// declaration, and the 'size' will be set automatically. + /// + /// \param _name Member name. Must not be empty. + /// \param _type Member type. Must not a valid C type declaration. + /// \param[in] _offset Member offset in bits. It is the caller's + /// responsibility to specify correct offsets. + udm_t(const char *_name, const char *_type, uint64 _offset = 0) + : offset(_offset), name(_name) + { + if ( type.parse(_type, nullptr, PT_SEMICOLON) ) + size = type.get_size() * 8; + } + + // a udt member must at least have a type + bool empty() const { return type.empty(); } + + bool is_bitfield() const { return type.is_decl_bitfield(); } + bool is_zero_bitfield() const { return size == 0 && is_bitfield(); } + bool is_unaligned() const { return (tafld_bits & TAFLD_UNALIGNED) != 0; } + bool is_baseclass() const { return (tafld_bits & TAFLD_BASECLASS) != 0; } + bool is_virtbase() const { return (tafld_bits & TAFLD_VIRTBASE) != 0; } + bool is_vftable() const { return (tafld_bits & TAFLD_VFTABLE) != 0; } + bool is_method() const { return (tafld_bits & TAFLD_METHOD) != 0; } + bool is_gap() const { return (tafld_bits & TAFLD_GAP) != 0; } + bool is_regcmt() const { return (tafld_bits & TAFLD_REGCMT) != 0; } + bool is_retaddr() const { return (tafld_bits & TAFLD_FRAME_R) != 0; } + bool is_savregs() const { return (tafld_bits & TAFLD_FRAME_S) != 0; } + bool is_special_member() const { return is_retaddr() || is_savregs(); } + bool is_by_til() const { return (tafld_bits & TAFLD_BYTIL) != 0; } + + void set_unaligned(bool on=true) { setflag(tafld_bits, TAFLD_UNALIGNED, on); } + void set_baseclass(bool on=true) { setflag(tafld_bits, TAFLD_BASECLASS, on); } + void set_virtbase(bool on=true) { setflag(tafld_bits, TAFLD_VIRTBASE, on); } + void set_vftable(bool on=true) { setflag(tafld_bits, TAFLD_VFTABLE, on); } + void set_method(bool on=true) { setflag(tafld_bits, TAFLD_METHOD, on); } + void set_regcmt(bool on=true) { setflag(tafld_bits, TAFLD_REGCMT, on); } + void set_retaddr(bool on=true) { setflag(tafld_bits, TAFLD_FRAME_R, on); } + void set_savregs(bool on=true) { setflag(tafld_bits, TAFLD_FRAME_S, on); } + void set_by_til(bool on=true) { setflag(tafld_bits, TAFLD_BYTIL, on); } + void clr_unaligned() { tafld_bits &= ~TAFLD_UNALIGNED; } + void clr_baseclass() { tafld_bits &= ~TAFLD_BASECLASS; } + void clr_virtbase() { tafld_bits &= ~TAFLD_VIRTBASE; } + void clr_vftable() { tafld_bits &= ~TAFLD_VFTABLE; } + void clr_method() { tafld_bits &= ~TAFLD_METHOD; } + uint64 begin() const { return offset; } + uint64 end() const { return offset + size; } + bool operator < (const udm_t &r) const + { + return offset < r.offset; + } + bool operator == (const udm_t &r) const + { + return compare_with(r, 0); + } + bool compare_with(const udm_t &r, int tcflags) const + { + return offset == r.offset + && size == r.size + && name == r.name +// && cmt == r.cmt + && type.compare_with(r.type, tcflags) + && fda == r.fda + && tafld_bits == r.tafld_bits + && effalign == r.effalign; + } + bool operator != (const udm_t &r) const { return !(*this == r); } + + DEFINE_MEMORY_ALLOCATION_FUNCS() + void swap(udm_t &r) { qswap(*this, r); } + + // the user cannot enter anonymous fields in ida (they can come only from tils), + // so we use the following trick: if the field type starts with $ and the name + // with __, then we consider the field as anonymous + bool is_anonymous_udm() const + { + return name[0] == '_' && name[1] == '_' && type.is_anonymous_udt(); + } + + void set_value_repr(const value_repr_t &r) { repr = r; } + bool can_be_dtor() const { return name[0] == '~'; } + bool can_rename() const + { + return !is_gap() && !is_baseclass() && !can_be_dtor(); + } + +}; +DECLARE_TYPE_AS_MOVABLE(udm_t); +struct udtmembervec_t : public qvector<udm_t> {}; ///< vector of udt member objects + +struct udt_type_data_t : public udtmembervec_t // #udt +{ + static constexpr int VERSION = 1; + size_t total_size = 0; ///< total structure size in bytes + size_t unpadded_size = 0; ///< unpadded structure size in bytes + uint32 effalign = 0; ///< effective structure alignment (in bytes) + uint32 taudt_bits = 0; ///< TA... and TAUDT... bits + uchar version = VERSION; ///< version of udt_type_data_t + uchar sda = 0; ///< declared structure alignment (shift amount+1). 0 - unspecified + uchar pack = 0; ///< #pragma pack() alignment (shift amount) + bool is_union = false; ///< is union or struct? + + void swap(udt_type_data_t &r) { qswap(*this, r); } + DEFINE_MEMORY_ALLOCATION_FUNCS() + bool is_unaligned() const { return (taudt_bits & TAUDT_UNALIGNED) != 0; } + bool is_msstruct() const { return (taudt_bits & TAUDT_MSSTRUCT) != 0; } + bool is_cppobj() const { return (taudt_bits & TAUDT_CPPOBJ) != 0; } + bool is_vftable() const { return (taudt_bits & TAUDT_VFTABLE) != 0; } + bool is_fixed() const { return (taudt_bits & TAUDT_FIXED) != 0; } + bool is_tuple() const { return (taudt_bits & TAUDT_TUPLE) != 0; } + + void set_vftable(bool on=true) { setflag(taudt_bits, TAUDT_VFTABLE, on); } + void set_fixed(bool on=true) { setflag(taudt_bits, TAUDT_FIXED, on); } + void set_tuple(bool on=true) { setflag(taudt_bits, TAUDT_TUPLE, on); } + + bool is_last_baseclass(size_t idx) // we assume idx is valid + { + return at(idx).is_baseclass() + && (idx+1 == size() || !at(idx+1).is_baseclass()); + } + + /// Add a new member to a structure or union. + /// This function just pushes a new member to the back of the structure/union member vector. + /// + /// \param _name Member name. Must not be nullptr. + /// \param _type Member type. Must not be empty. + /// \param[in] _offset Member offset in bits. It is the caller's responsibility to specify correct offsets. + /// + /// \return { Reference to the newly added member } + udm_t &add_member(const char *_name, const tinfo_t &_type, uint64 _offset = 0) + { + udm_t &udm = push_back(); + udm.name = _name; + udm.type = _type; + udm.size = _type.get_size() * 8; + udm.offset = _offset; + return udm; + } + + /// \ref tinfo_t::find_udm + /// \note STRMEM_VFTABLE is not supported + /// \return the index of the found member or -1 + ssize_t find_member(udm_t *pattern_udm, int strmem_flags) const { return udt_type_data_t__find_member(this, pattern_udm, strmem_flags); } + + ssize_t find_member(const char *name) const + { + udm_t udm; + udm.name = name; + return find_member(&udm, STRMEM_NAME); + } + + ssize_t find_member(uint64 bit_offset) const + { + udm_t udm; + udm.offset = bit_offset; + return find_member(&udm, STRMEM_OFFSET); + } + + /// Get the member that is most likely referenced by the specified offset. + /// Useful for offsets > sizeof(struct). + ssize_t get_best_fit_member(asize_t disp) const { return udt_type_data_t__get_best_fit_member(this, disp); } + + /// Get the member that is most likely referenced by the specified offset. + /// Useful for offsets > sizeof(struct). + /// \param[out] out udm_t storage. Must not be nullptr + /// \param disp the byte offset + inline ssize_t get_best_fit_member(udm_t *out, asize_t disp) const; + +}; +DECLARE_TYPE_AS_MOVABLE(udt_type_data_t); + +// separator to construct full udm name +#define STRUC_SEPARATOR '.' ///< structname.fieldname + +// The type name of a virtual function table (__vftable) of a class is +// constructed by appending the following suffix to the class name. +// In the case of multiple inheritance we append the vft offset +// to the class name (with format %04X) +// Example: CLS_0024_vtbl is used for the vft located at the offset 0x24 of CLS + +#define VTBL_SUFFIX "_vtbl" + +// The member name of a virtual function table +// Complex cases are not handled yet. + +#define VTBL_MEMNAME "__vftable" + +//-------------------------------------------------------------------------- +/// Should display a structure offset expression as the structure size? +inline bool stroff_as_size(int plen, const tinfo_t &tif, asize_t value) +{ + return plen == 1 + && value > 0 + && !tif.is_varstruct() + && value == tif.get_size(); +} + +//-------------------------------------------------------------------------- +struct udm_visitor_t +{ + /// \param tid udt tid + /// \param tif udt type info (may be nullptr for corrupted idbs) + /// \param udt udt type data (may be nullptr for corrupted idbs) + /// \param idx the index of udt the member (may be -1 if udm was not found) + virtual int idaapi visit_udm( + tid_t tid, + const tinfo_t *tif, + const udt_type_data_t *udt, + ssize_t idx) = 0; + virtual ~udm_visitor_t() {} +}; + +//-------------------------------------------------------------------------- +/// Visit structure fields in a stroff expression or in a reference to a struct data variable. +/// This function can be used to enumerate all components of an expression like 'a.b.c'. +/// \param sfv visitor object +/// \param path struct path (path[0] contains the initial struct id) +/// \param plen len +/// \param[in,out] disp offset into structure +/// \param appzero should visit field at offset zero? +/// \return visitor result +idaman int ida_export visit_stroff_udms( + udm_visitor_t &sfv, + const tid_t *path, + int plen, + adiff_t *disp, + bool appzero); + +//------------------------------------------------------------------------- +/// Bitfield type information (see tinfo_t::get_bitfield_details()) +struct bitfield_type_data_t // #bitfield +{ + uchar nbytes; ///< enclosing type size (1,2,4,8 bytes) + uchar width; ///< number of bits + bool is_unsigned; ///< is bitfield unsigned? + bitfield_type_data_t(uchar _nbytes=0, uchar _width=0, bool _is_unsigned=false) + : nbytes(_nbytes), width(_width), is_unsigned(_is_unsigned) + { + } + bool serialize(qtype *type, type_t mods) const; + DECLARE_COMPARISONS(bitfield_type_data_t) + { + if ( nbytes != r.nbytes ) + return nbytes > r.nbytes ? 1 : -1; + if ( width != r.width ) + return width > r.width ? 1 : -1; + if ( is_unsigned ) + { + if ( !r.is_unsigned ) + return 1; + } + else + { + if ( r.is_unsigned ) + return -1; + } + return 0; + } + void swap(bitfield_type_data_t &r) { qswap(*this, r); } + bool is_valid_bitfield() const + { + if ( nbytes != 1 && nbytes != 2 && nbytes != 4 && nbytes != 8 ) + return false; + if ( width > nbytes*8 ) + return false; + return true; + } +}; +DECLARE_TYPE_AS_MOVABLE(bitfield_type_data_t); + +//-------------------------------------------------------------------------- +// This tag can be used at the beginning of +// udm_t::cmt +// funcarg_t::cmt +// edm_t::cmt +// to specify the line number where it is defined. +// Example: "\x05123." means the line number 123 +#define TPOS_LNNUM "\x05" + +// Tag to denote a regular comment in serialized form. +// If a comment has it as its first character, it is a regular comment, +// otherwise it is a repeatable comment. The comments returned by +// ::get_named_type and ::get_numbered_type may have this symbol. +#define TPOS_REGCMT '\x06' + +//------------------------------------------------------------------------- +/// Is bitmask one bit? +inline THREAD_SAFE bool is_one_bit_mask(uval_t mask) +{ + return is_pow2(mask); +} + +//------------------------------------------------------------------------- +inline bool inf_pack_stkargs(callcnv_t cc) +{ + return is_golang_cc(get_effective_cc(cc)) || inf_pack_stkargs(); +} + +//------------------------------------------------------------------------- +inline bool inf_big_arg_align(callcnv_t cc) +{ + return !is_golang_cc(get_effective_cc(cc)) && inf_big_arg_align(); +} + +//------------------------------------------------------------------------- +inline bool inf_huge_arg_align(callcnv_t cc) +{ + return !is_golang_cc(get_effective_cc(cc)) && inf_huge_arg_align(); +} + +//------------------------------------------------------------------------- +// return argument alignment (depends on ABI, CC and natural type alignment) +inline int get_arg_align(int type_align, int slotsize, callcnv_t cc=CM_CC_UNKNOWN) +{ + QASSERT(2858, is_pow2(type_align)); + if ( type_align > slotsize*2 ) + { + if ( inf_huge_arg_align(cc) ) + return type_align; + type_align = slotsize*2; + } + return type_align < slotsize + ? inf_pack_stkargs(cc) ? type_align : slotsize + : inf_big_arg_align(cc) ? type_align : slotsize; +} + +inline int get_arg_align(const tinfo_t &tif, int slotsize, callcnv_t cc=CM_CC_UNKNOWN) +{ + uint32 align = 0; + tif.get_size(&align); + return get_arg_align(align, slotsize, cc); +} + +//------------------------------------------------------------------------- +inline sval_t align_stkarg_up(sval_t spoff, int type_align, int slotsize, callcnv_t cc=CM_CC_UNKNOWN) +{ + uint32 align = get_arg_align(type_align, slotsize, cc); + return align_up(spoff, align); +} + +inline sval_t align_stkarg_up(sval_t spoff, const tinfo_t &tif, int slotsize, callcnv_t cc=CM_CC_UNKNOWN) +{ + uint32 align = get_arg_align(tif, slotsize, cc); + return align_up(spoff, align); +} + +inline bool argloc_t::has_reg() const +{ + if ( !is_scattered() ) + return is_reg(); + for ( const auto &part : scattered() ) + if ( part.is_reg() ) + return true; + return false; +}; + +inline bool argloc_t::has_stkoff() const +{ + if ( !is_scattered() ) + return is_stkoff(); + for ( const auto &part : scattered() ) + if ( part.is_stkoff() ) + return true; + return false; +}; + +inline bool argloc_t::in_stack() const +{ + if ( !is_scattered() ) + return is_stkoff(); + for ( const auto &part : scattered() ) + if ( !part.is_stkoff() ) + return false; + return true; +} + +inline bool argloc_t::is_mixed_scattered() const +{ + if ( !is_scattered() ) + return false; + bool reg_found = false; + bool stkoff_found = false; + for ( const auto &part : scattered() ) + { + if ( part.is_reg() ) + reg_found = true; + if ( part.is_stkoff() ) + stkoff_found = true; + } + return reg_found && stkoff_found; +} + +inline bool tinfo_t::get_named_type( + const til_t *til, + const char *name, + type_t decl_type, + bool resolve, + bool try_ordinal) +{ + if ( name == nullptr ) + return false; + typedef_type_data_t tp(til, name, resolve); + return create_typedef(tp, decl_type, try_ordinal); +} + +inline bool tinfo_t::get_numbered_type( + const til_t *til, + uint32 ordinal, + type_t decl_type, + bool resolve) +{ + typedef_type_data_t tp(til, ordinal, resolve); + return create_typedef(tp, decl_type, false); +} + +inline bool tinfo_t::create_udt(bool is_union) +{ + udt_type_data_t p; + p.is_union = is_union; + return create_udt(p); +} + +inline bool tinfo_t::create_udt(udt_type_data_t &p) +{ + return create_udt(p, p.is_union ? BTF_UNION : BTF_STRUCT); +} + +inline bool tinfo_t::create_enum(bte_t bte) +{ + enum_type_data_t p; + p.bte = BTE_ALWAYS | bte; + return create_enum(p); +} + +inline bool tinfo_t::create_ptr( + const tinfo_t &tif, + uchar bps, + type_t decl_type) +{ + ptr_type_data_t pi(tinfo_t(), bps); + pi.obj_type = tif; + return create_ptr(pi, decl_type); +} + +inline bool tinfo_t::create_array( + const tinfo_t &tif, + uint32 nelems, + uint32 base, + type_t decl_type) +{ + array_type_data_t ai(base, nelems); + ai.elem_type = tif; + return create_array(ai, decl_type); +} + +inline bool tinfo_t::create_bitfield( + uchar nbytes, + uchar width, + bool _is_unsigned, + type_t decl_type) +{ + bitfield_type_data_t bi(nbytes, width, _is_unsigned); + return create_bitfield(bi, decl_type); +} + +inline bool tinfo_t::convert_array_to_ptr() +{ + bool ok = false; + array_type_data_t ai; + if ( get_array_details(&ai) ) + { + ptr_type_data_t pi; + pi.obj_type.swap(ai.elem_type); + create_ptr(pi); + ok = true; + } + return ok; +} + +inline tinfo_code_t tinfo_t::add_udm( + const char *name, + const tinfo_t &type, + uint64 offset, + uint etf_flags, + size_t times, + ssize_t idx) +{ + return add_udm(udm_t(name, type, offset), etf_flags, times, idx); +} + +inline tinfo_code_t tinfo_t::add_udm( + const char *name, + type_t type, + uint64 offset, + uint etf_flags, + size_t times, + ssize_t idx) +{ + return add_udm(udm_t(name, type, offset), etf_flags, times, idx); +} + +inline tinfo_code_t tinfo_t::add_udm( + const char *name, + const char *type, + uint64 offset, + uint etf_flags, + size_t times, + ssize_t idx) +{ + return add_udm(udm_t(name, type, offset), etf_flags, times, idx); +} + +inline int tinfo_t::find_udm(uint64 offset, int strmem_flags) const +{ + udm_t udm; + udm.offset = offset; + return find_tinfo_udt_member(&udm, typid, STRMEM_OFFSET|strmem_flags); +} + +inline int tinfo_t::find_udm(const char *name, int strmem_flags) const +{ + udm_t udm; + udm.name = name; + return find_tinfo_udt_member(&udm, typid, STRMEM_NAME|strmem_flags); +} + +inline int tinfo_t::get_udm(udm_t *out, const char *name) const +{ + udm_t tmp; + tmp.name = name; + int rc = find_udm(&tmp, STRMEM_NAME); + if ( rc >= 0 ) + out->swap(tmp); + return rc; +} + +inline int tinfo_t::get_udm(udm_t *out, size_t index) const +{ + udm_t tmp; + tmp.offset = index; + int rc = find_udm(&tmp, STRMEM_INDEX); + if ( rc >= 0 ) + out->swap(tmp); + return rc; + +} + +inline int tinfo_t::get_udm_by_offset(udm_t *out, uint64 offset) const +{ + udm_t tmp; + tmp.offset = offset; + int rc = find_udm(&tmp, STRMEM_OFFSET); + if ( rc >= 0 ) + out->swap(tmp); + return rc; +} + +inline tinfo_code_t tinfo_t::add_edm( + const char *name, + uint64 value, + bmask64_t bmask, + uint etf_flags, + ssize_t idx) +{ + return add_edm(edm_t(name, value), bmask, etf_flags, idx); +} + +inline tinfo_code_t tinfo_t::del_edm( + const char *name, + uint etf_flags) +{ + edm_t edm; + ssize_t idx = get_edm(&edm, name); + return idx < 0 ? TERR_NOT_FOUND : del_edm(idx, etf_flags); +} + +inline tinfo_code_t tinfo_t::del_edm_by_value( + uint64 value, + uint etf_flags, + bmask64_t bmask, + uchar serial) +{ + edm_t edm; + ssize_t idx = get_edm_by_value(&edm, value, bmask, serial); + return idx < 0 ? TERR_NOT_FOUND : del_edm(idx, etf_flags); +} + +inline ssize_t udt_type_data_t::get_best_fit_member(udm_t *out, asize_t disp) const +{ + ssize_t rc = get_best_fit_member(disp); + if ( rc >= 0 ) + *out = at(rc); + return rc; +} + +/// ::BT_PTR: If the current type is a pointer, return the pointed object. +/// If the current type is not a pointer, return the current type. +/// See also get_ptrarr_object() and get_pointed_object() +inline tinfo_t remove_pointer(const tinfo_t &tif) +{ + tinfo_t r; + r.typid = get_tinfo_property(tif.typid, tinfo_t::GTA_SAFE_PTR_OBJ); + return r; +} + +/// Information about how to modify the current type, used by ::tinfo_visitor_t. +struct type_mods_t +{ + tinfo_t type; ///< current type + qstring name; ///< current type name + qstring cmt; ///< comment for current type + int flags = 0;///< \ref TVIS_ +/// \defgroup TVIS_ Type modification bits +/// used by type_mods_t::flags +///@{ +#define TVIS_TYPE 0x0001 ///< new type info is present +#define TVIS_NAME 0x0002 ///< new name is present (only for funcargs and udt members) +#define TVIS_CMT 0x0004 ///< new comment is present (only for udt members) +#define TVIS_RPTCMT 0x0008 ///< the new comment is repeatable +///@} + void clear() { flags = 0; } + + /// The visit_type() function may optionally save the modified type info. + /// Use the following functions for that. The new name and comment will be applied + /// only if the current tinfo element has storage for them. + void set_new_type(const tinfo_t &t) { type = t; flags |= TVIS_TYPE; } + void set_new_name(const qstring &n) { name = n; flags |= TVIS_NAME; } + void set_new_cmt(const qstring &c, bool rptcmt) + { + cmt = c; + flags |= TVIS_CMT; + setflag(flags, TVIS_RPTCMT, rptcmt); + } + + bool has_type() const { return (flags & TVIS_TYPE) != 0; } + bool has_name() const { return (flags & TVIS_NAME) != 0; } + bool has_cmt() const { return (flags & TVIS_CMT) != 0; } + bool is_rptcmt() const { return (flags & TVIS_RPTCMT) != 0; } + bool has_info() const { return flags != 0; } +}; + +/// Visit all subtypes of a type. Derive your visitor from this class and use apply_to() +struct tinfo_visitor_t +{ + int state; ///< \ref TVST_ +/// \defgroup TVST_ tinfo visitor states +/// used by tinfo_visitor_t::state +///@{ +#define TVST_PRUNE 0x01 ///< don't visit children of current type +#define TVST_DEF 0x02 ///< visit type definition (meaningful for typerefs) +#define TVST_LEVEL 0x04 // has level member (internal use) +///@} + int level; // recursion level (internal use) + tinfo_visitor_t(int s=0) : state(s|TVST_LEVEL), level(0) {} + + virtual ~tinfo_visitor_t() {} + + /// Visit a subtype. + /// this function must be implemented in the derived class. + /// it may optionally fill out with the new type info. this can be used to + /// modify types (in this case the 'out' argument of apply_to() may not be nullptr) + /// return 0 to continue the traversal. + /// return !=0 to stop the traversal. + virtual int idaapi visit_type( + type_mods_t *out, + const tinfo_t &tif, + const char *name, + const char *cmt) = 0; + + /// To refuse to visit children of the current type, use this: + void prune_now() { state |= TVST_PRUNE; } + + /// Call this function to initiate the traversal + int apply_to(const tinfo_t &tif, type_mods_t *out=nullptr, const char *name=nullptr, const char *cmt=nullptr) + { + return visit_subtypes(this, out, tif, name, cmt); + } +}; + + +//------------------------------------------------------------------------ +// Definitions for packing/unpacking idc objects + +/// Object that represents a register +struct regobj_t +{ + int regidx; ///< index into dbg->registers + int relocate; ///< 0-plain num, 1-must relocate + bytevec_t value; + size_t size() const { return value.size(); } +}; +DECLARE_TYPE_AS_MOVABLE(regobj_t); +typedef qvector<regobj_t> regobjvec_t; + +struct regobjs_t : public regobjvec_t {}; /// Collection of register objects + + +/// Read a typed idc object from the database + +idaman error_t ida_export unpack_idcobj_from_idb( + idc_value_t *obj, + const tinfo_t &tif, + ea_t ea, + const bytevec_t *off0, // if !nullptr: bytevec that represents object at 'ea' + int pio_flags=0); +#define PIO_NOATTR_FAIL 0x0004 ///< missing attributes are not ok +#define PIO_IGNORE_PTRS 0x0008 ///< do not follow pointers + + +/// Read a typed idc object from the byte vector + +idaman error_t ida_export unpack_idcobj_from_bv( + idc_value_t *obj, + const tinfo_t &tif, + const bytevec_t &bytes, + int pio_flags=0); + + +/// Write a typed idc object to the database + +idaman error_t ida_export pack_idcobj_to_idb( + const idc_value_t *obj, + const tinfo_t &tif, + ea_t ea, + int pio_flags=0); + + +/// Write a typed idc object to the byte vector. +/// Byte vector may be non-empty, this function will append data to it + +idaman error_t ida_export pack_idcobj_to_bv( + const idc_value_t *obj, + const tinfo_t &tif, + relobj_t *bytes, + void *objoff, // nullptr - append object to 'bytes' + // if not nullptr: + // in: int32*: offset in 'bytes' for the object + // -1 means 'do not store the object itself in bytes + // store only pointed objects' + // out: data for object (if *(int32*)objoff == -1) + int pio_flags=0); + + +/// Helper function for the processor modules. +/// to be called from \ph{use_stkarg_type} + +idaman bool ida_export apply_tinfo_to_stkarg( + const insn_t &insn, + const op_t &x, + uval_t v, + const tinfo_t &tif, + const char *name); + +//------------------------------------------------------------------------ +// Helper struct for the processor modules: process call arguments +struct argtinfo_helper_t +{ + size_t reserved = 0; + + virtual ~argtinfo_helper_t() {} + + /// Set the operand type as specified + virtual bool idaapi set_op_tinfo( + const insn_t &insn, + const op_t &x, + const tinfo_t &tif, + const char *name) = 0; + + /// Is the current insn a stkarg load?. + /// if yes: + /// - src: index of the source operand in \insn_t{ops} + /// - dst: index of the destination operand in \insn_t{ops} + /// \insn_t{ops}[dst].addr is expected to have the stack offset + virtual bool idaapi is_stkarg_load(const insn_t &insn, int *src, int *dst) = 0; + + /// The call instruction with a delay slot?. + virtual bool idaapi has_delay_slot(ea_t /*caller*/) { return false; } + + /// This function is to be called by the processor module in response + /// to ev_use_arg_types. + inline void use_arg_tinfos(ea_t caller, func_type_data_t *fti, funcargvec_t *rargs); +}; + +/// Do not call this function directly, use argtinfo_helper_t +idaman void ida_export gen_use_arg_tinfos( + struct argtinfo_helper_t *_this, + ea_t caller, + func_type_data_t *fti, + funcargvec_t *rargs); + +inline void argtinfo_helper_t::use_arg_tinfos( + ea_t caller, + func_type_data_t *fti, + funcargvec_t *rargs) +{ + gen_use_arg_tinfos(this, caller, fti, rargs); +} + +//------------------------------------------------------------------------- + +/// Looks for a hole at the beginning of the stack arguments. Will make use +/// of the IDB's func_t function at that place (if present) to help determine +/// the presence of such a hole. + +idaman bool ida_export func_has_stkframe_hole(ea_t ea, const func_type_data_t &fti); + +//------------------------------------------------------------------------- +/// Interface class - see ::ida_lowertype_helper_t +class lowertype_helper_t +{ +public: + virtual ~lowertype_helper_t() {} + virtual bool idaapi func_has_stkframe_hole( + const tinfo_t &candidate, + const func_type_data_t &candidate_data) = 0; + + virtual int idaapi get_func_purged_bytes( + const tinfo_t &candidate, + const func_type_data_t &candidate_data) = 0; +}; + +//------------------------------------------------------------------------- +/// An implementation of ::lowertype_helper_t that has access to the +/// IDB, and thus can help spot holes in the stack arguments. +class ida_lowertype_helper_t : public lowertype_helper_t +{ + const tinfo_t &tif; + ea_t ea; + int purged_bytes; + +public: + ida_lowertype_helper_t(const tinfo_t &_tif, ea_t _ea, int _pb) + : tif(_tif), ea(_ea), purged_bytes(_pb) {} + + virtual bool idaapi func_has_stkframe_hole( + const tinfo_t &candidate, + const func_type_data_t &candidate_data) override + { + return candidate == tif + ? ::func_has_stkframe_hole(ea, candidate_data) + : false; + } + + virtual int idaapi get_func_purged_bytes( + const tinfo_t &candidate, + const func_type_data_t &) override + { + return candidate == tif + ? purged_bytes + : -1; + } +}; + +//------------------------------------------------------------------------- +/// Lower type. +/// Inspect the type and lower all function subtypes using lower_func_type(). \n +/// We call the prototypes usually encountered in source files "high level" \n +/// They may have implicit arguments, array arguments, big structure retvals, etc \n +/// We introduce explicit arguments (i.e. 'this' pointer) and call the result \n +/// "low level prototype". See #FTI_HIGH. +/// +/// In order to improve heuristics for recognition of big structure retvals, \n +/// it is recommended to pass a helper that will be used to make decisions. \n +/// That helper will be used only for lowering 'tif', and not for the children \n +/// types walked through by recursion. +/// \retval 1 removed #FTI_HIGH, +/// \retval 2 made substantial changes +/// \retval -1 failure + +idaman int ida_export lower_type( + til_t *til, + tinfo_t *tif, + const char *name=nullptr, + lowertype_helper_t *_helper=nullptr); + + +/// Replace references to ordinal types by name references. +/// This function 'unties' the type from the current local type library +/// and makes it easier to export it. +/// \param til type library to use. may be nullptr. +/// \param tif type to modify (in/out) +/// \retval number of replaced subtypes, -1 on failure + +idaman int ida_export replace_ordinal_typerefs(til_t *til, tinfo_t *tif); + + +/// See begin_type_updating() +enum update_type_t +{ + UTP_ENUM, + UTP_STRUCT, +}; + +/// Mark the beginning of a large update operation on the types. +/// Can be used with add_enum_member(), add_struc_member, etc... +/// Also see end_type_updating() + +idaman void ida_export begin_type_updating(update_type_t utp); + + +/// Mark the end of a large update operation on the types (see begin_type_updating()) + +idaman void ida_export end_type_updating(update_type_t utp); + +//------------------------------------------------------------------------- +/// \defgroup type_helpers Local types information and manipulation helpers +///@{ + +/// Get named local type TID +/// \param name type name +/// \return TID or BADADDR +idaman tid_t ida_export get_named_type_tid(const char *name); + +inline tid_t edm_t::get_tid() const { return get_named_type_tid(name.c_str()); } + + +/// Get a type name for the specified TID +/// \param tid type TID +/// \param[out] out type name +/// \return true if there is type with TID +/// \note this function is the inverse to get_named_type_tid +idaman bool ida_export get_tid_name(qstring *out, tid_t tid); + + +/// Get type ordinal number for TID +/// \param tid type/enum constant/udt member TID +/// \return type ordinal number or 0 +idaman uint32 ida_export get_tid_ordinal(tid_t tid); + + +/// Get udt member by full name +/// \param[out] udm member, can be NULL +/// \param fullname udt member name in format <udt name>.<member name> +/// \return member index into udt_type_data_t or -1 +idaman ssize_t ida_export get_udm_by_fullname(udm_t *udm, const char *fullname); + + +/// Calculate IDA info from udt member +/// \param flags[out] flags (see \ref bytes.hpp) for udt member +/// \param ti[out] additional representation information, see \ref set_opinfo() +/// \param udm udt member +/// \param refinfo_ea if specified will be used to adjust the \ref refinfo_t data +/// \note any output argument may be nullptr +idaman bool ida_export get_idainfo_by_udm( + flags64_t *flags, + opinfo_t *ti, + const udm_t &udm, + ea_t refinfo_ea=BADADDR); + + +/// Create type enum +/// \param enum_name type name +/// \param ei enum type data +/// \param enum_width the width of an enum element +/// allowed values: 0 (unspecified),1,2,4,8,16,32,64 +/// \param sign enum sign +/// \param convert_to_bitmask +/// try convert enum to bitmask enum +/// \param enum_cmt enum type comment +/// \return enum TID +inline tid_t create_enum_type( + const char *enum_name, + enum_type_data_t &ei, + int enum_width, + type_sign_t sign, + bool convert_to_bitmask, + const char *enum_cmt=nullptr) +{ + if ( sign == type_signed ) + ei.taenum_bits |= TAENUM_SIGNED; + else if ( sign == type_unsigned ) + ei.taenum_bits |= TAENUM_UNSIGNED; + ei.set_nbytes(enum_width); + + tid_t tid = BADADDR; + tinfo_t tif; + if ( tif.create_enum(ei) + && (enum_cmt == nullptr || tif.set_type_cmt(enum_cmt) == TERR_OK) + && tif.set_enum_is_bitmask(convert_to_bitmask ? tinfo_t::ENUMBM_ON : tinfo_t::ENUMBM_OFF) == TERR_OK + && tif.set_named_type(nullptr, enum_name, NTF_TYPE|NTF_REPLACE) == TERR_OK ) + { + tid = get_named_type_tid(enum_name); + } + return tid; +} + +///@} type_helpers + +//------------------------------------------------------------------------- +/// See format_cdata() +struct format_data_info_t +{ + int ptvf; /// \ref PTV_ +/// \defgroup PTV_ C data formatting properties +/// used by format_data_info_t::ptvf +///@{ +#define PTV_DEREF 0x0001 ///< take value to print from the debugged process. + ///< #VT_LONG: the address is specified by idc_value_t::num + ///< #VT_PVOID: argloc_t is pointed by idc_value_t::pvoid +#define PTV_QUEST 0x0002 ///< print '?' for uninited data +#define PTV_EMPTY 0x0004 ///< return empty string for uninited data; + ///< should not specify PTV_QUEST and PTV_EMPTY together +#define PTV_CSTR 0x0008 ///< print constant strings inline +#define PTV_EXPAND 0x0010 ///< print only top level on separate lines; + ///< max_length applies to separate lines; + ///< margin is ignored +#define PTV_LZERO 0x0020 ///< print numbers with leading zeroes (only for hex/oct/bin) +#define PTV_STPFLT 0x0040 ///< fail on bad floating point numbers + ///< (if not set, just print ?flt for them) +#define PTV_SPACE 0x0080 ///< add spaces after commas and around braces +#define PTV_DEBUG 0x0100 ///< format output for debugger +#define PTV_NOPTR 0x0200 ///< prevent pointer values from appearing in the output +#define PTV_NTOP 0x40000000 ///< internal flag, do not use +#define PTV_KEEP 0x80000000 ///< internal flag, do not use +///@} + int radix; ///< number representation (8,10,16) + int max_length; ///< max length of the formatted text (0 means no limit) + ///< should be used to format huge arrays for the screen, + ///< we cannot display the whole array anyway + ///< if this limit is hit, the function returns false + ///< and qerrno is set to eMaxLengthExceeded + int arrbase; ///< for arrays: the first element of array to print + int arrnelems; ///< for arrays: number of elements to print + int margin; ///< length of one line (0 means to print everything on one line) + ///< if an item cannot be printed in a shorter way, + ///< some output lines can be considerably longer + ///< 1 means each item on its own line + int indent; ///< how many spaces to use to indent nested structures/arrays + + format_data_info_t() + : ptvf(PTV_EMPTY|PTV_CSTR|PTV_SPACE), radix(10), max_length(0), + arrbase(0), arrnelems(0), + margin(80), indent(2) {} +}; + +/// Additional information about the output lines +struct valinfo_t +{ + argloc_t loc; + qstring label; + tinfo_t type; + valinfo_t(argloc_t l=argloc_t(), const char *name=nullptr, const tinfo_t &tif=tinfo_t()) + : loc(l), label(name), type(tif) {} + void swap(valinfo_t &r) + { + loc.swap(r.loc); + label.swap(r.label); + type.swap(r.type); + } + DEFINE_MEMORY_ALLOCATION_FUNCS() +}; +DECLARE_TYPE_AS_MOVABLE(valinfo_t); + + +/// Text representation of a data value (value string). +/// This structure is used before we decide how to represent it, +/// on one line or on many lines +class valstr_t +{ +public: + qstring oneline; ///< result if printed on one line in UTF-8 encoding + size_t length; ///< length if printed on one line + struct valstrs_t *members; ///< strings for members, each member separately + valinfo_t *info; ///< additional info + int props; ///< temporary properties, used internally +#define VALSTR_OPEN 0x01 ///< printed opening curly brace '{' + + valstr_t() : length(0), members(nullptr), info(nullptr), props(0) {} + ~valstr_t(); + DEFINE_MEMORY_ALLOCATION_FUNCS() +private: + struct flatten_args_t + { + const valstr_t *may_not_collapse; + int ptvf; + int max_length; + int margin; + int indent; + }; + friend struct valstr_sink_t; + void update_length(int ptvf); + void set_oneline(const char *line, int len) + { + oneline.append(line, len); + length = oneline.length(); + } + void consume_oneline(const qstring &line) + { + oneline.append(line); + length = oneline.length(); + } + bool append_char(char c, int max_length); + bool convert_to_one_line(int ptvf, int max_length); + bool flatten(const flatten_args_t &flargs, int level); +}; +DECLARE_TYPE_AS_MOVABLE(valstr_t); +typedef qvector<valstr_t> valstrvec_t; + +struct valstrs_t : public valstrvec_t {}; ///< Collection of value strings + +inline valstr_t::~valstr_t() +{ + delete members; + delete info; +} + + +/// Format a data value as a C initializer. +/// \param outvec buffer for the formatted string(s). may be nullptr +/// \param idc_value value to format +/// \param tif type of the data to format. +/// if nullptr and #PTV_DEREF is specified, take tinfo from idb +/// \param vtree more detailed output info +/// \param fdi formatting options +/// \return success. if failed, see qerrno for more info + +idaman bool ida_export format_cdata( + qstrvec_t *outvec, + const idc_value_t &idc_value, + const tinfo_t *tif, + valstr_t *vtree=nullptr, + const format_data_info_t *fdi=nullptr); + +/// Flush formatted text +struct text_sink_t +{ + virtual ~text_sink_t() {} + /// \return 0-ok, otherwise print_cdata will stop + virtual int idaapi print(const char *str) = 0; +}; + + +/// The same as format_cdata(), but instead of returning the answer in a vector, print it. +/// This function can handle very huge data volume without using too much memory. +/// As soon as the output text becomes too long, the function prints it and +/// flushes its internal buffers. +/// \retval 0 ok +/// \retval -1 printing failed, check qerrno +/// \retval else code returned by text_sink_t::print() + +idaman int ida_export print_cdata( + text_sink_t &printer, + const idc_value_t &idc_value, + const tinfo_t *tif, + const format_data_info_t *fdi=nullptr); + +//------------------------------------------------------------------------- +/// \defgroup PDF_ print_decls() flags +///@{ +#define PDF_INCL_DEPS 0x1 ///< Include all type dependencies +#define PDF_DEF_FWD 0x2 ///< Allow forward declarations +#define PDF_DEF_BASE 0x4 ///< Include base types: __int8, __int16, etc.. +#define PDF_HEADER_CMT 0x8 ///< Prepend output with a descriptive comment +///@} + +typedef qvector<uint32> ordvec_t; + +/// Print types (and possibly their dependencies) in a format suitable +/// for using in a header file. This is the reverse parse_decls(). +/// \param printer a handler for printing text +/// \param til the type library holding the ordinals +/// \param ordinals ordinals of types to export. nullptr means: all ordinals in til +/// \param pdf_flags flags for the algorithm. A combination of PDF_ constants +/// \retval >0 the number of types exported +/// \retval 0 an error occurred +/// \retval <0 the negated number of types exported. There were minor errors +/// and the resulting output might not be compilable. + +idaman int ida_export print_decls( + text_sink_t &printer, + const til_t *til, + const ordvec_t *ordinals, + uint32 pdf_flags); + + +/// Calculate max number of lines of a formatted c data, when expanded (#PTV_EXPAND). +/// \param loc location of the data (::ALOC_STATIC or ::ALOC_CUSTOM) +/// \param tif type info +/// \param dont_deref_ptr consider 'ea' as the ptr value +/// \retval 0 data is not expandable +/// \retval -1 error, see qerrno +/// \retval else the max number of lines + +idaman int ida_export calc_number_of_children( + const argloc_t &loc, + const tinfo_t &tif, + bool dont_deref_ptr=false); + + +/// Return a C expression that can be used to represent an enum member. +/// If the value does not correspond to any single enum member, this function tries +/// to find a bitwise combination of enum members that correspond to it. +/// If more than half of value bits do not match any enum members, it fails. +/// \param buf output buffer +/// \param tif enumeration type +/// \param serial which enumeration member to use (0 means the first with the given value) +/// \param value value to search in the enumeration type +/// \return success + +idaman bool ida_export get_enum_member_expr( + qstring *buf, + const tinfo_t &tif, + int serial, + uint64 value); + + +//------------------------------------------------------------------------- +// Dialogs to choose a symbol from a type library +//------------------------------------------------------------------------ + +/// A symbol in a type library +struct til_symbol_t +{ + const char *name; ///< symbol name + const til_t *til; ///< pointer to til + til_symbol_t(const char *n = nullptr, const til_t *t = nullptr): name(n), til(t) {} +}; +DECLARE_TYPE_AS_MOVABLE(til_symbol_t); + + +/// Helper class for choose_named_type(). +/// Controls which types are displayed when choosing types. + +struct predicate_t +{ + virtual bool idaapi should_display( + const til_t *til, + const char *name, + const type_t *type, + const p_list *fields) = 0; + virtual ~predicate_t() {} +}; + + +/// Choose a type from a type library. +/// \param out_sym pointer to be filled with the chosen type +/// \param root_til pointer to starting til (the function will inspect the base tils if allowed by flags) +/// \param title title of listbox to display +/// \param ntf_flags combination of \ref NTF_ +/// \param predicate predicate to select types to display (maybe nullptr) +/// \return false if nothing is chosen, otherwise true + +idaman bool ida_export choose_named_type( + til_symbol_t *out_sym, + const til_t *root_til, + const char *title, + int ntf_flags, + predicate_t *predicate=nullptr); + + +/// Controls which types are displayed/selected when choosing local types. +/// \retval 0 skip type +/// \retval 1 include + +typedef int idaapi local_tinfo_predicate_t(uint32 ord, const tinfo_t &type, void *ud); + + +/// Choose a type from the local type library. +/// \param ti pointer to til +/// \param title title of listbox to display +/// \param func predicate to select types to display (maybe nullptr) +/// \param def_ord ordinal to position cursor before choose +/// \param ud user data +/// \return == 0 means nothing is chosen, otherwise an ordinal number + +idaman uint32 ida_export choose_local_tinfo( + const til_t *ti, + const char *title, + local_tinfo_predicate_t *func = nullptr, + uint32 def_ord = 0, + void *ud = nullptr); + + +/// Choose a type from the local type library and specify the pointer shift value. +/// \param delta pointer shift value +/// \param ti pointer to til +/// \param title title of listbox to display +/// \param func predicate to select types to display (maybe nullptr) +/// \param def_ord ordinal to position cursor before choose +/// \param ud user data +/// \return == 0 means nothing is chosen, otherwise an ordinal number + +idaman uint32 ida_export choose_local_tinfo_and_delta( + int32 *delta, + const til_t *ti, + const char *title, + local_tinfo_predicate_t *func = nullptr, + uint32 def_ord = 0, + void *ud = nullptr); + + +/// Callback for \ref visit_edms +/// \param ei enum type details +/// \param idx constant index +/// \param value applied value +/// \param bmask group bitmask +/// \return 0 to continue, stop visiting enum members otherwise +using enum_type_visitor_t = std::function<ssize_t(const struct enum_type_data_t &ei, size_t idx, uint64 value, uint64 bmask)>; + +/// Visit enum members having the specified value +/// \param tif enum type +/// \param value value to visit +/// \param nbytes size of value in bytes +/// \param serial use the enum constant with the specified serial; if it does not exist, use serial 0 +/// \param visitor the visitor function +/// \return -1 if TIF is not a enum type; +/// code from VISITOR or 0 if no member was visited +/// \note +/// 1. For ordinary enums, two values are checked for equality: +/// * the zero-extended VALUE +/// * the sign-extended VALUE +/// 2. For bitmask enums, the following members are visited: +/// * the regular member of the bitmask group +/// * the bitmask itself +/// 3. Enum constant with value 0 is acceptable +idaman ssize_t ida_export visit_edms( + const tinfo_t &tif, + uint64 value, + int nbytes, + uchar serial, + const enum_type_visitor_t &visitor); + +//------------------------------------------------------------------------- +inline ssize_t processor_t::equal_reglocs(const argloc_t &a1, const argloc_t &a2) +{ + if ( PH.ti() ) + return notify(ev_equal_reglocs, &a1, &a2); + else + return a1.compare(a2); +} + +//------------------------------------------------------------------------- +inline ssize_t processor_t::_decorate_name( + qstring *outbuf, + const char *name, + bool mangle, + callcnv_t cc, + const tinfo_t &type) +{ + ssize_t code = notify(ev_decorate_name, outbuf, name, mangle, cc, &type); + if ( code == 0 ) + code = gen_decorate_name(outbuf, name, mangle, cc, &type); + return code; +} + +//------------------------------------------------------------------------- +inline bool calc_retloc(argloc_t *retloc, const tinfo_t &rettype, callcnv_t cc) +{ + func_type_data_t fti; + fti.set_cc(cc); + fti.rettype = rettype; + if ( !calc_retloc(&fti) ) + return false; + retloc->swap(fti.retloc); + return true; +} + +//------------------------------------------------------------------------- +// this structure is used to hold context data for the 'types' view +struct til_type_ref_t +{ + size_t cb = sizeof(til_type_ref_t); + tinfo_t tif; //< type info, !empty() during runtime + tif_cursor_t cursor = TIF_CURSOR_HEADER; //< 'pointer' inside a type + uint32 ordinal = 0; //< cached value of tif.get_ordinal() + bool is_writable = false; //< can we modify the type? (either in a RW til, or writable backend) + bool is_detached = false; //< type is not backed by a backend that will cause views to be updated automatically + bool is_forward = false; //< is the type a forward declaration? + type_t kind = BT_UNK; //< one of BTF_TYPEDEF, BTF_STRUCT, BTF_UNION, BTF_ENUM, BT_FUNC + ssize_t memidx = -1; //< index of the current BTF_STRUCT, BTF_UNION, BTF_ENUM member or BT_FUNC argument + size_t nmembers = 0; //< number of members in the BTF_STRUCT, BTF_UNION, BTF_ENUM or number of arguments in the BT_FUNC + + /// BTF_STRUCT or BTF_UNION: the current member + udm_t udm; + size_t total_size = 0; + size_t unpadded_size = 0; + uint64 last_udm_offset = 0; + uint64 bucket_start = uint64(-1); //< bucket offset (equal to 'offset' except for bitfields) + int bf_bitoff = -1; //< for bitfields, bit offset from bucket_start + uint64 offset = uint64(-1); //< the current offset (regardless if + //< we have a defined member at the current + //< offset or not) + + /// BTF_ENUM: the current enum member + edm_t edm; + + /// BT_FUNC: the current argument, nullptr - ellipsis + const funcarg_t *fa = nullptr; + + void clear() { *this = til_type_ref_t(); } + bool on_member() const { return memidx != -1; } + bool is_typedef() const { return kind == BTF_TYPEDEF; } + bool is_struct() const { return kind == BTF_STRUCT; } + bool is_union() const { return kind == BTF_UNION; } + bool is_enum() const { return kind == BTF_ENUM; } + bool is_func() const { return kind == BT_FUNC; } + bool is_udt() const { return kind == BTF_STRUCT || kind == BTF_UNION; } +}; + + +#endif // _TYPEINF_HPP + +``` + +`IdaSDK/ua.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _UA_HPP +#define _UA_HPP + +#include <kernwin.hpp> // for btoa() +#include <lines.hpp> // for colors +#include <xref.hpp> // add_cref() +#include <bytes.hpp> // get_byte(), ... + +/*! \file ua.hpp + + \brief Functions that deal with the disassembling of program instructions. + + There are 2 kinds of functions: + - functions that are called from the kernel + to disassemble an instruction. These functions + call IDP module for it. + - functions that are called from IDP module to + disassemble an instruction. We will call them + 'helper functions'. + + Disassembly of an instruction is made in three steps: + -# analysis: ana.cpp + -# emulation: emu.cpp + -# conversion to text: out.cpp + + The kernel calls the IDP module to perform these steps. + At first, the kernel always calls the analysis. The analyzer + must decode the instruction and fill the insn_t instance + that it receives through its callback. + It must not change anything in the database. + + The second step, the emulation, is called for each instruction. + This step must make necessary changes to the database, + plan analysis of subsequent instructions, track register + values, memory contents, etc. Please keep in mind that the kernel may call + the emulation step for any address in the program - there is no + ordering of addresses. Usually, the emulation is called + for consecutive addresses but this is not guaranteed. + + The last step, conversion to text, is called each time an instruction + is displayed on the screen. The kernel will always call the analysis step + before calling the text conversion step. + The emulation and the text conversion steps should use the information stored + in the insn_t instance they receive. They should not access the bytes + of the instruction and decode it again - this should only be done in + the analysis step. +*/ + +struct procmod_t; +struct processor_t; +struct asm_t; + +/// \defgroup operands Operands +/// Work with instruction operands + +//-------------------------------------------------------------------------- +// T Y P E O F O P E R A N D +//-------------------------------------------------------------------------- +typedef uchar optype_t; ///< see \ref o_ +/// \defgroup o_ Operand types +/// \ingroup operands +/// +/// Various types of instruction operands. +/// The kernel already knows about some operand types and associates them +/// with fields in ::op_t. +/// +/// IDA also allows you define processor specific operand types (o_idpspec...). +/// You are free to give any meaning to these types. We suggest you to create a +/// #define to use mnemonic names. However, don't forget that the kernel will +/// know nothing about operands of those types. +/// You may use any additional fields of ::op_t to store +/// processor specific operand information. +///@{ +const optype_t + o_void = 0, ///< No Operand. + o_reg = 1, ///< General Register (al,ax,es,ds...). + o_mem = 2, ///< A direct memory reference to a data item. + ///< Use this operand type when the address can be + ///< calculated statically. + o_phrase = 3, ///< An indirect memory reference that uses a register: [reg] + ///< There can be several registers but no displacement. + o_displ = 4, ///< An indirect memory reference that uses a register and + ///< has an immediate constant added to it: [reg+N] + ///< There can be several registers. + o_imm = 5, ///< An immediate Value (constant). + o_far = 6, ///< An immediate far code reference (inter-segment) + o_near = 7, ///< An immediate near code reference (intra-segment) + o_idpspec0 = 8, ///< processor specific type. + o_idpspec1 = 9, ///< processor specific type. + o_idpspec2 = 10, ///< processor specific type. + o_idpspec3 = 11, ///< processor specific type. + o_idpspec4 = 12, ///< processor specific type. + o_idpspec5 = 13; ///< processor specific type. + ///< (there can be more processor specific types) +///@} + +/// \var o_reg +/// The register number should be stored in op_t::reg. +/// All processor registers, including special registers, can be +/// represented by this operand type. +/// +/// \var o_mem +/// A direct memory data reference whose target address is known at compilation time. +/// The target virtual address is stored in op_t::addr and the full address +/// is calculated as to_ea( \insn_t{cs}, op_t::addr ). For the processors with +/// complex memory organization the final address can be calculated +/// using other segment registers. For flat memories, op_t::addr is the final +/// address and \insn_t{cs} is usually equal to zero. In any case, the address +/// within the segment should be stored in op_t::addr. +/// +/// \var o_phrase +/// A memory reference using register contents. Indexed, register based, +/// and other addressing modes can be represented with the operand type. +/// This addressing mode cannot contain immediate values (use ::o_displ instead). +/// The phrase number should be stored in op_t::phrase. To denote the pre-increment +/// and similar features please use additional operand fields like op_t::specflag... +/// Usually op_t::phrase contains the register number and additional information +/// is stored in op_t::specflags... Please note that this operand type cannot +/// contain immediate values (except the scaling coefficients). +/// +/// \var o_displ +/// A memory reference using register contents with displacement. +/// The displacement should be stored in the op_t::addr field. The rest of information +/// is stored the same way as in ::o_phrase. +/// +/// \var o_imm +/// Any operand consisting of only a number is represented by this operand type. +/// The value should be stored in op_t::value. You may sign extend short (1-2 byte) values. +/// In any case don't forget to specify op_t::dtype (should be set for all operand types). +/// +/// \var o_far +/// If the current processor has a special addressing mode for inter-segment +/// references, then this operand type should be used instead of ::o_near. +/// If you want, you may use #PR_CHK_XREF in \ph{flag} to disable inter-segment +/// calls if ::o_near operand type is used. Currently only IBM PC uses this flag. +/// +/// \var o_near +/// A direct memory code reference whose target address is known at the compilation time. +/// The target virtual address is stored in op_t::addr and the final address +/// is always to_ea(\insn_t{cs}, op_t::addr). Usually this operand type is used for +/// the branches and calls whose target address is known. If the current +/// processor has 2 different types of references for inter-segment and intra-segment +/// references, then this should be used only for intra-segment references. +/// +/// If the above operand types do not cover all possible addressing modes, +/// then use o_idpspec... operand types. + +//-------------------------------------------------------------------------- +// O P E R A N D O F I N S T R U C T I O N +//-------------------------------------------------------------------------- +/// \defgroup operands_t Operand structure +/// \ingroup operands +/// Definition of ::op_t and related items. + +/// Operand of an instruction. \ingroup operands_t +/// This structure is filled by the analyzer. +/// Upon entrance to the analyzer, some fields of this structure are initialized: +/// - #type: ::o_void +/// - #offb: 0 +/// - #offo: 0 +/// - #flags: #OF_SHOW +class op_t +{ +public: + + /// Number of operand (0,1,2). Initialized once at the start of work. + /// You have no right to change its value. + uchar n = 0; + + /// Type of operand (see \ref o_) + optype_t type = o_void; + + /// Offset of operand value from the instruction start (0 means unknown). + /// Of course this field is meaningful only for certain types of operands. + /// Leave it equal to zero if the operand has no offset. + /// This offset should point to the 'interesting' part of operand. + /// For example, it may point to the address of a function in + /// \v{call func} + /// or it may point to bytes holding '5' in + /// \v{mov ax, [bx+5]} + /// Usually bytes pointed to this offset are relocated (have fixup information). + char offb = 0; + + /// Same as #offb (some operands have 2 numeric values used to form an operand). + /// This field is used for the second part of operand if it exists. + /// Currently this field is used only for outer offsets of Motorola processors. + /// Leave it equal to zero if the operand has no offset. + char offo = 0; + + uchar flags = 0; ///< \ref OF_ +/// \defgroup OF_ Operand flags +/// \ingroup operands_t +/// Used by op_t::flags +///@{ +#define OF_NO_BASE_DISP 0x80 ///< base displacement doesn't exist. + ///< meaningful only for ::o_displ type. + ///< if set, base displacement (op_t::addr) + ///< doesn't exist. +#define OF_OUTER_DISP 0x40 ///< outer displacement exists. + ///< meaningful only for ::o_displ type. + ///< if set, outer displacement (op_t::value) exists. +#define PACK_FORM_DEF 0x20 ///< packed factor defined. + ///< (!::o_reg + #dt_packreal) +#define OF_NUMBER 0x10 ///< the operand can be converted to a number only +#define OF_SHOW 0x08 ///< should the operand be displayed? +///@} + + /// Set operand to be shown + void set_shown() { flags |= OF_SHOW; } + /// Set operand to hidden + void clr_shown() { flags &= ~OF_SHOW; } + /// Is operand set to be shown? + bool shown() const { return (flags & OF_SHOW) != 0; } + + /// Type of operand value (see \ref dt_). + /// This is the type of the operand itself, not the size of the addressing mode. + /// for example, byte ptr [epb+32_bit_offset] will have the #dt_byte type. + op_dtype_t dtype = 0; +/// \defgroup dt_ Operand value types +/// \ingroup operands_t +/// Used by op_t::dtype +///@{ +// from here.. +#define dt_byte 0 ///< 8 bit integer +#define dt_word 1 ///< 16 bit integer +#define dt_dword 2 ///< 32 bit integer +#define dt_float 3 ///< 4 byte floating point +#define dt_double 4 ///< 8 byte floating point +#define dt_tbyte 5 ///< variable size (\ph{tbyte_size}) floating point +#define dt_packreal 6 ///< packed real format for mc68040 +// ...to here the order should not be changed, see mc68000 +#define dt_qword 7 ///< 64 bit integer +#define dt_byte16 8 ///< 128 bit integer +#define dt_code 9 ///< ptr to code +#define dt_void 10 ///< none +#define dt_fword 11 ///< 48 bit +#define dt_bitfild 12 ///< bit field (mc680x0) +#define dt_string 13 ///< pointer to asciiz string +#define dt_unicode 14 ///< pointer to unicode string +#define dt_ldbl 15 ///< long double (which may be different from tbyte) +#define dt_byte32 16 ///< 256 bit integer +#define dt_byte64 17 ///< 512 bit integer +#define dt_half 18 ///< 2-byte floating point +///@} + + // REG + union + { + uint16 reg; ///< number of register (::o_reg) + uint16 phrase; ///< number of register phrase (::o_phrase,::o_displ). + ///< you yourself define numbers of phrases + ///< as you like + }; + + /// Is register operand? + bool is_reg(int r) const { return type == o_reg && reg == r; } + + // Next fields (value, addr) bytes are used by mc68k for some float types + + // VALUE + union + { + uval_t value; ///< operand value (::o_imm) or + ///< outer displacement (::o_displ+#OF_OUTER_DISP). + ///< integer values should be in IDA's (little-endian) order. + ///< when using ieee_realcvt(), floating point values should be in the processor's native byte order. + ///< #dt_double and #dt_qword values take up 8 bytes (value and addr fields for 32-bit modules). + ///< NB: in case a #dt_dword/#dt_qword immediate is forced to float by user, + ///< the kernel converts it to processor's native order before calling FP conversion routines. + + /// This structure is defined for + /// your convenience only + struct + { + uint16 low; + uint16 high; + } value_shorts; + }; + + /// Is immediate operand? + bool is_imm(uval_t v) const { return type == o_imm && value == v; } + + // VIRTUAL ADDRESS (offset within the segment) + union + { + ea_t addr; ///< virtual address pointed or used by the operand. + ///< (::o_mem,::o_displ,::o_far,::o_near) + + /// This structure is defined for + /// your convenience only + struct + { + uint16 low; + uint16 high; + } addr_shorts; + }; + + // IDP SPECIFIC INFORMATION + union + { + ea_t specval; ///< This field may be used as you want. + /// This structure is defined for + /// your convenience only + struct + { + uint16 low; ///< IBM PC: segment register number (::o_mem,::o_far,::o_near) + uint16 high; ///< IBM PC: segment selector value (::o_mem,::o_far,::o_near) + } specval_shorts; + }; + + /// \name Special flags + /// The following fields are used only in idp modules. + /// You may use them as you want to store additional information about + /// the operand. + ///@{ + char specflag1 = 0; + char specflag2 = 0; + char specflag3 = 0; + char specflag4 = 0; + ///@} + + op_t() + { + reg = 0; + value = 0; + addr = 0; + specval = 0; + specflag1 = 0; + specflag2 = 0; + specflag3 = 0; + specflag4 = 0; + } +}; + + +//-------------------------------------------------------------------------- +// I N S T R U C T I O N +//-------------------------------------------------------------------------- +/// \defgroup instruction Instruction +/// Definition of ::insn_t and related items. + +/// Structure to hold information about an instruction. \ingroup instruction +/// This structure is filled by the analysis step of IDP and used by +/// the emulation and conversion to text steps. The kernel uses this structure too. +/// All structure fields except #cs, #ip, #ea, and op_t::n,op_t::flags of #ops +/// are initialized to zero by the kernel. The rest should be filled by ana(). + +class insn_t; +#define DECLARE_INSN_HELPERS(decl) \ +decl uint8 ida_export insn_get_next_byte(insn_t *insn); \ +decl uint16 ida_export insn_get_next_word(insn_t *insn); \ +decl uint32 ida_export insn_get_next_dword(insn_t *insn); \ +decl uint64 ida_export insn_get_next_qword(insn_t *insn); \ +decl bool ida_export insn_create_op_data(const insn_t &insn, ea_t ea, int opoff, op_dtype_t dtype); \ +decl void ida_export insn_add_cref(const insn_t &insn, ea_t to, int opoff, cref_t type); \ +decl void ida_export insn_add_dref(const insn_t &insn, ea_t to, int opoff, dref_t type); \ +decl ea_t ida_export insn_add_off_drefs(const insn_t &insn, const op_t &x, dref_t type, int outf); \ +decl bool ida_export insn_create_stkvar(const insn_t &insn, const op_t &x, adiff_t v, int flags); + +DECLARE_INSN_HELPERS(idaman) + + +class insn_t +{ +public: + insn_t() : auxpref(0) {} + + /// Current segment base paragraph. Initialized by the kernel. + ea_t cs = BADADDR; + + /// Virtual address of the instruction (address within the segment). + /// Initialized by the kernel. + ea_t ip = BADADDR; + + /// Linear address of the instruction. + /// Initialized by the kernel. + ea_t ea = BADADDR; + + /// Internal code of instruction (only for canonical insns - not user defined!). + /// IDP should define its own instruction codes. These codes are usually + /// defined in ins.hpp. The array of instruction names and features (ins.cpp) + /// is accessed using this code. + uint16 itype = 0; + + inline bool is_canon_insn(const processor_t &ph) const; ///< see \ph{is_canon_insn()} + inline uint32 get_canon_feature(const processor_t &ph) const; ///< see instruc_t::feature + inline const char *get_canon_mnem(const processor_t &ph) const; ///< see instruc_t::name + + /// Size of instruction in bytes. + /// The analyzer should put here the actual size of the instruction. + uint16 size = 0; + + union + { + uint32 auxpref; ///< processor dependent field + uint16 auxpref_u16[2]; + uint8 auxpref_u8[4]; + }; + /*u*/ char segpref = 0; ///< processor dependent field + /*u*/ char insnpref = 0; ///< processor dependent field + + /*u*/ int16 flags = 0; ///< \ref INSN_ + + op_t ops[UA_MAXOP]; ///< array of operands + + /// \defgroup Op_ Operand shortcuts + /// \ingroup instruction + /// Used for accessing members of insn_t::ops + ///@{ + #define Op1 ops[0] ///< first operand + #define Op2 ops[1] ///< second operand + #define Op3 ops[2] ///< third operand + #define Op4 ops[3] ///< fourth operand + #define Op5 ops[4] ///< fifth operand + #define Op6 ops[5] ///< sixth operand + #define Op7 ops[6] ///< seventh operand + #define Op8 ops[7] ///< eighth operand + ///@} + +/// \defgroup INSN_ Instruction flags +/// \ingroup instruction +/// Used by insn_t::flags +///@{ +#define INSN_MACRO 0x01 ///< macro instruction +#define INSN_MODMAC 0x02 ///< may modify the database to make room for the macro insn +#define INSN_64BIT 0x04 ///< belongs to 64bit segment? +///@} + + /// Is a macro instruction? + bool is_macro(void) const { return (flags & INSN_MACRO) != 0; } + + /// Belongs to a 64bit segment? +#ifdef __EA64__ + bool is_64bit(void) const { return (flags & INSN_64BIT) != 0; } +#else + bool is_64bit(void) const { return false; } +#endif + + /// \name Analysis helpers + /// The following functions return the next byte, 2 bytes, 4 bytes, + /// and 8 bytes of insn. They use and modify the size field (\insn_t{size}). + /// Normally they are used in the analyzer to get bytes of the instruction. + /// \warning These methods work only for normal (8bit) byte processors! + ///@{ + uint8 get_next_byte() + { + return insn_get_next_byte(this); + } + uint16 get_next_word() + { + return insn_get_next_word(this); + } + uint32 get_next_dword() + { + return insn_get_next_dword(this); + } + uint64 get_next_qword() + { + return insn_get_next_qword(this); + } + ///@} + + /// \name Emulator helpers + ///@{ + + /// Convert to data using information about operand value type (op_t::dtype). + /// Emulator could use this function to convert unexplored bytes to data + /// when an instruction references them. + /// This function creates data only if the address was unexplored. + /// \param ea_ linear address to be converted to data + /// \param opoff offset of the operand from the start of instruction + /// if the offset is unknown, then 0 + /// \param dtype operand value type (from op_t::dtype) + /// \retval true ok + /// \retval false failed to create data item + + bool create_op_data(ea_t ea_, int opoff, op_dtype_t dtype) const + { + return insn_create_op_data(*this, ea_, opoff, dtype); + } + + /// Convenient alias + bool create_op_data(ea_t ea_, const op_t &op) const + { + return insn_create_op_data(*this, ea_, op.offb, op.dtype); + } + + + /// Create or modify a stack variable in the function frame. + /// The emulator could use this function to create stack variables + /// in the function frame before converting the operand to a stack variable. + /// Please check with may_create_stkvars() before calling this function. + /// \param x operand (used to determine the addressing type) + /// \param v a displacement in the operand + /// \param flags_ \ref STKVAR_2 + /// \retval 1 ok, a stack variable exists now + /// \retval 0 no, couldn't create stack variable + + bool create_stkvar(const op_t &x, adiff_t v, int flags_) const + { + return insn_create_stkvar(*this, x, v, flags_); + } + + /// \defgroup STKVAR_2 Stack variable flags + /// Passed as 'flags' parameter to create_stkvar() + ///@{ +#define STKVAR_VALID_SIZE 0x0001 ///< x.dtype contains correct variable type + ///< (for insns like 'lea' this bit must be off). + ///< in general, dr_O references do not allow + ///< to determine the variable size +#define STKVAR_KEEP_EXISTING 0x0002 ///< if a stack variable for this operand already + ///< exists then we do not create a new variable + ///@} + + + /// Add a code cross-reference from the instruction. + /// \param opoff offset of the operand from the start of instruction. + /// if the offset is unknown, then 0. + /// \param to target linear address + /// \param type type of xref + + void add_cref(ea_t to, int opoff, cref_t type) const + { + insn_add_cref(*this, to, opoff, type); + } + + + /// Add a data cross-reference from the instruction. + /// See add_off_drefs() - usually it can be used in most cases. + /// \param opoff offset of the operand from the start of instruction + /// if the offset is unknown, then 0 + /// \param to target linear address + /// \param type type of xref + + void add_dref(ea_t to, int opoff, dref_t type) const + { + insn_add_dref(*this, to, opoff, type); + } + + + /// Add xrefs for an operand of the instruction. + /// This function creates all cross references for 'enum', 'offset' and + /// 'structure offset' operands. + /// Use add_off_drefs() in the presence of negative offsets. + /// \param x reference to operand + /// \param type type of xref + /// \param outf out_value() flags. These flags should match + /// the flags used to output the operand + /// \retval if is_off(): the reference target address (the same as calc_reference_data). + /// \retval if is_stroff(): #BADADDR because for stroffs the target address is unknown + /// \retval otherwise: #BADADDR because enums do not represent addresses + + ea_t add_off_drefs(const op_t &x, dref_t type, int outf) const + { + return insn_add_off_drefs(*this, x, type, outf); + } + + ///@} + +}; +#ifdef __EA64__ +CASSERT(sizeof(insn_t) == 360); +#else +CASSERT(sizeof(insn_t) == 216); +#endif + +//-------------------------------------------------------------------------- +// V A L U E O F O P E R A N D +//-------------------------------------------------------------------------- +#ifndef SWIG +/// This union is used to pass byte values to various helper functions. +union value_union_t // 16 bytes max +{ + uint8 v_char; + uint16 v_short; + uint32 v_long; + uint64 v_int64; + uval_t v_uval; + struct dq_t { uint32 low; uint32 high; } _dq; + struct dt_t { uint32 low; uint32 high; uint16 upper; } dt; + struct d128_t { uint64 low; uint64 high; } d128; + uint8 byte16[16]; +}; + +#endif // SWIG + +/// Get immediate values at the specified address. +/// This function decodes instruction at the specified address or inspects +/// the data item. It finds immediate values and copies them to 'out'. +/// This function will store the original value of the operands in 'out', +/// unless the last bits of 'F' are "...0 11111111", in which case the +/// transformed values (as needed for printing) will be stored instead. +/// \param out array of immediate values (at least 2*#UA_MAXOP elements) +/// \param ea address to analyze +/// \param n 0..#UA_MAXOP-1 operand number, OPND_ALL all the operands +/// \param F flags for the specified address +/// \param cache optional already decoded instruction or buffer for it. +/// if the cache does not contain the decoded instruction, +/// it will be updated (useful if we call get_immvals for the same +/// address multiple times) +/// \return number of immediate values (0..2*#UA_MAXOP) + +idaman size_t ida_export get_immvals( + uval_t *out, + ea_t ea, + int n, + flags64_t F, + insn_t *cache=nullptr); + + +/// Get immediate ready-to-print values at the specified address +/// \param out array of immediate values (at least 2*#UA_MAXOP elements) +/// \param ea address to analyze +/// \param n 0..#UA_MAXOP-1 operand number, OPND_ALL all the operands +/// \param F flags for the specified address +/// \param cache optional already decoded instruction or buffer for it. +/// if the cache does not contain the decoded instruction, +/// it will be updated (useful if we call get_immvals for the same +/// address multiple times) +/// \return number of immediate values (0..2*#UA_MAXOP) + +inline size_t get_printable_immvals( + uval_t *out, + ea_t ea, + int n, + flags64_t F, + insn_t *cache=nullptr) +{ + F &= ~0x100; // no FF_IVL... + F |= 0xFF; // ...but a value of 0xFF + return get_immvals(out, ea, n, F, cache); +} + + +/// Number of instructions to look back. +/// This variable is not used by the kernel. +/// Its value may be specified in ida.cfg: +/// LOOKBACK = <number>. +/// IDP may use it as you like it. +/// (TMS module uses it) + +idaman int ida_export get_lookback(void); + + +//-------------------------------------------------------------------------- +// I D P H E L P E R F U N C T I O N S - C O M M O N +//-------------------------------------------------------------------------- + +/// \name Address translation +/// The following functions can be used by processor modules to map +/// addresses from one region to another. They are especially useful +/// for microprocessors that map the same memory region to multiple address +/// ranges or use memory bank switching. +/// The user can use the following techniques to desribe address translations: +/// - some processors support the segment transation feature. +/// the user can specify the mapping in Edit, Segments, Change segment translation +/// - the user can specify mapping for an individual direct call instruction +/// by specifying it as an offset (Edit, Operand types, Offset) +/// - specify the value of the data segment virtual register (ds). +/// it will be used to calculate data addresses +///@{ + +/// Get data segment for the instruction operand. +/// 'opnum' and 'rgnum' are meaningful only if the processor +/// has segment registers. + +idaman ea_t ida_export calc_dataseg(const insn_t &insn, int n=-1, int rgnum=-1); + +/// Map a data address. +/// \param insn the current instruction +/// \param addr the referenced address to map +/// \param opnum operand number + +inline ea_t map_data_ea(const insn_t &insn, ea_t addr, int opnum=-1) +{ + return to_ea(calc_dataseg(insn, opnum), addr); +} + +inline ea_t map_data_ea(const insn_t &insn, const op_t &op) +{ + return map_data_ea(insn, op.addr, op.n); +} + +/// Map a code address. +/// This function takes into account the segment translations. +/// \param insn the current instruction +/// \param addr the referenced address to map +/// \param opnum operand number + +idaman ea_t ida_export map_code_ea(const insn_t &insn, ea_t addr, int opnum); + +inline ea_t map_code_ea(const insn_t &insn, const op_t &op) +{ + return map_code_ea(insn, op.addr, op.n); +} + +inline ea_t map_ea(const insn_t &insn, const op_t &op, bool iscode) +{ + return iscode ? map_code_ea(insn, op) : map_data_ea(insn, op); +} + +inline ea_t map_ea(const insn_t &insn, ea_t addr, int opnum, bool iscode) +{ + return iscode ? map_code_ea(insn, addr, opnum) : map_data_ea(insn, addr, opnum); +} + +///@} + +//-------------------------------------------------------------------------- +// I D P H E L P E R F U N C T I O N S - O U T P U T +//-------------------------------------------------------------------------- +struct outctx_base_t +{ + ea_helper_t &_eah; + // information for creating one line + ea_t insn_ea; + qstring outbuf; ///< buffer for the current output line + ///< once ready, it is moved to lnar + ssize_t regname_idx = -1; ///< to rename registers + int suspop; ///< controls color for out_long() + flags_t F32; ///< please use outctx_t::F instead + uval_t *outvalues = nullptr; ///< at least 2*UA_MAXOP elements + int outvalue_getn_flags = 0; ///< additional flags for print_operand() + void *user_data = nullptr; ///< pointer to be used by the processor module for any purpose + void *kern_data = nullptr; ///< internal info used by the kernel + + // information for generating many lines + qstrvec_t *lnar = nullptr; ///< vector of output lines + int lnar_maxsize = 0; ///< max permitted size of lnar + int default_lnnum = -1; ///< index of the most important line in lnar + + qstring line_prefix; ///< usually segname:offset + ssize_t prefix_len = 0; ///< visible length of line_prefix + int ctxflags = 0; ///< various bits +#define CTXF_MAIN 0x00001 ///< produce only the essential line(s) +#define CTXF_MULTI 0x00002 ///< enable multi-line essential lines +#define CTXF_CODE 0x00004 ///< display as code regardless of the database flags +#define CTXF_STACK 0x00008 ///< stack view (display undefined items as 2/4/8 bytes) +#define CTXF_GEN_XREFS 0x00010 ///< generate the xrefs along with the next line +#define CTXF_XREF_STATE 0x00060 ///< xref state: +#define XREFSTATE_NONE 0x00 ///< not generated yet +#define XREFSTATE_GO 0x20 ///< being generated +#define XREFSTATE_DONE 0x40 ///< have been generated +#define CTXF_GEN_CMT 0x00080 ///< generate the comment along with the next line +#define CTXF_CMT_STATE 0x00300 ///< comment state: +#define COMMSTATE_NONE 0x000 ///< not generated yet +#define COMMSTATE_GO 0x100 ///< being generated +#define COMMSTATE_DONE 0x200 ///< have been generated +#define CTXF_VOIDS 0x00400 ///< display void marks +#define CTXF_NORMAL_LABEL 0x00800 ///< generate plain label (+demangled label as cmt) +#define CTXF_DEMANGLED_LABEL 0x01000 ///< generate only demangled label as comment +#define CTXF_LABEL_OK 0x02000 ///< the label have been generated +#define CTXF_DEMANGLED_OK 0x04000 ///< the label has been demangled successfully +#define CTXF_OVSTORE_PRNT 0x08000 ///< out_value should store modified values +#define CTXF_OUTCTX_T 0x10000 ///< instance is, in fact, a outctx_t +#define CTXF_DBLIND_OPND 0x20000 ///< an operand was printed with double indirection (e.g. =var in arm) +#define CTXF_BINOP_STATE 0xC0000 ///< opcode bytes state: +#define BINOPSTATE_NONE 0x00000 ///< not generated yet +#define BINOPSTATE_GO 0x40000 ///< being generated +#define BINOPSTATE_DONE 0x80000 ///< have been generated +#define CTXF_HIDDEN_ADDR 0x100000 ///< generate an hidden addr tag at the beginning of the line +#define CTXF_BIT_PREFIX 0x200000 ///< generate a line prefix with a bit offset, e.g.: 12345678.3 +#define CTXF_UNHIDE 0x400000 ///< display hidden objects (segment, function, range) + + // internal data used by the kernel + int ind0 = 0; + ea_t cmt_ea = BADADDR; ///< indirectly referenced address (used to generate cmt) + qstring cmtbuf; ///< indented comment + const char *cmtptr = nullptr; ///< rest of indented comment + color_t cmtcolor = 0xFF; ///< comment color + + inline bool only_main_line() const { return (ctxflags & CTXF_MAIN) != 0; } + inline bool multiline() const { return (ctxflags & CTXF_MULTI) != 0; } + inline bool force_code() const { return (ctxflags & CTXF_CODE) != 0; } + inline bool stack_view() const { return (ctxflags & CTXF_STACK) != 0; } + inline bool display_voids() const { return (ctxflags & CTXF_VOIDS) != 0; } + inline bool display_hidden() const { return (ctxflags & CTXF_UNHIDE) != 0; } + inline void set_gen_xrefs(bool on=true) { setflag(ctxflags, CTXF_GEN_XREFS, on); } + inline int get_xrefgen_state() const { return ctxflags & CTXF_XREF_STATE; } + inline void set_gen_cmt(bool on=true) { setflag(ctxflags, CTXF_GEN_CMT, on); } + inline int get_cmtgen_state() const { return ctxflags & CTXF_CMT_STATE; } + inline int get_binop_state() const { return ctxflags & CTXF_BINOP_STATE; } + inline void clr_gen_label(void) { ctxflags &= ~(CTXF_NORMAL_LABEL|CTXF_DEMANGLED_LABEL); } + inline void set_gen_label(void) { ctxflags |= CTXF_NORMAL_LABEL; } + inline void set_gen_demangled_label(void) { ctxflags |= CTXF_DEMANGLED_LABEL; ctxflags &= ~CTXF_NORMAL_LABEL; } + inline void set_comment_addr(ea_t ea) { cmt_ea = ea; } + inline void set_dlbind_opnd(void) { ctxflags |= CTXF_DBLIND_OPND; } + inline bool print_label_now() const + { + return (ctxflags & (CTXF_LABEL_OK|CTXF_MAIN)) == 0 // label not ready + && (ctxflags & (CTXF_NORMAL_LABEL|CTXF_DEMANGLED_LABEL)) != 0; // requested it + } + int forbid_annotations() + { // temporarily forbid printing of xrefs, label, cmt + int bits = CTXF_GEN_XREFS|CTXF_NORMAL_LABEL|CTXF_DEMANGLED_LABEL|CTXF_GEN_CMT; + int saved_flags = ctxflags & bits; + ctxflags &= ~bits; + return saved_flags; + } + void restore_ctxflags(int saved_flags) + { + ctxflags |= saved_flags; + } + + outctx_base_t(ea_t ea, flags64_t flags, int _suspop=0); + outctx_base_t &operator =(const outctx_base_t &) = delete; + virtual ~outctx_base_t(void); + + ///------------------------------------------------------------------------- + /// Functions to append text to the current output buffer (outbuf) + + /// Append a formatted string to the output string. + /// \return the number of characters appended + AS_PRINTF(2, 3) size_t out_printf(const char *format, ...) + { + va_list va; + va_start(va, format); + size_t len = out_vprintf(format, va); + va_end(va); + return len; + } + + GCC_DIAG_OFF(format-nonliteral); + size_t nowarn_out_printf(const char *format, ...) + { + va_list va; + va_start(va, format); + size_t len = out_vprintf(format, va); + va_end(va); + return len; + } + GCC_DIAG_ON(format-nonliteral); + + virtual AS_PRINTF(2, 0) size_t idaapi out_vprintf(const char *format, va_list va); + + /// Output immediate value. + /// Try to use this function to output all constants of instruction operands. + /// This function outputs a number from x.addr or x.value in the form + /// determined by F. It outputs colored text. + /// \param x value to output + /// \param outf \ref OOF_ + /// \return flags of the output value, otherwise: + /// \retval -1 if printed a number with #COLOR_ERROR + /// \retval 0 if printed a nice number or character or segment or enum + virtual flags64_t idaapi out_value(const op_t &x, int outf=0); + + /// \defgroup OOF_ Output value flags + /// Flags passed to out_value(). + /// (don't use #OOF_SIGNMASK and #OOF_WIDTHMASK, they are for the kernel) + ///@{ +#define OOF_SIGNMASK 0x0003 ///< sign symbol (+/-) output +#define OOFS_IFSIGN 0x0000 ///< output sign if needed +#define OOFS_NOSIGN 0x0001 ///< don't output sign, forbid the user to change the sign +#define OOFS_NEEDSIGN 0x0002 ///< always out sign (+-) +#define OOF_SIGNED 0x0004 ///< output as signed if < 0 +#define OOF_NUMBER 0x0008 ///< always as a number +#define OOF_WIDTHMASK 0x0070 ///< width of value in bits +#define OOFW_IMM 0x0000 ///< take from x.dtype +#define OOFW_8 0x0010 ///< 8 bit width +#define OOFW_16 0x0020 ///< 16 bit width +#define OOFW_24 0x0030 ///< 24 bit width +#define OOFW_32 0x0040 ///< 32 bit width +#define OOFW_64 0x0050 ///< 64 bit width +#define OOF_ADDR 0x0080 ///< output x.addr, otherwise x.value + ///< OOF_WIDTHMASK must be explicitly specified with it +#define OOF_OUTER 0x0100 ///< output outer operand +#define OOF_ZSTROFF 0x0200 ///< meaningful only if is_stroff(F); + ///< append a struct field name if + ///< the field offset is zero? + ///< if #AFL_ZSTROFF is set, then this flag + ///< is ignored. +#define OOF_NOBNOT 0x0400 ///< prohibit use of binary not +#define OOF_SPACES 0x0800 ///< do not suppress leading spaces; + ///< currently works only for floating point numbers +#define OOF_ANYSERIAL 0x1000 ///< if enum: select first available serial +#define OOF_LZEROES 0x2000 ///< print leading zeroes +#define OOF_NO_LZEROES 0x4000 ///< do not print leading zeroes; + ///< if none of OOF_LZEROES and OOF_NO_LZEROES + ///< was specified, is_lzero() is used + ///@} + + /// Output a character with #COLOR_SYMBOL color. + virtual void idaapi out_symbol(char c); + + /// Append a character multiple times + virtual void idaapi out_chars(char c, int n); + + /// Appends spaces to outbuf until its tag_strlen becomes 'len' + void out_spaces(ssize_t len) { add_spaces(&outbuf, len); } + virtual void idaapi add_spaces(qstring *buf, ssize_t len); + + /// Output a string with the specified color. + virtual void idaapi out_line(const char *str, color_t color=0); + + /// Output a string with #COLOR_KEYWORD color. + inline void out_keyword(const char *str) + { + out_line(str, COLOR_KEYWORD); + } + + /// Output a character with #COLOR_REG color. + inline void out_register(const char *str) + { + out_line(str, COLOR_REG); + } + + /// Output local variable name with #COLOR_LOCNAME color. + inline void out_lvar(const char *name, int width=-1) + { + out_line(name, COLOR_LOCNAME); + if ( width != -1 && name != nullptr ) + { + size_t len = ::qstrlen(name); + if ( len < width ) + out_chars(' ', width - len); + } + } + + /// Output "turn color on" escape sequence + virtual void idaapi out_tagon(color_t tag); + + /// Output "turn color off" escape sequence + virtual void idaapi out_tagoff(color_t tag); + + /// Output "address" escape sequence + virtual void idaapi out_addr_tag(ea_t ea); + + /// Output a colored line with register names in it. + /// The register names will be substituted by user-defined names (regvar_t) + /// Please note that out_tagoff tries to make substitutions too (when called with COLOR_REG) + virtual void idaapi out_colored_register_line(const char *str); + + /// Output one character. + /// The character is output without color codes. + /// see also out_symbol() + virtual void idaapi out_char(char c) { outbuf.append(c); } + + /// Output a number with the specified base (binary, octal, decimal, hex) + /// The number is output without color codes. + /// see also out_long() + virtual void idaapi out_btoa(uval_t Word, char radix=0); + + /// \fn void out_long(sval_t, char) + /// Output a number with appropriate color. + /// Low level function. Use out_value() if you can. + /// if 'suspop' is set then + /// this function uses #COLOR_VOIDOP instead of #COLOR_NUMBER. + /// 'suspop' is initialized: + /// - in out_one_operand() + /// - in ..\ida\gl.cpp (before calling \ph{d_out()}) + /// + /// \param v value to output + /// \param radix base (2,8,10,16) + /// \param suspop ::suspop + /// - suspop==0: operand is ok + /// - suspop==1: operand is suspicious and should be output with #COLOR_VOIDOP + /// - suspop==2: operand can't be output as requested and should be output with #COLOR_ERROR + virtual void idaapi out_long(sval_t v, char radix); + + /// Output a name expression. + /// \param x instruction operand referencing the name expression + /// \param ea address to convert to name expression + /// \param off the value of name expression. this parameter is used only to + /// check that the name expression will have the wanted value. + /// You may pass #BADADDR for this parameter but I discourage it + /// because it prohibits checks. + /// \return true if the name expression has been produced + virtual bool idaapi out_name_expr( + const op_t &x, + ea_t ea, + adiff_t off=BADADDR); + + // Generate the closing comment if if it required by the assembler + + inline void close_comment(void) { out_line(closing_comment()); } + + ///------------------------------------------------------------------------- + /// Functions to populate the output line array (lnar) + + /// Move the contents of the output buffer to the line array (outbuf->lnar) + /// The kernel augments the outbuf contents with additional text like + /// the line prefix, user-defined comments, xrefs, etc at this call. + virtual bool idaapi flush_outbuf(int indent=-1); + + /// Append contents of 'buf' to the line array. + /// Behaves like flush_outbuf but accepts an arbitrary buffer + virtual bool idaapi flush_buf(const char *buf, int indent=-1); + + /// Finalize the output context. + /// \return the number of generated lines. + virtual int idaapi term_outctx(const char *prefix=nullptr); + + /// See gen_printf() + virtual AS_PRINTF(3, 0) bool idaapi gen_vprintf( + int indent, + const char *format, + va_list va); + + /// printf-like function to add lines to the line array. + /// \param indent indention of the line. + /// if indent == -1, the kernel will indent the line + /// at \inf{indent}. if indent < 0, -indent will be used for indention. + /// The first line printed with indent < 0 is considered as the + /// most important line at the current address. Usually it is + /// the line with the instruction itself. This line will be + /// displayed in the cross-reference lists and other places. + /// If you need to output an additional line before the main line + /// then pass DEFAULT_INDENT instead of -1. The kernel will know + /// that your line is not the most important one. + /// \param format printf style colored line to generate + /// \return overflow, lnar_maxsize has been reached + + AS_PRINTF(3, 4) inline bool gen_printf(int indent, const char *format, ...) + { + va_list va; + va_start(va, format); + bool code = gen_vprintf(indent, format, va); + va_end(va); + return code; + } +#define DEFAULT_INDENT 0xFFFF + + /// Generate empty line. This function does nothing if generation of empty + /// lines is disabled. + /// \return overflow, lnar_maxsize has been reached + + virtual bool idaapi gen_empty_line(void); + + + /// Generate thin border line. This function does nothing if generation + /// of border lines is disabled. + /// \param solid generate solid border line (with =), otherwise with - + /// \return overflow, lnar_maxsize has been reached + + virtual bool idaapi gen_border_line(bool solid=false); + + /// See gen_cmt_line() + virtual AS_PRINTF(3, 0) bool idaapi gen_colored_cmt_line_v( + color_t color, + const char *format, + va_list va); + + /// See gen_cmt_line() + + AS_PRINTF(2, 0) inline bool gen_cmt_line_v(const char *format, va_list va) + { + return gen_colored_cmt_line_v(COLOR_AUTOCMT, format, va); + } + + /// Generate one non-indented comment line, colored with ::COLOR_AUTOCMT. + /// \param format printf() style format line. The resulting comment line + /// should not include comment character (;) + /// \return overflow, lnar_maxsize has been reached + + AS_PRINTF(2, 3) inline bool gen_cmt_line(const char *format, ...) + { + va_list va; + va_start(va, format); + bool code = gen_cmt_line_v(format, va); + va_end(va); + return code; + } + + /// Generate one non-indented comment line, colored with ::COLOR_COLLAPSED. + /// \param format printf() style format line. The resulting comment line + /// should not include comment character (;) + /// \return overflow, lnar_maxsize has been reached + + AS_PRINTF(2, 3) inline bool gen_collapsed_line(const char *format, ...) + { + va_list va; + va_start(va,format); + bool answer = gen_colored_cmt_line_v(COLOR_COLLAPSED, format, va); + va_end(va); + return answer; + } + + /// Generate big non-indented comment lines. + /// \param cmt comment text. may contain \\n characters to denote new lines. + /// should not contain comment character (;) + /// \param color color of comment text (one of \ref COLOR_) + /// \return overflow, lnar_maxsize has been reached + + virtual bool idaapi gen_block_cmt(const char *cmt, color_t color); + + //------------------------------------------------------------------------- + /// Initialization; normally used only by the kernel + virtual void idaapi setup_outctx(const char *prefix, int makeline_flags); +#define MAKELINE_NONE 0x00 +#define MAKELINE_BINPREF 0x01 ///< allow display of binary prefix +#define MAKELINE_VOID 0x02 ///< allow display of '<suspicious>' marks +#define MAKELINE_STACK 0x04 ///< allow display of sp trace prefix + + virtual ssize_t idaapi retrieve_cmt(void) { return -1; } + virtual ssize_t idaapi retrieve_name(qstring *, color_t *) { return -1; } + virtual bool idaapi gen_xref_lines(void) { return false; } + + virtual void idaapi init_lines_array(qstrvec_t *answers, int maxsize); + + virtual ssize_t idaapi get_stkvar(const op_t &x, uval_t v, sval_t *vv, int *is_sp_based, tinfo_t *_frame); + + void gen_empty_line_without_annotations(void) + { + int saved_flags = forbid_annotations(); + gen_empty_line(); + restore_ctxflags(saved_flags); + } + + inline flags64_t getF() const; + + +protected: + virtual bool idaapi flush_and_reinit(void); + virtual void idaapi append_user_prefix(const char *, int) {} + virtual void idaapi add_aux_prefix(const char *, int) {} + virtual void idaapi out_label_addr_tag(void) {} + virtual void idaapi out_aux_cmts(void) {} +}; + +//-------------------------------------------------------------------------- +// This class is used to print instructions and data items +struct outctx_t : public outctx_base_t +{ + // kernel only data: + ea_t bin_ea; // Current binary format EA + char bin_state; // =0 not generated,1-in process,2-finished + int gl_bpsize = 0; // binary line prefix size + int bin_width = 0; + + // instruction to display: + insn_t insn; // valid only when ph.out_insn() is called + + // colorized and demangled label of the current address + qstring curlabel; + + // opinfo_t to use for out_value() + const printop_t *wif; + + // processor module and its description + procmod_t *procmod; + processor_t &ph; + asm_t &ash; + + // out_value() saves the printed values here + uval_t saved_immvals[UA_MAXOP] = { 0 }; + + ea_t prefix_ea = BADADDR; + ea_t next_line_ea = BADADDR; // EA of next line (for prefix) + flags64_t F = 0; + + outctx_t( + procmod_t *p, + processor_t &ph, + asm_t &ash, + ea_t ea, + flags64_t flags=0, + int _suspop=0, + const printop_t *_wif=nullptr); + ~outctx_t(void) + { + } + virtual void idaapi setup_outctx(const char *prefix, int flags) override; + virtual int idaapi term_outctx(const char *prefix=nullptr) override; + virtual ssize_t idaapi retrieve_cmt(void) override; + virtual ssize_t idaapi retrieve_name(qstring *, color_t *) override; + virtual bool idaapi gen_xref_lines(void) override; + virtual void idaapi out_btoa(uval_t Word, char radix=0) override; + + void set_bin_state(int value) + { + bin_state = value; + ctxflags &= ~CTXF_BINOP_STATE; + ctxflags |= value == 0 ? BINOPSTATE_NONE + : value == 1 ? BINOPSTATE_GO + : BINOPSTATE_DONE; + } + + /// Output instruction mnemonic for 'insn' using information in 'ph.instruc' array. + /// This function outputs colored text. + /// It should be called from \ph{ev_out_insn()} or \ph{ev_out_mnem()} handler. + /// It will output at least one space after the instruction. + /// mnemonic even if the specified 'width' is not enough. + /// \param width width of field with mnemonic. + /// if < 0, then 'postfix' will be output before + /// the mnemonic, i.e. as a prefix + /// \param postfix optional postfix added to the instruction mnemonic + virtual void idaapi out_mnem(int width=8, const char *postfix=nullptr) newapi; + + /// Output custom mnemonic for 'insn'. + /// E.g. if it should differ from the one in 'ph.instruc'. + /// This function outputs colored text. See \ref out_mnem + /// \param mnem custom mnemonic + /// \param width width of field with mnemonic. + /// if < 0, then 'postfix' will be output before + /// the mnemonic, i.e. as a prefix + /// \param postfix optional postfix added to 'mnem' + virtual void idaapi out_custom_mnem( + const char *mnem, + int width=8, + const char *postfix=nullptr) newapi; + + /// Output instruction mnemonic using information in 'insn'. + /// It should be called from \ph{ev_out_insn()} and + /// it will call \ph{ev_out_mnem()} or \ref out_mnem. + /// This function outputs colored text. + virtual void idaapi out_mnemonic(void) newapi; + + /// Use this function to output an operand of an instruction. + /// This function checks for the existence of a manually defined operand + /// and will output it if it exists. + /// It should be called from \ph{ev_out_insn()} and it will call \ph{ev_out_operand()}. + /// This function outputs colored text. + /// \param n 0..#UA_MAXOP-1 operand number + /// \retval 1 operand is displayed + /// \retval 0 operand is hidden + virtual bool idaapi out_one_operand(int n) newapi; + + /// Get the immediate values used at the specified address. + /// This function can handle instructions and data items. + /// \param out array of values, size at least 2*UA_MAXOP + /// \param i operand number + /// \return number of immediate values + virtual size_t idaapi get_immvals(uval_t *out, int i) newapi; + + /// Print all operand values as commented character constants. + /// This function is used to comment void operands with their representation + /// in the form of character constants. + /// This function outputs colored text. + virtual void idaapi out_immchar_cmts(void) newapi; + + virtual void idaapi gen_func_header(func_t *pfn) newapi; + virtual void idaapi gen_func_footer(const func_t *pfn) newapi; + + // display data items and undefined bytes. + virtual void idaapi out_data(bool analyze_only) newapi; + + // generate declaration for item in a special segment + // return: 0-ok, 1-overflow + virtual bool idaapi out_specea(uchar segtype) newapi; + + // convenience functions for processor modules + // print lines from ash.header + virtual void idaapi gen_header_extra() newapi; + + // flags for gen_header() +#define GH_PRINT_PROC (1 << 0) ///< processor name +#define GH_PRINT_ASM (1 << 1) ///< selected assembler +#define GH_PRINT_BYTESEX (1 << 2) ///< byte sex +#define GH_PRINT_HEADER (1 << 3) ///< lines from ash.header +#define GH_BYTESEX_HAS_HIGHBYTE (1 << 4) ///< describe inf.is_wide_high_byte_first() +#define GH_PRINT_PROC_AND_ASM (GH_PRINT_PROC | GH_PRINT_ASM) +#define GH_PRINT_PROC_ASM_AND_BYTESEX (GH_PRINT_PROC_AND_ASM | GH_PRINT_BYTESEX) +#define GH_PRINT_ALL (GH_PRINT_PROC_ASM_AND_BYTESEX | GH_PRINT_HEADER) +#define GH_PRINT_ALL_BUT_BYTESEX (GH_PRINT_PROC_AND_ASM | GH_PRINT_HEADER) + virtual void idaapi gen_header( + int flags = GH_PRINT_PROC_AND_ASM, + const char *proc_name = nullptr, + const char *proc_flavour = nullptr) newapi; + + /// Print addresses referenced *from* the specified address as commented + /// symbolic names. + /// This function is used to show, for example, multiple callees of an + /// indirect call. + /// This function outputs colored text. + virtual void idaapi out_fcref_names() newapi; +}; + +//------------------------------------------------------------------------- +inline flags64_t outctx_base_t::getF() const +{ + return (ctxflags & CTXF_OUTCTX_T) != 0 ? ((outctx_t *) this)->F : F32; +} + +//-------------------------------------------------------------------------- +/// Create a new output context. +/// To delete it, just use "delete pctx" + +idaman outctx_base_t *ida_export create_outctx(ea_t ea, flags64_t F=0, int suspop=0); + + +/// Print instruction mnemonics. +/// \param out output buffer +/// \param ea linear address of the instruction +/// \return success + +idaman bool ida_export print_insn_mnem(qstring *out, ea_t ea); + + +/// \defgroup FCBF_ format flags +/// Used by format_charlit +///@{ +#define FCBF_CONT 0x00000001 ///< don't stop on decoding, or any other kind of error +#define FCBF_ERR_REPL 0x00000002 ///< in case of an error, use a CP_REPLCHAR instead + ///< of a hex representation of the problematic byte + +#define FCBF_FF_LIT 0x00000004 ///< in case of codepoints == 0xFF, use it as-is (i.e., LATIN SMALL LETTER Y WITH DIAERESIS). + ///< If both this, and FCBF_REPL are specified, + ///< this will take precedence + +#define FCBF_DELIM 0x00000008 ///< add the 'ash'-specified delimiters around the generated data. + ///< Note: if those are not defined and the INFFL_ALLASM is not set, + ///< format_charlit() will return an error +///@} + + +/// Format character literal. +/// +/// Try and format 'size' bytes pointed to by '*ptr', as literal characters, +/// using the 'encidx' encoding, and with the specified 'flags' directives. +/// +/// By default, format_charlit() will fail and return an error, in +/// any of the following cases: +/// - a byte cannot be decoded using the specified (or default) encoding +/// - a codepoint is < 0x20 (i.e., ' ') +/// - a codepoint is present in 'ash.esccodes' +/// - a codepoint is 0xFF +/// - a codepoint is >= 0x80, and AS_NHIAS was specified in ash.flag +/// The function can be told to keep going instead of bailing out, for any +/// of these situations, by using one of the FCBF_*_OK flags. +/// +/// If the function is told to proceed on a specific error, by default +/// it will format the byte as a C-encoded byte value (i.e., '\xNN'), +/// unless the corresponding FCBF_*_REPL flag is passed, in which case +/// the problematic byte/codepoint will be replaced by the Unicode +/// replacement character in the output. +/// +/// \param out output buffer (can be nullptr) +/// \param ptr pointer to pointer to bytes to print (will be advanced +/// by the number of bytes that were successfully printed) +/// \param size size of input value in bytes +/// \param flags \ref FCBF_ +/// \param encidx the 1 byte-per-unit encoding to use (or 0 to use the default 1 BPU encoding) +/// \return success + +idaman bool ida_export format_charlit( + qstring *out, + const uchar **ptr, + size_t size, + uint32 flags=0, + int encidx=0); + + +/// Print a floating point value. +/// \param buf output buffer. may be nullptr +/// \param bufsize size of the output buffer +/// \param v floating point value in processor native format +/// \param size size of the value in bytes +/// \return success +/// \retval true ok +/// \retval false can't represent as floating point number + +idaman bool ida_export print_fpval(char *buf, size_t bufsize, const void *v, int size); + + +//-------------------------------------------------------------------------- +// I D P H E L P E R F U N C T I O N S - E M U L A T O R +//-------------------------------------------------------------------------- +/// Get flags for op_t::dtype field +idaman flags64_t ida_export get_dtype_flag(op_dtype_t dtype); +/// Get size of opt_::dtype field +idaman size_t ida_export get_dtype_size(op_dtype_t dtype); +/// Get op_t::dtype from size +idaman op_dtype_t ida_export get_dtype_by_size(asize_t size); + +/// Is a floating type operand? +inline bool is_floating_dtype(op_dtype_t dtype) +{ + return dtype == dt_float + || dtype == dt_double + || dtype == dt_tbyte + || dtype == dt_ldbl + || dtype == dt_half; +} + + +//-------------------------------------------------------------------------- +// K E R N E L I N T E R F A C E T O I D P F U N C T I O N S +//-------------------------------------------------------------------------- +/// Create an instruction at the specified address. +/// This function checks if an instruction is present at the specified address +/// and will try to create one if there is none. It will fail if there is +/// a data item or other items hindering the creation of the new instruction. +/// This function will also fill the 'out' structure. +/// \param ea linear address +/// \param out the resulting instruction +/// \return the length of the instruction or 0 +idaman int ida_export create_insn(ea_t ea, insn_t *out=nullptr); + + +/// Analyze the specified address and fill 'out'. +/// This function does not modify the database. +/// It just tries to interpret the specified address as an instruction and fills +/// the 'out' structure. +/// \param out the resulting instruction +/// \param ea linear address +/// \return the length of the (possible) instruction or 0 + +idaman int ida_export decode_insn(insn_t *out, ea_t ea); + +/// Can the bytes at address 'ea' be decoded as instruction? +/// \param ea linear address +/// \return whether or not the contents at that address could be a valid instruction + +inline bool can_decode(ea_t ea) { insn_t insn; return decode_insn(&insn, ea) > 0; } + + +/// Generate text representation for operand #n. +/// This function will generate the text representation of the specified +/// operand (includes color codes.) +/// \param out output buffer +/// \param ea the item address (instruction or data) +/// \param n 0..#UA_MAXOP-1 operand number, meaningful only for instructions +/// \param getn_flags \ref GETN_ +/// Currently only #GETN_NODUMMY is accepted. +/// \param newtype if specified, print the operand using the specified type +/// \return success + +idaman bool ida_export print_operand( + qstring *out, + ea_t ea, + int n, + int getn_flags=0, + struct printop_t *newtype=nullptr); + + +//-------------------------------------------------------------------------- +// Helper functions for the processor emulator/analyzer +//-------------------------------------------------------------------------- + +/// Decode previous instruction if it exists, fill 'out'. +/// \param out the resulting instruction +/// \param ea the address to decode the previous instruction from +/// \return the previous instruction address (#BADADDR-no such insn) + +idaman ea_t ida_export decode_prev_insn(insn_t *out, ea_t ea); + + +/// Decode preceding instruction in the execution flow. +/// Prefer far xrefs from addresses < the current to ordinary flows. +/// \param out the resulting instruction +/// \param ea the address to decode the preceding instruction from +/// \param p_farref will contain 'true' if followed an xref, false otherwise. +/// \return the preceding instruction address (#BADADDR-no such insn) and 'out'. + +idaman ea_t ida_export decode_preceding_insn(insn_t *out, ea_t ea, bool *p_farref=nullptr); + + +/// Helper class for processor modules to build macro instructions. +struct macro_constructor_t +{ + size_t reserved = 0; + + virtual ~macro_constructor_t() {} + + /// Construct a macro instruction. + /// This function may be called from ana() to generate a macro instruction. + /// + /// The real work is done by the 'build_macro()' virtual function. + /// It must be defined by the processor module. + /// + /// construct_macro() modifies the database using the info provided + /// by build_macro(). It verifies if the instruction can really be created + /// (for example, that other items do not hinder), may plan to reanalyze + /// the macro, etc. + /// If the macro instructions are disabled by the user, construct_macro() + /// will destroy the macro instruction. Note: if INSN_MODMAC is not set in + /// insn.flags, the database will not be modified. + /// + /// \param insn the instruction to modify into a macro + /// \param enable enable macro generation + /// \retval true the macro instruction is generated in 'insn' + /// \retval false did not create a macro + inline bool construct_macro(insn_t *insn, bool enable); + + /// Try to extend the instruction. + /// + /// This function may modify 'insn' and return false; these changes will be + /// accepted by the kernel but the instruction will not be considered as a macro. + /// + /// \param insn Instruction to modify, usually the first + /// instruction of the macro + /// \param may_go_forward Is it ok to consider the next instruction for the macro? + /// This argument may be false, for example, if there is + /// a cross reference to the end of INSN. In this case + /// creating a macro is not desired. However, it may still + /// be useful to perform minor tweaks to the instruction + /// using the information about the surrounding instructions. + /// \return true if created an macro instruction. + virtual bool idaapi build_macro(insn_t *insn, bool may_go_forward) = 0; +}; + +// Do not directly call this function, use macro_constructor_t +idaman bool ida_export construct_macro( + macro_constructor_t *_this, + insn_t *insn, + bool enable); + +inline bool macro_constructor_t::construct_macro(insn_t *insn, bool enable) +{ + return ::construct_macro(this, insn, enable); +} + + +/// Does the instruction spoil any register from 'regs'?. +/// This function checks the \ref CF_ flags from the instructions array. +/// Only ::o_reg operand types are consulted. +/// \param insn the instruction +/// \param regs array with register indexes +/// \param n size of 'regs' +/// \return index in the 'regs' array or -1 + +idaman int ida_export get_spoiled_reg(const insn_t &insn, const uint32 *regs, size_t n); + + +#endif // _UA_HPP + +``` + +`IdaSDK/undo.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 2005-2025 Hex-Rays SA <support@hex-rays.com> + * ALL RIGHTS RESERVED. + * + * Undo functionality. + * + * Glossary: + * + * undo point: a point in the undo history to which it is possible + * to return. + * undo record: record in the undo history about one chunk of a change. + * each record has an undo code, which determines the nature + * of the change and the undo handler. + * + */ + +#ifndef _UNDO_HPP +#define _UNDO_HPP + +/// Create a new restore point. +/// The user can undo to this point in the future. +/// \param bytes body of the record for UNDO_ACTION_START +/// \param size size of the record for UNDO_ACTION_START +/// \return success; fails if undo is disabled +idaman bool ida_export create_undo_point(const uchar *bytes, size_t size); + +/// Get the label of the action that will be undone. +/// This function returns the text that can be displayed in the undo menu +/// \return success +idaman bool ida_export get_undo_action_label(qstring *action_to_be_undone); + +/// Get the label of the action that will be redone. +/// This function returns the text that can be displayed in the redo menu +/// \return success +idaman bool ida_export get_redo_action_label(qstring *action_to_be_redone); + +/// Perform undo. +/// \return success +idaman bool ida_export perform_undo(); + +/// Perform redo. +/// \return success +idaman bool ida_export perform_redo(); + +#endif // _UNDO_HPP + +``` + +`IdaSDK/workarounds.hpp`: + +```hpp +// various workarounds/fixes for different compilers + +#ifndef _IDA_WORKAROUNDS_H +#define _IDA_WORKAROUNDS_H + +// MS Visual C++: +// this file should be included once in module (exe or dll) where std::stable_sort() is used +#ifdef _MSC_VER +#if (_MSC_VER < 1700) // before VS2012 +// the following fixes unneeded exports caused by use +// of std::stable_sort, which uses new(std::nothrow) +// reference: http://social.msdn.microsoft.com/Forums/vstudio/en-US/4692205a-0296-4f41-adbb-fa8339597f5c/unwanted-exports + +namespace std { extern const __declspec(selectany) nothrow_t nothrow = nothrow_t(); } + +// explanation by Javier Blazquez: + +/* +The reason why this eliminates the _Init_locks export is because the linker +no longer has to go find and use the nothrow.obj file (part of msvcprt.lib) +during linking for the definition of std::nothrow, it can just use the +definition you provided. That nothrow.obj file not only contains the +definition of std::nothrow, it also contains a dllexport definition of +std::_Init_locks::operator= (in fact, the only such definition of this +function anywhere in the standard libraries), so preventing the linker from +using the standard nothrow.obj at all has the effect of removing this ugly +export altogether. +*/ +#endif // (_MSC_VER < 1700) +#endif // _MSC_VER + +#ifdef __LINUX__ + // suppress dependency on __longjmp_chk + // idea stolen from http://code.google.com/p/webm/issues/detail?id=166 + // this file should be included in modules where longjmp is used + #if !defined(__ARM__) && defined(__GNUC_PREREQ) && __GNUC_PREREQ(2,11) + #ifndef __X86__ + __asm__(".symver __longjmp_chk,longjmp@GLIBC_2.2.5"); + #else + __asm__(".symver __longjmp_chk,longjmp@GLIBC_2.0"); + #endif // !__X86__ + #endif // __GNUC_PREREQ +#endif // __LINUX__ + +#endif // _IDA_WORKAROUNDS_H + + +``` + +`IdaSDK/xref.hpp`: + +```hpp +/* + * Interactive disassembler (IDA). + * Copyright (c) 1990-2025 Hex-Rays + * ALL RIGHTS RESERVED. + * + */ + +#ifndef _XREF_HPP +#define _XREF_HPP + +/*! \file xref.hpp + + \brief Functions that deal with cross-references (xrefs). + + There are 2 groups of xrefs: CODE and DATA references. + All xrefs are kept in the bTree except the ordinary execution flow + to the next instruction. The ordinary execution flow to the next instruction + is kept in flags (see bytes.hpp) + + The source address of an xref must be an item head (is_head) + or a structure member id. Even if an xref is generated by an + element in the middle of the item, the item head address must be used. + There are some exceptions to the rule but they are not worth mentioning here. + + Xrefs are automatically sorted by addresses. However, the flow + to the next instruction is always at the beginning of the list. + + Xrefs are usually created by the processor module, as a reaction + to the ev_emu_insn event. Plugins may create xrefs too but please note + that upon a reanalysis of an item, all its xrefs, except the ones marked with + XREF_USER, are deleted by the kernel. + +*/ + +class func_t; // #include <funcs.hpp> + +//--------------------------------------------------------------------------- +// C R O S S - R E F E R E N C E T Y P E S +//--------------------------------------------------------------------------- + +/// \defgroup xref_type Cross-Reference types +/// Describe categories of code/data xrefs. +/// \note IDA handles the xrefs automatically and may delete an xref +/// added by the user if it does not contain the #XREF_USER bit. +///@{ + +/// CODE xref types +enum cref_t +{ + fl_U, ///< unknown -- for compatibility with old + ///< versions. Should not be used anymore. + fl_CF = 16, ///< Call Far + ///< This xref creates a function at the + ///< referenced location + fl_CN, ///< Call Near + ///< This xref creates a function at the + ///< referenced location + fl_JF, ///< Jump Far + fl_JN, ///< Jump Near + fl_USobsolete, ///< User specified (obsolete) + fl_F, ///< Ordinary flow: used to specify execution + ///< flow to the next instruction. +}; + +/// DATA xref types +enum dref_t +{ + dr_U, ///< Unknown -- for compatibility with old + ///< versions. Should not be used anymore. + dr_O, ///< Offset + ///< The reference uses 'offset' of data + ///< rather than its value + ///< OR + ///< The reference appeared because the "OFFSET" + ///< flag of instruction is set. + ///< The meaning of this type is IDP dependent. + dr_W, ///< Write access + dr_R, ///< Read access + dr_T, ///< Text (for forced operands only) + ///< Name of data is used in manual operand + dr_I, ///< Informational + ///< (a derived java class references its base + ///< class informationally) + dr_S, ///< Reference to enum member (symbolic constant) +}; + + +/// \defgroup XREF_T Cross-Reference type flags +/// These flags are combined with a ::cref_t or ::dref_t value to describe the +/// type of a cross reference. +/// These bits are accessible to the kernel. +///@{ +#define XREF_USER 0x20 ///< User specified xref. + ///< This xref will not be deleted by IDA. + ///< This bit should be combined with + ///< the existing xref types (::cref_t & ::dref_t) + ///< Cannot be used for fl_F xrefs +#define XREF_TAIL 0x40 ///< Reference to tail byte in extrn symbols +#define XREF_BASE 0x80 ///< Reference to the base part of an offset +#define XREF_MASK 0x1F ///< Mask to get xref type + +#define XREF_PASTEND 0x100 ///< Reference is past item. This bit may be passed to + ///< add_dref() functions but it won't be saved in + ///< the database. It will prevent the destruction + ///< of eventual alignment directives. +///@} + +/// Get character describing the xref type. +/// \param xrtype combination of \ref XREF_T and a ::cref_t of ::dref_t value +idaman char ida_export xrefchar(char xrtype); + +///@} xref_type + +//--------------------------------------------------------------------------- +// A D D / D E L E T E C R O S S - R E F E R E N C E S +//--------------------------------------------------------------------------- + +/// Create a code cross-reference. +/// \param from linear address of referencing instruction +/// \param to linear address of referenced instruction +/// \param type cross-reference type +/// \return success + +idaman bool ida_export add_cref(ea_t from, ea_t to, cref_t type); + + +/// Delete a code cross-reference. +/// \param from linear address of referencing instruction +/// \param to linear address of referenced instruction +/// \param expand policy to delete the referenced instruction +/// - 1: plan to delete the referenced instruction if it has +/// no more references. +/// - 0: don't delete the referenced instruction even if no +/// more cross-references point to it +/// \retval true if the referenced instruction will be deleted + +idaman bool ida_export del_cref(ea_t from, ea_t to, bool expand); + + +/// Create a data cross-reference. +/// \param from linear address of referencing instruction or data +/// \param to linear address of referenced data +/// \param type cross-reference type +/// \return success (may fail if user-defined xref exists from->to) + +idaman bool ida_export add_dref(ea_t from, ea_t to, dref_t type); + + +/// Delete a data cross-reference. +/// \param from linear address of referencing instruction or data +/// \param to linear address of referenced data + +idaman void ida_export del_dref(ea_t from, ea_t to); + + +//------------------------------------------------------------------------- +// E N U M E R A T E ( G E T ) C R O S S - R E F E R E N C E S +//------------------------------------------------------------------------- + +struct xrefblk_t; +/// \name Helper functions +/// Should not be called directly! +///@{ +idaman bool ida_export xrefblk_t_first_from(xrefblk_t *, ea_t from, int flags); +idaman bool ida_export xrefblk_t_next_from(xrefblk_t *); +idaman bool ida_export xrefblk_t_first_to(xrefblk_t *, ea_t to, int flags); +idaman bool ida_export xrefblk_t_next_to(xrefblk_t *); +///@} + +/// Structure to enumerate all xrefs. +/// This structure provides a way to access cross-references from a given address. +/// For example: +/// +/// \code +/// xrefblk_t xb; +/// for ( bool ok=xb.first_from(ea, XREF_FLOW); ok; ok=xb.next_from() ) +/// { +/// // xb.to - contains the referenced address +/// } +/// \endcode +/// or: +/// \code +/// xrefblk_t xb; +/// for ( bool ok=xb.first_to(ea, XREF_FLOW); ok; ok=xb.next_to() ) +/// { +/// // xb.from - contains the referencing address +/// } +/// \endcode +/// +/// First, all code references will be returned, then all data references. +/// If you need only code references, stop calling next() as soon as you get a dref. +/// +/// If you need only data references, pass #XREF_DATA flag to first(). +/// You may not modify the contents of a xrefblk_t structure! It is read only. +struct xrefblk_t +{ + ea_t from; ///< the referencing address - filled by first_to(),next_to() + ea_t to; ///< the referenced address - filled by first_from(), next_from() + bool iscode; ///< is code reference (::cref_t)? + ///< otherwise it is a data reference (::dref_t) + uchar type; ///< type of the last returned reference (::cref_t & ::dref_t) + bool user; ///< is user defined xref? otherwise defined by ida + uchar _flags; + +/// \defgroup XREF_ Xref enumeration flags +/// Passed as 'flags' parameter to functions in ::xrefblk_t +///@{ +#define XREF_FLOW 0x00 ///< return all references, including ordinary flow xrefs +#define XREF_NOFLOW 0x01 ///< skip ordinary flow xrefs + ///< (code xrefs to the next insn) +#define XREF_DATA 0x02 ///< return only data references (dr_...) +#define XREF_CODE 0x04 ///< return only code references (fl_...) +#define XREF_EA 0x08 ///< return only program addresses +#define XREF_TID 0x10 ///< return only type ids. + ///< XREF_EA and XREF_TID are exclusive, + ///< only one of them can be specified +///@} + + /// \name Get first/next + /// The following functions first return code references, then data references. + /// If you need only code references, you need to check 'iscode' after each call. + /// If you need only data references, use #XREF_DATA bit. + /// \param flags \ref XREF_ + /// \retval 1 ok + /// \retval 0 no more xrefs + ///@{ + + /// Get first xref from the given address (store in #to) + bool first_from(ea_t _from, int flags=XREF_FLOW) + { return xrefblk_t_first_from(this, _from, flags); } + + /// Get next xref from address provided to first_from() + bool next_from() + { return xrefblk_t_next_from(this); } + + /// Get xref to given address (store in #from) + bool first_to(ea_t _to, int flags=XREF_FLOW) + { return xrefblk_t_first_to(this, _to, flags); } + + /// Get next xref to address provided to first_to() + bool next_to() + { return xrefblk_t_next_to(this); } + + /// Get xref from '_from' that comes after '_to' + bool next_from(ea_t _from, ea_t _to, int flags) + { + if ( first_from(_from, flags) ) + { + to = _to; + return next_from(); + } + return false; + } + + /// Get xref to '_to' that comes after '_from' + bool next_to(ea_t _from, ea_t _to, int flags) + { + if ( first_to(_to, flags) ) + { + from = _from; + return next_to(); + } + return false; + } + + ///@} +}; + +//------------------------------------------------------------------------- +/// Get first data referenced from the specified address. +/// \param from linear address of referencing instruction or data +/// \return linear address of first (lowest) data referenced from the specified address. +/// Return #BADADDR if the specified instruction/data doesn't reference +/// to anything. + +idaman ea_t ida_export get_first_dref_from(ea_t from); + + +/// Get next data referenced from the specified address. +/// \param from linear address of referencing instruction or data +/// \param current linear address of current referenced data. +/// This value is returned by get_first_dref_from() or +/// previous call to get_next_dref_from() functions. +/// \return linear address of next data or #BADADDR. + +idaman ea_t ida_export get_next_dref_from(ea_t from, ea_t current); + + +/// Get address of instruction/data referencing to the specified data. +/// \param to linear address of referencing instruction or data +/// \return #BADADDR if nobody refers to the specified data. + +idaman ea_t ida_export get_first_dref_to(ea_t to); + + +/// Get address of instruction/data referencing to the specified data +/// \param to linear address of referencing instruction or data +/// \param current current linear address. +/// This value is returned by get_first_dref_to() or +/// previous call to get_next_dref_to() functions. +/// \return #BADADDR if nobody refers to the specified data. + +idaman ea_t ida_export get_next_dref_to(ea_t to, ea_t current); + + +/// Get first instruction referenced from the specified instruction. +/// If the specified instruction passes execution to the next instruction +/// then the next instruction is returned. Otherwise the lowest referenced +/// address is returned (remember that xrefs are kept sorted!). +/// \param from linear address of referencing instruction +/// \return first referenced address. +/// If the specified instruction doesn't reference to other instructions +/// then returns #BADADDR. + +idaman ea_t ida_export get_first_cref_from(ea_t from); + + +/// Get next instruction referenced from the specified instruction. +/// \param from linear address of referencing instruction +/// \param current linear address of current referenced instruction +/// This value is returned by get_first_cref_from() or +/// previous call to get_next_cref_from() functions. +/// \return next referenced address or #BADADDR. + +idaman ea_t ida_export get_next_cref_from(ea_t from, ea_t current); + + +/// Get first instruction referencing to the specified instruction. +/// If the specified instruction may be executed immediately after its +/// previous instruction then the previous instruction is returned. +/// Otherwise the lowest referencing address is returned. +/// (remember that xrefs are kept sorted!). +/// \param to linear address of referenced instruction +/// \return linear address of the first referencing instruction or #BADADDR. + +idaman ea_t ida_export get_first_cref_to(ea_t to); + + +/// Get next instruction referencing to the specified instruction. +/// \param to linear address of referenced instruction +/// \param current linear address of current referenced instruction +/// This value is returned by get_first_cref_to() or +/// previous call to get_next_cref_to() functions. +/// \return linear address of the next referencing instruction or #BADADDR. + +idaman ea_t ida_export get_next_cref_to(ea_t to, ea_t current); + + +/// \name Far code references +/// The following functions are similar to get_{first|next}_cref_{from|to} +/// functions. The only difference is that they don't take into account +/// ordinary flow of execution. Only jump and call xrefs are returned. +/// (fcref means "far code reference") +///@{ +idaman ea_t ida_export get_first_fcref_from(ea_t from); +idaman ea_t ida_export get_next_fcref_from(ea_t from, ea_t current); +idaman ea_t ida_export get_first_fcref_to(ea_t to); +idaman ea_t ida_export get_next_fcref_to(ea_t to, ea_t current); +///@} + + +/// Does 'ea' have references from outside of 'pfn'? + +idaman bool ida_export has_external_refs(func_t *pfn, ea_t ea); + + +/// Are there jump or flow references to EA? + +idaman bool ida_export has_jump_or_flow_xref(ea_t ea); + + +//------------------------------------------------------------------------- +// S W I T C H T A B L E F U N C T I O N S +//------------------------------------------------------------------------- +#ifdef NALT_HPP + +/// Create switch table from the switch information. +/// Usually there is no need to call this function directly because the kernel +/// will call it for the result of \ph{is_switch()}. +/// \param insn_ea address of the 'indirect jump' instruction +/// \param si switch information +/// \return success + +idaman bool ida_export create_switch_table( + ea_t insn_ea, + const switch_info_t &si); + + +/// Create code xrefs for the switch table. +/// This function creates xrefs from the indirect jump. +/// Usually there is no need to call this function directly because the kernel +/// will call it for switch tables +/// \param insn_ea address of the 'indirect jump' instruction +/// \param si switch information + +idaman void ida_export create_switch_xrefs( + ea_t insn_ea, + const switch_info_t &si); + + +/// Vector of case values - see calc_switch_cases() +typedef qvector<svalvec_t> casevec_t; + + +/// Get detailed information about the switch table cases. +/// \param casevec vector of case values... +/// \param targets ...and corresponding target addresses +/// \param insn_ea address of the 'indirect jump' instruction +/// \param si switch information +/// \return success + +idaman bool ida_export calc_switch_cases( + casevec_t *casevec, + eavec_t *targets, + ea_t insn_ea, + const switch_info_t &si); + +/// Delete information created by the call of create_switch_table(). +/// It delete parent address for each switch target. +/// \param insn_ea address of the 'indirect jump' instruction +/// \param si switch information +idaman void ida_export delete_switch_table( + ea_t jump_ea, + const switch_info_t &si); + +#endif + +//------------------------------------------------------------------------- +// F U N C T I O N S F O R T H E K E R N E L +//------------------------------------------------------------------------- + +/// \cond +idaman bool ida_export create_xrefs_from(ea_t ea); // returns false: no item at ea +idaman void ida_export delete_all_xrefs_from(ea_t ea, bool expand); +/// \endcond + + +#if !defined(NO_OBSOLETE_FUNCS) +#define XREF_ALL XREF_FLOW +#define XREF_FAR XREF_NOFLOW +#endif + +#endif // _XREF_HPP + +``` + +`LICENSE`: + +``` +MIT License + +Copyright (c) 2025 Christian Stolev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + +`Lua/lapi.c`: + +```c +/* +** $Id: lapi.c $ +** Lua API +** See Copyright Notice in lua.h +*/ + +#define lapi_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <limits.h> +#include <stdarg.h> +#include <string.h> + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" + + + +const char lua_ident[] = + "$LuaVersion: " LUA_COPYRIGHT " $" + "$LuaAuthors: " LUA_AUTHORS " $"; + + + +/* +** Test for a valid index (one that is not the 'nilvalue'). +*/ +#define isvalid(L, o) ((o) != &G(L)->nilvalue) + + +/* test for pseudo index */ +#define ispseudo(i) ((i) <= LUA_REGISTRYINDEX) + +/* test for upvalue */ +#define isupvalue(i) ((i) < LUA_REGISTRYINDEX) + + +/* +** Convert an acceptable index to a pointer to its respective value. +** Non-valid indices return the special nil value 'G(L)->nilvalue'. +*/ +static TValue *index2value (lua_State *L, int idx) { + CallInfo *ci = L->ci; + if (idx > 0) { + StkId o = ci->func.p + idx; + api_check(L, idx <= ci->top.p - (ci->func.p + 1), "unacceptable index"); + if (o >= L->top.p) return &G(L)->nilvalue; + else return s2v(o); + } + else if (!ispseudo(idx)) { /* negative index */ + api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1), + "invalid index"); + return s2v(L->top.p + idx); + } + else if (idx == LUA_REGISTRYINDEX) + return &G(L)->l_registry; + else { /* upvalues */ + idx = LUA_REGISTRYINDEX - idx; + api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large"); + if (ttisCclosure(s2v(ci->func.p))) { /* C closure? */ + CClosure *func = clCvalue(s2v(ci->func.p)); + return (idx <= func->nupvalues) ? &func->upvalue[idx-1] + : &G(L)->nilvalue; + } + else { /* light C function or Lua function (through a hook)?) */ + api_check(L, ttislcf(s2v(ci->func.p)), "caller not a C function"); + return &G(L)->nilvalue; /* no upvalues */ + } + } +} + + + +/* +** Convert a valid actual index (not a pseudo-index) to its address. +*/ +static StkId index2stack (lua_State *L, int idx) { + CallInfo *ci = L->ci; + if (idx > 0) { + StkId o = ci->func.p + idx; + api_check(L, o < L->top.p, "invalid index"); + return o; + } + else { /* non-positive index */ + api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1), + "invalid index"); + api_check(L, !ispseudo(idx), "invalid index"); + return L->top.p + idx; + } +} + + +LUA_API int lua_checkstack (lua_State *L, int n) { + int res; + CallInfo *ci; + lua_lock(L); + ci = L->ci; + api_check(L, n >= 0, "negative 'n'"); + if (L->stack_last.p - L->top.p > n) /* stack large enough? */ + res = 1; /* yes; check is OK */ + else /* need to grow stack */ + res = luaD_growstack(L, n, 0); + if (res && ci->top.p < L->top.p + n) + ci->top.p = L->top.p + n; /* adjust frame top */ + lua_unlock(L); + return res; +} + + +LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { + int i; + if (from == to) return; + lua_lock(to); + api_checkpop(from, n); + api_check(from, G(from) == G(to), "moving among independent states"); + api_check(from, to->ci->top.p - to->top.p >= n, "stack overflow"); + from->top.p -= n; + for (i = 0; i < n; i++) { + setobjs2s(to, to->top.p, from->top.p + i); + to->top.p++; /* stack already checked by previous 'api_check' */ + } + lua_unlock(to); +} + + +LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { + lua_CFunction old; + lua_lock(L); + old = G(L)->panic; + G(L)->panic = panicf; + lua_unlock(L); + return old; +} + + +LUA_API lua_Number lua_version (lua_State *L) { + UNUSED(L); + return LUA_VERSION_NUM; +} + + + +/* +** basic stack manipulation +*/ + + +/* +** convert an acceptable stack index into an absolute index +*/ +LUA_API int lua_absindex (lua_State *L, int idx) { + return (idx > 0 || ispseudo(idx)) + ? idx + : cast_int(L->top.p - L->ci->func.p) + idx; +} + + +LUA_API int lua_gettop (lua_State *L) { + return cast_int(L->top.p - (L->ci->func.p + 1)); +} + + +LUA_API void lua_settop (lua_State *L, int idx) { + CallInfo *ci; + StkId func, newtop; + ptrdiff_t diff; /* difference for new top */ + lua_lock(L); + ci = L->ci; + func = ci->func.p; + if (idx >= 0) { + api_check(L, idx <= ci->top.p - (func + 1), "new top too large"); + diff = ((func + 1) + idx) - L->top.p; + for (; diff > 0; diff--) + setnilvalue(s2v(L->top.p++)); /* clear new slots */ + } + else { + api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top"); + diff = idx + 1; /* will "subtract" index (as it is negative) */ + } + newtop = L->top.p + diff; + if (diff < 0 && L->tbclist.p >= newtop) { + lua_assert(ci->callstatus & CIST_TBC); + newtop = luaF_close(L, newtop, CLOSEKTOP, 0); + } + L->top.p = newtop; /* correct top only after closing any upvalue */ + lua_unlock(L); +} + + +LUA_API void lua_closeslot (lua_State *L, int idx) { + StkId level; + lua_lock(L); + level = index2stack(L, idx); + api_check(L, (L->ci->callstatus & CIST_TBC) && (L->tbclist.p == level), + "no variable to close at given level"); + level = luaF_close(L, level, CLOSEKTOP, 0); + setnilvalue(s2v(level)); + lua_unlock(L); +} + + +/* +** Reverse the stack segment from 'from' to 'to' +** (auxiliary to 'lua_rotate') +** Note that we move(copy) only the value inside the stack. +** (We do not move additional fields that may exist.) +*/ +static void reverse (lua_State *L, StkId from, StkId to) { + for (; from < to; from++, to--) { + TValue temp; + setobj(L, &temp, s2v(from)); + setobjs2s(L, from, to); + setobj2s(L, to, &temp); + } +} + + +/* +** Let x = AB, where A is a prefix of length 'n'. Then, +** rotate x n == BA. But BA == (A^r . B^r)^r. +*/ +LUA_API void lua_rotate (lua_State *L, int idx, int n) { + StkId p, t, m; + lua_lock(L); + t = L->top.p - 1; /* end of stack segment being rotated */ + p = index2stack(L, idx); /* start of segment */ + api_check(L, L->tbclist.p < p, "moving a to-be-closed slot"); + api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'"); + m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */ + reverse(L, p, m); /* reverse the prefix with length 'n' */ + reverse(L, m + 1, t); /* reverse the suffix */ + reverse(L, p, t); /* reverse the entire segment */ + lua_unlock(L); +} + + +LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { + TValue *fr, *to; + lua_lock(L); + fr = index2value(L, fromidx); + to = index2value(L, toidx); + api_check(L, isvalid(L, to), "invalid index"); + setobj(L, to, fr); + if (isupvalue(toidx)) /* function upvalue? */ + luaC_barrier(L, clCvalue(s2v(L->ci->func.p)), fr); + /* LUA_REGISTRYINDEX does not need gc barrier + (collector revisits it before finishing collection) */ + lua_unlock(L); +} + + +LUA_API void lua_pushvalue (lua_State *L, int idx) { + lua_lock(L); + setobj2s(L, L->top.p, index2value(L, idx)); + api_incr_top(L); + lua_unlock(L); +} + + + +/* +** access functions (stack -> C) +*/ + + +LUA_API int lua_type (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (isvalid(L, o) ? ttype(o) : LUA_TNONE); +} + + +LUA_API const char *lua_typename (lua_State *L, int t) { + UNUSED(L); + api_check(L, LUA_TNONE <= t && t < LUA_NUMTYPES, "invalid type"); + return ttypename(t); +} + + +LUA_API int lua_iscfunction (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (ttislcf(o) || (ttisCclosure(o))); +} + + +LUA_API int lua_isinteger (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return ttisinteger(o); +} + + +LUA_API int lua_isnumber (lua_State *L, int idx) { + lua_Number n; + const TValue *o = index2value(L, idx); + return tonumber(o, &n); +} + + +LUA_API int lua_isstring (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (ttisstring(o) || cvt2str(o)); +} + + +LUA_API int lua_isuserdata (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (ttisfulluserdata(o) || ttislightuserdata(o)); +} + + +LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { + const TValue *o1 = index2value(L, index1); + const TValue *o2 = index2value(L, index2); + return (isvalid(L, o1) && isvalid(L, o2)) ? luaV_rawequalobj(o1, o2) : 0; +} + + +LUA_API void lua_arith (lua_State *L, int op) { + lua_lock(L); + if (op != LUA_OPUNM && op != LUA_OPBNOT) + api_checkpop(L, 2); /* all other operations expect two operands */ + else { /* for unary operations, add fake 2nd operand */ + api_checkpop(L, 1); + setobjs2s(L, L->top.p, L->top.p - 1); + api_incr_top(L); + } + /* first operand at top - 2, second at top - 1; result go to top - 2 */ + luaO_arith(L, op, s2v(L->top.p - 2), s2v(L->top.p - 1), L->top.p - 2); + L->top.p--; /* pop second operand */ + lua_unlock(L); +} + + +LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) { + const TValue *o1; + const TValue *o2; + int i = 0; + lua_lock(L); /* may call tag method */ + o1 = index2value(L, index1); + o2 = index2value(L, index2); + if (isvalid(L, o1) && isvalid(L, o2)) { + switch (op) { + case LUA_OPEQ: i = luaV_equalobj(L, o1, o2); break; + case LUA_OPLT: i = luaV_lessthan(L, o1, o2); break; + case LUA_OPLE: i = luaV_lessequal(L, o1, o2); break; + default: api_check(L, 0, "invalid option"); + } + } + lua_unlock(L); + return i; +} + + +LUA_API unsigned (lua_numbertocstring) (lua_State *L, int idx, char *buff) { + const TValue *o = index2value(L, idx); + if (ttisnumber(o)) { + unsigned len = luaO_tostringbuff(o, buff); + buff[len++] = '\0'; /* add final zero */ + return len; + } + else + return 0; +} + + +LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) { + size_t sz = luaO_str2num(s, s2v(L->top.p)); + if (sz != 0) + api_incr_top(L); + return sz; +} + + +LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum) { + lua_Number n = 0; + const TValue *o = index2value(L, idx); + int isnum = tonumber(o, &n); + if (pisnum) + *pisnum = isnum; + return n; +} + + +LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum) { + lua_Integer res = 0; + const TValue *o = index2value(L, idx); + int isnum = tointeger(o, &res); + if (pisnum) + *pisnum = isnum; + return res; +} + + +LUA_API int lua_toboolean (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return !l_isfalse(o); +} + + +LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { + TValue *o; + lua_lock(L); + o = index2value(L, idx); + if (!ttisstring(o)) { + if (!cvt2str(o)) { /* not convertible? */ + if (len != NULL) *len = 0; + lua_unlock(L); + return NULL; + } + luaO_tostring(L, o); + luaC_checkGC(L); + o = index2value(L, idx); /* previous call may reallocate the stack */ + } + lua_unlock(L); + if (len != NULL) + return getlstr(tsvalue(o), *len); + else + return getstr(tsvalue(o)); +} + + +LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + switch (ttypetag(o)) { + case LUA_VSHRSTR: return cast(lua_Unsigned, tsvalue(o)->shrlen); + case LUA_VLNGSTR: return cast(lua_Unsigned, tsvalue(o)->u.lnglen); + case LUA_VUSERDATA: return cast(lua_Unsigned, uvalue(o)->len); + case LUA_VTABLE: { + lua_Unsigned res; + lua_lock(L); + res = luaH_getn(L, hvalue(o)); + lua_unlock(L); + return res; + } + default: return 0; + } +} + + +LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + if (ttislcf(o)) return fvalue(o); + else if (ttisCclosure(o)) + return clCvalue(o)->f; + else return NULL; /* not a C function */ +} + + +l_sinline void *touserdata (const TValue *o) { + switch (ttype(o)) { + case LUA_TUSERDATA: return getudatamem(uvalue(o)); + case LUA_TLIGHTUSERDATA: return pvalue(o); + default: return NULL; + } +} + + +LUA_API void *lua_touserdata (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return touserdata(o); +} + + +LUA_API lua_State *lua_tothread (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + return (!ttisthread(o)) ? NULL : thvalue(o); +} + + +/* +** Returns a pointer to the internal representation of an object. +** Note that ISO C does not allow the conversion of a pointer to +** function to a 'void*', so the conversion here goes through +** a 'size_t'. (As the returned pointer is only informative, this +** conversion should not be a problem.) +*/ +LUA_API const void *lua_topointer (lua_State *L, int idx) { + const TValue *o = index2value(L, idx); + switch (ttypetag(o)) { + case LUA_VLCF: return cast_voidp(cast_sizet(fvalue(o))); + case LUA_VUSERDATA: case LUA_VLIGHTUSERDATA: + return touserdata(o); + default: { + if (iscollectable(o)) + return gcvalue(o); + else + return NULL; + } + } +} + + + +/* +** push functions (C -> stack) +*/ + + +LUA_API void lua_pushnil (lua_State *L) { + lua_lock(L); + setnilvalue(s2v(L->top.p)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { + lua_lock(L); + setfltvalue(s2v(L->top.p), n); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { + lua_lock(L); + setivalue(s2v(L->top.p), n); + api_incr_top(L); + lua_unlock(L); +} + + +/* +** Pushes on the stack a string with given length. Avoid using 's' when +** 'len' == 0 (as 's' can be NULL in that case), due to later use of +** 'memcmp' and 'memcpy'. +*/ +LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { + TString *ts; + lua_lock(L); + ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len); + setsvalue2s(L, L->top.p, ts); + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return getstr(ts); +} + + +LUA_API const char *lua_pushexternalstring (lua_State *L, + const char *s, size_t len, lua_Alloc falloc, void *ud) { + TString *ts; + lua_lock(L); + api_check(L, len <= MAX_SIZE, "string too large"); + api_check(L, s[len] == '\0', "string not ending with zero"); + ts = luaS_newextlstr (L, s, len, falloc, ud); + setsvalue2s(L, L->top.p, ts); + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return getstr(ts); +} + + +LUA_API const char *lua_pushstring (lua_State *L, const char *s) { + lua_lock(L); + if (s == NULL) + setnilvalue(s2v(L->top.p)); + else { + TString *ts; + ts = luaS_new(L, s); + setsvalue2s(L, L->top.p, ts); + s = getstr(ts); /* internal copy's address */ + } + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return s; +} + + +LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, + va_list argp) { + const char *ret; + lua_lock(L); + ret = luaO_pushvfstring(L, fmt, argp); + luaC_checkGC(L); + lua_unlock(L); + return ret; +} + + +LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { + const char *ret; + va_list argp; + lua_lock(L); + pushvfstring(L, argp, fmt, ret); + luaC_checkGC(L); + lua_unlock(L); + return ret; +} + + +LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { + lua_lock(L); + if (n == 0) { + setfvalue(s2v(L->top.p), fn); + api_incr_top(L); + } + else { + int i; + CClosure *cl; + api_checkpop(L, n); + api_check(L, n <= MAXUPVAL, "upvalue index too large"); + cl = luaF_newCclosure(L, n); + cl->f = fn; + for (i = 0; i < n; i++) { + setobj2n(L, &cl->upvalue[i], s2v(L->top.p - n + i)); + /* does not need barrier because closure is white */ + lua_assert(iswhite(cl)); + } + L->top.p -= n; + setclCvalue(L, s2v(L->top.p), cl); + api_incr_top(L); + luaC_checkGC(L); + } + lua_unlock(L); +} + + +LUA_API void lua_pushboolean (lua_State *L, int b) { + lua_lock(L); + if (b) + setbtvalue(s2v(L->top.p)); + else + setbfvalue(s2v(L->top.p)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { + lua_lock(L); + setpvalue(s2v(L->top.p), p); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API int lua_pushthread (lua_State *L) { + lua_lock(L); + setthvalue(L, s2v(L->top.p), L); + api_incr_top(L); + lua_unlock(L); + return (mainthread(G(L)) == L); +} + + + +/* +** get functions (Lua -> stack) +*/ + + +static int auxgetstr (lua_State *L, const TValue *t, const char *k) { + lu_byte tag; + TString *str = luaS_new(L, k); + luaV_fastget(t, str, s2v(L->top.p), luaH_getstr, tag); + if (!tagisempty(tag)) + api_incr_top(L); + else { + setsvalue2s(L, L->top.p, str); + api_incr_top(L); + tag = luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, tag); + } + lua_unlock(L); + return novariant(tag); +} + + +/* +** The following function assumes that the registry cannot be a weak +** table; so, an emergency collection while using the global table +** cannot collect it. +*/ +static void getGlobalTable (lua_State *L, TValue *gt) { + Table *registry = hvalue(&G(L)->l_registry); + lu_byte tag = luaH_getint(registry, LUA_RIDX_GLOBALS, gt); + (void)tag; /* avoid not-used warnings when checks are off */ + api_check(L, novariant(tag) == LUA_TTABLE, "global table must exist"); +} + + +LUA_API int lua_getglobal (lua_State *L, const char *name) { + TValue gt; + lua_lock(L); + getGlobalTable(L, >); + return auxgetstr(L, >, name); +} + + +LUA_API int lua_gettable (lua_State *L, int idx) { + lu_byte tag; + TValue *t; + lua_lock(L); + api_checkpop(L, 1); + t = index2value(L, idx); + luaV_fastget(t, s2v(L->top.p - 1), s2v(L->top.p - 1), luaH_get, tag); + if (tagisempty(tag)) + tag = luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, tag); + lua_unlock(L); + return novariant(tag); +} + + +LUA_API int lua_getfield (lua_State *L, int idx, const char *k) { + lua_lock(L); + return auxgetstr(L, index2value(L, idx), k); +} + + +LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) { + TValue *t; + lu_byte tag; + lua_lock(L); + t = index2value(L, idx); + luaV_fastgeti(t, n, s2v(L->top.p), tag); + if (tagisempty(tag)) { + TValue key; + setivalue(&key, n); + tag = luaV_finishget(L, t, &key, L->top.p, tag); + } + api_incr_top(L); + lua_unlock(L); + return novariant(tag); +} + + +static int finishrawget (lua_State *L, lu_byte tag) { + if (tagisempty(tag)) /* avoid copying empty items to the stack */ + setnilvalue(s2v(L->top.p)); + api_incr_top(L); + lua_unlock(L); + return novariant(tag); +} + + +l_sinline Table *gettable (lua_State *L, int idx) { + TValue *t = index2value(L, idx); + api_check(L, ttistable(t), "table expected"); + return hvalue(t); +} + + +LUA_API int lua_rawget (lua_State *L, int idx) { + Table *t; + lu_byte tag; + lua_lock(L); + api_checkpop(L, 1); + t = gettable(L, idx); + tag = luaH_get(t, s2v(L->top.p - 1), s2v(L->top.p - 1)); + L->top.p--; /* pop key */ + return finishrawget(L, tag); +} + + +LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) { + Table *t; + lu_byte tag; + lua_lock(L); + t = gettable(L, idx); + luaH_fastgeti(t, n, s2v(L->top.p), tag); + return finishrawget(L, tag); +} + + +LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) { + Table *t; + TValue k; + lua_lock(L); + t = gettable(L, idx); + setpvalue(&k, cast_voidp(p)); + return finishrawget(L, luaH_get(t, &k, s2v(L->top.p))); +} + + +LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { + Table *t; + lua_lock(L); + t = luaH_new(L); + sethvalue2s(L, L->top.p, t); + api_incr_top(L); + if (narray > 0 || nrec > 0) + luaH_resize(L, t, cast_uint(narray), cast_uint(nrec)); + luaC_checkGC(L); + lua_unlock(L); +} + + +LUA_API int lua_getmetatable (lua_State *L, int objindex) { + const TValue *obj; + Table *mt; + int res = 0; + lua_lock(L); + obj = index2value(L, objindex); + switch (ttype(obj)) { + case LUA_TTABLE: + mt = hvalue(obj)->metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(obj)->metatable; + break; + default: + mt = G(L)->mt[ttype(obj)]; + break; + } + if (mt != NULL) { + sethvalue2s(L, L->top.p, mt); + api_incr_top(L); + res = 1; + } + lua_unlock(L); + return res; +} + + +LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) { + TValue *o; + int t; + lua_lock(L); + o = index2value(L, idx); + api_check(L, ttisfulluserdata(o), "full userdata expected"); + if (n <= 0 || n > uvalue(o)->nuvalue) { + setnilvalue(s2v(L->top.p)); + t = LUA_TNONE; + } + else { + setobj2s(L, L->top.p, &uvalue(o)->uv[n - 1].uv); + t = ttype(s2v(L->top.p)); + } + api_incr_top(L); + lua_unlock(L); + return t; +} + + +/* +** set functions (stack -> Lua) +*/ + +/* +** t[k] = value at the top of the stack (where 'k' is a string) +*/ +static void auxsetstr (lua_State *L, const TValue *t, const char *k) { + int hres; + TString *str = luaS_new(L, k); + api_checkpop(L, 1); + luaV_fastset(t, str, s2v(L->top.p - 1), hres, luaH_psetstr); + if (hres == HOK) { + luaV_finishfastset(L, t, s2v(L->top.p - 1)); + L->top.p--; /* pop value */ + } + else { + setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */ + api_incr_top(L); + luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), hres); + L->top.p -= 2; /* pop value and key */ + } + lua_unlock(L); /* lock done by caller */ +} + + +LUA_API void lua_setglobal (lua_State *L, const char *name) { + TValue gt; + lua_lock(L); /* unlock done in 'auxsetstr' */ + getGlobalTable(L, >); + auxsetstr(L, >, name); +} + + +LUA_API void lua_settable (lua_State *L, int idx) { + TValue *t; + int hres; + lua_lock(L); + api_checkpop(L, 2); + t = index2value(L, idx); + luaV_fastset(t, s2v(L->top.p - 2), s2v(L->top.p - 1), hres, luaH_pset); + if (hres == HOK) + luaV_finishfastset(L, t, s2v(L->top.p - 1)); + else + luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), hres); + L->top.p -= 2; /* pop index and value */ + lua_unlock(L); +} + + +LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { + lua_lock(L); /* unlock done in 'auxsetstr' */ + auxsetstr(L, index2value(L, idx), k); +} + + +LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) { + TValue *t; + int hres; + lua_lock(L); + api_checkpop(L, 1); + t = index2value(L, idx); + luaV_fastseti(t, n, s2v(L->top.p - 1), hres); + if (hres == HOK) + luaV_finishfastset(L, t, s2v(L->top.p - 1)); + else { + TValue temp; + setivalue(&temp, n); + luaV_finishset(L, t, &temp, s2v(L->top.p - 1), hres); + } + L->top.p--; /* pop value */ + lua_unlock(L); +} + + +static void aux_rawset (lua_State *L, int idx, TValue *key, int n) { + Table *t; + lua_lock(L); + api_checkpop(L, n); + t = gettable(L, idx); + luaH_set(L, t, key, s2v(L->top.p - 1)); + invalidateTMcache(t); + luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1)); + L->top.p -= n; + lua_unlock(L); +} + + +LUA_API void lua_rawset (lua_State *L, int idx) { + aux_rawset(L, idx, s2v(L->top.p - 2), 2); +} + + +LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) { + TValue k; + setpvalue(&k, cast_voidp(p)); + aux_rawset(L, idx, &k, 1); +} + + +LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) { + Table *t; + lua_lock(L); + api_checkpop(L, 1); + t = gettable(L, idx); + luaH_setint(L, t, n, s2v(L->top.p - 1)); + luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1)); + L->top.p--; + lua_unlock(L); +} + + +LUA_API int lua_setmetatable (lua_State *L, int objindex) { + TValue *obj; + Table *mt; + lua_lock(L); + api_checkpop(L, 1); + obj = index2value(L, objindex); + if (ttisnil(s2v(L->top.p - 1))) + mt = NULL; + else { + api_check(L, ttistable(s2v(L->top.p - 1)), "table expected"); + mt = hvalue(s2v(L->top.p - 1)); + } + switch (ttype(obj)) { + case LUA_TTABLE: { + hvalue(obj)->metatable = mt; + if (mt) { + luaC_objbarrier(L, gcvalue(obj), mt); + luaC_checkfinalizer(L, gcvalue(obj), mt); + } + break; + } + case LUA_TUSERDATA: { + uvalue(obj)->metatable = mt; + if (mt) { + luaC_objbarrier(L, uvalue(obj), mt); + luaC_checkfinalizer(L, gcvalue(obj), mt); + } + break; + } + default: { + G(L)->mt[ttype(obj)] = mt; + break; + } + } + L->top.p--; + lua_unlock(L); + return 1; +} + + +LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) { + TValue *o; + int res; + lua_lock(L); + api_checkpop(L, 1); + o = index2value(L, idx); + api_check(L, ttisfulluserdata(o), "full userdata expected"); + if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue))) + res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */ + else { + setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top.p - 1)); + luaC_barrierback(L, gcvalue(o), s2v(L->top.p - 1)); + res = 1; + } + L->top.p--; + lua_unlock(L); + return res; +} + + +/* +** 'load' and 'call' functions (run Lua code) +*/ + + +#define checkresults(L,na,nr) \ + (api_check(L, (nr) == LUA_MULTRET \ + || (L->ci->top.p - L->top.p >= (nr) - (na)), \ + "results from function overflow current stack size"), \ + api_check(L, LUA_MULTRET <= (nr) && (nr) <= MAXRESULTS, \ + "invalid number of results")) + + +LUA_API void lua_callk (lua_State *L, int nargs, int nresults, + lua_KContext ctx, lua_KFunction k) { + StkId func; + lua_lock(L); + api_check(L, k == NULL || !isLua(L->ci), + "cannot use continuations inside hooks"); + api_checkpop(L, nargs + 1); + api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); + checkresults(L, nargs, nresults); + func = L->top.p - (nargs+1); + if (k != NULL && yieldable(L)) { /* need to prepare continuation? */ + L->ci->u.c.k = k; /* save continuation */ + L->ci->u.c.ctx = ctx; /* save context */ + luaD_call(L, func, nresults); /* do the call */ + } + else /* no continuation or no yieldable */ + luaD_callnoyield(L, func, nresults); /* just do the call */ + adjustresults(L, nresults); + lua_unlock(L); +} + + + +/* +** Execute a protected call. +*/ +struct CallS { /* data to 'f_call' */ + StkId func; + int nresults; +}; + + +static void f_call (lua_State *L, void *ud) { + struct CallS *c = cast(struct CallS *, ud); + luaD_callnoyield(L, c->func, c->nresults); +} + + + +LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc, + lua_KContext ctx, lua_KFunction k) { + struct CallS c; + TStatus status; + ptrdiff_t func; + lua_lock(L); + api_check(L, k == NULL || !isLua(L->ci), + "cannot use continuations inside hooks"); + api_checkpop(L, nargs + 1); + api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); + checkresults(L, nargs, nresults); + if (errfunc == 0) + func = 0; + else { + StkId o = index2stack(L, errfunc); + api_check(L, ttisfunction(s2v(o)), "error handler must be a function"); + func = savestack(L, o); + } + c.func = L->top.p - (nargs+1); /* function to be called */ + if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */ + c.nresults = nresults; /* do a 'conventional' protected call */ + status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); + } + else { /* prepare continuation (call is already protected by 'resume') */ + CallInfo *ci = L->ci; + ci->u.c.k = k; /* save continuation */ + ci->u.c.ctx = ctx; /* save context */ + /* save information for error recovery */ + ci->u2.funcidx = cast_int(savestack(L, c.func)); + ci->u.c.old_errfunc = L->errfunc; + L->errfunc = func; + setoah(ci, L->allowhook); /* save value of 'allowhook' */ + ci->callstatus |= CIST_YPCALL; /* function can do error recovery */ + luaD_call(L, c.func, nresults); /* do the call */ + ci->callstatus &= ~CIST_YPCALL; + L->errfunc = ci->u.c.old_errfunc; + status = LUA_OK; /* if it is here, there were no errors */ + } + adjustresults(L, nresults); + lua_unlock(L); + return APIstatus(status); +} + + +LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, + const char *chunkname, const char *mode) { + ZIO z; + TStatus status; + lua_lock(L); + if (!chunkname) chunkname = "?"; + luaZ_init(L, &z, reader, data); + status = luaD_protectedparser(L, &z, chunkname, mode); + if (status == LUA_OK) { /* no errors? */ + LClosure *f = clLvalue(s2v(L->top.p - 1)); /* get new function */ + if (f->nupvalues >= 1) { /* does it have an upvalue? */ + /* get global table from registry */ + TValue gt; + getGlobalTable(L, >); + /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */ + setobj(L, f->upvals[0]->v.p, >); + luaC_barrier(L, f->upvals[0], >); + } + } + lua_unlock(L); + return APIstatus(status); +} + + +/* +** Dump a Lua function, calling 'writer' to write its parts. Ensure +** the stack returns with its original size. +*/ +LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) { + int status; + ptrdiff_t otop = savestack(L, L->top.p); /* original top */ + TValue *f = s2v(L->top.p - 1); /* function to be dumped */ + lua_lock(L); + api_checkpop(L, 1); + api_check(L, isLfunction(f), "Lua function expected"); + status = luaU_dump(L, clLvalue(f)->p, writer, data, strip); + L->top.p = restorestack(L, otop); /* restore top */ + lua_unlock(L); + return status; +} + + +LUA_API int lua_status (lua_State *L) { + return APIstatus(L->status); +} + + +/* +** Garbage-collection function +*/ +LUA_API int lua_gc (lua_State *L, int what, ...) { + va_list argp; + int res = 0; + global_State *g = G(L); + if (g->gcstp & (GCSTPGC | GCSTPCLS)) /* internal stop? */ + return -1; /* all options are invalid when stopped */ + lua_lock(L); + va_start(argp, what); + switch (what) { + case LUA_GCSTOP: { + g->gcstp = GCSTPUSR; /* stopped by the user */ + break; + } + case LUA_GCRESTART: { + luaE_setdebt(g, 0); + g->gcstp = 0; /* (other bits must be zero here) */ + break; + } + case LUA_GCCOLLECT: { + luaC_fullgc(L, 0); + break; + } + case LUA_GCCOUNT: { + /* GC values are expressed in Kbytes: #bytes/2^10 */ + res = cast_int(gettotalbytes(g) >> 10); + break; + } + case LUA_GCCOUNTB: { + res = cast_int(gettotalbytes(g) & 0x3ff); + break; + } + case LUA_GCSTEP: { + lu_byte oldstp = g->gcstp; + l_mem n = cast(l_mem, va_arg(argp, size_t)); + int work = 0; /* true if GC did some work */ + g->gcstp = 0; /* allow GC to run (other bits must be zero here) */ + if (n <= 0) + n = g->GCdebt; /* force to run one basic step */ + luaE_setdebt(g, g->GCdebt - n); + luaC_condGC(L, (void)0, work = 1); + if (work && g->gcstate == GCSpause) /* end of cycle? */ + res = 1; /* signal it */ + g->gcstp = oldstp; /* restore previous state */ + break; + } + case LUA_GCISRUNNING: { + res = gcrunning(g); + break; + } + case LUA_GCGEN: { + res = (g->gckind == KGC_INC) ? LUA_GCINC : LUA_GCGEN; + luaC_changemode(L, KGC_GENMINOR); + break; + } + case LUA_GCINC: { + res = (g->gckind == KGC_INC) ? LUA_GCINC : LUA_GCGEN; + luaC_changemode(L, KGC_INC); + break; + } + case LUA_GCPARAM: { + int param = va_arg(argp, int); + int value = va_arg(argp, int); + api_check(L, 0 <= param && param < LUA_GCPN, "invalid parameter"); + res = cast_int(luaO_applyparam(g->gcparams[param], 100)); + if (value >= 0) + g->gcparams[param] = luaO_codeparam(cast_uint(value)); + break; + } + default: res = -1; /* invalid option */ + } + va_end(argp); + lua_unlock(L); + return res; +} + + + +/* +** miscellaneous functions +*/ + + +LUA_API int lua_error (lua_State *L) { + TValue *errobj; + lua_lock(L); + errobj = s2v(L->top.p - 1); + api_checkpop(L, 1); + /* error object is the memory error message? */ + if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg)) + luaM_error(L); /* raise a memory error */ + else + luaG_errormsg(L); /* raise a regular error */ + /* code unreachable; will unlock when control actually leaves the kernel */ + return 0; /* to avoid warnings */ +} + + +LUA_API int lua_next (lua_State *L, int idx) { + Table *t; + int more; + lua_lock(L); + api_checkpop(L, 1); + t = gettable(L, idx); + more = luaH_next(L, t, L->top.p - 1); + if (more) + api_incr_top(L); + else /* no more elements */ + L->top.p--; /* pop key */ + lua_unlock(L); + return more; +} + + +LUA_API void lua_toclose (lua_State *L, int idx) { + StkId o; + lua_lock(L); + o = index2stack(L, idx); + api_check(L, L->tbclist.p < o, "given index below or equal a marked one"); + luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ + L->ci->callstatus |= CIST_TBC; /* mark that function has TBC slots */ + lua_unlock(L); +} + + +LUA_API void lua_concat (lua_State *L, int n) { + lua_lock(L); + api_checknelems(L, n); + if (n > 0) { + luaV_concat(L, n); + luaC_checkGC(L); + } + else { /* nothing to concatenate */ + setsvalue2s(L, L->top.p, luaS_newlstr(L, "", 0)); /* push empty string */ + api_incr_top(L); + } + lua_unlock(L); +} + + +LUA_API void lua_len (lua_State *L, int idx) { + TValue *t; + lua_lock(L); + t = index2value(L, idx); + luaV_objlen(L, L->top.p, t); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) { + lua_Alloc f; + lua_lock(L); + if (ud) *ud = G(L)->ud; + f = G(L)->frealloc; + lua_unlock(L); + return f; +} + + +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { + lua_lock(L); + G(L)->ud = ud; + G(L)->frealloc = f; + lua_unlock(L); +} + + +void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) { + lua_lock(L); + G(L)->ud_warn = ud; + G(L)->warnf = f; + lua_unlock(L); +} + + +void lua_warning (lua_State *L, const char *msg, int tocont) { + lua_lock(L); + luaE_warning(L, msg, tocont); + lua_unlock(L); +} + + + +LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) { + Udata *u; + lua_lock(L); + api_check(L, 0 <= nuvalue && nuvalue < SHRT_MAX, "invalid value"); + u = luaS_newudata(L, size, cast(unsigned short, nuvalue)); + setuvalue(L, s2v(L->top.p), u); + api_incr_top(L); + luaC_checkGC(L); + lua_unlock(L); + return getudatamem(u); +} + + + +static const char *aux_upvalue (TValue *fi, int n, TValue **val, + GCObject **owner) { + switch (ttypetag(fi)) { + case LUA_VCCL: { /* C closure */ + CClosure *f = clCvalue(fi); + if (!(cast_uint(n) - 1u < cast_uint(f->nupvalues))) + return NULL; /* 'n' not in [1, f->nupvalues] */ + *val = &f->upvalue[n-1]; + if (owner) *owner = obj2gco(f); + return ""; + } + case LUA_VLCL: { /* Lua closure */ + LClosure *f = clLvalue(fi); + TString *name; + Proto *p = f->p; + if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues))) + return NULL; /* 'n' not in [1, p->sizeupvalues] */ + *val = f->upvals[n-1]->v.p; + if (owner) *owner = obj2gco(f->upvals[n - 1]); + name = p->upvalues[n-1].name; + return (name == NULL) ? "(no name)" : getstr(name); + } + default: return NULL; /* not a closure */ + } +} + + +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val = NULL; /* to avoid warnings */ + lua_lock(L); + name = aux_upvalue(index2value(L, funcindex), n, &val, NULL); + if (name) { + setobj2s(L, L->top.p, val); + api_incr_top(L); + } + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val = NULL; /* to avoid warnings */ + GCObject *owner = NULL; /* to avoid warnings */ + TValue *fi; + lua_lock(L); + fi = index2value(L, funcindex); + api_checknelems(L, 1); + name = aux_upvalue(fi, n, &val, &owner); + if (name) { + L->top.p--; + setobj(L, val, s2v(L->top.p)); + luaC_barrier(L, owner, val); + } + lua_unlock(L); + return name; +} + + +static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) { + static const UpVal *const nullup = NULL; + LClosure *f; + TValue *fi = index2value(L, fidx); + api_check(L, ttisLclosure(fi), "Lua function expected"); + f = clLvalue(fi); + if (pf) *pf = f; + if (1 <= n && n <= f->p->sizeupvalues) + return &f->upvals[n - 1]; /* get its upvalue pointer */ + else + return (UpVal**)&nullup; +} + + +LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) { + TValue *fi = index2value(L, fidx); + switch (ttypetag(fi)) { + case LUA_VLCL: { /* lua closure */ + return *getupvalref(L, fidx, n, NULL); + } + case LUA_VCCL: { /* C closure */ + CClosure *f = clCvalue(fi); + if (1 <= n && n <= f->nupvalues) + return &f->upvalue[n - 1]; + /* else */ + } /* FALLTHROUGH */ + case LUA_VLCF: + return NULL; /* light C functions have no upvalues */ + default: { + api_check(L, 0, "function expected"); + return NULL; + } + } +} + + +LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1, + int fidx2, int n2) { + LClosure *f1; + UpVal **up1 = getupvalref(L, fidx1, n1, &f1); + UpVal **up2 = getupvalref(L, fidx2, n2, NULL); + api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index"); + *up1 = *up2; + luaC_objbarrier(L, f1, *up1); +} + + + +``` + +`Lua/lapi.h`: + +```h +/* +** $Id: lapi.h $ +** Auxiliary functions from Lua API +** See Copyright Notice in lua.h +*/ + +#ifndef lapi_h +#define lapi_h + + +#include "llimits.h" +#include "lstate.h" + + +#if defined(LUA_USE_APICHECK) +#include <assert.h> +#define api_check(l,e,msg) assert(e) +#else /* for testing */ +#define api_check(l,e,msg) ((void)(l), lua_assert((e) && msg)) +#endif + + + +/* Increments 'L->top.p', checking for stack overflows */ +#define api_incr_top(L) \ + (L->top.p++, api_check(L, L->top.p <= L->ci->top.p, "stack overflow")) + + +/* +** macros that are executed whenever program enters the Lua core +** ('lua_lock') and leaves the core ('lua_unlock') +*/ +#if !defined(lua_lock) +#define lua_lock(L) ((void) 0) +#define lua_unlock(L) ((void) 0) +#endif + + + +/* +** If a call returns too many multiple returns, the callee may not have +** stack space to accommodate all results. In this case, this macro +** increases its stack space ('L->ci->top.p'). +*/ +#define adjustresults(L,nres) \ + { if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \ + L->ci->top.p = L->top.p; } + + +/* Ensure the stack has at least 'n' elements */ +#define api_checknelems(L,n) \ + api_check(L, (n) < (L->top.p - L->ci->func.p), \ + "not enough elements in the stack") + + +/* Ensure the stack has at least 'n' elements to be popped. (Some +** functions only update a slot after checking it for popping, but that +** is only an optimization for a pop followed by a push.) +*/ +#define api_checkpop(L,n) \ + api_check(L, (n) < L->top.p - L->ci->func.p && \ + L->tbclist.p < L->top.p - (n), \ + "not enough free elements in the stack") + +#endif + +``` + +`Lua/lauxlib.c`: + +```c +/* +** $Id: lauxlib.c $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + +#define lauxlib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + + +/* +** This file uses only the official API of Lua. +** Any function declared here could be written as an application function. +*/ + +#include "lua.h" + +#include "lauxlib.h" +#include "llimits.h" + + +/* +** {====================================================== +** Traceback +** ======================================================= +*/ + + +#define LEVELS1 10 /* size of the first part of the stack */ +#define LEVELS2 11 /* size of the second part of the stack */ + + + +/* +** Search for 'objidx' in table at index -1. ('objidx' must be an +** absolute index.) Return 1 + string at top if it found a good name. +*/ +static int findfield (lua_State *L, int objidx, int level) { + if (level == 0 || !lua_istable(L, -1)) + return 0; /* not found */ + lua_pushnil(L); /* start 'next' loop */ + while (lua_next(L, -2)) { /* for each pair in table */ + if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */ + if (lua_rawequal(L, objidx, -1)) { /* found object? */ + lua_pop(L, 1); /* remove value (but keep name) */ + return 1; + } + else if (findfield(L, objidx, level - 1)) { /* try recursively */ + /* stack: lib_name, lib_table, field_name (top) */ + lua_pushliteral(L, "."); /* place '.' between the two names */ + lua_replace(L, -3); /* (in the slot occupied by table) */ + lua_concat(L, 3); /* lib_name.field_name */ + return 1; + } + } + lua_pop(L, 1); /* remove value */ + } + return 0; /* not found */ +} + + +/* +** Search for a name for a function in all loaded modules +*/ +static int pushglobalfuncname (lua_State *L, lua_Debug *ar) { + int top = lua_gettop(L); + lua_getinfo(L, "f", ar); /* push function */ + lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + luaL_checkstack(L, 6, "not enough stack"); /* slots for 'findfield' */ + if (findfield(L, top + 1, 2)) { + const char *name = lua_tostring(L, -1); + if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */ + lua_pushstring(L, name + 3); /* push name without prefix */ + lua_remove(L, -2); /* remove original name */ + } + lua_copy(L, -1, top + 1); /* copy name to proper place */ + lua_settop(L, top + 1); /* remove table "loaded" and name copy */ + return 1; + } + else { + lua_settop(L, top); /* remove function and global table */ + return 0; + } +} + + +static void pushfuncname (lua_State *L, lua_Debug *ar) { + if (*ar->namewhat != '\0') /* is there a name from code? */ + lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */ + else if (*ar->what == 'm') /* main? */ + lua_pushliteral(L, "main chunk"); + else if (pushglobalfuncname(L, ar)) { /* try a global name */ + lua_pushfstring(L, "function '%s'", lua_tostring(L, -1)); + lua_remove(L, -2); /* remove name */ + } + else if (*ar->what != 'C') /* for Lua functions, use <file:line> */ + lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); + else /* nothing left... */ + lua_pushliteral(L, "?"); +} + + +static int lastlevel (lua_State *L) { + lua_Debug ar; + int li = 1, le = 1; + /* find an upper bound */ + while (lua_getstack(L, le, &ar)) { li = le; le *= 2; } + /* do a binary search */ + while (li < le) { + int m = (li + le)/2; + if (lua_getstack(L, m, &ar)) li = m + 1; + else le = m; + } + return le - 1; +} + + +LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, + const char *msg, int level) { + luaL_Buffer b; + lua_Debug ar; + int last = lastlevel(L1); + int limit2show = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1; + luaL_buffinit(L, &b); + if (msg) { + luaL_addstring(&b, msg); + luaL_addchar(&b, '\n'); + } + luaL_addstring(&b, "stack traceback:"); + while (lua_getstack(L1, level++, &ar)) { + if (limit2show-- == 0) { /* too many levels? */ + int n = last - level - LEVELS2 + 1; /* number of levels to skip */ + lua_pushfstring(L, "\n\t...\t(skipping %d levels)", n); + luaL_addvalue(&b); /* add warning about skip */ + level += n; /* and skip to last levels */ + } + else { + lua_getinfo(L1, "Slnt", &ar); + if (ar.currentline <= 0) + lua_pushfstring(L, "\n\t%s: in ", ar.short_src); + else + lua_pushfstring(L, "\n\t%s:%d: in ", ar.short_src, ar.currentline); + luaL_addvalue(&b); + pushfuncname(L, &ar); + luaL_addvalue(&b); + if (ar.istailcall) + luaL_addstring(&b, "\n\t(...tail calls...)"); + } + } + luaL_pushresult(&b); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Error-report functions +** ======================================================= +*/ + +LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) { + lua_Debug ar; + const char *argword; + if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ + return luaL_error(L, "bad argument #%d (%s)", arg, extramsg); + lua_getinfo(L, "nt", &ar); + if (arg <= ar.extraargs) /* error in an extra argument? */ + argword = "extra argument"; + else { + arg -= ar.extraargs; /* do not count extra arguments */ + if (strcmp(ar.namewhat, "method") == 0) { /* colon syntax? */ + arg--; /* do not count (extra) self argument */ + if (arg == 0) /* error in self argument? */ + return luaL_error(L, "calling '%s' on bad self (%s)", + ar.name, extramsg); + /* else go through; error in a regular argument */ + } + argword = "argument"; + } + if (ar.name == NULL) + ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?"; + return luaL_error(L, "bad %s #%d to '%s' (%s)", + argword, arg, ar.name, extramsg); +} + + +LUALIB_API int luaL_typeerror (lua_State *L, int arg, const char *tname) { + const char *msg; + const char *typearg; /* name for the type of the actual argument */ + if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING) + typearg = lua_tostring(L, -1); /* use the given type name */ + else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA) + typearg = "light userdata"; /* special name for messages */ + else + typearg = luaL_typename(L, arg); /* standard name */ + msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg); + return luaL_argerror(L, arg, msg); +} + + +static void tag_error (lua_State *L, int arg, int tag) { + luaL_typeerror(L, arg, lua_typename(L, tag)); +} + + +/* +** The use of 'lua_pushfstring' ensures this function does not +** need reserved stack space when called. +*/ +LUALIB_API void luaL_where (lua_State *L, int level) { + lua_Debug ar; + if (lua_getstack(L, level, &ar)) { /* check function at level */ + lua_getinfo(L, "Sl", &ar); /* get info about it */ + if (ar.currentline > 0) { /* is there info? */ + lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); + return; + } + } + lua_pushfstring(L, ""); /* else, no information available... */ +} + + +/* +** Again, the use of 'lua_pushvfstring' ensures this function does +** not need reserved stack space when called. (At worst, it generates +** a memory error instead of the given message.) +*/ +LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + luaL_where(L, 1); + lua_pushvfstring(L, fmt, argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + + +LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { + int en = errno; /* calls to Lua API may change this value */ + if (stat) { + lua_pushboolean(L, 1); + return 1; + } + else { + const char *msg; + luaL_pushfail(L); + msg = (en != 0) ? strerror(en) : "(no extra info)"; + if (fname) + lua_pushfstring(L, "%s: %s", fname, msg); + else + lua_pushstring(L, msg); + lua_pushinteger(L, en); + return 3; + } +} + + +#if !defined(l_inspectstat) /* { */ + +#if defined(LUA_USE_POSIX) + +#include <sys/wait.h> + +/* +** use appropriate macros to interpret 'pclose' return status +*/ +#define l_inspectstat(stat,what) \ + if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ + else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } + +#else + +#define l_inspectstat(stat,what) /* no op */ + +#endif + +#endif /* } */ + + +LUALIB_API int luaL_execresult (lua_State *L, int stat) { + if (stat != 0 && errno != 0) /* error with an 'errno'? */ + return luaL_fileresult(L, 0, NULL); + else { + const char *what = "exit"; /* type of termination */ + l_inspectstat(stat, what); /* interpret result */ + if (*what == 'e' && stat == 0) /* successful termination? */ + lua_pushboolean(L, 1); + else + luaL_pushfail(L); + lua_pushstring(L, what); + lua_pushinteger(L, stat); + return 3; /* return true/fail,what,code */ + } +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Userdata's metatable manipulation +** ======================================================= +*/ + +LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { + if (luaL_getmetatable(L, tname) != LUA_TNIL) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + lua_createtable(L, 0, 2); /* create metatable */ + lua_pushstring(L, tname); + lua_setfield(L, -2, "__name"); /* metatable.__name = tname */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ + return 1; +} + + +LUALIB_API void luaL_setmetatable (lua_State *L, const char *tname) { + luaL_getmetatable(L, tname); + lua_setmetatable(L, -2); +} + + +LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + luaL_getmetatable(L, tname); /* get correct metatable */ + if (!lua_rawequal(L, -1, -2)) /* not the same? */ + p = NULL; /* value is a userdata with wrong metatable */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + return NULL; /* value is not a userdata with a metatable */ +} + + +LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { + void *p = luaL_testudata(L, ud, tname); + luaL_argexpected(L, p != NULL, ud, tname); + return p; +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Argument check functions +** ======================================================= +*/ + +LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def, + const char *const lst[]) { + const char *name = (def) ? luaL_optstring(L, arg, def) : + luaL_checkstring(L, arg); + int i; + for (i=0; lst[i]; i++) + if (strcmp(lst[i], name) == 0) + return i; + return luaL_argerror(L, arg, + lua_pushfstring(L, "invalid option '%s'", name)); +} + + +/* +** Ensures the stack has at least 'space' extra slots, raising an error +** if it cannot fulfill the request. (The error handling needs a few +** extra slots to format the error message. In case of an error without +** this extra space, Lua will generate the same 'stack overflow' error, +** but without 'msg'.) +*/ +LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) { + if (l_unlikely(!lua_checkstack(L, space))) { + if (msg) + luaL_error(L, "stack overflow (%s)", msg); + else + luaL_error(L, "stack overflow"); + } +} + + +LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) { + if (l_unlikely(lua_type(L, arg) != t)) + tag_error(L, arg, t); +} + + +LUALIB_API void luaL_checkany (lua_State *L, int arg) { + if (l_unlikely(lua_type(L, arg) == LUA_TNONE)) + luaL_argerror(L, arg, "value expected"); +} + + +LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) { + const char *s = lua_tolstring(L, arg, len); + if (l_unlikely(!s)) tag_error(L, arg, LUA_TSTRING); + return s; +} + + +LUALIB_API const char *luaL_optlstring (lua_State *L, int arg, + const char *def, size_t *len) { + if (lua_isnoneornil(L, arg)) { + if (len) + *len = (def ? strlen(def) : 0); + return def; + } + else return luaL_checklstring(L, arg, len); +} + + +LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) { + int isnum; + lua_Number d = lua_tonumberx(L, arg, &isnum); + if (l_unlikely(!isnum)) + tag_error(L, arg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number def) { + return luaL_opt(L, luaL_checknumber, arg, def); +} + + +static void interror (lua_State *L, int arg) { + if (lua_isnumber(L, arg)) + luaL_argerror(L, arg, "number has no integer representation"); + else + tag_error(L, arg, LUA_TNUMBER); +} + + +LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) { + int isnum; + lua_Integer d = lua_tointegerx(L, arg, &isnum); + if (l_unlikely(!isnum)) { + interror(L, arg); + } + return d; +} + + +LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int arg, + lua_Integer def) { + return luaL_opt(L, luaL_checkinteger, arg, def); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + +/* userdata to box arbitrary data */ +typedef struct UBox { + void *box; + size_t bsize; +} UBox; + + +/* Resize the buffer used by a box. Optimize for the common case of +** resizing to the old size. (For instance, __gc will resize the box +** to 0 even after it was closed. 'pushresult' may also resize it to a +** final size that is equal to the one set when the buffer was created.) +*/ +static void *resizebox (lua_State *L, int idx, size_t newsize) { + UBox *box = (UBox *)lua_touserdata(L, idx); + if (box->bsize == newsize) /* not changing size? */ + return box->box; /* keep the buffer */ + else { + void *ud; + lua_Alloc allocf = lua_getallocf(L, &ud); + void *temp = allocf(ud, box->box, box->bsize, newsize); + if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */ + lua_pushliteral(L, "not enough memory"); + lua_error(L); /* raise a memory error */ + } + box->box = temp; + box->bsize = newsize; + return temp; + } +} + + +static int boxgc (lua_State *L) { + resizebox(L, 1, 0); + return 0; +} + + +static const luaL_Reg boxmt[] = { /* box metamethods */ + {"__gc", boxgc}, + {"__close", boxgc}, + {NULL, NULL} +}; + + +static void newbox (lua_State *L) { + UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0); + box->box = NULL; + box->bsize = 0; + if (luaL_newmetatable(L, "_UBOX*")) /* creating metatable? */ + luaL_setfuncs(L, boxmt, 0); /* set its metamethods */ + lua_setmetatable(L, -2); +} + + +/* +** check whether buffer is using a userdata on the stack as a temporary +** buffer +*/ +#define buffonstack(B) ((B)->b != (B)->init.b) + + +/* +** Whenever buffer is accessed, slot 'idx' must either be a box (which +** cannot be NULL) or it is a placeholder for the buffer. +*/ +#define checkbufferlevel(B,idx) \ + lua_assert(buffonstack(B) ? lua_touserdata(B->L, idx) != NULL \ + : lua_touserdata(B->L, idx) == (void*)B) + + +/* +** Compute new size for buffer 'B', enough to accommodate extra 'sz' +** bytes plus one for a terminating zero. +*/ +static size_t newbuffsize (luaL_Buffer *B, size_t sz) { + size_t newsize = B->size; + if (l_unlikely(sz >= MAX_SIZE - B->n)) + return cast_sizet(luaL_error(B->L, "resulting string too large")); + /* else B->n + sz + 1 <= MAX_SIZE */ + if (newsize <= MAX_SIZE/3 * 2) /* no overflow? */ + newsize += (newsize >> 1); /* new size *= 1.5 */ + if (newsize < B->n + sz + 1) /* not big enough? */ + newsize = B->n + sz + 1; + return newsize; +} + + +/* +** Returns a pointer to a free area with at least 'sz' bytes in buffer +** 'B'. 'boxidx' is the relative position in the stack where is the +** buffer's box or its placeholder. +*/ +static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) { + checkbufferlevel(B, boxidx); + if (B->size - B->n >= sz) /* enough space? */ + return B->b + B->n; + else { + lua_State *L = B->L; + char *newbuff; + size_t newsize = newbuffsize(B, sz); + /* create larger buffer */ + if (buffonstack(B)) /* buffer already has a box? */ + newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */ + else { /* no box yet */ + lua_remove(L, boxidx); /* remove placeholder */ + newbox(L); /* create a new box */ + lua_insert(L, boxidx); /* move box to its intended position */ + lua_toclose(L, boxidx); + newbuff = (char *)resizebox(L, boxidx, newsize); + memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ + } + B->b = newbuff; + B->size = newsize; + return newbuff + B->n; + } +} + +/* +** returns a pointer to a free area with at least 'sz' bytes +*/ +LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { + return prepbuffsize(B, sz, -1); +} + + +LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { + if (l > 0) { /* avoid 'memcpy' when 's' can be NULL */ + char *b = prepbuffsize(B, l, -1); + memcpy(b, s, l * sizeof(char)); + luaL_addsize(B, l); + } +} + + +LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { + luaL_addlstring(B, s, strlen(s)); +} + + +LUALIB_API void luaL_pushresult (luaL_Buffer *B) { + lua_State *L = B->L; + checkbufferlevel(B, -1); + if (!buffonstack(B)) /* using static buffer? */ + lua_pushlstring(L, B->b, B->n); /* save result as regular string */ + else { /* reuse buffer already allocated */ + UBox *box = (UBox *)lua_touserdata(L, -1); + void *ud; + lua_Alloc allocf = lua_getallocf(L, &ud); /* function to free buffer */ + size_t len = B->n; /* final string length */ + char *s; + resizebox(L, -1, len + 1); /* adjust box size to content size */ + s = (char*)box->box; /* final buffer address */ + s[len] = '\0'; /* add ending zero */ + /* clear box, as Lua will take control of the buffer */ + box->bsize = 0; box->box = NULL; + lua_pushexternalstring(L, s, len, allocf, ud); + lua_closeslot(L, -2); /* close the box */ + lua_gc(L, LUA_GCSTEP, len); + } + lua_remove(L, -2); /* remove box or placeholder from the stack */ +} + + +LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) { + luaL_addsize(B, sz); + luaL_pushresult(B); +} + + +/* +** 'luaL_addvalue' is the only function in the Buffer system where the +** box (if existent) is not on the top of the stack. So, instead of +** calling 'luaL_addlstring', it replicates the code using -2 as the +** last argument to 'prepbuffsize', signaling that the box is (or will +** be) below the string being added to the buffer. (Box creation can +** trigger an emergency GC, so we should not remove the string from the +** stack before we have the space guaranteed.) +*/ +LUALIB_API void luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t len; + const char *s = lua_tolstring(L, -1, &len); + char *b = prepbuffsize(B, len, -2); + memcpy(b, s, len * sizeof(char)); + luaL_addsize(B, len); + lua_pop(L, 1); /* pop string */ +} + + +LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; + B->b = B->init.b; + B->n = 0; + B->size = LUAL_BUFFERSIZE; + lua_pushlightuserdata(L, (void*)B); /* push placeholder */ +} + + +LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { + luaL_buffinit(L, B); + return prepbuffsize(B, sz, -1); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Reference system +** ======================================================= +*/ + +/* +** The previously freed references form a linked list: t[1] is the index +** of a first free index, t[t[1]] is the index of the second element, +** etc. A zero signals the end of the list. +*/ +LUALIB_API int luaL_ref (lua_State *L, int t) { + int ref; + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove from stack */ + return LUA_REFNIL; /* 'nil' has a unique fixed reference */ + } + t = lua_absindex(L, t); + if (lua_rawgeti(L, t, 1) == LUA_TNUMBER) /* already initialized? */ + ref = (int)lua_tointeger(L, -1); /* ref = t[1] */ + else { /* first access */ + lua_assert(!lua_toboolean(L, -1)); /* must be nil or false */ + ref = 0; /* list is empty */ + lua_pushinteger(L, 0); /* initialize as an empty list */ + lua_rawseti(L, t, 1); /* ref = t[1] = 0 */ + } + lua_pop(L, 1); /* remove element from stack */ + if (ref != 0) { /* any free element? */ + lua_rawgeti(L, t, ref); /* remove it from list */ + lua_rawseti(L, t, 1); /* (t[1] = t[ref]) */ + } + else /* no free elements */ + ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */ + lua_rawseti(L, t, ref); + return ref; +} + + +LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { + if (ref >= 0) { + t = lua_absindex(L, t); + lua_rawgeti(L, t, 1); + lua_assert(lua_isinteger(L, -1)); + lua_rawseti(L, t, ref); /* t[ref] = t[1] */ + lua_pushinteger(L, ref); + lua_rawseti(L, t, 1); /* t[1] = ref */ + } +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Load functions +** ======================================================= +*/ + +typedef struct LoadF { + unsigned n; /* number of pre-read characters */ + FILE *f; /* file being read */ + char buff[BUFSIZ]; /* area for reading file */ +} LoadF; + + +static const char *getF (lua_State *L, void *ud, size_t *size) { + LoadF *lf = (LoadF *)ud; + UNUSED(L); + if (lf->n > 0) { /* are there pre-read characters to be read? */ + *size = lf->n; /* return them (chars already in buffer) */ + lf->n = 0; /* no more pre-read characters */ + } + else { /* read a block from file */ + /* 'fread' can return > 0 *and* set the EOF flag. If next call to + 'getF' called 'fread', it might still wait for user input. + The next check avoids this problem. */ + if (feof(lf->f)) return NULL; + *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ + } + return lf->buff; +} + + +static int errfile (lua_State *L, const char *what, int fnameindex) { + int err = errno; + const char *filename = lua_tostring(L, fnameindex) + 1; + if (err != 0) + lua_pushfstring(L, "cannot %s %s: %s", what, filename, strerror(err)); + else + lua_pushfstring(L, "cannot %s %s", what, filename); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + + +/* +** Skip an optional BOM at the start of a stream. If there is an +** incomplete BOM (the first character is correct but the rest is +** not), returns the first character anyway to force an error +** (as no chunk can start with 0xEF). +*/ +static int skipBOM (FILE *f) { + int c = getc(f); /* read first character */ + if (c == 0xEF && getc(f) == 0xBB && getc(f) == 0xBF) /* correct BOM? */ + return getc(f); /* ignore BOM and return next char */ + else /* no (valid) BOM */ + return c; /* return first character */ +} + + +/* +** reads the first character of file 'f' and skips an optional BOM mark +** in its beginning plus its first line if it starts with '#'. Returns +** true if it skipped the first line. In any case, '*cp' has the +** first "valid" character of the file (after the optional BOM and +** a first-line comment). +*/ +static int skipcomment (FILE *f, int *cp) { + int c = *cp = skipBOM(f); + if (c == '#') { /* first line is a comment (Unix exec. file)? */ + do { /* skip first line */ + c = getc(f); + } while (c != EOF && c != '\n'); + *cp = getc(f); /* next character after comment, if present */ + return 1; /* there was a comment */ + } + else return 0; /* no comment */ +} + + +LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename, + const char *mode) { + LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = stdin; + } + else { + lua_pushfstring(L, "@%s", filename); + errno = 0; + lf.f = fopen(filename, "r"); + if (lf.f == NULL) return errfile(L, "open", fnameindex); + } + lf.n = 0; + if (skipcomment(lf.f, &c)) /* read initial portion */ + lf.buff[lf.n++] = '\n'; /* add newline to correct line numbers */ + if (c == LUA_SIGNATURE[0]) { /* binary file? */ + lf.n = 0; /* remove possible newline */ + if (filename) { /* "real" file? */ + errno = 0; + lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + if (lf.f == NULL) return errfile(L, "reopen", fnameindex); + skipcomment(lf.f, &c); /* re-read initial portion */ + } + } + if (c != EOF) + lf.buff[lf.n++] = cast_char(c); /* 'c' is the first character */ + status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode); + readstatus = ferror(lf.f); + errno = 0; /* no useful error number until here */ + if (filename) fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ + return errfile(L, "read", fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + + +typedef struct LoadS { + const char *s; + size_t size; +} LoadS; + + +static const char *getS (lua_State *L, void *ud, size_t *size) { + LoadS *ls = (LoadS *)ud; + UNUSED(L); + if (ls->size == 0) return NULL; + *size = ls->size; + ls->size = 0; + return ls->s; +} + + +LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size, + const char *name, const char *mode) { + LoadS ls; + ls.s = buff; + ls.size = size; + return lua_load(L, getS, &ls, name, mode); +} + + +LUALIB_API int luaL_loadstring (lua_State *L, const char *s) { + return luaL_loadbuffer(L, s, strlen(s), s); +} + +/* }====================================================== */ + + + +LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { + if (!lua_getmetatable(L, obj)) /* no metatable? */ + return LUA_TNIL; + else { + int tt; + lua_pushstring(L, event); + tt = lua_rawget(L, -2); + if (tt == LUA_TNIL) /* is metafield nil? */ + lua_pop(L, 2); /* remove metatable and metafield */ + else + lua_remove(L, -2); /* remove only metatable */ + return tt; /* return metafield type */ + } +} + + +LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { + obj = lua_absindex(L, obj); + if (luaL_getmetafield(L, obj, event) == LUA_TNIL) /* no metafield? */ + return 0; + lua_pushvalue(L, obj); + lua_call(L, 1, 1); + return 1; +} + + +LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) { + lua_Integer l; + int isnum; + lua_len(L, idx); + l = lua_tointegerx(L, -1, &isnum); + if (l_unlikely(!isnum)) + luaL_error(L, "object length is not an integer"); + lua_pop(L, 1); /* remove object */ + return l; +} + + +LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { + idx = lua_absindex(L,idx); + if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */ + if (!lua_isstring(L, -1)) + luaL_error(L, "'__tostring' must return a string"); + } + else { + switch (lua_type(L, idx)) { + case LUA_TNUMBER: { + char buff[LUA_N2SBUFFSZ]; + lua_numbertocstring(L, idx, buff); + lua_pushstring(L, buff); + break; + } + case LUA_TSTRING: + lua_pushvalue(L, idx); + break; + case LUA_TBOOLEAN: + lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false")); + break; + case LUA_TNIL: + lua_pushliteral(L, "nil"); + break; + default: { + int tt = luaL_getmetafield(L, idx, "__name"); /* try name */ + const char *kind = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : + luaL_typename(L, idx); + lua_pushfstring(L, "%s: %p", kind, lua_topointer(L, idx)); + if (tt != LUA_TNIL) + lua_remove(L, -2); /* remove '__name' */ + break; + } + } + } + return lua_tolstring(L, -1, len); +} + + +/* +** set functions from list 'l' into table at top - 'nup'; each +** function gets the 'nup' elements at the top as upvalues. +** Returns with only the table at the stack. +*/ +LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkstack(L, nup, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + if (l->func == NULL) /* placeholder? */ + lua_pushboolean(L, 0); + else { + int i; + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + } + lua_setfield(L, -(nup + 2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + + +/* +** ensure that stack[idx][fname] has a table and push that table +** into the stack +*/ +LUALIB_API int luaL_getsubtable (lua_State *L, int idx, const char *fname) { + if (lua_getfield(L, idx, fname) == LUA_TTABLE) + return 1; /* table already there */ + else { + lua_pop(L, 1); /* remove previous result */ + idx = lua_absindex(L, idx); + lua_newtable(L); + lua_pushvalue(L, -1); /* copy to be left at top */ + lua_setfield(L, idx, fname); /* assign new table to field */ + return 0; /* false, because did not find table there */ + } +} + + +/* +** Stripped-down 'require': After checking "loaded" table, calls 'openf' +** to open a module, registers the result in 'package.loaded' table and, +** if 'glb' is true, also registers the result in the global table. +** Leaves resulting module on the top. +*/ +LUALIB_API void luaL_requiref (lua_State *L, const char *modname, + lua_CFunction openf, int glb) { + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_getfield(L, -1, modname); /* LOADED[modname] */ + if (!lua_toboolean(L, -1)) { /* package not already loaded? */ + lua_pop(L, 1); /* remove field */ + lua_pushcfunction(L, openf); + lua_pushstring(L, modname); /* argument to open function */ + lua_call(L, 1, 1); /* call 'openf' to open module */ + lua_pushvalue(L, -1); /* make copy of module (call result) */ + lua_setfield(L, -3, modname); /* LOADED[modname] = module */ + } + lua_remove(L, -2); /* remove LOADED table */ + if (glb) { + lua_pushvalue(L, -1); /* copy of module */ + lua_setglobal(L, modname); /* _G[modname] = module */ + } +} + + +LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s, + const char *p, const char *r) { + const char *wild; + size_t l = strlen(p); + while ((wild = strstr(s, p)) != NULL) { + luaL_addlstring(b, s, ct_diff2sz(wild - s)); /* push prefix */ + luaL_addstring(b, r); /* push replacement in place of pattern */ + s = wild + l; /* continue after 'p' */ + } + luaL_addstring(b, s); /* push last suffix */ +} + + +LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, + const char *p, const char *r) { + luaL_Buffer b; + luaL_buffinit(L, &b); + luaL_addgsub(&b, s, p, r); + luaL_pushresult(&b); + return lua_tostring(L, -1); +} + + +void *luaL_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { + UNUSED(ud); UNUSED(osize); + if (nsize == 0) { + free(ptr); + return NULL; + } + else + return realloc(ptr, nsize); +} + + +/* +** Standard panic function just prints an error message. The test +** with 'lua_type' avoids possible memory errors in 'lua_tostring'. +*/ +static int panic (lua_State *L) { + const char *msg = (lua_type(L, -1) == LUA_TSTRING) + ? lua_tostring(L, -1) + : "error object is not a string"; + lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", + msg); + return 0; /* return to Lua to abort */ +} + + +/* +** Warning functions: +** warnfoff: warning system is off +** warnfon: ready to start a new message +** warnfcont: previous message is to be continued +*/ +static void warnfoff (void *ud, const char *message, int tocont); +static void warnfon (void *ud, const char *message, int tocont); +static void warnfcont (void *ud, const char *message, int tocont); + + +/* +** Check whether message is a control message. If so, execute the +** control or ignore it if unknown. +*/ +static int checkcontrol (lua_State *L, const char *message, int tocont) { + if (tocont || *(message++) != '@') /* not a control message? */ + return 0; + else { + if (strcmp(message, "off") == 0) + lua_setwarnf(L, warnfoff, L); /* turn warnings off */ + else if (strcmp(message, "on") == 0) + lua_setwarnf(L, warnfon, L); /* turn warnings on */ + return 1; /* it was a control message */ + } +} + + +static void warnfoff (void *ud, const char *message, int tocont) { + checkcontrol((lua_State *)ud, message, tocont); +} + + +/* +** Writes the message and handle 'tocont', finishing the message +** if needed and setting the next warn function. +*/ +static void warnfcont (void *ud, const char *message, int tocont) { + lua_State *L = (lua_State *)ud; + lua_writestringerror("%s", message); /* write message */ + if (tocont) /* not the last part? */ + lua_setwarnf(L, warnfcont, L); /* to be continued */ + else { /* last part */ + lua_writestringerror("%s", "\n"); /* finish message with end-of-line */ + lua_setwarnf(L, warnfon, L); /* next call is a new message */ + } +} + + +static void warnfon (void *ud, const char *message, int tocont) { + if (checkcontrol((lua_State *)ud, message, tocont)) /* control message? */ + return; /* nothing else to be done */ + lua_writestringerror("%s", "Lua warning: "); /* start a new warning */ + warnfcont(ud, message, tocont); /* finish processing */ +} + + + +/* +** A function to compute an unsigned int with some level of +** randomness. Rely on Address Space Layout Randomization (if present) +** and the current time. +*/ +#if !defined(luai_makeseed) + +#include <time.h> + + +/* Size for the buffer, in bytes */ +#define BUFSEEDB (sizeof(void*) + sizeof(time_t)) + +/* Size for the buffer in int's, rounded up */ +#define BUFSEED ((BUFSEEDB + sizeof(int) - 1) / sizeof(int)) + +/* +** Copy the contents of variable 'v' into the buffer pointed by 'b'. +** (The '&b[0]' disguises 'b' to fix an absurd warning from clang.) +*/ +#define addbuff(b,v) (memcpy(&b[0], &(v), sizeof(v)), b += sizeof(v)) + + +static unsigned int luai_makeseed (void) { + unsigned int buff[BUFSEED]; + unsigned int res; + unsigned int i; + time_t t = time(NULL); + char *b = (char*)buff; + addbuff(b, b); /* local variable's address */ + addbuff(b, t); /* time */ + /* fill (rare but possible) remain of the buffer with zeros */ + memset(b, 0, sizeof(buff) - BUFSEEDB); + res = buff[0]; + for (i = 1; i < BUFSEED; i++) + res ^= (res >> 3) + (res << 7) + buff[i]; + return res; +} + +#endif + + +LUALIB_API unsigned int luaL_makeseed (lua_State *L) { + UNUSED(L); + return luai_makeseed(); +} + + +/* +** Use the name with parentheses so that headers can redefine it +** as a macro. +*/ +LUALIB_API lua_State *(luaL_newstate) (void) { + lua_State *L = lua_newstate(luaL_alloc, NULL, luaL_makeseed(NULL)); + if (l_likely(L)) { + lua_atpanic(L, &panic); + lua_setwarnf(L, warnfon, L); + } + return L; +} + + +LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver, size_t sz) { + lua_Number v = lua_version(L); + if (sz != LUAL_NUMSIZES) /* check numeric types */ + luaL_error(L, "core and library have incompatible numeric types"); + else if (v != ver) + luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f", + (LUAI_UACNUMBER)ver, (LUAI_UACNUMBER)v); +} + + +``` + +`Lua/lauxlib.h`: + +```h +/* +** $Id: lauxlib.h $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include <stddef.h> +#include <stdio.h> + +#include "luaconf.h" +#include "lua.h" + + +/* global table */ +#define LUA_GNAME "_G" + + +typedef struct luaL_Buffer luaL_Buffer; + + +/* extra error code for 'luaL_loadfilex' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + + +/* key, in the registry, for table of loaded modules */ +#define LUA_LOADED_TABLE "_LOADED" + + +/* key, in the registry, for table of preloaded loaders */ +#define LUA_PRELOAD_TABLE "_PRELOAD" + + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + + +#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number)) + +LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz); +#define luaL_checkversion(L) \ + luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES) + +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len); +LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg); +LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int arg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def, + const char *const lst[]); + +LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname); +LUALIB_API int (luaL_execresult) (lua_State *L, int stat); + +LUALIB_API void *luaL_alloc (void *ud, void *ptr, size_t osize, + size_t nsize); + + +/* predefined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename, + const char *mode); + +#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL) + +LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, + const char *name, const char *mode); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + +LUALIB_API unsigned luaL_makeseed (lua_State *L); + +LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx); + +LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s, + const char *p, const char *r); +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, + const char *p, const char *r); + +LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); + +LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname); + +LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1, + const char *msg, int level); + +LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, + lua_CFunction openf, int glb); + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + + +#define luaL_newlibtable(L,l) \ + lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) + +#define luaL_newlib(L,l) \ + (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) + +#define luaL_argcheck(L, cond,arg,extramsg) \ + ((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg)))) + +#define luaL_argexpected(L,cond,arg,tname) \ + ((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname)))) + +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) + + +/* +** Perform arithmetic operations on lua_Integer values with wrap-around +** semantics, as the Lua core does. +*/ +#define luaL_intop(op,v1,v2) \ + ((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2))) + + +/* push the value used to represent failure/error */ +#if defined(LUA_FAILISFALSE) +#define luaL_pushfail(L) lua_pushboolean(L, 0) +#else +#define luaL_pushfail(L) lua_pushnil(L) +#endif + + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + +struct luaL_Buffer { + char *b; /* buffer address */ + size_t size; /* buffer size */ + size_t n; /* number of characters in buffer */ + lua_State *L; + union { + LUAI_MAXALIGN; /* ensure maximum alignment for buffer */ + char b[LUAL_BUFFERSIZE]; /* initial buffer */ + } init; +}; + + +#define luaL_bufflen(bf) ((bf)->n) +#define luaL_buffaddr(bf) ((bf)->b) + + +#define luaL_addchar(B,c) \ + ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \ + ((B)->b[(B)->n++] = (c))) + +#define luaL_addsize(B,s) ((B)->n += (s)) + +#define luaL_buffsub(B,s) ((B)->n -= (s)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz); +LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz); + +#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE) + +/* }====================================================== */ + + + +/* +** {====================================================== +** File handles for IO library +** ======================================================= +*/ + +/* +** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and +** initial structure 'luaL_Stream' (it may contain other fields +** after that initial structure). +*/ + +#define LUA_FILEHANDLE "FILE*" + + +typedef struct luaL_Stream { + FILE *f; /* stream (NULL for incompletely created streams) */ + lua_CFunction closef; /* to close stream (NULL for closed streams) */ +} luaL_Stream; + +/* }====================================================== */ + + +/* +** {============================================================ +** Compatibility with deprecated conversions +** ============================================================= +*/ +#if defined(LUA_COMPAT_APIINTCASTS) + +#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a)) +#define luaL_optunsigned(L,a,d) \ + ((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d))) + +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) + +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#endif +/* }============================================================ */ + + + +#endif + + + +``` + +`Lua/lbaselib.c`: + +```c +/* +** $Id: lbaselib.c $ +** Basic library +** See Copyright Notice in lua.h +*/ + +#define lbaselib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "llimits.h" + + +static int luaB_print (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + for (i = 1; i <= n; i++) { /* for each argument */ + size_t l; + const char *s = luaL_tolstring(L, i, &l); /* convert it to string */ + if (i > 1) /* not the first element? */ + lua_writestring("\t", 1); /* add a tab before it */ + lua_writestring(s, l); /* print it */ + lua_pop(L, 1); /* pop result */ + } + lua_writeline(); + return 0; +} + + +/* +** Creates a warning with all given arguments. +** Check first for errors; otherwise an error may interrupt +** the composition of a warning, leaving it unfinished. +*/ +static int luaB_warn (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + luaL_checkstring(L, 1); /* at least one argument */ + for (i = 2; i <= n; i++) + luaL_checkstring(L, i); /* make sure all arguments are strings */ + for (i = 1; i < n; i++) /* compose warning */ + lua_warning(L, lua_tostring(L, i), 1); + lua_warning(L, lua_tostring(L, n), 0); /* close warning */ + return 0; +} + + +#define SPACECHARS " \f\n\r\t\v" + +static const char *b_str2int (const char *s, unsigned base, lua_Integer *pn) { + lua_Unsigned n = 0; + int neg = 0; + s += strspn(s, SPACECHARS); /* skip initial spaces */ + if (*s == '-') { s++; neg = 1; } /* handle sign */ + else if (*s == '+') s++; + if (!isalnum(cast_uchar(*s))) /* no digit? */ + return NULL; + do { + unsigned digit = cast_uint(isdigit(cast_uchar(*s)) + ? *s - '0' + : (toupper(cast_uchar(*s)) - 'A') + 10); + if (digit >= base) return NULL; /* invalid numeral */ + n = n * base + digit; + s++; + } while (isalnum(cast_uchar(*s))); + s += strspn(s, SPACECHARS); /* skip trailing spaces */ + *pn = (lua_Integer)((neg) ? (0u - n) : n); + return s; +} + + +static int luaB_tonumber (lua_State *L) { + if (lua_isnoneornil(L, 2)) { /* standard conversion? */ + if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */ + lua_settop(L, 1); /* yes; return it */ + return 1; + } + else { + size_t l; + const char *s = lua_tolstring(L, 1, &l); + if (s != NULL && lua_stringtonumber(L, s) == l + 1) + return 1; /* successful conversion to number */ + /* else not a number */ + luaL_checkany(L, 1); /* (but there must be some parameter) */ + } + } + else { + size_t l; + const char *s; + lua_Integer n = 0; /* to avoid warnings */ + lua_Integer base = luaL_checkinteger(L, 2); + luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */ + s = lua_tolstring(L, 1, &l); + luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); + if (b_str2int(s, cast_uint(base), &n) == s + l) { + lua_pushinteger(L, n); + return 1; + } /* else not a number */ + } /* else not a number */ + luaL_pushfail(L); /* not a number */ + return 1; +} + + +static int luaB_error (lua_State *L) { + int level = (int)luaL_optinteger(L, 2, 1); + lua_settop(L, 1); + if (lua_type(L, 1) == LUA_TSTRING && level > 0) { + luaL_where(L, level); /* add extra information */ + lua_pushvalue(L, 1); + lua_concat(L, 2); + } + return lua_error(L); +} + + +static int luaB_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); + return 1; /* no metatable */ + } + luaL_getmetafield(L, 1, "__metatable"); + return 1; /* returns either __metatable field (if present) or metatable */ +} + + +static int luaB_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table"); + if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL)) + return luaL_error(L, "cannot change a protected metatable"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; +} + + +static int luaB_rawequal (lua_State *L) { + luaL_checkany(L, 1); + luaL_checkany(L, 2); + lua_pushboolean(L, lua_rawequal(L, 1, 2)); + return 1; +} + + +static int luaB_rawlen (lua_State *L) { + int t = lua_type(L, 1); + luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1, + "table or string"); + lua_pushinteger(L, l_castU2S(lua_rawlen(L, 1))); + return 1; +} + + +static int luaB_rawget (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_rawget(L, 1); + return 1; +} + +static int luaB_rawset (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + luaL_checkany(L, 3); + lua_settop(L, 3); + lua_rawset(L, 1); + return 1; +} + + +static int pushmode (lua_State *L, int oldmode) { + if (oldmode == -1) + luaL_pushfail(L); /* invalid call to 'lua_gc' */ + else + lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental" + : "generational"); + return 1; +} + + +/* +** check whether call to 'lua_gc' was valid (not inside a finalizer) +*/ +#define checkvalres(res) { if (res == -1) break; } + +static int luaB_collectgarbage (lua_State *L) { + static const char *const opts[] = {"stop", "restart", "collect", + "count", "step", "isrunning", "generational", "incremental", + "param", NULL}; + static const char optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, + LUA_GCCOUNT, LUA_GCSTEP, LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC, + LUA_GCPARAM}; + int o = optsnum[luaL_checkoption(L, 1, "collect", opts)]; + switch (o) { + case LUA_GCCOUNT: { + int k = lua_gc(L, o); + int b = lua_gc(L, LUA_GCCOUNTB); + checkvalres(k); + lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024)); + return 1; + } + case LUA_GCSTEP: { + lua_Integer n = luaL_optinteger(L, 2, 0); + int res = lua_gc(L, o, cast_sizet(n)); + checkvalres(res); + lua_pushboolean(L, res); + return 1; + } + case LUA_GCISRUNNING: { + int res = lua_gc(L, o); + checkvalres(res); + lua_pushboolean(L, res); + return 1; + } + case LUA_GCGEN: { + return pushmode(L, lua_gc(L, o)); + } + case LUA_GCINC: { + return pushmode(L, lua_gc(L, o)); + } + case LUA_GCPARAM: { + static const char *const params[] = { + "minormul", "majorminor", "minormajor", + "pause", "stepmul", "stepsize", NULL}; + static const char pnum[] = { + LUA_GCPMINORMUL, LUA_GCPMAJORMINOR, LUA_GCPMINORMAJOR, + LUA_GCPPAUSE, LUA_GCPSTEPMUL, LUA_GCPSTEPSIZE}; + int p = pnum[luaL_checkoption(L, 2, NULL, params)]; + lua_Integer value = luaL_optinteger(L, 3, -1); + lua_pushinteger(L, lua_gc(L, o, p, (int)value)); + return 1; + } + default: { + int res = lua_gc(L, o); + checkvalres(res); + lua_pushinteger(L, res); + return 1; + } + } + luaL_pushfail(L); /* invalid call (inside a finalizer) */ + return 1; +} + + +static int luaB_type (lua_State *L) { + int t = lua_type(L, 1); + luaL_argcheck(L, t != LUA_TNONE, 1, "value expected"); + lua_pushstring(L, lua_typename(L, t)); + return 1; +} + + +static int luaB_next (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, 1)) + return 2; + else { + lua_pushnil(L); + return 1; + } +} + + +static int pairscont (lua_State *L, int status, lua_KContext k) { + (void)L; (void)status; (void)k; /* unused */ + return 4; /* __pairs did all the work, just return its results */ +} + +static int luaB_pairs (lua_State *L) { + luaL_checkany(L, 1); + if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */ + lua_pushcfunction(L, luaB_next); /* will return generator and */ + lua_pushvalue(L, 1); /* state */ + lua_pushnil(L); /* initial value */ + lua_pushnil(L); /* to-be-closed object */ + } + else { + lua_pushvalue(L, 1); /* argument 'self' to metamethod */ + lua_callk(L, 1, 4, 0, pairscont); /* get 4 values from metamethod */ + } + return 4; +} + + +/* +** Traversal function for 'ipairs' +*/ +static int ipairsaux (lua_State *L) { + lua_Integer i = luaL_checkinteger(L, 2); + i = luaL_intop(+, i, 1); + lua_pushinteger(L, i); + return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2; +} + + +/* +** 'ipairs' function. Returns 'ipairsaux', given "table", 0. +** (The given "table" may not be a table.) +*/ +static int luaB_ipairs (lua_State *L) { + luaL_checkany(L, 1); + lua_pushcfunction(L, ipairsaux); /* iteration function */ + lua_pushvalue(L, 1); /* state */ + lua_pushinteger(L, 0); /* initial value */ + return 3; +} + + +static int load_aux (lua_State *L, int status, int envidx) { + if (l_likely(status == LUA_OK)) { + if (envidx != 0) { /* 'env' parameter? */ + lua_pushvalue(L, envidx); /* environment for loaded function */ + if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */ + lua_pop(L, 1); /* remove 'env' if not used by previous call */ + } + return 1; + } + else { /* error (message is on top of the stack) */ + luaL_pushfail(L); + lua_insert(L, -2); /* put before error message */ + return 2; /* return fail plus error message */ + } +} + + +static const char *getMode (lua_State *L, int idx) { + const char *mode = luaL_optstring(L, idx, "bt"); + if (strchr(mode, 'B') != NULL) /* Lua code cannot use fixed buffers */ + luaL_argerror(L, idx, "invalid mode"); + return mode; +} + + +static int luaB_loadfile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + const char *mode = getMode(L, 2); + int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */ + int status = luaL_loadfilex(L, fname, mode); + return load_aux(L, status, env); +} + + +/* +** {====================================================== +** Generic Read function +** ======================================================= +*/ + + +/* +** reserved slot, above all arguments, to hold a copy of the returned +** string to avoid it being collected while parsed. 'load' has four +** optional arguments (chunk, source name, mode, and environment). +*/ +#define RESERVEDSLOT 5 + + +/* +** Reader for generic 'load' function: 'lua_load' uses the +** stack for internal stuff, so the reader cannot change the +** stack top. Instead, it keeps its resulting string in a +** reserved slot inside the stack. +*/ +static const char *generic_reader (lua_State *L, void *ud, size_t *size) { + (void)(ud); /* not used */ + luaL_checkstack(L, 2, "too many nested functions"); + lua_pushvalue(L, 1); /* get function */ + lua_call(L, 0, 1); /* call it */ + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* pop result */ + *size = 0; + return NULL; + } + else if (l_unlikely(!lua_isstring(L, -1))) + luaL_error(L, "reader function must return a string"); + lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */ + return lua_tolstring(L, RESERVEDSLOT, size); +} + + +static int luaB_load (lua_State *L) { + int status; + size_t l; + const char *s = lua_tolstring(L, 1, &l); + const char *mode = getMode(L, 3); + int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */ + if (s != NULL) { /* loading a string? */ + const char *chunkname = luaL_optstring(L, 2, s); + status = luaL_loadbufferx(L, s, l, chunkname, mode); + } + else { /* loading from a reader function */ + const char *chunkname = luaL_optstring(L, 2, "=(load)"); + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, RESERVEDSLOT); /* create reserved slot */ + status = lua_load(L, generic_reader, NULL, chunkname, mode); + } + return load_aux(L, status, env); +} + +/* }====================================================== */ + + +static int dofilecont (lua_State *L, int d1, lua_KContext d2) { + (void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */ + return lua_gettop(L) - 1; +} + + +static int luaB_dofile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + lua_settop(L, 1); + if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK)) + return lua_error(L); + lua_callk(L, 0, LUA_MULTRET, 0, dofilecont); + return dofilecont(L, 0, 0); +} + + +static int luaB_assert (lua_State *L) { + if (l_likely(lua_toboolean(L, 1))) /* condition is true? */ + return lua_gettop(L); /* return all arguments */ + else { /* error */ + luaL_checkany(L, 1); /* there must be a condition */ + lua_remove(L, 1); /* remove it */ + lua_pushliteral(L, "assertion failed!"); /* default message */ + lua_settop(L, 1); /* leave only message (default if no other one) */ + return luaB_error(L); /* call 'error' */ + } +} + + +static int luaB_select (lua_State *L) { + int n = lua_gettop(L); + if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') { + lua_pushinteger(L, n-1); + return 1; + } + else { + lua_Integer i = luaL_checkinteger(L, 1); + if (i < 0) i = n + i; + else if (i > n) i = n; + luaL_argcheck(L, 1 <= i, 1, "index out of range"); + return n - (int)i; + } +} + + +/* +** Continuation function for 'pcall' and 'xpcall'. Both functions +** already pushed a 'true' before doing the call, so in case of success +** 'finishpcall' only has to return everything in the stack minus +** 'extra' values (where 'extra' is exactly the number of items to be +** ignored). +*/ +static int finishpcall (lua_State *L, int status, lua_KContext extra) { + if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */ + lua_pushboolean(L, 0); /* first result (false) */ + lua_pushvalue(L, -2); /* error message */ + return 2; /* return false, msg */ + } + else + return lua_gettop(L) - (int)extra; /* return all results */ +} + + +static int luaB_pcall (lua_State *L) { + int status; + luaL_checkany(L, 1); + lua_pushboolean(L, 1); /* first result if no errors */ + lua_insert(L, 1); /* put it in place */ + status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall); + return finishpcall(L, status, 0); +} + + +/* +** Do a protected call with error handling. After 'lua_rotate', the +** stack will have <f, err, true, f, [args...]>; so, the function passes +** 2 to 'finishpcall' to skip the 2 first values when returning results. +*/ +static int luaB_xpcall (lua_State *L) { + int status; + int n = lua_gettop(L); + luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */ + lua_pushboolean(L, 1); /* first result */ + lua_pushvalue(L, 1); /* function */ + lua_rotate(L, 3, 2); /* move them below function's arguments */ + status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall); + return finishpcall(L, status, 2); +} + + +static int luaB_tostring (lua_State *L) { + luaL_checkany(L, 1); + luaL_tolstring(L, 1, NULL); + return 1; +} + + +static const luaL_Reg base_funcs[] = { + {"assert", luaB_assert}, + {"collectgarbage", luaB_collectgarbage}, + {"dofile", luaB_dofile}, + {"error", luaB_error}, + {"getmetatable", luaB_getmetatable}, + {"ipairs", luaB_ipairs}, + {"loadfile", luaB_loadfile}, + {"load", luaB_load}, + {"next", luaB_next}, + {"pairs", luaB_pairs}, + {"pcall", luaB_pcall}, + {"print", luaB_print}, + {"warn", luaB_warn}, + {"rawequal", luaB_rawequal}, + {"rawlen", luaB_rawlen}, + {"rawget", luaB_rawget}, + {"rawset", luaB_rawset}, + {"select", luaB_select}, + {"setmetatable", luaB_setmetatable}, + {"tonumber", luaB_tonumber}, + {"tostring", luaB_tostring}, + {"type", luaB_type}, + {"xpcall", luaB_xpcall}, + /* placeholders */ + {LUA_GNAME, NULL}, + {"_VERSION", NULL}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_base (lua_State *L) { + /* open lib into global table */ + lua_pushglobaltable(L); + luaL_setfuncs(L, base_funcs, 0); + /* set global _G */ + lua_pushvalue(L, -1); + lua_setfield(L, -2, LUA_GNAME); + /* set global _VERSION */ + lua_pushliteral(L, LUA_VERSION); + lua_setfield(L, -2, "_VERSION"); + return 1; +} + + +``` + +`Lua/lcode.c`: + +```c +/* +** $Id: lcode.c $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#define lcode_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <float.h> +#include <limits.h> +#include <math.h> +#include <stdlib.h> + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstring.h" +#include "ltable.h" +#include "lvm.h" + + +/* (note that expressions VJMP also have jumps.) */ +#define hasjumps(e) ((e)->t != (e)->f) + + +static int codesJ (FuncState *fs, OpCode o, int sj, int k); + + + +/* semantic error */ +l_noret luaK_semerror (LexState *ls, const char *fmt, ...) { + const char *msg; + va_list argp; + pushvfstring(ls->L, argp, fmt, msg); + ls->t.token = 0; /* remove "near <token>" from final message */ + ls->linenumber = ls->lastline; /* back to line of last used token */ + luaX_syntaxerror(ls, msg); +} + + +/* +** If expression is a numeric constant, fills 'v' with its value +** and returns 1. Otherwise, returns 0. +*/ +static int tonumeral (const expdesc *e, TValue *v) { + if (hasjumps(e)) + return 0; /* not a numeral */ + switch (e->k) { + case VKINT: + if (v) setivalue(v, e->u.ival); + return 1; + case VKFLT: + if (v) setfltvalue(v, e->u.nval); + return 1; + default: return 0; + } +} + + +/* +** Get the constant value from a constant expression +*/ +static TValue *const2val (FuncState *fs, const expdesc *e) { + lua_assert(e->k == VCONST); + return &fs->ls->dyd->actvar.arr[e->u.info].k; +} + + +/* +** If expression is a constant, fills 'v' with its value +** and returns 1. Otherwise, returns 0. +*/ +int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) { + if (hasjumps(e)) + return 0; /* not a constant */ + switch (e->k) { + case VFALSE: + setbfvalue(v); + return 1; + case VTRUE: + setbtvalue(v); + return 1; + case VNIL: + setnilvalue(v); + return 1; + case VKSTR: { + setsvalue(fs->ls->L, v, e->u.strval); + return 1; + } + case VCONST: { + setobj(fs->ls->L, v, const2val(fs, e)); + return 1; + } + default: return tonumeral(e, v); + } +} + + +/* +** Return the previous instruction of the current code. If there +** may be a jump target between the current instruction and the +** previous one, return an invalid instruction (to avoid wrong +** optimizations). +*/ +static Instruction *previousinstruction (FuncState *fs) { + static const Instruction invalidinstruction = ~(Instruction)0; + if (fs->pc > fs->lasttarget) + return &fs->f->code[fs->pc - 1]; /* previous instruction */ + else + return cast(Instruction*, &invalidinstruction); +} + + +/* +** Create a OP_LOADNIL instruction, but try to optimize: if the previous +** instruction is also OP_LOADNIL and ranges are compatible, adjust +** range of previous instruction instead of emitting a new one. (For +** instance, 'local a; local b' will generate a single opcode.) +*/ +void luaK_nil (FuncState *fs, int from, int n) { + int l = from + n - 1; /* last register to set nil */ + Instruction *previous = previousinstruction(fs); + if (GET_OPCODE(*previous) == OP_LOADNIL) { /* previous is LOADNIL? */ + int pfrom = GETARG_A(*previous); /* get previous range */ + int pl = pfrom + GETARG_B(*previous); + if ((pfrom <= from && from <= pl + 1) || + (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */ + if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */ + if (pl > l) l = pl; /* l = max(l, pl) */ + SETARG_A(*previous, from); + SETARG_B(*previous, l - from); + return; + } /* else go through */ + } + luaK_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */ +} + + +/* +** Gets the destination address of a jump instruction. Used to traverse +** a list of jumps. +*/ +static int getjump (FuncState *fs, int pc) { + int offset = GETARG_sJ(fs->f->code[pc]); + if (offset == NO_JUMP) /* point to itself represents end of list */ + return NO_JUMP; /* end of list */ + else + return (pc+1)+offset; /* turn offset into absolute position */ +} + + +/* +** Fix jump instruction at position 'pc' to jump to 'dest'. +** (Jump addresses are relative in Lua) +*/ +static void fixjump (FuncState *fs, int pc, int dest) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest - (pc + 1); + lua_assert(dest != NO_JUMP); + if (!(-OFFSET_sJ <= offset && offset <= MAXARG_sJ - OFFSET_sJ)) + luaX_syntaxerror(fs->ls, "control structure too long"); + lua_assert(GET_OPCODE(*jmp) == OP_JMP); + SETARG_sJ(*jmp, offset); +} + + +/* +** Concatenate jump-list 'l2' into jump-list 'l1' +*/ +void luaK_concat (FuncState *fs, int *l1, int l2) { + if (l2 == NO_JUMP) return; /* nothing to concatenate? */ + else if (*l1 == NO_JUMP) /* no original list? */ + *l1 = l2; /* 'l1' points to 'l2' */ + else { + int list = *l1; + int next; + while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ + list = next; + fixjump(fs, list, l2); /* last element links to 'l2' */ + } +} + + +/* +** Create a jump instruction and return its position, so its destination +** can be fixed later (with 'fixjump'). +*/ +int luaK_jump (FuncState *fs) { + return codesJ(fs, OP_JMP, NO_JUMP, 0); +} + + +/* +** Code a 'return' instruction +*/ +void luaK_ret (FuncState *fs, int first, int nret) { + OpCode op; + switch (nret) { + case 0: op = OP_RETURN0; break; + case 1: op = OP_RETURN1; break; + default: op = OP_RETURN; break; + } + luaY_checklimit(fs, nret + 1, MAXARG_B, "returns"); + luaK_codeABC(fs, op, first, nret + 1, 0); +} + + +/* +** Code a "conditional jump", that is, a test or comparison opcode +** followed by a jump. Return jump position. +*/ +static int condjump (FuncState *fs, OpCode op, int A, int B, int C, int k) { + luaK_codeABCk(fs, op, A, B, C, k); + return luaK_jump(fs); +} + + +/* +** returns current 'pc' and marks it as a jump target (to avoid wrong +** optimizations with consecutive instructions not in the same basic block). +*/ +int luaK_getlabel (FuncState *fs) { + fs->lasttarget = fs->pc; + return fs->pc; +} + + +/* +** Returns the position of the instruction "controlling" a given +** jump (that is, its condition), or the jump itself if it is +** unconditional. +*/ +static Instruction *getjumpcontrol (FuncState *fs, int pc) { + Instruction *pi = &fs->f->code[pc]; + if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) + return pi-1; + else + return pi; +} + + +/* +** Patch destination register for a TESTSET instruction. +** If instruction in position 'node' is not a TESTSET, return 0 ("fails"). +** Otherwise, if 'reg' is not 'NO_REG', set it as the destination +** register. Otherwise, change instruction to a simple 'TEST' (produces +** no register value) +*/ +static int patchtestreg (FuncState *fs, int node, int reg) { + Instruction *i = getjumpcontrol(fs, node); + if (GET_OPCODE(*i) != OP_TESTSET) + return 0; /* cannot patch other instructions */ + if (reg != NO_REG && reg != GETARG_B(*i)) + SETARG_A(*i, reg); + else { + /* no register to put value or register already has the value; + change instruction to simple test */ + *i = CREATE_ABCk(OP_TEST, GETARG_B(*i), 0, 0, GETARG_k(*i)); + } + return 1; +} + + +/* +** Traverse a list of tests ensuring no one produces a value +*/ +static void removevalues (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) + patchtestreg(fs, list, NO_REG); +} + + +/* +** Traverse a list of tests, patching their destination address and +** registers: tests producing values jump to 'vtarget' (and put their +** values in 'reg'), other tests jump to 'dtarget'. +*/ +static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, + int dtarget) { + while (list != NO_JUMP) { + int next = getjump(fs, list); + if (patchtestreg(fs, list, reg)) + fixjump(fs, list, vtarget); + else + fixjump(fs, list, dtarget); /* jump to default target */ + list = next; + } +} + + +/* +** Path all jumps in 'list' to jump to 'target'. +** (The assert means that we cannot fix a jump to a forward address +** because we only know addresses once code is generated.) +*/ +void luaK_patchlist (FuncState *fs, int list, int target) { + lua_assert(target <= fs->pc); + patchlistaux(fs, list, target, NO_REG, target); +} + + +void luaK_patchtohere (FuncState *fs, int list) { + int hr = luaK_getlabel(fs); /* mark "here" as a jump target */ + luaK_patchlist(fs, list, hr); +} + + +/* limit for difference between lines in relative line info. */ +#define LIMLINEDIFF 0x80 + + +/* +** Save line info for a new instruction. If difference from last line +** does not fit in a byte, of after that many instructions, save a new +** absolute line info; (in that case, the special value 'ABSLINEINFO' +** in 'lineinfo' signals the existence of this absolute information.) +** Otherwise, store the difference from last line in 'lineinfo'. +*/ +static void savelineinfo (FuncState *fs, Proto *f, int line) { + int linedif = line - fs->previousline; + int pc = fs->pc - 1; /* last instruction coded */ + if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ >= MAXIWTHABS) { + luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo, + f->sizeabslineinfo, AbsLineInfo, INT_MAX, "lines"); + f->abslineinfo[fs->nabslineinfo].pc = pc; + f->abslineinfo[fs->nabslineinfo++].line = line; + linedif = ABSLINEINFO; /* signal that there is absolute information */ + fs->iwthabs = 1; /* restart counter */ + } + luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte, + INT_MAX, "opcodes"); + f->lineinfo[pc] = cast(ls_byte, linedif); + fs->previousline = line; /* last line saved */ +} + + +/* +** Remove line information from the last instruction. +** If line information for that instruction is absolute, set 'iwthabs' +** above its max to force the new (replacing) instruction to have +** absolute line info, too. +*/ +static void removelastlineinfo (FuncState *fs) { + Proto *f = fs->f; + int pc = fs->pc - 1; /* last instruction coded */ + if (f->lineinfo[pc] != ABSLINEINFO) { /* relative line info? */ + fs->previousline -= f->lineinfo[pc]; /* correct last line saved */ + fs->iwthabs--; /* undo previous increment */ + } + else { /* absolute line information */ + lua_assert(f->abslineinfo[fs->nabslineinfo - 1].pc == pc); + fs->nabslineinfo--; /* remove it */ + fs->iwthabs = MAXIWTHABS + 1; /* force next line info to be absolute */ + } +} + + +/* +** Remove the last instruction created, correcting line information +** accordingly. +*/ +static void removelastinstruction (FuncState *fs) { + removelastlineinfo(fs); + fs->pc--; +} + + +/* +** Emit instruction 'i', checking for array sizes and saving also its +** line information. Return 'i' position. +*/ +int luaK_code (FuncState *fs, Instruction i) { + Proto *f = fs->f; + /* put new instruction in code array */ + luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction, + INT_MAX, "opcodes"); + f->code[fs->pc++] = i; + savelineinfo(fs, f, fs->ls->lastline); + return fs->pc - 1; /* index of new instruction */ +} + + +/* +** Format and emit an 'iABC' instruction. (Assertions check consistency +** of parameters versus opcode.) +*/ +int luaK_codeABCk (FuncState *fs, OpCode o, int A, int B, int C, int k) { + lua_assert(getOpMode(o) == iABC); + lua_assert(A <= MAXARG_A && B <= MAXARG_B && + C <= MAXARG_C && (k & ~1) == 0); + return luaK_code(fs, CREATE_ABCk(o, A, B, C, k)); +} + + +int luaK_codevABCk (FuncState *fs, OpCode o, int A, int B, int C, int k) { + lua_assert(getOpMode(o) == ivABC); + lua_assert(A <= MAXARG_A && B <= MAXARG_vB && + C <= MAXARG_vC && (k & ~1) == 0); + return luaK_code(fs, CREATE_vABCk(o, A, B, C, k)); +} + + +/* +** Format and emit an 'iABx' instruction. +*/ +int luaK_codeABx (FuncState *fs, OpCode o, int A, int Bc) { + lua_assert(getOpMode(o) == iABx); + lua_assert(A <= MAXARG_A && Bc <= MAXARG_Bx); + return luaK_code(fs, CREATE_ABx(o, A, Bc)); +} + + +/* +** Format and emit an 'iAsBx' instruction. +*/ +static int codeAsBx (FuncState *fs, OpCode o, int A, int Bc) { + int b = Bc + OFFSET_sBx; + lua_assert(getOpMode(o) == iAsBx); + lua_assert(A <= MAXARG_A && b <= MAXARG_Bx); + return luaK_code(fs, CREATE_ABx(o, A, b)); +} + + +/* +** Format and emit an 'isJ' instruction. +*/ +static int codesJ (FuncState *fs, OpCode o, int sj, int k) { + int j = sj + OFFSET_sJ; + lua_assert(getOpMode(o) == isJ); + lua_assert(j <= MAXARG_sJ && (k & ~1) == 0); + return luaK_code(fs, CREATE_sJ(o, j, k)); +} + + +/* +** Emit an "extra argument" instruction (format 'iAx') +*/ +static int codeextraarg (FuncState *fs, int A) { + lua_assert(A <= MAXARG_Ax); + return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, A)); +} + + +/* +** Emit a "load constant" instruction, using either 'OP_LOADK' +** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX' +** instruction with "extra argument". +*/ +static int luaK_codek (FuncState *fs, int reg, int k) { + if (k <= MAXARG_Bx) + return luaK_codeABx(fs, OP_LOADK, reg, k); + else { + int p = luaK_codeABx(fs, OP_LOADKX, reg, 0); + codeextraarg(fs, k); + return p; + } +} + + +/* +** Check register-stack level, keeping track of its maximum size +** in field 'maxstacksize' +*/ +void luaK_checkstack (FuncState *fs, int n) { + int newstack = fs->freereg + n; + if (newstack > fs->f->maxstacksize) { + luaY_checklimit(fs, newstack, MAX_FSTACK, "registers"); + fs->f->maxstacksize = cast_byte(newstack); + } +} + + +/* +** Reserve 'n' registers in register stack +*/ +void luaK_reserveregs (FuncState *fs, int n) { + luaK_checkstack(fs, n); + fs->freereg = cast_byte(fs->freereg + n); +} + + +/* +** Free register 'reg', if it is neither a constant index nor +** a local variable. +) +*/ +static void freereg (FuncState *fs, int reg) { + if (reg >= luaY_nvarstack(fs)) { + fs->freereg--; + lua_assert(reg == fs->freereg); + } +} + + +/* +** Free two registers in proper order +*/ +static void freeregs (FuncState *fs, int r1, int r2) { + if (r1 > r2) { + freereg(fs, r1); + freereg(fs, r2); + } + else { + freereg(fs, r2); + freereg(fs, r1); + } +} + + +/* +** Free register used by expression 'e' (if any) +*/ +static void freeexp (FuncState *fs, expdesc *e) { + if (e->k == VNONRELOC) + freereg(fs, e->u.info); +} + + +/* +** Free registers used by expressions 'e1' and 'e2' (if any) in proper +** order. +*/ +static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) { + int r1 = (e1->k == VNONRELOC) ? e1->u.info : -1; + int r2 = (e2->k == VNONRELOC) ? e2->u.info : -1; + freeregs(fs, r1, r2); +} + + +/* +** Add constant 'v' to prototype's list of constants (field 'k'). +*/ +static int addk (FuncState *fs, Proto *f, TValue *v) { + lua_State *L = fs->ls->L; + int oldsize = f->sizek; + int k = fs->nk; + luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants"); + while (oldsize < f->sizek) + setnilvalue(&f->k[oldsize++]); + setobj(L, &f->k[k], v); + fs->nk++; + luaC_barrier(L, f, v); + return k; +} + + +/* +** Use scanner's table to cache position of constants in constant list +** and try to reuse constants. Because some values should not be used +** as keys (nil cannot be a key, integer keys can collapse with float +** keys), the caller must provide a useful 'key' for indexing the cache. +*/ +static int k2proto (FuncState *fs, TValue *key, TValue *v) { + TValue val; + Proto *f = fs->f; + int tag = luaH_get(fs->kcache, key, &val); /* query scanner table */ + if (!tagisempty(tag)) { /* is there an index there? */ + int k = cast_int(ivalue(&val)); + /* collisions can happen only for float keys */ + lua_assert(ttisfloat(key) || luaV_rawequalobj(&f->k[k], v)); + return k; /* reuse index */ + } + else { /* constant not found; create a new entry */ + int k = addk(fs, f, v); + /* cache it for reuse; numerical value does not need GC barrier; + table is not a metatable, so it does not need to invalidate cache */ + setivalue(&val, k); + luaH_set(fs->ls->L, fs->kcache, key, &val); + return k; + } +} + + +/* +** Add a string to list of constants and return its index. +*/ +static int stringK (FuncState *fs, TString *s) { + TValue o; + setsvalue(fs->ls->L, &o, s); + return k2proto(fs, &o, &o); /* use string itself as key */ +} + + +/* +** Add an integer to list of constants and return its index. +*/ +static int luaK_intK (FuncState *fs, lua_Integer n) { + TValue o; + setivalue(&o, n); + return k2proto(fs, &o, &o); /* use integer itself as key */ +} + +/* +** Add a float to list of constants and return its index. Floats +** with integral values need a different key, to avoid collision +** with actual integers. To that end, we add to the number its smaller +** power-of-two fraction that is still significant in its scale. +** (For doubles, the fraction would be 2^-52). +** This method is not bulletproof: different numbers may generate the +** same key (e.g., very large numbers will overflow to 'inf') and for +** floats larger than 2^53 the result is still an integer. For those +** cases, just generate a new entry. At worst, this only wastes an entry +** with a duplicate. +*/ +static int luaK_numberK (FuncState *fs, lua_Number r) { + TValue o, kv; + setfltvalue(&o, r); /* value as a TValue */ + if (r == 0) { /* handle zero as a special case */ + setpvalue(&kv, fs); /* use FuncState as index */ + return k2proto(fs, &kv, &o); /* cannot collide */ + } + else { + const int nbm = l_floatatt(MANT_DIG); + const lua_Number q = l_mathop(ldexp)(l_mathop(1.0), -nbm + 1); + const lua_Number k = r * (1 + q); /* key */ + lua_Integer ik; + setfltvalue(&kv, k); /* key as a TValue */ + if (!luaV_flttointeger(k, &ik, F2Ieq)) { /* not an integer value? */ + int n = k2proto(fs, &kv, &o); /* use key */ + if (luaV_rawequalobj(&fs->f->k[n], &o)) /* correct value? */ + return n; + } + /* else, either key is still an integer or there was a collision; + anyway, do not try to reuse constant; instead, create a new one */ + return addk(fs, fs->f, &o); + } +} + + +/* +** Add a false to list of constants and return its index. +*/ +static int boolF (FuncState *fs) { + TValue o; + setbfvalue(&o); + return k2proto(fs, &o, &o); /* use boolean itself as key */ +} + + +/* +** Add a true to list of constants and return its index. +*/ +static int boolT (FuncState *fs) { + TValue o; + setbtvalue(&o); + return k2proto(fs, &o, &o); /* use boolean itself as key */ +} + + +/* +** Add nil to list of constants and return its index. +*/ +static int nilK (FuncState *fs) { + TValue k, v; + setnilvalue(&v); + /* cannot use nil as key; instead use table itself */ + sethvalue(fs->ls->L, &k, fs->kcache); + return k2proto(fs, &k, &v); +} + + +/* +** Check whether 'i' can be stored in an 'sC' operand. Equivalent to +** (0 <= int2sC(i) && int2sC(i) <= MAXARG_C) but without risk of +** overflows in the hidden addition inside 'int2sC'. +*/ +static int fitsC (lua_Integer i) { + return (l_castS2U(i) + OFFSET_sC <= cast_uint(MAXARG_C)); +} + + +/* +** Check whether 'i' can be stored in an 'sBx' operand. +*/ +static int fitsBx (lua_Integer i) { + return (-OFFSET_sBx <= i && i <= MAXARG_Bx - OFFSET_sBx); +} + + +void luaK_int (FuncState *fs, int reg, lua_Integer i) { + if (fitsBx(i)) + codeAsBx(fs, OP_LOADI, reg, cast_int(i)); + else + luaK_codek(fs, reg, luaK_intK(fs, i)); +} + + +static void luaK_float (FuncState *fs, int reg, lua_Number f) { + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Ieq) && fitsBx(fi)) + codeAsBx(fs, OP_LOADF, reg, cast_int(fi)); + else + luaK_codek(fs, reg, luaK_numberK(fs, f)); +} + + +/* +** Get the value of 'var' in a register and generate an opcode to check +** whether that register is nil. 'k' is the index of the variable name +** in the list of constants. If its value cannot be encoded in Bx, a 0 +** will use '?' for the name. +*/ +void luaK_codecheckglobal (FuncState *fs, expdesc *var, int k, int line) { + luaK_exp2anyreg(fs, var); + luaK_fixline(fs, line); + k = (k >= MAXARG_Bx) ? 0 : k + 1; + luaK_codeABx(fs, OP_ERRNNIL, var->u.info, k); + luaK_fixline(fs, line); + freeexp(fs, var); +} + + +/* +** Convert a constant in 'v' into an expression description 'e' +*/ +static void const2exp (TValue *v, expdesc *e) { + switch (ttypetag(v)) { + case LUA_VNUMINT: + e->k = VKINT; e->u.ival = ivalue(v); + break; + case LUA_VNUMFLT: + e->k = VKFLT; e->u.nval = fltvalue(v); + break; + case LUA_VFALSE: + e->k = VFALSE; + break; + case LUA_VTRUE: + e->k = VTRUE; + break; + case LUA_VNIL: + e->k = VNIL; + break; + case LUA_VSHRSTR: case LUA_VLNGSTR: + e->k = VKSTR; e->u.strval = tsvalue(v); + break; + default: lua_assert(0); + } +} + + +/* +** Fix an expression to return the number of results 'nresults'. +** 'e' must be a multi-ret expression (function call or vararg). +*/ +void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { + Instruction *pc = &getinstruction(fs, e); + luaY_checklimit(fs, nresults + 1, MAXARG_C, "multiple results"); + if (e->k == VCALL) /* expression is an open function call? */ + SETARG_C(*pc, nresults + 1); + else { + lua_assert(e->k == VVARARG); + SETARG_C(*pc, nresults + 1); + SETARG_A(*pc, fs->freereg); + luaK_reserveregs(fs, 1); + } +} + + +/* +** Convert a VKSTR to a VK +*/ +static int str2K (FuncState *fs, expdesc *e) { + lua_assert(e->k == VKSTR); + e->u.info = stringK(fs, e->u.strval); + e->k = VK; + return e->u.info; +} + + +/* +** Fix an expression to return one result. +** If expression is not a multi-ret expression (function call or +** vararg), it already returns one result, so nothing needs to be done. +** Function calls become VNONRELOC expressions (as its result comes +** fixed in the base register of the call), while vararg expressions +** become VRELOC (as OP_VARARG puts its results where it wants). +** (Calls are created returning one result, so that does not need +** to be fixed.) +*/ +void luaK_setoneret (FuncState *fs, expdesc *e) { + if (e->k == VCALL) { /* expression is an open function call? */ + /* already returns 1 value */ + lua_assert(GETARG_C(getinstruction(fs, e)) == 2); + e->k = VNONRELOC; /* result has fixed position */ + e->u.info = GETARG_A(getinstruction(fs, e)); + } + else if (e->k == VVARARG) { + SETARG_C(getinstruction(fs, e), 2); + e->k = VRELOC; /* can relocate its simple result */ + } +} + +/* +** Change a vararg parameter into a regular local variable +*/ +void luaK_vapar2local (FuncState *fs, expdesc *var) { + needvatab(fs->f); /* function will need a vararg table */ + /* now a vararg parameter is equivalent to a regular local variable */ + var->k = VLOCAL; +} + + +/* +** Ensure that expression 'e' is not a variable (nor a <const>). +** (Expression still may have jump lists.) +*/ +void luaK_dischargevars (FuncState *fs, expdesc *e) { + switch (e->k) { + case VCONST: { + const2exp(const2val(fs, e), e); + break; + } + case VVARGVAR: { + luaK_vapar2local(fs, e); /* turn it into a local variable */ + } /* FALLTHROUGH */ + case VLOCAL: { /* already in a register */ + int temp = e->u.var.ridx; + e->u.info = temp; /* (can't do a direct assignment; values overlap) */ + e->k = VNONRELOC; /* becomes a non-relocatable value */ + break; + } + case VUPVAL: { /* move value to some (pending) register */ + e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0); + e->k = VRELOC; + break; + } + case VINDEXUP: { + e->u.info = luaK_codeABC(fs, OP_GETTABUP, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VINDEXI: { + freereg(fs, e->u.ind.t); + e->u.info = luaK_codeABC(fs, OP_GETI, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VINDEXSTR: { + freereg(fs, e->u.ind.t); + e->u.info = luaK_codeABC(fs, OP_GETFIELD, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VINDEXED: { + freeregs(fs, e->u.ind.t, e->u.ind.idx); + e->u.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VVARGIND: { + freeregs(fs, e->u.ind.t, e->u.ind.idx); + e->u.info = luaK_codeABC(fs, OP_GETVARG, 0, e->u.ind.t, e->u.ind.idx); + e->k = VRELOC; + break; + } + case VVARARG: case VCALL: { + luaK_setoneret(fs, e); + break; + } + default: break; /* there is one value available (somewhere) */ + } +} + + +/* +** Ensure expression value is in register 'reg', making 'e' a +** non-relocatable expression. +** (Expression still may have jump lists.) +*/ +static void discharge2reg (FuncState *fs, expdesc *e, int reg) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: { + luaK_nil(fs, reg, 1); + break; + } + case VFALSE: { + luaK_codeABC(fs, OP_LOADFALSE, reg, 0, 0); + break; + } + case VTRUE: { + luaK_codeABC(fs, OP_LOADTRUE, reg, 0, 0); + break; + } + case VKSTR: { + str2K(fs, e); + } /* FALLTHROUGH */ + case VK: { + luaK_codek(fs, reg, e->u.info); + break; + } + case VKFLT: { + luaK_float(fs, reg, e->u.nval); + break; + } + case VKINT: { + luaK_int(fs, reg, e->u.ival); + break; + } + case VRELOC: { + Instruction *pc = &getinstruction(fs, e); + SETARG_A(*pc, reg); /* instruction will put result in 'reg' */ + break; + } + case VNONRELOC: { + if (reg != e->u.info) + luaK_codeABC(fs, OP_MOVE, reg, e->u.info, 0); + break; + } + default: { + lua_assert(e->k == VJMP); + return; /* nothing to do... */ + } + } + e->u.info = reg; + e->k = VNONRELOC; +} + + +/* +** Ensure expression value is in a register, making 'e' a +** non-relocatable expression. +** (Expression still may have jump lists.) +*/ +static void discharge2anyreg (FuncState *fs, expdesc *e) { + if (e->k != VNONRELOC) { /* no fixed register yet? */ + luaK_reserveregs(fs, 1); /* get a register */ + discharge2reg(fs, e, fs->freereg-1); /* put value there */ + } +} + + +static int code_loadbool (FuncState *fs, int A, OpCode op) { + luaK_getlabel(fs); /* those instructions may be jump targets */ + return luaK_codeABC(fs, op, A, 0, 0); +} + + +/* +** check whether list has any jump that do not produce a value +** or produce an inverted value +*/ +static int need_value (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) { + Instruction i = *getjumpcontrol(fs, list); + if (GET_OPCODE(i) != OP_TESTSET) return 1; + } + return 0; /* not found */ +} + + +/* +** Ensures final expression result (which includes results from its +** jump lists) is in register 'reg'. +** If expression has jumps, need to patch these jumps either to +** its final position or to "load" instructions (for those tests +** that do not produce values). +*/ +static void exp2reg (FuncState *fs, expdesc *e, int reg) { + discharge2reg(fs, e, reg); + if (e->k == VJMP) /* expression itself is a test? */ + luaK_concat(fs, &e->t, e->u.info); /* put this jump in 't' list */ + if (hasjumps(e)) { + int final; /* position after whole expression */ + int p_f = NO_JUMP; /* position of an eventual LOAD false */ + int p_t = NO_JUMP; /* position of an eventual LOAD true */ + if (need_value(fs, e->t) || need_value(fs, e->f)) { + int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); + p_f = code_loadbool(fs, reg, OP_LFALSESKIP); /* skip next inst. */ + p_t = code_loadbool(fs, reg, OP_LOADTRUE); + /* jump around these booleans if 'e' is not a test */ + luaK_patchtohere(fs, fj); + } + final = luaK_getlabel(fs); + patchlistaux(fs, e->f, final, reg, p_f); + patchlistaux(fs, e->t, final, reg, p_t); + } + e->f = e->t = NO_JUMP; + e->u.info = reg; + e->k = VNONRELOC; +} + + +/* +** Ensures final expression result is in next available register. +*/ +void luaK_exp2nextreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + freeexp(fs, e); + luaK_reserveregs(fs, 1); + exp2reg(fs, e, fs->freereg - 1); +} + + +/* +** Ensures final expression result is in some (any) register +** and return that register. +*/ +int luaK_exp2anyreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + if (e->k == VNONRELOC) { /* expression already has a register? */ + if (!hasjumps(e)) /* no jumps? */ + return e->u.info; /* result is already in a register */ + if (e->u.info >= luaY_nvarstack(fs)) { /* reg. is not a local? */ + exp2reg(fs, e, e->u.info); /* put final result in it */ + return e->u.info; + } + /* else expression has jumps and cannot change its register + to hold the jump values, because it is a local variable. + Go through to the default case. */ + } + luaK_exp2nextreg(fs, e); /* default: use next available register */ + return e->u.info; +} + + +/* +** Ensures final expression result is either in a register, +** in an upvalue, or it is the vararg parameter. +*/ +void luaK_exp2anyregup (FuncState *fs, expdesc *e) { + if ((e->k != VUPVAL && e->k != VVARGVAR) || hasjumps(e)) + luaK_exp2anyreg(fs, e); +} + + +/* +** Ensures final expression result is either in a register +** or it is a constant. +*/ +void luaK_exp2val (FuncState *fs, expdesc *e) { + if (e->k == VJMP || hasjumps(e)) + luaK_exp2anyreg(fs, e); + else + luaK_dischargevars(fs, e); +} + + +/* +** Try to make 'e' a K expression with an index in the range of R/K +** indices. Return true iff succeeded. +*/ +static int luaK_exp2K (FuncState *fs, expdesc *e) { + if (!hasjumps(e)) { + int info; + switch (e->k) { /* move constants to 'k' */ + case VTRUE: info = boolT(fs); break; + case VFALSE: info = boolF(fs); break; + case VNIL: info = nilK(fs); break; + case VKINT: info = luaK_intK(fs, e->u.ival); break; + case VKFLT: info = luaK_numberK(fs, e->u.nval); break; + case VKSTR: info = stringK(fs, e->u.strval); break; + case VK: info = e->u.info; break; + default: return 0; /* not a constant */ + } + if (info <= MAXINDEXRK) { /* does constant fit in 'argC'? */ + e->k = VK; /* make expression a 'K' expression */ + e->u.info = info; + return 1; + } + } + /* else, expression doesn't fit; leave it unchanged */ + return 0; +} + + +/* +** Ensures final expression result is in a valid R/K index +** (that is, it is either in a register or in 'k' with an index +** in the range of R/K indices). +** Returns 1 iff expression is K. +*/ +static int exp2RK (FuncState *fs, expdesc *e) { + if (luaK_exp2K(fs, e)) + return 1; + else { /* not a constant in the right range: put it in a register */ + luaK_exp2anyreg(fs, e); + return 0; + } +} + + +static void codeABRK (FuncState *fs, OpCode o, int A, int B, + expdesc *ec) { + int k = exp2RK(fs, ec); + luaK_codeABCk(fs, o, A, B, ec->u.info, k); +} + + +/* +** Generate code to store result of expression 'ex' into variable 'var'. +*/ +void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { + switch (var->k) { + case VLOCAL: { + freeexp(fs, ex); + exp2reg(fs, ex, var->u.var.ridx); /* compute 'ex' into proper place */ + return; + } + case VUPVAL: { + int e = luaK_exp2anyreg(fs, ex); + luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0); + break; + } + case VINDEXUP: { + codeABRK(fs, OP_SETTABUP, var->u.ind.t, var->u.ind.idx, ex); + break; + } + case VINDEXI: { + codeABRK(fs, OP_SETI, var->u.ind.t, var->u.ind.idx, ex); + break; + } + case VINDEXSTR: { + codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex); + break; + } + case VVARGIND: { + needvatab(fs->f); /* function will need a vararg table */ + /* now, assignment is to a regular table */ + } /* FALLTHROUGH */ + case VINDEXED: { + codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex); + break; + } + default: lua_assert(0); /* invalid var kind to store */ + } + freeexp(fs, ex); +} + + +/* +** Negate condition 'e' (where 'e' is a comparison). +*/ +static void negatecondition (FuncState *fs, expdesc *e) { + Instruction *pc = getjumpcontrol(fs, e->u.info); + lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && + GET_OPCODE(*pc) != OP_TEST); + SETARG_k(*pc, (GETARG_k(*pc) ^ 1)); +} + + +/* +** Emit instruction to jump if 'e' is 'cond' (that is, if 'cond' +** is true, code will jump if 'e' is true.) Return jump position. +** Optimize when 'e' is 'not' something, inverting the condition +** and removing the 'not'. +*/ +static int jumponcond (FuncState *fs, expdesc *e, int cond) { + if (e->k == VRELOC) { + Instruction ie = getinstruction(fs, e); + if (GET_OPCODE(ie) == OP_NOT) { + removelastinstruction(fs); /* remove previous OP_NOT */ + return condjump(fs, OP_TEST, GETARG_B(ie), 0, 0, !cond); + } + /* else go through */ + } + discharge2anyreg(fs, e); + freeexp(fs, e); + return condjump(fs, OP_TESTSET, NO_REG, e->u.info, 0, cond); +} + + +/* +** Emit code to go through if 'e' is true, jump otherwise. +*/ +void luaK_goiftrue (FuncState *fs, expdesc *e) { + int pc; /* pc of new jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VJMP: { /* condition? */ + negatecondition(fs, e); /* jump when it is false */ + pc = e->u.info; /* save jump position */ + break; + } + case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: { + pc = NO_JUMP; /* always true; do nothing */ + break; + } + default: { + pc = jumponcond(fs, e, 0); /* jump when false */ + break; + } + } + luaK_concat(fs, &e->f, pc); /* insert new jump in false list */ + luaK_patchtohere(fs, e->t); /* true list jumps to here (to go through) */ + e->t = NO_JUMP; +} + + +/* +** Emit code to go through if 'e' is false, jump otherwise. +*/ +static void luaK_goiffalse (FuncState *fs, expdesc *e) { + int pc; /* pc of new jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VJMP: { + pc = e->u.info; /* already jump if true */ + break; + } + case VNIL: case VFALSE: { + pc = NO_JUMP; /* always false; do nothing */ + break; + } + default: { + pc = jumponcond(fs, e, 1); /* jump if true */ + break; + } + } + luaK_concat(fs, &e->t, pc); /* insert new jump in 't' list */ + luaK_patchtohere(fs, e->f); /* false list jumps to here (to go through) */ + e->f = NO_JUMP; +} + + +/* +** Code 'not e', doing constant folding. +*/ +static void codenot (FuncState *fs, expdesc *e) { + switch (e->k) { + case VNIL: case VFALSE: { + e->k = VTRUE; /* true == not nil == not false */ + break; + } + case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: { + e->k = VFALSE; /* false == not "x" == not 0.5 == not 1 == not true */ + break; + } + case VJMP: { + negatecondition(fs, e); + break; + } + case VRELOC: + case VNONRELOC: { + discharge2anyreg(fs, e); + freeexp(fs, e); + e->u.info = luaK_codeABC(fs, OP_NOT, 0, e->u.info, 0); + e->k = VRELOC; + break; + } + default: lua_assert(0); /* cannot happen */ + } + /* interchange true and false lists */ + { int temp = e->f; e->f = e->t; e->t = temp; } + removevalues(fs, e->f); /* values are useless when negated */ + removevalues(fs, e->t); +} + + +/* +** Check whether expression 'e' is a short literal string +*/ +static int isKstr (FuncState *fs, expdesc *e) { + return (e->k == VK && !hasjumps(e) && e->u.info <= MAXINDEXRK && + ttisshrstring(&fs->f->k[e->u.info])); +} + +/* +** Check whether expression 'e' is a literal integer. +*/ +static int isKint (expdesc *e) { + return (e->k == VKINT && !hasjumps(e)); +} + + +/* +** Check whether expression 'e' is a literal integer in +** proper range to fit in register C +*/ +static int isCint (expdesc *e) { + return isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C)); +} + + +/* +** Check whether expression 'e' is a literal integer in +** proper range to fit in register sC +*/ +static int isSCint (expdesc *e) { + return isKint(e) && fitsC(e->u.ival); +} + + +/* +** Check whether expression 'e' is a literal integer or float in +** proper range to fit in a register (sB or sC). +*/ +static int isSCnumber (expdesc *e, int *pi, int *isfloat) { + lua_Integer i; + if (e->k == VKINT) + i = e->u.ival; + else if (e->k == VKFLT && luaV_flttointeger(e->u.nval, &i, F2Ieq)) + *isfloat = 1; + else + return 0; /* not a number */ + if (!hasjumps(e) && fitsC(i)) { + *pi = int2sC(cast_int(i)); + return 1; + } + else + return 0; +} + + +/* +** Emit SELF instruction or equivalent: the code will convert +** expression 'e' into 'e.key(e,'. +*/ +void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { + int ereg, base; + luaK_exp2anyreg(fs, e); + ereg = e->u.info; /* register where 'e' (the receiver) was placed */ + freeexp(fs, e); + base = e->u.info = fs->freereg; /* base register for op_self */ + e->k = VNONRELOC; /* self expression has a fixed register */ + luaK_reserveregs(fs, 2); /* method and 'self' produced by op_self */ + lua_assert(key->k == VKSTR); + /* is method name a short string in a valid K index? */ + if (strisshr(key->u.strval) && luaK_exp2K(fs, key)) { + /* can use 'self' opcode */ + luaK_codeABCk(fs, OP_SELF, base, ereg, key->u.info, 0); + } + else { /* cannot use 'self' opcode; use move+gettable */ + luaK_exp2anyreg(fs, key); /* put method name in a register */ + luaK_codeABC(fs, OP_MOVE, base + 1, ereg, 0); /* copy self to base+1 */ + luaK_codeABC(fs, OP_GETTABLE, base, ereg, key->u.info); /* get method */ + } + freeexp(fs, key); +} + + +/* auxiliary function to define indexing expressions */ +static void fillidxk (expdesc *t, int idx, expkind k) { + t->u.ind.idx = cast_byte(idx); + t->k = k; +} + + +/* +** Create expression 't[k]'. 't' must have its final result already in a +** register or upvalue. Upvalues can only be indexed by literal strings. +** Keys can be literal strings in the constant table or arbitrary +** values in registers. +*/ +void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { + int keystr = -1; + if (k->k == VKSTR) + keystr = str2K(fs, k); + lua_assert(!hasjumps(t) && + (t->k == VLOCAL || t->k == VVARGVAR || + t->k == VNONRELOC || t->k == VUPVAL)); + if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ + luaK_exp2anyreg(fs, t); /* put it in a register */ + if (t->k == VUPVAL) { + lu_byte temp = cast_byte(t->u.info); /* upvalue index */ + t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */ + lua_assert(isKstr(fs, k)); + fillidxk(t, k->u.info, VINDEXUP); /* literal short string */ + } + else if (t->k == VVARGVAR) { /* indexing the vararg parameter? */ + int kreg = luaK_exp2anyreg(fs, k); /* put key in some register */ + lu_byte vreg = cast_byte(t->u.var.ridx); /* register with vararg param. */ + lua_assert(vreg == fs->f->numparams); + t->u.ind.t = vreg; /* (avoid a direct assignment; values may overlap) */ + fillidxk(t, kreg, VVARGIND); /* 't' represents 'vararg[k]' */ + } + else { + /* register index of the table */ + t->u.ind.t = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info); + if (isKstr(fs, k)) + fillidxk(t, k->u.info, VINDEXSTR); /* literal short string */ + else if (isCint(k)) /* int. constant in proper range? */ + fillidxk(t, cast_int(k->u.ival), VINDEXI); + else + fillidxk(t, luaK_exp2anyreg(fs, k), VINDEXED); /* register */ + } + t->u.ind.keystr = keystr; /* string index in 'k' */ + t->u.ind.ro = 0; /* by default, not read-only */ +} + + +/* +** Return false if folding can raise an error. +** Bitwise operations need operands convertible to integers; division +** operations cannot have 0 as divisor. +*/ +static int validop (int op, TValue *v1, TValue *v2) { + switch (op) { + case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: + case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */ + lua_Integer i; + return (luaV_tointegerns(v1, &i, LUA_FLOORN2I) && + luaV_tointegerns(v2, &i, LUA_FLOORN2I)); + } + case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */ + return (nvalue(v2) != 0); + default: return 1; /* everything else is valid */ + } +} + + +/* +** Try to "constant-fold" an operation; return 1 iff successful. +** (In this case, 'e1' has the final result.) +*/ +static int constfolding (FuncState *fs, int op, expdesc *e1, + const expdesc *e2) { + TValue v1, v2, res; + if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2)) + return 0; /* non-numeric operands or not safe to fold */ + luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */ + if (ttisinteger(&res)) { + e1->k = VKINT; + e1->u.ival = ivalue(&res); + } + else { /* folds neither NaN nor 0.0 (to avoid problems with -0.0) */ + lua_Number n = fltvalue(&res); + if (luai_numisnan(n) || n == 0) + return 0; + e1->k = VKFLT; + e1->u.nval = n; + } + return 1; +} + + +/* +** Convert a BinOpr to an OpCode (ORDER OPR - ORDER OP) +*/ +l_sinline OpCode binopr2op (BinOpr opr, BinOpr baser, OpCode base) { + lua_assert(baser <= opr && + ((baser == OPR_ADD && opr <= OPR_SHR) || + (baser == OPR_LT && opr <= OPR_LE))); + return cast(OpCode, (cast_int(opr) - cast_int(baser)) + cast_int(base)); +} + + +/* +** Convert a UnOpr to an OpCode (ORDER OPR - ORDER OP) +*/ +l_sinline OpCode unopr2op (UnOpr opr) { + return cast(OpCode, (cast_int(opr) - cast_int(OPR_MINUS)) + + cast_int(OP_UNM)); +} + + +/* +** Convert a BinOpr to a tag method (ORDER OPR - ORDER TM) +*/ +l_sinline TMS binopr2TM (BinOpr opr) { + lua_assert(OPR_ADD <= opr && opr <= OPR_SHR); + return cast(TMS, (cast_int(opr) - cast_int(OPR_ADD)) + cast_int(TM_ADD)); +} + + +/* +** Emit code for unary expressions that "produce values" +** (everything but 'not'). +** Expression to produce final result will be encoded in 'e'. +*/ +static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int line) { + int r = luaK_exp2anyreg(fs, e); /* opcodes operate only on registers */ + freeexp(fs, e); + e->u.info = luaK_codeABC(fs, op, 0, r, 0); /* generate opcode */ + e->k = VRELOC; /* all those operations are relocatable */ + luaK_fixline(fs, line); +} + + +/* +** Emit code for binary expressions that "produce values" +** (everything but logical operators 'and'/'or' and comparison +** operators). +** Expression to produce final result will be encoded in 'e1'. +*/ +static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2, + OpCode op, int v2, int flip, int line, + OpCode mmop, TMS event) { + int v1 = luaK_exp2anyreg(fs, e1); + int pc = luaK_codeABCk(fs, op, 0, v1, v2, 0); + freeexps(fs, e1, e2); + e1->u.info = pc; + e1->k = VRELOC; /* all those operations are relocatable */ + luaK_fixline(fs, line); + luaK_codeABCk(fs, mmop, v1, v2, cast_int(event), flip); /* metamethod */ + luaK_fixline(fs, line); +} + + +/* +** Emit code for binary expressions that "produce values" over +** two registers. +*/ +static void codebinexpval (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int line) { + OpCode op = binopr2op(opr, OPR_ADD, OP_ADD); + int v2 = luaK_exp2anyreg(fs, e2); /* make sure 'e2' is in a register */ + /* 'e1' must be already in a register or it is a constant */ + lua_assert((VNIL <= e1->k && e1->k <= VKSTR) || + e1->k == VNONRELOC || e1->k == VRELOC); + lua_assert(OP_ADD <= op && op <= OP_SHR); + finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, binopr2TM(opr)); +} + + +/* +** Code binary operators with immediate operands. +*/ +static void codebini (FuncState *fs, OpCode op, + expdesc *e1, expdesc *e2, int flip, int line, + TMS event) { + int v2 = int2sC(cast_int(e2->u.ival)); /* immediate operand */ + lua_assert(e2->k == VKINT); + finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINI, event); +} + + +/* +** Code binary operators with K operand. +*/ +static void codebinK (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + TMS event = binopr2TM(opr); + int v2 = e2->u.info; /* K index */ + OpCode op = binopr2op(opr, OPR_ADD, OP_ADDK); + finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event); +} + + +/* Try to code a binary operator negating its second operand. +** For the metamethod, 2nd operand must keep its original value. +*/ +static int finishbinexpneg (FuncState *fs, expdesc *e1, expdesc *e2, + OpCode op, int line, TMS event) { + if (!isKint(e2)) + return 0; /* not an integer constant */ + else { + lua_Integer i2 = e2->u.ival; + if (!(fitsC(i2) && fitsC(-i2))) + return 0; /* not in the proper range */ + else { /* operating a small integer constant */ + int v2 = cast_int(i2); + finishbinexpval(fs, e1, e2, op, int2sC(-v2), 0, line, OP_MMBINI, event); + /* correct metamethod argument */ + SETARG_B(fs->f->code[fs->pc - 1], int2sC(v2)); + return 1; /* successfully coded */ + } + } +} + + +static void swapexps (expdesc *e1, expdesc *e2) { + expdesc temp = *e1; *e1 = *e2; *e2 = temp; /* swap 'e1' and 'e2' */ +} + + +/* +** Code binary operators with no constant operand. +*/ +static void codebinNoK (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + if (flip) + swapexps(e1, e2); /* back to original order */ + codebinexpval(fs, opr, e1, e2, line); /* use standard operators */ +} + + +/* +** Code arithmetic operators ('+', '-', ...). If second operand is a +** constant in the proper range, use variant opcodes with K operands. +*/ +static void codearith (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int flip, int line) { + if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) /* K operand? */ + codebinK(fs, opr, e1, e2, flip, line); + else /* 'e2' is neither an immediate nor a K operand */ + codebinNoK(fs, opr, e1, e2, flip, line); +} + + +/* +** Code commutative operators ('+', '*'). If first operand is a +** numeric constant, change order of operands to try to use an +** immediate or K operator. +*/ +static void codecommutative (FuncState *fs, BinOpr op, + expdesc *e1, expdesc *e2, int line) { + int flip = 0; + if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */ + swapexps(e1, e2); /* change order */ + flip = 1; + } + if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */ + codebini(fs, OP_ADDI, e1, e2, flip, line, TM_ADD); + else + codearith(fs, op, e1, e2, flip, line); +} + + +/* +** Code bitwise operations; they are all commutative, so the function +** tries to put an integer constant as the 2nd operand (a K operand). +*/ +static void codebitwise (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int line) { + int flip = 0; + if (e1->k == VKINT) { + swapexps(e1, e2); /* 'e2' will be the constant operand */ + flip = 1; + } + if (e2->k == VKINT && luaK_exp2K(fs, e2)) /* K operand? */ + codebinK(fs, opr, e1, e2, flip, line); + else /* no constants */ + codebinNoK(fs, opr, e1, e2, flip, line); +} + + +/* +** Emit code for order comparisons. When using an immediate operand, +** 'isfloat' tells whether the original value was a float. +*/ +static void codeorder (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { + int r1, r2; + int im; + int isfloat = 0; + OpCode op; + if (isSCnumber(e2, &im, &isfloat)) { + /* use immediate operand */ + r1 = luaK_exp2anyreg(fs, e1); + r2 = im; + op = binopr2op(opr, OPR_LT, OP_LTI); + } + else if (isSCnumber(e1, &im, &isfloat)) { + /* transform (A < B) to (B > A) and (A <= B) to (B >= A) */ + r1 = luaK_exp2anyreg(fs, e2); + r2 = im; + op = binopr2op(opr, OPR_LT, OP_GTI); + } + else { /* regular case, compare two registers */ + r1 = luaK_exp2anyreg(fs, e1); + r2 = luaK_exp2anyreg(fs, e2); + op = binopr2op(opr, OPR_LT, OP_LT); + } + freeexps(fs, e1, e2); + e1->u.info = condjump(fs, op, r1, r2, isfloat, 1); + e1->k = VJMP; +} + + +/* +** Emit code for equality comparisons ('==', '~='). +** 'e1' was already put as RK by 'luaK_infix'. +*/ +static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { + int r1, r2; + int im; + int isfloat = 0; /* not needed here, but kept for symmetry */ + OpCode op; + if (e1->k != VNONRELOC) { + lua_assert(e1->k == VK || e1->k == VKINT || e1->k == VKFLT); + swapexps(e1, e2); + } + r1 = luaK_exp2anyreg(fs, e1); /* 1st expression must be in register */ + if (isSCnumber(e2, &im, &isfloat)) { + op = OP_EQI; + r2 = im; /* immediate operand */ + } + else if (exp2RK(fs, e2)) { /* 2nd expression is constant? */ + op = OP_EQK; + r2 = e2->u.info; /* constant index */ + } + else { + op = OP_EQ; /* will compare two registers */ + r2 = luaK_exp2anyreg(fs, e2); + } + freeexps(fs, e1, e2); + e1->u.info = condjump(fs, op, r1, r2, isfloat, (opr == OPR_EQ)); + e1->k = VJMP; +} + + +/* +** Apply prefix operation 'op' to expression 'e'. +*/ +void luaK_prefix (FuncState *fs, UnOpr opr, expdesc *e, int line) { + static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; + luaK_dischargevars(fs, e); + switch (opr) { + case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */ + if (constfolding(fs, cast_int(opr + LUA_OPUNM), e, &ef)) + break; + /* else */ /* FALLTHROUGH */ + case OPR_LEN: + codeunexpval(fs, unopr2op(opr), e, line); + break; + case OPR_NOT: codenot(fs, e); break; + default: lua_assert(0); + } +} + + +/* +** Process 1st operand 'v' of binary operation 'op' before reading +** 2nd operand. +*/ +void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { + luaK_dischargevars(fs, v); + switch (op) { + case OPR_AND: { + luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */ + break; + } + case OPR_OR: { + luaK_goiffalse(fs, v); /* go ahead only if 'v' is false */ + break; + } + case OPR_CONCAT: { + luaK_exp2nextreg(fs, v); /* operand must be on the stack */ + break; + } + case OPR_ADD: case OPR_SUB: + case OPR_MUL: case OPR_DIV: case OPR_IDIV: + case OPR_MOD: case OPR_POW: + case OPR_BAND: case OPR_BOR: case OPR_BXOR: + case OPR_SHL: case OPR_SHR: { + if (!tonumeral(v, NULL)) + luaK_exp2anyreg(fs, v); + /* else keep numeral, which may be folded or used as an immediate + operand */ + break; + } + case OPR_EQ: case OPR_NE: { + if (!tonumeral(v, NULL)) + exp2RK(fs, v); + /* else keep numeral, which may be an immediate operand */ + break; + } + case OPR_LT: case OPR_LE: + case OPR_GT: case OPR_GE: { + int dummy, dummy2; + if (!isSCnumber(v, &dummy, &dummy2)) + luaK_exp2anyreg(fs, v); + /* else keep numeral, which may be an immediate operand */ + break; + } + default: lua_assert(0); + } +} + +/* +** Create code for '(e1 .. e2)'. +** For '(e1 .. e2.1 .. e2.2)' (which is '(e1 .. (e2.1 .. e2.2))', +** because concatenation is right associative), merge both CONCATs. +*/ +static void codeconcat (FuncState *fs, expdesc *e1, expdesc *e2, int line) { + Instruction *ie2 = previousinstruction(fs); + if (GET_OPCODE(*ie2) == OP_CONCAT) { /* is 'e2' a concatenation? */ + int n = GETARG_B(*ie2); /* # of elements concatenated in 'e2' */ + lua_assert(e1->u.info + 1 == GETARG_A(*ie2)); + freeexp(fs, e2); + SETARG_A(*ie2, e1->u.info); /* correct first element ('e1') */ + SETARG_B(*ie2, n + 1); /* will concatenate one more element */ + } + else { /* 'e2' is not a concatenation */ + luaK_codeABC(fs, OP_CONCAT, e1->u.info, 2, 0); /* new concat opcode */ + freeexp(fs, e2); + luaK_fixline(fs, line); + } +} + + +/* +** Finalize code for binary operation, after reading 2nd operand. +*/ +void luaK_posfix (FuncState *fs, BinOpr opr, + expdesc *e1, expdesc *e2, int line) { + luaK_dischargevars(fs, e2); + if (foldbinop(opr) && constfolding(fs, cast_int(opr + LUA_OPADD), e1, e2)) + return; /* done by folding */ + switch (opr) { + case OPR_AND: { + lua_assert(e1->t == NO_JUMP); /* list closed by 'luaK_infix' */ + luaK_concat(fs, &e2->f, e1->f); + *e1 = *e2; + break; + } + case OPR_OR: { + lua_assert(e1->f == NO_JUMP); /* list closed by 'luaK_infix' */ + luaK_concat(fs, &e2->t, e1->t); + *e1 = *e2; + break; + } + case OPR_CONCAT: { /* e1 .. e2 */ + luaK_exp2nextreg(fs, e2); + codeconcat(fs, e1, e2, line); + break; + } + case OPR_ADD: case OPR_MUL: { + codecommutative(fs, opr, e1, e2, line); + break; + } + case OPR_SUB: { + if (finishbinexpneg(fs, e1, e2, OP_ADDI, line, TM_SUB)) + break; /* coded as (r1 + -I) */ + /* ELSE */ + } /* FALLTHROUGH */ + case OPR_DIV: case OPR_IDIV: case OPR_MOD: case OPR_POW: { + codearith(fs, opr, e1, e2, 0, line); + break; + } + case OPR_BAND: case OPR_BOR: case OPR_BXOR: { + codebitwise(fs, opr, e1, e2, line); + break; + } + case OPR_SHL: { + if (isSCint(e1)) { + swapexps(e1, e2); + codebini(fs, OP_SHLI, e1, e2, 1, line, TM_SHL); /* I << r2 */ + } + else if (finishbinexpneg(fs, e1, e2, OP_SHRI, line, TM_SHL)) { + /* coded as (r1 >> -I) */; + } + else /* regular case (two registers) */ + codebinexpval(fs, opr, e1, e2, line); + break; + } + case OPR_SHR: { + if (isSCint(e2)) + codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */ + else /* regular case (two registers) */ + codebinexpval(fs, opr, e1, e2, line); + break; + } + case OPR_EQ: case OPR_NE: { + codeeq(fs, opr, e1, e2); + break; + } + case OPR_GT: case OPR_GE: { + /* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */ + swapexps(e1, e2); + opr = cast(BinOpr, (opr - OPR_GT) + OPR_LT); + } /* FALLTHROUGH */ + case OPR_LT: case OPR_LE: { + codeorder(fs, opr, e1, e2); + break; + } + default: lua_assert(0); + } +} + + +/* +** Change line information associated with current position, by removing +** previous info and adding it again with new line. +*/ +void luaK_fixline (FuncState *fs, int line) { + removelastlineinfo(fs); + savelineinfo(fs, fs->f, line); +} + + +void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) { + Instruction *inst = &fs->f->code[pc]; + int extra = asize / (MAXARG_vC + 1); /* higher bits of array size */ + int rc = asize % (MAXARG_vC + 1); /* lower bits of array size */ + int k = (extra > 0); /* true iff needs extra argument */ + hsize = (hsize != 0) ? luaO_ceillog2(cast_uint(hsize)) + 1 : 0; + *inst = CREATE_vABCk(OP_NEWTABLE, ra, hsize, rc, k); + *(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra); +} + + +/* +** Emit a SETLIST instruction. +** 'base' is register that keeps table; +** 'nelems' is #table plus those to be stored now; +** 'tostore' is number of values (in registers 'base + 1',...) to add to +** table (or LUA_MULTRET to add up to stack top). +*/ +void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { + lua_assert(tostore != 0); + if (tostore == LUA_MULTRET) + tostore = 0; + if (nelems <= MAXARG_vC) + luaK_codevABCk(fs, OP_SETLIST, base, tostore, nelems, 0); + else { + int extra = nelems / (MAXARG_vC + 1); + nelems %= (MAXARG_vC + 1); + luaK_codevABCk(fs, OP_SETLIST, base, tostore, nelems, 1); + codeextraarg(fs, extra); + } + fs->freereg = cast_byte(base + 1); /* free registers with list values */ +} + + +/* +** return the final target of a jump (skipping jumps to jumps) +*/ +static int finaltarget (Instruction *code, int i) { + int count; + for (count = 0; count < 100; count++) { /* avoid infinite loops */ + Instruction pc = code[i]; + if (GET_OPCODE(pc) != OP_JMP) + break; + else + i += GETARG_sJ(pc) + 1; + } + return i; +} + + +/* +** Do a final pass over the code of a function, doing small peephole +** optimizations and adjustments. +*/ +#include "lopnames.h" +void luaK_finish (FuncState *fs) { + int i; + Proto *p = fs->f; + if (p->flag & PF_VATAB) /* will it use a vararg table? */ + p->flag &= cast_byte(~PF_VAHID); /* then it will not use hidden args. */ + for (i = 0; i < fs->pc; i++) { + Instruction *pc = &p->code[i]; + /* avoid "not used" warnings when assert is off (for 'onelua.c') */ + (void)luaP_isOT; (void)luaP_isIT; + lua_assert(i == 0 || luaP_isOT(*(pc - 1)) == luaP_isIT(*pc)); + switch (GET_OPCODE(*pc)) { + case OP_RETURN0: case OP_RETURN1: { + if (!(fs->needclose || (p->flag & PF_VAHID))) + break; /* no extra work */ + /* else use OP_RETURN to do the extra work */ + SET_OPCODE(*pc, OP_RETURN); + } /* FALLTHROUGH */ + case OP_RETURN: case OP_TAILCALL: { + if (fs->needclose) + SETARG_k(*pc, 1); /* signal that it needs to close */ + if (p->flag & PF_VAHID) /* does it use hidden arguments? */ + SETARG_C(*pc, p->numparams + 1); /* signal that */ + break; + } + case OP_GETVARG: { + if (p->flag & PF_VATAB) /* function has a vararg table? */ + SET_OPCODE(*pc, OP_GETTABLE); /* must get vararg there */ + break; + } + case OP_VARARG: { + if (p->flag & PF_VATAB) /* function has a vararg table? */ + SETARG_k(*pc, 1); /* must get vararg there */ + break; + } + case OP_JMP: { /* to optimize jumps to jumps */ + int target = finaltarget(p->code, i); + fixjump(fs, i, target); /* jump directly to final target */ + break; + } + default: break; + } + } +} + +``` + +`Lua/lcode.h`: + +```h +/* +** $Id: lcode.h $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lcode_h +#define lcode_h + +#include "llex.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" + + +/* +** Marks the end of a patch list. It is an invalid value both as an absolute +** address, and as a list link (would link an element to itself). +*/ +#define NO_JUMP (-1) + + +/* +** grep "ORDER OPR" if you change these enums (ORDER OP) +*/ +typedef enum BinOpr { + /* arithmetic operators */ + OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW, + OPR_DIV, OPR_IDIV, + /* bitwise operators */ + OPR_BAND, OPR_BOR, OPR_BXOR, + OPR_SHL, OPR_SHR, + /* string operator */ + OPR_CONCAT, + /* comparison operators */ + OPR_EQ, OPR_LT, OPR_LE, + OPR_NE, OPR_GT, OPR_GE, + /* logical operators */ + OPR_AND, OPR_OR, + OPR_NOBINOPR +} BinOpr; + + +/* true if operation is foldable (that is, it is arithmetic or bitwise) */ +#define foldbinop(op) ((op) <= OPR_SHR) + + +#define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0) + + +typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; + + +/* get (pointer to) instruction of given 'expdesc' */ +#define getinstruction(fs,e) ((fs)->f->code[(e)->u.info]) + + +#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) + +#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t) + +LUAI_FUNC int luaK_code (FuncState *fs, Instruction i); +LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, int Bx); +LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, int B, int C, + int k); +LUAI_FUNC int luaK_codevABCk (FuncState *fs, OpCode o, int A, int B, int C, + int k); +LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v); +LUAI_FUNC void luaK_fixline (FuncState *fs, int line); +LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); +LUAI_FUNC void luaK_codecheckglobal (FuncState *fs, expdesc *var, int k, + int line); +LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); +LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); +LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n); +LUAI_FUNC void luaK_vapar2local (FuncState *fs, expdesc *var); +LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); +LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); +LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); +LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); +LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_jump (FuncState *fs); +LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); +LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); +LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); +LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); +LUAI_FUNC int luaK_getlabel (FuncState *fs); +LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line); +LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); +LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, + expdesc *v2, int line); +LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc, + int ra, int asize, int hsize); +LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); +LUAI_FUNC void luaK_finish (FuncState *fs); +LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *fmt, ...); + + +#endif + +``` + +`Lua/lcorolib.c`: + +```c +/* +** $Id: lcorolib.c $ +** Coroutine Library +** See Copyright Notice in lua.h +*/ + +#define lcorolib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include <stdlib.h> + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "llimits.h" + + +static lua_State *getco (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argexpected(L, co, 1, "thread"); + return co; +} + + +/* +** Resumes a coroutine. Returns the number of results for non-error +** cases or -1 for errors. +*/ +static int auxresume (lua_State *L, lua_State *co, int narg) { + int status, nres; + if (l_unlikely(!lua_checkstack(co, narg))) { + lua_pushliteral(L, "too many arguments to resume"); + return -1; /* error flag */ + } + lua_xmove(L, co, narg); + status = lua_resume(co, L, narg, &nres); + if (l_likely(status == LUA_OK || status == LUA_YIELD)) { + if (l_unlikely(!lua_checkstack(L, nres + 1))) { + lua_pop(co, nres); /* remove results anyway */ + lua_pushliteral(L, "too many results to resume"); + return -1; /* error flag */ + } + lua_xmove(co, L, nres); /* move yielded values */ + return nres; + } + else { + lua_xmove(co, L, 1); /* move error message */ + return -1; /* error flag */ + } +} + + +static int luaB_coresume (lua_State *L) { + lua_State *co = getco(L); + int r; + r = auxresume(L, co, lua_gettop(L) - 1); + if (l_unlikely(r < 0)) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; /* return false + error message */ + } + else { + lua_pushboolean(L, 1); + lua_insert(L, -(r + 1)); + return r + 1; /* return true + 'resume' returns */ + } +} + + +static int luaB_auxwrap (lua_State *L) { + lua_State *co = lua_tothread(L, lua_upvalueindex(1)); + int r = auxresume(L, co, lua_gettop(L)); + if (l_unlikely(r < 0)) { /* error? */ + int stat = lua_status(co); + if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ + stat = lua_closethread(co, L); /* close its tbc variables */ + lua_assert(stat != LUA_OK); + lua_xmove(co, L, 1); /* move error message to the caller */ + } + if (stat != LUA_ERRMEM && /* not a memory error and ... */ + lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */ + luaL_where(L, 1); /* add extra info, if available */ + lua_insert(L, -2); + lua_concat(L, 2); + } + return lua_error(L); /* propagate error */ + } + return r; +} + + +static int luaB_cocreate (lua_State *L) { + lua_State *NL; + luaL_checktype(L, 1, LUA_TFUNCTION); + NL = lua_newthread(L); + lua_pushvalue(L, 1); /* move function to top */ + lua_xmove(L, NL, 1); /* move function from L to NL */ + return 1; +} + + +static int luaB_cowrap (lua_State *L) { + luaB_cocreate(L); + lua_pushcclosure(L, luaB_auxwrap, 1); + return 1; +} + + +static int luaB_yield (lua_State *L) { + return lua_yield(L, lua_gettop(L)); +} + + +#define COS_RUN 0 +#define COS_DEAD 1 +#define COS_YIELD 2 +#define COS_NORM 3 + + +static const char *const statname[] = + {"running", "dead", "suspended", "normal"}; + + +static int auxstatus (lua_State *L, lua_State *co) { + if (L == co) return COS_RUN; + else { + switch (lua_status(co)) { + case LUA_YIELD: + return COS_YIELD; + case LUA_OK: { + lua_Debug ar; + if (lua_getstack(co, 0, &ar)) /* does it have frames? */ + return COS_NORM; /* it is running */ + else if (lua_gettop(co) == 0) + return COS_DEAD; + else + return COS_YIELD; /* initial state */ + } + default: /* some error occurred */ + return COS_DEAD; + } + } +} + + +static int luaB_costatus (lua_State *L) { + lua_State *co = getco(L); + lua_pushstring(L, statname[auxstatus(L, co)]); + return 1; +} + + +static lua_State *getoptco (lua_State *L) { + return (lua_isnone(L, 1) ? L : getco(L)); +} + + +static int luaB_yieldable (lua_State *L) { + lua_State *co = getoptco(L); + lua_pushboolean(L, lua_isyieldable(co)); + return 1; +} + + +static int luaB_corunning (lua_State *L) { + int ismain = lua_pushthread(L); + lua_pushboolean(L, ismain); + return 2; +} + + +static int luaB_close (lua_State *L) { + lua_State *co = getoptco(L); + int status = auxstatus(L, co); + switch (status) { + case COS_DEAD: case COS_YIELD: { + status = lua_closethread(co, L); + if (status == LUA_OK) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushboolean(L, 0); + lua_xmove(co, L, 1); /* move error message */ + return 2; + } + } + case COS_NORM: + return luaL_error(L, "cannot close a %s coroutine", statname[status]); + case COS_RUN: + lua_geti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD); /* get main */ + if (lua_tothread(L, -1) == co) + return luaL_error(L, "cannot close main thread"); + lua_closethread(co, L); /* close itself */ + /* previous call does not return *//* FALLTHROUGH */ + default: + lua_assert(0); + return 0; + } +} + + +static const luaL_Reg co_funcs[] = { + {"create", luaB_cocreate}, + {"resume", luaB_coresume}, + {"running", luaB_corunning}, + {"status", luaB_costatus}, + {"wrap", luaB_cowrap}, + {"yield", luaB_yield}, + {"isyieldable", luaB_yieldable}, + {"close", luaB_close}, + {NULL, NULL} +}; + + + +LUAMOD_API int luaopen_coroutine (lua_State *L) { + luaL_newlib(L, co_funcs); + return 1; +} + + +``` + +`Lua/lctype.c`: + +```c +/* +** $Id: lctype.c $ +** 'ctype' functions for Lua +** See Copyright Notice in lua.h +*/ + +#define lctype_c +#define LUA_CORE + +#include "lprefix.h" + + +#include "lctype.h" + +#if !LUA_USE_CTYPE /* { */ + +#include <limits.h> + + +#if defined (LUA_UCID) /* accept UniCode IDentifiers? */ +/* consider all non-ASCII codepoints to be alphabetic */ +#define NONA 0x01 +#else +#define NONA 0x00 /* default */ +#endif + + +LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = { + 0x00, /* EOZ */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */ + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */ + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */ + 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */ + 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05, + 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */ + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */ + 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + 0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */ + NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, + NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +#endif /* } */ + +``` + +`Lua/lctype.h`: + +```h +/* +** $Id: lctype.h $ +** 'ctype' functions for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lctype_h +#define lctype_h + +#include "lua.h" + + +/* +** WARNING: the functions defined here do not necessarily correspond +** to the similar functions in the standard C ctype.h. They are +** optimized for the specific needs of Lua. +*/ + +#if !defined(LUA_USE_CTYPE) + +#if 'A' == 65 && '0' == 48 +/* ASCII case: can use its own tables; faster and fixed */ +#define LUA_USE_CTYPE 0 +#else +/* must use standard C ctype */ +#define LUA_USE_CTYPE 1 +#endif + +#endif + + +#if !LUA_USE_CTYPE /* { */ + +#include <limits.h> + +#include "llimits.h" + + +#define ALPHABIT 0 +#define DIGITBIT 1 +#define PRINTBIT 2 +#define SPACEBIT 3 +#define XDIGITBIT 4 + + +#define MASK(B) (1 << (B)) + + +/* +** add 1 to char to allow index -1 (EOZ) +*/ +#define testprop(c,p) (luai_ctype_[(c)+1] & (p)) + +/* +** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_' +*/ +#define lislalpha(c) testprop(c, MASK(ALPHABIT)) +#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT))) +#define lisdigit(c) testprop(c, MASK(DIGITBIT)) +#define lisspace(c) testprop(c, MASK(SPACEBIT)) +#define lisprint(c) testprop(c, MASK(PRINTBIT)) +#define lisxdigit(c) testprop(c, MASK(XDIGITBIT)) + + +/* +** In ASCII, this 'ltolower' is correct for alphabetic characters and +** for '.'. That is enough for Lua needs. ('check_exp' ensures that +** the character either is an upper-case letter or is unchanged by +** the transformation, which holds for lower-case letters and '.'.) +*/ +#define ltolower(c) \ + check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \ + (c) | ('A' ^ 'a')) + + +/* one entry for each character and for -1 (EOZ) */ +LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];) + + +#else /* }{ */ + +/* +** use standard C ctypes +*/ + +#include <ctype.h> + + +#define lislalpha(c) (isalpha(c) || (c) == '_') +#define lislalnum(c) (isalnum(c) || (c) == '_') +#define lisdigit(c) (isdigit(c)) +#define lisspace(c) (isspace(c)) +#define lisprint(c) (isprint(c)) +#define lisxdigit(c) (isxdigit(c)) + +#define ltolower(c) (tolower(c)) + +#endif /* } */ + +#endif + + +``` + +`Lua/ldblib.c`: + +```c +/* +** $Id: ldblib.c $ +** Interface from Lua to its debug API +** See Copyright Notice in lua.h +*/ + +#define ldblib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "llimits.h" + + +/* +** The hook table at registry[HOOKKEY] maps threads to their current +** hook function. +*/ +static const char *const HOOKKEY = "_HOOKKEY"; + + +/* +** If L1 != L, L1 can be in any state, and therefore there are no +** guarantees about its stack space; any push in L1 must be +** checked. +*/ +static void checkstack (lua_State *L, lua_State *L1, int n) { + if (l_unlikely(L != L1 && !lua_checkstack(L1, n))) + luaL_error(L, "stack overflow"); +} + + +static int db_getregistry (lua_State *L) { + lua_pushvalue(L, LUA_REGISTRYINDEX); + return 1; +} + + +static int db_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); /* no metatable */ + } + return 1; +} + + +static int db_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; /* return 1st argument */ +} + + +static int db_getuservalue (lua_State *L) { + int n = (int)luaL_optinteger(L, 2, 1); + if (lua_type(L, 1) != LUA_TUSERDATA) + luaL_pushfail(L); + else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) { + lua_pushboolean(L, 1); + return 2; + } + return 1; +} + + +static int db_setuservalue (lua_State *L) { + int n = (int)luaL_optinteger(L, 3, 1); + luaL_checktype(L, 1, LUA_TUSERDATA); + luaL_checkany(L, 2); + lua_settop(L, 2); + if (!lua_setiuservalue(L, 1, n)) + luaL_pushfail(L); + return 1; +} + + +/* +** Auxiliary function used by several library functions: check for +** an optional thread as function's first argument and set 'arg' with +** 1 if this argument is present (so that functions can skip it to +** access their other arguments) +*/ +static lua_State *getthread (lua_State *L, int *arg) { + if (lua_isthread(L, 1)) { + *arg = 1; + return lua_tothread(L, 1); + } + else { + *arg = 0; + return L; /* function will operate over current thread */ + } +} + + +/* +** Variations of 'lua_settable', used by 'db_getinfo' to put results +** from 'lua_getinfo' into result table. Key is always a string; +** value can be a string, an int, or a boolean. +*/ +static void settabss (lua_State *L, const char *k, const char *v) { + lua_pushstring(L, v); + lua_setfield(L, -2, k); +} + +static void settabsi (lua_State *L, const char *k, int v) { + lua_pushinteger(L, v); + lua_setfield(L, -2, k); +} + +static void settabsb (lua_State *L, const char *k, int v) { + lua_pushboolean(L, v); + lua_setfield(L, -2, k); +} + + +/* +** In function 'db_getinfo', the call to 'lua_getinfo' may push +** results on the stack; later it creates the result table to put +** these objects. Function 'treatstackoption' puts the result from +** 'lua_getinfo' on top of the result table so that it can call +** 'lua_setfield'. +*/ +static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { + if (L == L1) + lua_rotate(L, -2, 1); /* exchange object and table */ + else + lua_xmove(L1, L, 1); /* move object to the "main" stack */ + lua_setfield(L, -2, fname); /* put object into table */ +} + + +/* +** Calls 'lua_getinfo' and collects all results in a new table. +** L1 needs stack space for an optional input (function) plus +** two optional outputs (function and line table) from function +** 'lua_getinfo'. +*/ +static int db_getinfo (lua_State *L) { + lua_Debug ar; + int arg; + lua_State *L1 = getthread(L, &arg); + const char *options = luaL_optstring(L, arg+2, "flnSrtu"); + checkstack(L, L1, 3); + luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'"); + if (lua_isfunction(L, arg + 1)) { /* info about a function? */ + options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */ + lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */ + lua_xmove(L, L1, 1); + } + else { /* stack level */ + if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) { + luaL_pushfail(L); /* level out of range */ + return 1; + } + } + if (!lua_getinfo(L1, options, &ar)) + return luaL_argerror(L, arg+2, "invalid option"); + lua_newtable(L); /* table to collect results */ + if (strchr(options, 'S')) { + lua_pushlstring(L, ar.source, ar.srclen); + lua_setfield(L, -2, "source"); + settabss(L, "short_src", ar.short_src); + settabsi(L, "linedefined", ar.linedefined); + settabsi(L, "lastlinedefined", ar.lastlinedefined); + settabss(L, "what", ar.what); + } + if (strchr(options, 'l')) + settabsi(L, "currentline", ar.currentline); + if (strchr(options, 'u')) { + settabsi(L, "nups", ar.nups); + settabsi(L, "nparams", ar.nparams); + settabsb(L, "isvararg", ar.isvararg); + } + if (strchr(options, 'n')) { + settabss(L, "name", ar.name); + settabss(L, "namewhat", ar.namewhat); + } + if (strchr(options, 'r')) { + settabsi(L, "ftransfer", ar.ftransfer); + settabsi(L, "ntransfer", ar.ntransfer); + } + if (strchr(options, 't')) { + settabsb(L, "istailcall", ar.istailcall); + settabsi(L, "extraargs", ar.extraargs); + } + if (strchr(options, 'L')) + treatstackoption(L, L1, "activelines"); + if (strchr(options, 'f')) + treatstackoption(L, L1, "func"); + return 1; /* return table */ +} + + +static int db_getlocal (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */ + if (lua_isfunction(L, arg + 1)) { /* function argument? */ + lua_pushvalue(L, arg + 1); /* push function */ + lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */ + return 1; /* return only name (there is no value) */ + } + else { /* stack-level argument */ + lua_Debug ar; + const char *name; + int level = (int)luaL_checkinteger(L, arg + 1); + if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + checkstack(L, L1, 1); + name = lua_getlocal(L1, &ar, nvar); + if (name) { + lua_xmove(L1, L, 1); /* move local value */ + lua_pushstring(L, name); /* push name */ + lua_rotate(L, -2, 1); /* re-order */ + return 2; + } + else { + luaL_pushfail(L); /* no name (nor value) */ + return 1; + } + } +} + + +static int db_setlocal (lua_State *L) { + int arg; + const char *name; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + int level = (int)luaL_checkinteger(L, arg + 1); + int nvar = (int)luaL_checkinteger(L, arg + 2); + if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + luaL_checkany(L, arg+3); + lua_settop(L, arg+3); + checkstack(L, L1, 1); + lua_xmove(L, L1, 1); + name = lua_setlocal(L1, &ar, nvar); + if (name == NULL) + lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */ + lua_pushstring(L, name); + return 1; +} + + +/* +** get (if 'get' is true) or set an upvalue from a closure +*/ +static int auxupvalue (lua_State *L, int get) { + const char *name; + int n = (int)luaL_checkinteger(L, 2); /* upvalue index */ + luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */ + name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); + if (name == NULL) return 0; + lua_pushstring(L, name); + lua_insert(L, -(get+1)); /* no-op if get is false */ + return get + 1; +} + + +static int db_getupvalue (lua_State *L) { + return auxupvalue(L, 1); +} + + +static int db_setupvalue (lua_State *L) { + luaL_checkany(L, 3); + return auxupvalue(L, 0); +} + + +/* +** Check whether a given upvalue from a given closure exists and +** returns its index +*/ +static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) { + void *id; + int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */ + luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */ + id = lua_upvalueid(L, argf, nup); + if (pnup) { + luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index"); + *pnup = nup; + } + return id; +} + + +static int db_upvalueid (lua_State *L) { + void *id = checkupval(L, 1, 2, NULL); + if (id != NULL) + lua_pushlightuserdata(L, id); + else + luaL_pushfail(L); + return 1; +} + + +static int db_upvaluejoin (lua_State *L) { + int n1, n2; + checkupval(L, 1, 2, &n1); + checkupval(L, 3, 4, &n2); + luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected"); + luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected"); + lua_upvaluejoin(L, 1, n1, 3, n2); + return 0; +} + + +/* +** Call hook function registered at hook table for the current +** thread (if there is one) +*/ +static void hookf (lua_State *L, lua_Debug *ar) { + static const char *const hooknames[] = + {"call", "return", "line", "count", "tail call"}; + lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY); + lua_pushthread(L); + if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */ + lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */ + if (ar->currentline >= 0) + lua_pushinteger(L, ar->currentline); /* push current line */ + else lua_pushnil(L); + lua_assert(lua_getinfo(L, "lS", ar)); + lua_call(L, 2, 0); /* call hook function */ + } +} + + +/* +** Convert a string mask (for 'sethook') into a bit mask +*/ +static int makemask (const char *smask, int count) { + int mask = 0; + if (strchr(smask, 'c')) mask |= LUA_MASKCALL; + if (strchr(smask, 'r')) mask |= LUA_MASKRET; + if (strchr(smask, 'l')) mask |= LUA_MASKLINE; + if (count > 0) mask |= LUA_MASKCOUNT; + return mask; +} + + +/* +** Convert a bit mask (for 'gethook') into a string mask +*/ +static char *unmakemask (int mask, char *smask) { + int i = 0; + if (mask & LUA_MASKCALL) smask[i++] = 'c'; + if (mask & LUA_MASKRET) smask[i++] = 'r'; + if (mask & LUA_MASKLINE) smask[i++] = 'l'; + smask[i] = '\0'; + return smask; +} + + +static int db_sethook (lua_State *L) { + int arg, mask, count; + lua_Hook func; + lua_State *L1 = getthread(L, &arg); + if (lua_isnoneornil(L, arg+1)) { /* no hook? */ + lua_settop(L, arg+1); + func = NULL; mask = 0; count = 0; /* turn off hooks */ + } + else { + const char *smask = luaL_checkstring(L, arg+2); + luaL_checktype(L, arg+1, LUA_TFUNCTION); + count = (int)luaL_optinteger(L, arg + 3, 0); + func = hookf; mask = makemask(smask, count); + } + if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) { + /* table just created; initialize it */ + lua_pushliteral(L, "k"); + lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */ + lua_pushvalue(L, -1); + lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */ + } + checkstack(L, L1, 1); + lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */ + lua_pushvalue(L, arg + 1); /* value (hook function) */ + lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */ + lua_sethook(L1, func, mask, count); + return 0; +} + + +static int db_gethook (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + char buff[5]; + int mask = lua_gethookmask(L1); + lua_Hook hook = lua_gethook(L1); + if (hook == NULL) { /* no hook? */ + luaL_pushfail(L); + return 1; + } + else if (hook != hookf) /* external hook? */ + lua_pushliteral(L, "external hook"); + else { /* hook table must exist */ + lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY); + checkstack(L, L1, 1); + lua_pushthread(L1); lua_xmove(L1, L, 1); + lua_rawget(L, -2); /* 1st result = hooktable[L1] */ + lua_remove(L, -2); /* remove hook table */ + } + lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */ + lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */ + return 3; +} + + +static int db_debug (lua_State *L) { + for (;;) { + char buffer[250]; + lua_writestringerror("%s", "lua_debug> "); + if (fgets(buffer, sizeof(buffer), stdin) == NULL || + strcmp(buffer, "cont\n") == 0) + return 0; + if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || + lua_pcall(L, 0, 0, 0)) + lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL)); + lua_settop(L, 0); /* remove eventual returns */ + } +} + + +static int db_traceback (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + const char *msg = lua_tostring(L, arg + 1); + if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */ + lua_pushvalue(L, arg + 1); /* return it untouched */ + else { + int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0); + luaL_traceback(L, L1, msg, level); + } + return 1; +} + + +static const luaL_Reg dblib[] = { + {"debug", db_debug}, + {"getuservalue", db_getuservalue}, + {"gethook", db_gethook}, + {"getinfo", db_getinfo}, + {"getlocal", db_getlocal}, + {"getregistry", db_getregistry}, + {"getmetatable", db_getmetatable}, + {"getupvalue", db_getupvalue}, + {"upvaluejoin", db_upvaluejoin}, + {"upvalueid", db_upvalueid}, + {"setuservalue", db_setuservalue}, + {"sethook", db_sethook}, + {"setlocal", db_setlocal}, + {"setmetatable", db_setmetatable}, + {"setupvalue", db_setupvalue}, + {"traceback", db_traceback}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_debug (lua_State *L) { + luaL_newlib(L, dblib); + return 1; +} + + +``` + +`Lua/ldebug.c`: + +```c +/* +** $Id: ldebug.c $ +** Debug Interface +** See Copyright Notice in lua.h +*/ + +#define ldebug_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <stdarg.h> +#include <stddef.h> +#include <string.h> + +#include "lua.h" + +#include "lapi.h" +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + + +#define LuaClosure(f) ((f) != NULL && (f)->c.tt == LUA_VLCL) + +static const char strlocal[] = "local"; +static const char strupval[] = "upvalue"; + +static const char *funcnamefromcall (lua_State *L, CallInfo *ci, + const char **name); + + +static int currentpc (CallInfo *ci) { + lua_assert(isLua(ci)); + return pcRel(ci->u.l.savedpc, ci_func(ci)->p); +} + + +/* +** Get a "base line" to find the line corresponding to an instruction. +** Base lines are regularly placed at MAXIWTHABS intervals, so usually +** an integer division gets the right place. When the source file has +** large sequences of empty/comment lines, it may need extra entries, +** so the original estimate needs a correction. +** If the original estimate is -1, the initial 'if' ensures that the +** 'while' will run at least once. +** The assertion that the estimate is a lower bound for the correct base +** is valid as long as the debug info has been generated with the same +** value for MAXIWTHABS or smaller. (Previous releases use a little +** smaller value.) +*/ +static int getbaseline (const Proto *f, int pc, int *basepc) { + if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) { + *basepc = -1; /* start from the beginning */ + return f->linedefined; + } + else { + int i = pc / MAXIWTHABS - 1; /* get an estimate */ + /* estimate must be a lower bound of the correct base */ + lua_assert(i < 0 || + (i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc)); + while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc) + i++; /* low estimate; adjust it */ + *basepc = f->abslineinfo[i].pc; + return f->abslineinfo[i].line; + } +} + + +/* +** Get the line corresponding to instruction 'pc' in function 'f'; +** first gets a base line and from there does the increments until +** the desired instruction. +*/ +int luaG_getfuncline (const Proto *f, int pc) { + if (f->lineinfo == NULL) /* no debug information? */ + return -1; + else { + int basepc; + int baseline = getbaseline(f, pc, &basepc); + while (basepc++ < pc) { /* walk until given instruction */ + lua_assert(f->lineinfo[basepc] != ABSLINEINFO); + baseline += f->lineinfo[basepc]; /* correct line */ + } + return baseline; + } +} + + +static int getcurrentline (CallInfo *ci) { + return luaG_getfuncline(ci_func(ci)->p, currentpc(ci)); +} + + +/* +** Set 'trap' for all active Lua frames. +** This function can be called during a signal, under "reasonable" +** assumptions. A new 'ci' is completely linked in the list before it +** becomes part of the "active" list, and we assume that pointers are +** atomic; see comment in next function. +** (A compiler doing interprocedural optimizations could, theoretically, +** reorder memory writes in such a way that the list could be +** temporarily broken while inserting a new element. We simply assume it +** has no good reasons to do that.) +*/ +static void settraps (CallInfo *ci) { + for (; ci != NULL; ci = ci->previous) + if (isLua(ci)) + ci->u.l.trap = 1; +} + + +/* +** This function can be called during a signal, under "reasonable" +** assumptions. +** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount') +** are for debug only, and it is no problem if they get arbitrary +** values (causes at most one wrong hook call). 'hookmask' is an atomic +** value. We assume that pointers are atomic too (e.g., gcc ensures that +** for all platforms where it runs). Moreover, 'hook' is always checked +** before being called (see 'luaD_hook'). +*/ +LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { + if (func == NULL || mask == 0) { /* turn off hooks? */ + mask = 0; + func = NULL; + } + L->hook = func; + L->basehookcount = count; + resethookcount(L); + L->hookmask = cast_byte(mask); + if (mask) + settraps(L->ci); /* to trace inside 'luaV_execute' */ +} + + +LUA_API lua_Hook lua_gethook (lua_State *L) { + return L->hook; +} + + +LUA_API int lua_gethookmask (lua_State *L) { + return L->hookmask; +} + + +LUA_API int lua_gethookcount (lua_State *L) { + return L->basehookcount; +} + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { + int status; + CallInfo *ci; + if (level < 0) return 0; /* invalid (negative) level */ + lua_lock(L); + for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous) + level--; + if (level == 0 && ci != &L->base_ci) { /* level found? */ + status = 1; + ar->i_ci = ci; + } + else status = 0; /* no such level */ + lua_unlock(L); + return status; +} + + +static const char *upvalname (const Proto *p, int uv) { + TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name); + if (s == NULL) return "?"; + else return getstr(s); +} + + +static const char *findvararg (CallInfo *ci, int n, StkId *pos) { + if (clLvalue(s2v(ci->func.p))->p->flag & PF_VAHID) { + int nextra = ci->u.l.nextraargs; + if (n >= -nextra) { /* 'n' is negative */ + *pos = ci->func.p - nextra - (n + 1); + return "(vararg)"; /* generic name for any vararg */ + } + } + return NULL; /* no such vararg */ +} + + +const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) { + StkId base = ci->func.p + 1; + const char *name = NULL; + if (isLua(ci)) { + if (n < 0) /* access to vararg values? */ + return findvararg(ci, n, pos); + else + name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci)); + } + if (name == NULL) { /* no 'standard' name? */ + StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p; + if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */ + /* generic name for any valid slot */ + name = isLua(ci) ? "(temporary)" : "(C temporary)"; + } + else + return NULL; /* no name */ + } + if (pos) + *pos = base + (n - 1); + return name; +} + + +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { + const char *name; + lua_lock(L); + if (ar == NULL) { /* information about non-active function? */ + if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */ + name = NULL; + else /* consider live variables at function start (parameters) */ + name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0); + } + else { /* active function; get information through 'ar' */ + StkId pos = NULL; /* to avoid warnings */ + name = luaG_findlocal(L, ar->i_ci, n, &pos); + if (name) { + setobjs2s(L, L->top.p, pos); + api_incr_top(L); + } + } + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { + StkId pos = NULL; /* to avoid warnings */ + const char *name; + lua_lock(L); + name = luaG_findlocal(L, ar->i_ci, n, &pos); + if (name) { + api_checkpop(L, 1); + setobjs2s(L, pos, L->top.p - 1); + L->top.p--; /* pop value */ + } + lua_unlock(L); + return name; +} + + +static void funcinfo (lua_Debug *ar, Closure *cl) { + if (!LuaClosure(cl)) { + ar->source = "=[C]"; + ar->srclen = LL("=[C]"); + ar->linedefined = -1; + ar->lastlinedefined = -1; + ar->what = "C"; + } + else { + const Proto *p = cl->l.p; + if (p->source) { + ar->source = getlstr(p->source, ar->srclen); + } + else { + ar->source = "=?"; + ar->srclen = LL("=?"); + } + ar->linedefined = p->linedefined; + ar->lastlinedefined = p->lastlinedefined; + ar->what = (ar->linedefined == 0) ? "main" : "Lua"; + } + luaO_chunkid(ar->short_src, ar->source, ar->srclen); +} + + +static int nextline (const Proto *p, int currentline, int pc) { + if (p->lineinfo[pc] != ABSLINEINFO) + return currentline + p->lineinfo[pc]; + else + return luaG_getfuncline(p, pc); +} + + +static void collectvalidlines (lua_State *L, Closure *f) { + if (!LuaClosure(f)) { + setnilvalue(s2v(L->top.p)); + api_incr_top(L); + } + else { + const Proto *p = f->l.p; + int currentline = p->linedefined; + Table *t = luaH_new(L); /* new table to store active lines */ + sethvalue2s(L, L->top.p, t); /* push it on stack */ + api_incr_top(L); + if (p->lineinfo != NULL) { /* proto with debug information? */ + int i; + TValue v; + setbtvalue(&v); /* boolean 'true' to be the value of all indices */ + if (!(isvararg(p))) /* regular function? */ + i = 0; /* consider all instructions */ + else { /* vararg function */ + lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP); + currentline = nextline(p, currentline, 0); + i = 1; /* skip first instruction (OP_VARARGPREP) */ + } + for (; i < p->sizelineinfo; i++) { /* for each instruction */ + currentline = nextline(p, currentline, i); /* get its line */ + luaH_setint(L, t, currentline, &v); /* table[line] = true */ + } + } + } +} + + +static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { + /* calling function is a known function? */ + if (ci != NULL && !(ci->callstatus & CIST_TAIL)) + return funcnamefromcall(L, ci->previous, name); + else return NULL; /* no way to find a name */ +} + + +static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, + Closure *f, CallInfo *ci) { + int status = 1; + for (; *what; what++) { + switch (*what) { + case 'S': { + funcinfo(ar, f); + break; + } + case 'l': { + ar->currentline = (ci && isLua(ci)) ? getcurrentline(ci) : -1; + break; + } + case 'u': { + ar->nups = (f == NULL) ? 0 : f->c.nupvalues; + if (!LuaClosure(f)) { + ar->isvararg = 1; + ar->nparams = 0; + } + else { + ar->isvararg = (isvararg(f->l.p)) ? 1 : 0; + ar->nparams = f->l.p->numparams; + } + break; + } + case 't': { + if (ci != NULL) { + ar->istailcall = !!(ci->callstatus & CIST_TAIL); + ar->extraargs = + cast_uchar((ci->callstatus & MAX_CCMT) >> CIST_CCMT); + } + else { + ar->istailcall = 0; + ar->extraargs = 0; + } + break; + } + case 'n': { + ar->namewhat = getfuncname(L, ci, &ar->name); + if (ar->namewhat == NULL) { + ar->namewhat = ""; /* not found */ + ar->name = NULL; + } + break; + } + case 'r': { + if (ci == NULL || !(ci->callstatus & CIST_HOOKED)) + ar->ftransfer = ar->ntransfer = 0; + else { + ar->ftransfer = L->transferinfo.ftransfer; + ar->ntransfer = L->transferinfo.ntransfer; + } + break; + } + case 'L': + case 'f': /* handled by lua_getinfo */ + break; + default: status = 0; /* invalid option */ + } + } + return status; +} + + +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { + int status; + Closure *cl; + CallInfo *ci; + TValue *func; + lua_lock(L); + if (*what == '>') { + ci = NULL; + func = s2v(L->top.p - 1); + api_check(L, ttisfunction(func), "function expected"); + what++; /* skip the '>' */ + L->top.p--; /* pop function */ + } + else { + ci = ar->i_ci; + func = s2v(ci->func.p); + lua_assert(ttisfunction(func)); + } + cl = ttisclosure(func) ? clvalue(func) : NULL; + status = auxgetinfo(L, what, ar, cl, ci); + if (strchr(what, 'f')) { + setobj2s(L, L->top.p, func); + api_incr_top(L); + } + if (strchr(what, 'L')) + collectvalidlines(L, cl); + lua_unlock(L); + return status; +} + + +/* +** {====================================================== +** Symbolic Execution +** ======================================================= +*/ + + +static int filterpc (int pc, int jmptarget) { + if (pc < jmptarget) /* is code conditional (inside a jump)? */ + return -1; /* cannot know who sets that register */ + else return pc; /* current position sets that register */ +} + + +/* +** Try to find last instruction before 'lastpc' that modified register 'reg'. +*/ +static int findsetreg (const Proto *p, int lastpc, int reg) { + int pc; + int setreg = -1; /* keep last instruction that changed 'reg' */ + int jmptarget = 0; /* any code before this address is conditional */ + if (testMMMode(GET_OPCODE(p->code[lastpc]))) + lastpc--; /* previous instruction was not actually executed */ + for (pc = 0; pc < lastpc; pc++) { + Instruction i = p->code[pc]; + OpCode op = GET_OPCODE(i); + int a = GETARG_A(i); + int change; /* true if current instruction changed 'reg' */ + switch (op) { + case OP_LOADNIL: { /* set registers from 'a' to 'a+b' */ + int b = GETARG_B(i); + change = (a <= reg && reg <= a + b); + break; + } + case OP_TFORCALL: { /* affect all regs above its base */ + change = (reg >= a + 2); + break; + } + case OP_CALL: + case OP_TAILCALL: { /* affect all registers above base */ + change = (reg >= a); + break; + } + case OP_JMP: { /* doesn't change registers, but changes 'jmptarget' */ + int b = GETARG_sJ(i); + int dest = pc + 1 + b; + /* jump does not skip 'lastpc' and is larger than current one? */ + if (dest <= lastpc && dest > jmptarget) + jmptarget = dest; /* update 'jmptarget' */ + change = 0; + break; + } + default: /* any instruction that sets A */ + change = (testAMode(op) && reg == a); + break; + } + if (change) + setreg = filterpc(pc, jmptarget); + } + return setreg; +} + + +/* +** Find a "name" for the constant 'c'. +*/ +static const char *kname (const Proto *p, int index, const char **name) { + TValue *kvalue = &p->k[index]; + if (ttisstring(kvalue)) { + *name = getstr(tsvalue(kvalue)); + return "constant"; + } + else { + *name = "?"; + return NULL; + } +} + + +static const char *basicgetobjname (const Proto *p, int *ppc, int reg, + const char **name) { + int pc = *ppc; + *name = luaF_getlocalname(p, reg + 1, pc); + if (*name) /* is a local? */ + return strlocal; + /* else try symbolic execution */ + *ppc = pc = findsetreg(p, pc, reg); + if (pc != -1) { /* could find instruction? */ + Instruction i = p->code[pc]; + OpCode op = GET_OPCODE(i); + switch (op) { + case OP_MOVE: { + int b = GETARG_B(i); /* move from 'b' to 'a' */ + if (b < GETARG_A(i)) + return basicgetobjname(p, ppc, b, name); /* get name for 'b' */ + break; + } + case OP_GETUPVAL: { + *name = upvalname(p, GETARG_B(i)); + return strupval; + } + case OP_LOADK: return kname(p, GETARG_Bx(i), name); + case OP_LOADKX: return kname(p, GETARG_Ax(p->code[pc + 1]), name); + default: break; + } + } + return NULL; /* could not find reasonable name */ +} + + +/* +** Find a "name" for the register 'c'. +*/ +static void rname (const Proto *p, int pc, int c, const char **name) { + const char *what = basicgetobjname(p, &pc, c, name); /* search for 'c' */ + if (!(what && *what == 'c')) /* did not find a constant name? */ + *name = "?"; +} + + +/* +** Check whether table being indexed by instruction 'i' is the +** environment '_ENV' +*/ +static const char *isEnv (const Proto *p, int pc, Instruction i, int isup) { + int t = GETARG_B(i); /* table index */ + const char *name; /* name of indexed variable */ + if (isup) /* is 't' an upvalue? */ + name = upvalname(p, t); + else { /* 't' is a register */ + const char *what = basicgetobjname(p, &pc, t, &name); + /* 'name' must be the name of a local variable (at the current + level or an upvalue) */ + if (what != strlocal && what != strupval) + name = NULL; /* cannot be the variable _ENV */ + } + return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field"; +} + + +/* +** Extend 'basicgetobjname' to handle table accesses +*/ +static const char *getobjname (const Proto *p, int lastpc, int reg, + const char **name) { + const char *kind = basicgetobjname(p, &lastpc, reg, name); + if (kind != NULL) + return kind; + else if (lastpc != -1) { /* could find instruction? */ + Instruction i = p->code[lastpc]; + OpCode op = GET_OPCODE(i); + switch (op) { + case OP_GETTABUP: { + int k = GETARG_C(i); /* key index */ + kname(p, k, name); + return isEnv(p, lastpc, i, 1); + } + case OP_GETTABLE: { + int k = GETARG_C(i); /* key index */ + rname(p, lastpc, k, name); + return isEnv(p, lastpc, i, 0); + } + case OP_GETI: { + *name = "integer index"; + return "field"; + } + case OP_GETFIELD: { + int k = GETARG_C(i); /* key index */ + kname(p, k, name); + return isEnv(p, lastpc, i, 0); + } + case OP_SELF: { + int k = GETARG_C(i); /* key index */ + kname(p, k, name); + return "method"; + } + default: break; /* go through to return NULL */ + } + } + return NULL; /* could not find reasonable name */ +} + + +/* +** Try to find a name for a function based on the code that called it. +** (Only works when function was called by a Lua function.) +** Returns what the name is (e.g., "for iterator", "method", +** "metamethod") and sets '*name' to point to the name. +*/ +static const char *funcnamefromcode (lua_State *L, const Proto *p, + int pc, const char **name) { + TMS tm = (TMS)0; /* (initial value avoids warnings) */ + Instruction i = p->code[pc]; /* calling instruction */ + switch (GET_OPCODE(i)) { + case OP_CALL: + case OP_TAILCALL: + return getobjname(p, pc, GETARG_A(i), name); /* get function name */ + case OP_TFORCALL: { /* for iterator */ + *name = "for iterator"; + return "for iterator"; + } + /* other instructions can do calls through metamethods */ + case OP_SELF: case OP_GETTABUP: case OP_GETTABLE: + case OP_GETI: case OP_GETFIELD: + tm = TM_INDEX; + break; + case OP_SETTABUP: case OP_SETTABLE: case OP_SETI: case OP_SETFIELD: + tm = TM_NEWINDEX; + break; + case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: { + tm = cast(TMS, GETARG_C(i)); + break; + } + case OP_UNM: tm = TM_UNM; break; + case OP_BNOT: tm = TM_BNOT; break; + case OP_LEN: tm = TM_LEN; break; + case OP_CONCAT: tm = TM_CONCAT; break; + case OP_EQ: tm = TM_EQ; break; + /* no cases for OP_EQI and OP_EQK, as they don't call metamethods */ + case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break; + case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break; + case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break; + default: + return NULL; /* cannot find a reasonable name */ + } + *name = getshrstr(G(L)->tmname[tm]) + 2; + return "metamethod"; +} + + +/* +** Try to find a name for a function based on how it was called. +*/ +static const char *funcnamefromcall (lua_State *L, CallInfo *ci, + const char **name) { + if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */ + *name = "?"; + return "hook"; + } + else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */ + *name = "__gc"; + return "metamethod"; /* report it as such */ + } + else if (isLua(ci)) + return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name); + else + return NULL; +} + +/* }====================================================== */ + + + +/* +** Check whether pointer 'o' points to some value in the stack frame of +** the current function and, if so, returns its index. Because 'o' may +** not point to a value in this stack, we cannot compare it with the +** region boundaries (undefined behavior in ISO C). +*/ +static int instack (CallInfo *ci, const TValue *o) { + int pos; + StkId base = ci->func.p + 1; + for (pos = 0; base + pos < ci->top.p; pos++) { + if (o == s2v(base + pos)) + return pos; + } + return -1; /* not found */ +} + + +/* +** Checks whether value 'o' came from an upvalue. (That can only happen +** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on +** upvalues.) +*/ +static const char *getupvalname (CallInfo *ci, const TValue *o, + const char **name) { + LClosure *c = ci_func(ci); + int i; + for (i = 0; i < c->nupvalues; i++) { + if (c->upvals[i]->v.p == o) { + *name = upvalname(c->p, i); + return strupval; + } + } + return NULL; +} + + +static const char *formatvarinfo (lua_State *L, const char *kind, + const char *name) { + if (kind == NULL) + return ""; /* no information */ + else + return luaO_pushfstring(L, " (%s '%s')", kind, name); +} + +/* +** Build a string with a "description" for the value 'o', such as +** "variable 'x'" or "upvalue 'y'". +*/ +static const char *varinfo (lua_State *L, const TValue *o) { + CallInfo *ci = L->ci; + const char *name = NULL; /* to avoid warnings */ + const char *kind = NULL; + if (isLua(ci)) { + kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */ + if (!kind) { /* not an upvalue? */ + int reg = instack(ci, o); /* try a register */ + if (reg >= 0) /* is 'o' a register? */ + kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name); + } + } + return formatvarinfo(L, kind, name); +} + + +/* +** Raise a type error +*/ +static l_noret typeerror (lua_State *L, const TValue *o, const char *op, + const char *extra) { + const char *t = luaT_objtypename(L, o); + luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra); +} + + +/* +** Raise a type error with "standard" information about the faulty +** object 'o' (using 'varinfo'). +*/ +l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) { + typeerror(L, o, op, varinfo(L, o)); +} + + +/* +** Raise an error for calling a non-callable object. Try to find a name +** for the object based on how it was called ('funcnamefromcall'); if it +** cannot get a name there, try 'varinfo'. +*/ +l_noret luaG_callerror (lua_State *L, const TValue *o) { + CallInfo *ci = L->ci; + const char *name = NULL; /* to avoid warnings */ + const char *kind = funcnamefromcall(L, ci, &name); + const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o); + typeerror(L, o, "call", extra); +} + + +l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) { + luaG_runerror(L, "bad 'for' %s (number expected, got %s)", + what, luaT_objtypename(L, o)); +} + + +l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) { + if (ttisstring(p1) || cvt2str(p1)) p1 = p2; + luaG_typeerror(L, p1, "concatenate"); +} + + +l_noret luaG_opinterror (lua_State *L, const TValue *p1, + const TValue *p2, const char *msg) { + if (!ttisnumber(p1)) /* first operand is wrong? */ + p2 = p1; /* now second is wrong */ + luaG_typeerror(L, p2, msg); +} + + +/* +** Error when both values are convertible to numbers, but not to integers +*/ +l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) { + lua_Integer temp; + if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I)) + p2 = p1; + luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2)); +} + + +l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { + const char *t1 = luaT_objtypename(L, p1); + const char *t2 = luaT_objtypename(L, p2); + if (strcmp(t1, t2) == 0) + luaG_runerror(L, "attempt to compare two %s values", t1); + else + luaG_runerror(L, "attempt to compare %s with %s", t1, t2); +} + + +l_noret luaG_errnnil (lua_State *L, LClosure *cl, int k) { + const char *globalname = "?"; /* default name if k == 0 */ + if (k > 0) + kname(cl->p, k - 1, &globalname); + luaG_runerror(L, "global '%s' already defined", globalname); +} + + +/* add src:line information to 'msg' */ +const char *luaG_addinfo (lua_State *L, const char *msg, TString *src, + int line) { + if (src == NULL) /* no debug information? */ + return luaO_pushfstring(L, "?:?: %s", msg); + else { + char buff[LUA_IDSIZE]; + size_t idlen; + const char *id = getlstr(src, idlen); + luaO_chunkid(buff, id, idlen); + return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); + } +} + + +l_noret luaG_errormsg (lua_State *L) { + if (L->errfunc != 0) { /* is there an error handling function? */ + StkId errfunc = restorestack(L, L->errfunc); + lua_assert(ttisfunction(s2v(errfunc))); + setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */ + setobjs2s(L, L->top.p - 1, errfunc); /* push function */ + L->top.p++; /* assume EXTRA_STACK */ + luaD_callnoyield(L, L->top.p - 2, 1); /* call it */ + } + if (ttisnil(s2v(L->top.p - 1))) { /* error object is nil? */ + /* change it to a proper message */ + setsvalue2s(L, L->top.p - 1, luaS_newliteral(L, "<no error object>")); + } + luaD_throw(L, LUA_ERRRUN); +} + + +l_noret luaG_runerror (lua_State *L, const char *fmt, ...) { + CallInfo *ci = L->ci; + const char *msg; + va_list argp; + luaC_checkGC(L); /* error message uses memory */ + pushvfstring(L, argp, fmt, msg); + if (isLua(ci)) { /* Lua function? */ + /* add source:line information */ + luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci)); + setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */ + L->top.p--; + } + luaG_errormsg(L); +} + + +/* +** Check whether new instruction 'newpc' is in a different line from +** previous instruction 'oldpc'. More often than not, 'newpc' is only +** one or a few instructions after 'oldpc' (it must be after, see +** caller), so try to avoid calling 'luaG_getfuncline'. If they are +** too far apart, there is a good chance of a ABSLINEINFO in the way, +** so it goes directly to 'luaG_getfuncline'. +*/ +static int changedline (const Proto *p, int oldpc, int newpc) { + if (p->lineinfo == NULL) /* no debug information? */ + return 0; + if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */ + int delta = 0; /* line difference */ + int pc = oldpc; + for (;;) { + int lineinfo = p->lineinfo[++pc]; + if (lineinfo == ABSLINEINFO) + break; /* cannot compute delta; fall through */ + delta += lineinfo; + if (pc == newpc) + return (delta != 0); /* delta computed successfully */ + } + } + /* either instructions are too far apart or there is an absolute line + info in the way; compute line difference explicitly */ + return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc)); +} + + +/* +** Traces Lua calls. If code is running the first instruction of a function, +** and function is not vararg, and it is not coming from an yield, +** calls 'luaD_hookcall'. (Vararg functions will call 'luaD_hookcall' +** after adjusting its variable arguments; otherwise, they could call +** a line/count hook before the call hook. Functions coming from +** an yield already called 'luaD_hookcall' before yielding.) +*/ +int luaG_tracecall (lua_State *L) { + CallInfo *ci = L->ci; + Proto *p = ci_func(ci)->p; + ci->u.l.trap = 1; /* ensure hooks will be checked */ + if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */ + if (isvararg(p)) + return 0; /* hooks will start at VARARGPREP instruction */ + else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yielded? */ + luaD_hookcall(L, ci); /* check 'call' hook */ + } + return 1; /* keep 'trap' on */ +} + + +/* +** Traces the execution of a Lua function. Called before the execution +** of each opcode, when debug is on. 'L->oldpc' stores the last +** instruction traced, to detect line changes. When entering a new +** function, 'npci' will be zero and will test as a new line whatever +** the value of 'oldpc'. Some exceptional conditions may return to +** a function without setting 'oldpc'. In that case, 'oldpc' may be +** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc' +** at most causes an extra call to a line hook.) +** This function is not "Protected" when called, so it should correct +** 'L->top.p' before calling anything that can run the GC. +*/ +int luaG_traceexec (lua_State *L, const Instruction *pc) { + CallInfo *ci = L->ci; + lu_byte mask = cast_byte(L->hookmask); + const Proto *p = ci_func(ci)->p; + int counthook; + if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */ + ci->u.l.trap = 0; /* don't need to stop again */ + return 0; /* turn off 'trap' */ + } + pc++; /* reference is always next instruction */ + ci->u.l.savedpc = pc; /* save 'pc' */ + counthook = (mask & LUA_MASKCOUNT) && (--L->hookcount == 0); + if (counthook) + resethookcount(L); /* reset count */ + else if (!(mask & LUA_MASKLINE)) + return 1; /* no line hook and count != 0; nothing to be done now */ + if (ci->callstatus & CIST_HOOKYIELD) { /* hook yielded last time? */ + ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */ + return 1; /* do not call hook again (VM yielded, so it did not move) */ + } + if (!luaP_isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */ + L->top.p = ci->top.p; /* correct top */ + if (counthook) + luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */ + if (mask & LUA_MASKLINE) { + /* 'L->oldpc' may be invalid; use zero in this case */ + int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0; + int npci = pcRel(pc, p); + if (npci <= oldpc || /* call hook when jump back (loop), */ + changedline(p, oldpc, npci)) { /* or when enter new line */ + int newline = luaG_getfuncline(p, npci); + luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */ + } + L->oldpc = npci; /* 'pc' of last call to line hook */ + } + if (L->status == LUA_YIELD) { /* did hook yield? */ + if (counthook) + L->hookcount = 1; /* undo decrement to zero */ + ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */ + luaD_throw(L, LUA_YIELD); + } + return 1; /* keep 'trap' on */ +} + + +``` + +`Lua/ldebug.h`: + +```h +/* +** $Id: ldebug.h $ +** Auxiliary functions from Debug Interface module +** See Copyright Notice in lua.h +*/ + +#ifndef ldebug_h +#define ldebug_h + + +#include "lstate.h" + + +#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1) + + +/* Active Lua function (given call info) */ +#define ci_func(ci) (clLvalue(s2v((ci)->func.p))) + + +#define resethookcount(L) (L->hookcount = L->basehookcount) + +/* +** mark for entries in 'lineinfo' array that has absolute information in +** 'abslineinfo' array +*/ +#define ABSLINEINFO (-0x80) + + +/* +** MAXimum number of successive Instructions WiTHout ABSolute line +** information. (A power of two allows fast divisions.) +*/ +#if !defined(MAXIWTHABS) +#define MAXIWTHABS 128 +#endif + + +LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc); +LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, + StkId *pos); +LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o, + const char *opname); +LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o); +LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o, + const char *what); +LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1, + const TValue *p2, + const char *msg); +LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC l_noret luaG_errnnil (lua_State *L, LClosure *cl, int k); +LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...); +LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg, + TString *src, int line); +LUAI_FUNC l_noret luaG_errormsg (lua_State *L); +LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc); +LUAI_FUNC int luaG_tracecall (lua_State *L); + + +#endif + +``` + +`Lua/ldo.c`: + +```c +/* +** $Id: ldo.c $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + +#define ldo_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <setjmp.h> +#include <stdlib.h> +#include <string.h> + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" +#include "lzio.h" + + + +#define errorstatus(s) ((s) > LUA_YIELD) + + +/* +** these macros allow user-specific actions when a thread is +** resumed/yielded. +*/ +#if !defined(luai_userstateresume) +#define luai_userstateresume(L,n) ((void)L) +#endif + +#if !defined(luai_userstateyield) +#define luai_userstateyield(L,n) ((void)L) +#endif + + +/* +** {====================================================== +** Error-recovery functions +** ======================================================= +*/ + +/* chained list of long jump buffers */ +typedef struct lua_longjmp { + struct lua_longjmp *previous; + jmp_buf b; + volatile TStatus status; /* error code */ +} lua_longjmp; + + +/* +** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By +** default, Lua handles errors with exceptions when compiling as +** C++ code, with _longjmp/_setjmp when available (POSIX), and with +** longjmp/setjmp otherwise. +*/ +#if !defined(LUAI_THROW) /* { */ + +#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* { */ + +/* C++ exceptions */ +#define LUAI_THROW(L,c) throw(c) + +static void LUAI_TRY (lua_State *L, lua_longjmp *c, Pfunc f, void *ud) { + try { + f(L, ud); /* call function protected */ + } + catch (lua_longjmp *c1) { /* Lua error */ + if (c1 != c) /* not the correct level? */ + throw; /* rethrow to upper level */ + } + catch (...) { /* non-Lua exception */ + c->status = -1; /* create some error code */ + } +} + + +#elif defined(LUA_USE_POSIX) /* }{ */ + +/* in POSIX, use _longjmp/_setjmp (more efficient) */ +#define LUAI_THROW(L,c) _longjmp((c)->b, 1) +#define LUAI_TRY(L,c,f,ud) if (_setjmp((c)->b) == 0) ((f)(L, ud)) + +#else /* }{ */ + +/* ISO C handling with long jumps */ +#define LUAI_THROW(L,c) longjmp((c)->b, 1) +#define LUAI_TRY(L,c,f,ud) if (setjmp((c)->b) == 0) ((f)(L, ud)) + +#endif /* } */ + +#endif /* } */ + + +void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop) { + if (errcode == LUA_ERRMEM) { /* memory error? */ + setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */ + } + else { + lua_assert(errorstatus(errcode)); /* must be a real error */ + lua_assert(!ttisnil(s2v(L->top.p - 1))); /* with a non-nil object */ + setobjs2s(L, oldtop, L->top.p - 1); /* move it to 'oldtop' */ + } + L->top.p = oldtop + 1; /* top goes back to old top plus error object */ +} + + +l_noret luaD_throw (lua_State *L, TStatus errcode) { + if (L->errorJmp) { /* thread has an error handler? */ + L->errorJmp->status = errcode; /* set status */ + LUAI_THROW(L, L->errorJmp); /* jump to it */ + } + else { /* thread has no error handler */ + global_State *g = G(L); + lua_State *mainth = mainthread(g); + errcode = luaE_resetthread(L, errcode); /* close all upvalues */ + L->status = errcode; + if (mainth->errorJmp) { /* main thread has a handler? */ + setobjs2s(L, mainth->top.p++, L->top.p - 1); /* copy error obj. */ + luaD_throw(mainth, errcode); /* re-throw in main thread */ + } + else { /* no handler at all; abort */ + if (g->panic) { /* panic function? */ + lua_unlock(L); + g->panic(L); /* call panic function (last chance to jump out) */ + } + abort(); + } + } +} + + +l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode) { + if (L->errorJmp) { + /* unroll error entries up to the first level */ + while (L->errorJmp->previous != NULL) + L->errorJmp = L->errorJmp->previous; + } + luaD_throw(L, errcode); +} + + +TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { + l_uint32 oldnCcalls = L->nCcalls; + lua_longjmp lj; + lj.status = LUA_OK; + lj.previous = L->errorJmp; /* chain new error handler */ + L->errorJmp = &lj; + LUAI_TRY(L, &lj, f, ud); /* call 'f' catching errors */ + L->errorJmp = lj.previous; /* restore old error handler */ + L->nCcalls = oldnCcalls; + return lj.status; +} + +/* }====================================================== */ + + +/* +** {================================================================== +** Stack reallocation +** =================================================================== +*/ + +/* some stack space for error handling */ +#define STACKERRSPACE 200 + + +/* +** LUAI_MAXSTACK limits the size of the Lua stack. +** It must fit into INT_MAX/2. +*/ + +#if !defined(LUAI_MAXSTACK) +#if 1000000 < (INT_MAX / 2) +#define LUAI_MAXSTACK 1000000 +#else +#define LUAI_MAXSTACK (INT_MAX / 2u) +#endif +#endif + + +/* maximum stack size that respects size_t */ +#define MAXSTACK_BYSIZET ((MAX_SIZET / sizeof(StackValue)) - STACKERRSPACE) + +/* +** Minimum between LUAI_MAXSTACK and MAXSTACK_BYSIZET +** (Maximum size for the stack must respect size_t.) +*/ +#define MAXSTACK cast_int(LUAI_MAXSTACK < MAXSTACK_BYSIZET \ + ? LUAI_MAXSTACK : MAXSTACK_BYSIZET) + + +/* stack size with extra space for error handling */ +#define ERRORSTACKSIZE (MAXSTACK + STACKERRSPACE) + + +/* raise a stack error while running the message handler */ +l_noret luaD_errerr (lua_State *L) { + TString *msg = luaS_newliteral(L, "error in error handling"); + setsvalue2s(L, L->top.p, msg); + L->top.p++; /* assume EXTRA_STACK */ + luaD_throw(L, LUA_ERRERR); +} + + +/* +** Check whether stack has enough space to run a simple function (such +** as a finalizer): At least BASIC_STACK_SIZE in the Lua stack and +** 2 slots in the C stack. +*/ +int luaD_checkminstack (lua_State *L) { + return ((stacksize(L) < MAXSTACK - BASIC_STACK_SIZE) && + (getCcalls(L) < LUAI_MAXCCALLS - 2)); +} + + +/* +** In ISO C, any pointer use after the pointer has been deallocated is +** undefined behavior. So, before a stack reallocation, all pointers +** should be changed to offsets, and after the reallocation they should +** be changed back to pointers. As during the reallocation the pointers +** are invalid, the reallocation cannot run emergency collections. +** Alternatively, we can use the old address after the deallocation. +** That is not strict ISO C, but seems to work fine everywhere. +** The following macro chooses how strict is the code. +*/ +#if !defined(LUAI_STRICT_ADDRESS) +#define LUAI_STRICT_ADDRESS 1 +#endif + +#if LUAI_STRICT_ADDRESS +/* +** Change all pointers to the stack into offsets. +*/ +static void relstack (lua_State *L) { + CallInfo *ci; + UpVal *up; + L->top.offset = savestack(L, L->top.p); + L->tbclist.offset = savestack(L, L->tbclist.p); + for (up = L->openupval; up != NULL; up = up->u.open.next) + up->v.offset = savestack(L, uplevel(up)); + for (ci = L->ci; ci != NULL; ci = ci->previous) { + ci->top.offset = savestack(L, ci->top.p); + ci->func.offset = savestack(L, ci->func.p); + } +} + + +/* +** Change back all offsets into pointers. +*/ +static void correctstack (lua_State *L, StkId oldstack) { + CallInfo *ci; + UpVal *up; + UNUSED(oldstack); + L->top.p = restorestack(L, L->top.offset); + L->tbclist.p = restorestack(L, L->tbclist.offset); + for (up = L->openupval; up != NULL; up = up->u.open.next) + up->v.p = s2v(restorestack(L, up->v.offset)); + for (ci = L->ci; ci != NULL; ci = ci->previous) { + ci->top.p = restorestack(L, ci->top.offset); + ci->func.p = restorestack(L, ci->func.offset); + if (isLua(ci)) + ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ + } +} + +#else +/* +** Assume that it is fine to use an address after its deallocation, +** as long as we do not dereference it. +*/ + +static void relstack (lua_State *L) { UNUSED(L); } /* do nothing */ + + +/* +** Correct pointers into 'oldstack' to point into 'L->stack'. +*/ +static void correctstack (lua_State *L, StkId oldstack) { + CallInfo *ci; + UpVal *up; + StkId newstack = L->stack.p; + if (oldstack == newstack) + return; + L->top.p = L->top.p - oldstack + newstack; + L->tbclist.p = L->tbclist.p - oldstack + newstack; + for (up = L->openupval; up != NULL; up = up->u.open.next) + up->v.p = s2v(uplevel(up) - oldstack + newstack); + for (ci = L->ci; ci != NULL; ci = ci->previous) { + ci->top.p = ci->top.p - oldstack + newstack; + ci->func.p = ci->func.p - oldstack + newstack; + if (isLua(ci)) + ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */ + } +} +#endif + + +/* +** Reallocate the stack to a new size, correcting all pointers into it. +** In case of allocation error, raise an error or return false according +** to 'raiseerror'. +*/ +int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { + int oldsize = stacksize(L); + int i; + StkId newstack; + StkId oldstack = L->stack.p; + lu_byte oldgcstop = G(L)->gcstopem; + lua_assert(newsize <= MAXSTACK || newsize == ERRORSTACKSIZE); + relstack(L); /* change pointers to offsets */ + G(L)->gcstopem = 1; /* stop emergency collection */ + newstack = luaM_reallocvector(L, oldstack, oldsize + EXTRA_STACK, + newsize + EXTRA_STACK, StackValue); + G(L)->gcstopem = oldgcstop; /* restore emergency collection */ + if (l_unlikely(newstack == NULL)) { /* reallocation failed? */ + correctstack(L, oldstack); /* change offsets back to pointers */ + if (raiseerror) + luaM_error(L); + else return 0; /* do not raise an error */ + } + L->stack.p = newstack; + correctstack(L, oldstack); /* change offsets back to pointers */ + L->stack_last.p = L->stack.p + newsize; + for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++) + setnilvalue(s2v(newstack + i)); /* erase new segment */ + return 1; +} + + +/* +** Try to grow the stack by at least 'n' elements. When 'raiseerror' +** is true, raises any error; otherwise, return 0 in case of errors. +*/ +int luaD_growstack (lua_State *L, int n, int raiseerror) { + int size = stacksize(L); + if (l_unlikely(size > MAXSTACK)) { + /* if stack is larger than maximum, thread is already using the + extra space reserved for errors, that is, thread is handling + a stack error; cannot grow further than that. */ + lua_assert(stacksize(L) == ERRORSTACKSIZE); + if (raiseerror) + luaD_errerr(L); /* stack error inside message handler */ + return 0; /* if not 'raiseerror', just signal it */ + } + else if (n < MAXSTACK) { /* avoids arithmetic overflows */ + int newsize = size + (size >> 1); /* tentative new size (size * 1.5) */ + int needed = cast_int(L->top.p - L->stack.p) + n; + if (newsize > MAXSTACK) /* cannot cross the limit */ + newsize = MAXSTACK; + if (newsize < needed) /* but must respect what was asked for */ + newsize = needed; + if (l_likely(newsize <= MAXSTACK)) + return luaD_reallocstack(L, newsize, raiseerror); + } + /* else stack overflow */ + /* add extra size to be able to handle the error message */ + luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror); + if (raiseerror) + luaG_runerror(L, "stack overflow"); + return 0; +} + + +/* +** Compute how much of the stack is being used, by computing the +** maximum top of all call frames in the stack and the current top. +*/ +static int stackinuse (lua_State *L) { + CallInfo *ci; + int res; + StkId lim = L->top.p; + for (ci = L->ci; ci != NULL; ci = ci->previous) { + if (lim < ci->top.p) lim = ci->top.p; + } + lua_assert(lim <= L->stack_last.p + EXTRA_STACK); + res = cast_int(lim - L->stack.p) + 1; /* part of stack in use */ + if (res < LUA_MINSTACK) + res = LUA_MINSTACK; /* ensure a minimum size */ + return res; +} + + +/* +** If stack size is more than 3 times the current use, reduce that size +** to twice the current use. (So, the final stack size is at most 2/3 the +** previous size, and half of its entries are empty.) +** As a particular case, if stack was handling a stack overflow and now +** it is not, 'max' (limited by MAXSTACK) will be smaller than +** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack +** will be reduced to a "regular" size. +*/ +void luaD_shrinkstack (lua_State *L) { + int inuse = stackinuse(L); + int max = (inuse > MAXSTACK / 3) ? MAXSTACK : inuse * 3; + /* if thread is currently not handling a stack overflow and its + size is larger than maximum "reasonable" size, shrink it */ + if (inuse <= MAXSTACK && stacksize(L) > max) { + int nsize = (inuse > MAXSTACK / 2) ? MAXSTACK : inuse * 2; + luaD_reallocstack(L, nsize, 0); /* ok if that fails */ + } + else /* don't change stack */ + condmovestack(L,(void)0,(void)0); /* (change only for debugging) */ + luaE_shrinkCI(L); /* shrink CI list */ +} + + +void luaD_inctop (lua_State *L) { + L->top.p++; + luaD_checkstack(L, 1); +} + +/* }================================================================== */ + + +/* +** Call a hook for the given event. Make sure there is a hook to be +** called. (Both 'L->hook' and 'L->hookmask', which trigger this +** function, can be changed asynchronously by signals.) +*/ +void luaD_hook (lua_State *L, int event, int line, + int ftransfer, int ntransfer) { + lua_Hook hook = L->hook; + if (hook && L->allowhook) { /* make sure there is a hook */ + CallInfo *ci = L->ci; + ptrdiff_t top = savestack(L, L->top.p); /* preserve original 'top' */ + ptrdiff_t ci_top = savestack(L, ci->top.p); /* idem for 'ci->top' */ + lua_Debug ar; + ar.event = event; + ar.currentline = line; + ar.i_ci = ci; + L->transferinfo.ftransfer = ftransfer; + L->transferinfo.ntransfer = ntransfer; + if (isLua(ci) && L->top.p < ci->top.p) + L->top.p = ci->top.p; /* protect entire activation register */ + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + if (ci->top.p < L->top.p + LUA_MINSTACK) + ci->top.p = L->top.p + LUA_MINSTACK; + L->allowhook = 0; /* cannot call hooks inside a hook */ + ci->callstatus |= CIST_HOOKED; + lua_unlock(L); + (*hook)(L, &ar); + lua_lock(L); + lua_assert(!L->allowhook); + L->allowhook = 1; + ci->top.p = restorestack(L, ci_top); + L->top.p = restorestack(L, top); + ci->callstatus &= ~CIST_HOOKED; + } +} + + +/* +** Executes a call hook for Lua functions. This function is called +** whenever 'hookmask' is not zero, so it checks whether call hooks are +** active. +*/ +void luaD_hookcall (lua_State *L, CallInfo *ci) { + L->oldpc = 0; /* set 'oldpc' for new function */ + if (L->hookmask & LUA_MASKCALL) { /* is call hook on? */ + int event = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL + : LUA_HOOKCALL; + Proto *p = ci_func(ci)->p; + ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ + luaD_hook(L, event, -1, 1, p->numparams); + ci->u.l.savedpc--; /* correct 'pc' */ + } +} + + +/* +** Executes a return hook for Lua and C functions and sets/corrects +** 'oldpc'. (Note that this correction is needed by the line hook, so it +** is done even when return hooks are off.) +*/ +static void rethook (lua_State *L, CallInfo *ci, int nres) { + if (L->hookmask & LUA_MASKRET) { /* is return hook on? */ + StkId firstres = L->top.p - nres; /* index of first result */ + int delta = 0; /* correction for vararg functions */ + int ftransfer; + if (isLua(ci)) { + Proto *p = ci_func(ci)->p; + if (p->flag & PF_VAHID) + delta = ci->u.l.nextraargs + p->numparams + 1; + } + ci->func.p += delta; /* if vararg, back to virtual 'func' */ + ftransfer = cast_int(firstres - ci->func.p); + luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ + ci->func.p -= delta; + } + if (isLua(ci = ci->previous)) + L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */ +} + + +/* +** Check whether 'func' has a '__call' metafield. If so, put it in the +** stack, below original 'func', so that 'luaD_precall' can call it. +** Raise an error if there is no '__call' metafield. +** Bits CIST_CCMT in status count how many _call metamethods were +** invoked and how many corresponding extra arguments were pushed. +** (This count will be saved in the 'callstatus' of the call). +** Raise an error if this counter overflows. +*/ +static unsigned tryfuncTM (lua_State *L, StkId func, unsigned status) { + const TValue *tm; + StkId p; + tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); + if (l_unlikely(ttisnil(tm))) /* no metamethod? */ + luaG_callerror(L, s2v(func)); + for (p = L->top.p; p > func; p--) /* open space for metamethod */ + setobjs2s(L, p, p-1); + L->top.p++; /* stack space pre-allocated by the caller */ + setobj2s(L, func, tm); /* metamethod is the new function to be called */ + if ((status & MAX_CCMT) == MAX_CCMT) /* is counter full? */ + luaG_runerror(L, "'__call' chain too long"); + return status + (1u << CIST_CCMT); /* increment counter */ +} + + +/* Generic case for 'moveresult' */ +l_sinline void genmoveresults (lua_State *L, StkId res, int nres, + int wanted) { + StkId firstresult = L->top.p - nres; /* index of first result */ + int i; + if (nres > wanted) /* extra results? */ + nres = wanted; /* don't need them */ + for (i = 0; i < nres; i++) /* move all results to correct place */ + setobjs2s(L, res + i, firstresult + i); + for (; i < wanted; i++) /* complete wanted number of results */ + setnilvalue(s2v(res + i)); + L->top.p = res + wanted; /* top points after the last result */ +} + + +/* +** Given 'nres' results at 'firstResult', move 'fwanted-1' of them +** to 'res'. Handle most typical cases (zero results for commands, +** one result for expressions, multiple results for tail calls/single +** parameters) separated. The flag CIST_TBC in 'fwanted', if set, +** forces the switch to go to the default case. +*/ +l_sinline void moveresults (lua_State *L, StkId res, int nres, + l_uint32 fwanted) { + switch (fwanted) { /* handle typical cases separately */ + case 0 + 1: /* no values needed */ + L->top.p = res; + return; + case 1 + 1: /* one value needed */ + if (nres == 0) /* no results? */ + setnilvalue(s2v(res)); /* adjust with nil */ + else /* at least one result */ + setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ + L->top.p = res + 1; + return; + case LUA_MULTRET + 1: + genmoveresults(L, res, nres, nres); /* we want all results */ + break; + default: { /* two/more results and/or to-be-closed variables */ + int wanted = get_nresults(fwanted); + if (fwanted & CIST_TBC) { /* to-be-closed variables? */ + L->ci->u2.nres = nres; + L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ + res = luaF_close(L, res, CLOSEKTOP, 1); + L->ci->callstatus &= ~CIST_CLSRET; + if (L->hookmask) { /* if needed, call hook after '__close's */ + ptrdiff_t savedres = savestack(L, res); + rethook(L, L->ci, nres); + res = restorestack(L, savedres); /* hook can move stack */ + } + if (wanted == LUA_MULTRET) + wanted = nres; /* we want all results */ + } + genmoveresults(L, res, nres, wanted); + break; + } + } +} + + +/* +** Finishes a function call: calls hook if necessary, moves current +** number of results to proper place, and returns to previous call +** info. If function has to close variables, hook must be called after +** that. +*/ +void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { + l_uint32 fwanted = ci->callstatus & (CIST_TBC | CIST_NRESULTS); + if (l_unlikely(L->hookmask) && !(fwanted & CIST_TBC)) + rethook(L, ci, nres); + /* move results to proper place */ + moveresults(L, ci->func.p, nres, fwanted); + /* function cannot be in any of these cases when returning */ + lua_assert(!(ci->callstatus & + (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_CLSRET))); + L->ci = ci->previous; /* back to caller (after closing variables) */ +} + + + +#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) + + +/* +** Allocate and initialize CallInfo structure. At this point, the +** only valid fields in the call status are number of results, +** CIST_C (if it's a C function), and number of extra arguments. +** (All these bit-fields fit in 16-bit values.) +*/ +l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, unsigned status, + StkId top) { + CallInfo *ci = L->ci = next_ci(L); /* new frame */ + ci->func.p = func; + lua_assert((status & ~(CIST_NRESULTS | CIST_C | MAX_CCMT)) == 0); + ci->callstatus = status; + ci->top.p = top; + return ci; +} + + +/* +** precall for C functions +*/ +l_sinline int precallC (lua_State *L, StkId func, unsigned status, + lua_CFunction f) { + int n; /* number of returns */ + CallInfo *ci; + checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ + L->ci = ci = prepCallInfo(L, func, status | CIST_C, + L->top.p + LUA_MINSTACK); + lua_assert(ci->top.p <= L->stack_last.p); + if (l_unlikely(L->hookmask & LUA_MASKCALL)) { + int narg = cast_int(L->top.p - func) - 1; + luaD_hook(L, LUA_HOOKCALL, -1, 1, narg); + } + lua_unlock(L); + n = (*f)(L); /* do the actual call */ + lua_lock(L); + api_checknelems(L, n); + luaD_poscall(L, ci, n); + return n; +} + + +/* +** Prepare a function for a tail call, building its call info on top +** of the current call info. 'narg1' is the number of arguments plus 1 +** (so that it includes the function itself). Return the number of +** results, if it was a C function, or -1 for a Lua function. +*/ +int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, + int narg1, int delta) { + unsigned status = LUA_MULTRET + 1; + retry: + switch (ttypetag(s2v(func))) { + case LUA_VCCL: /* C closure */ + return precallC(L, func, status, clCvalue(s2v(func))->f); + case LUA_VLCF: /* light C function */ + return precallC(L, func, status, fvalue(s2v(func))); + case LUA_VLCL: { /* Lua function */ + Proto *p = clLvalue(s2v(func))->p; + int fsize = p->maxstacksize; /* frame size */ + int nfixparams = p->numparams; + int i; + checkstackp(L, fsize - delta, func); + ci->func.p -= delta; /* restore 'func' (if vararg) */ + for (i = 0; i < narg1; i++) /* move down function and arguments */ + setobjs2s(L, ci->func.p + i, func + i); + func = ci->func.p; /* moved-down function */ + for (; narg1 <= nfixparams; narg1++) + setnilvalue(s2v(func + narg1)); /* complete missing arguments */ + ci->top.p = func + 1 + fsize; /* top for new function */ + lua_assert(ci->top.p <= L->stack_last.p); + ci->u.l.savedpc = p->code; /* starting point */ + ci->callstatus |= CIST_TAIL; + L->top.p = func + narg1; /* set top */ + return -1; + } + default: { /* not a function */ + checkstackp(L, 1, func); /* space for metamethod */ + status = tryfuncTM(L, func, status); /* try '__call' metamethod */ + narg1++; + goto retry; /* try again */ + } + } +} + + +/* +** Prepares the call to a function (C or Lua). For C functions, also do +** the call. The function to be called is at '*func'. The arguments +** are on the stack, right after the function. Returns the CallInfo +** to be executed, if it was a Lua function. Otherwise (a C function) +** returns NULL, with all the results on the stack, starting at the +** original function position. +*/ +CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { + unsigned status = cast_uint(nresults + 1); + lua_assert(status <= MAXRESULTS + 1); + retry: + switch (ttypetag(s2v(func))) { + case LUA_VCCL: /* C closure */ + precallC(L, func, status, clCvalue(s2v(func))->f); + return NULL; + case LUA_VLCF: /* light C function */ + precallC(L, func, status, fvalue(s2v(func))); + return NULL; + case LUA_VLCL: { /* Lua function */ + CallInfo *ci; + Proto *p = clLvalue(s2v(func))->p; + int narg = cast_int(L->top.p - func) - 1; /* number of real arguments */ + int nfixparams = p->numparams; + int fsize = p->maxstacksize; /* frame size */ + checkstackp(L, fsize, func); + L->ci = ci = prepCallInfo(L, func, status, func + 1 + fsize); + ci->u.l.savedpc = p->code; /* starting point */ + for (; narg < nfixparams; narg++) + setnilvalue(s2v(L->top.p++)); /* complete missing arguments */ + lua_assert(ci->top.p <= L->stack_last.p); + return ci; + } + default: { /* not a function */ + checkstackp(L, 1, func); /* space for metamethod */ + status = tryfuncTM(L, func, status); /* try '__call' metamethod */ + goto retry; /* try again with metamethod */ + } + } +} + + +/* +** Call a function (C or Lua) through C. 'inc' can be 1 (increment +** number of recursive invocations in the C stack) or nyci (the same +** plus increment number of non-yieldable calls). +** This function can be called with some use of EXTRA_STACK, so it should +** check the stack before doing anything else. 'luaD_precall' already +** does that. +*/ +l_sinline void ccall (lua_State *L, StkId func, int nResults, l_uint32 inc) { + CallInfo *ci; + L->nCcalls += inc; + if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) { + checkstackp(L, 0, func); /* free any use of EXTRA_STACK */ + luaE_checkcstack(L); + } + if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */ + ci->callstatus |= CIST_FRESH; /* mark that it is a "fresh" execute */ + luaV_execute(L, ci); /* call it */ + } + L->nCcalls -= inc; +} + + +/* +** External interface for 'ccall' +*/ +void luaD_call (lua_State *L, StkId func, int nResults) { + ccall(L, func, nResults, 1); +} + + +/* +** Similar to 'luaD_call', but does not allow yields during the call. +*/ +void luaD_callnoyield (lua_State *L, StkId func, int nResults) { + ccall(L, func, nResults, nyci); +} + + +/* +** Finish the job of 'lua_pcallk' after it was interrupted by an yield. +** (The caller, 'finishCcall', does the final call to 'adjustresults'.) +** The main job is to complete the 'luaD_pcall' called by 'lua_pcallk'. +** If a '__close' method yields here, eventually control will be back +** to 'finishCcall' (when that '__close' method finally returns) and +** 'finishpcallk' will run again and close any still pending '__close' +** methods. Similarly, if a '__close' method errs, 'precover' calls +** 'unroll' which calls ''finishCcall' and we are back here again, to +** close any pending '__close' methods. +** Note that, up to the call to 'luaF_close', the corresponding +** 'CallInfo' is not modified, so that this repeated run works like the +** first one (except that it has at least one less '__close' to do). In +** particular, field CIST_RECST preserves the error status across these +** multiple runs, changing only if there is a new error. +*/ +static TStatus finishpcallk (lua_State *L, CallInfo *ci) { + TStatus status = getcistrecst(ci); /* get original status */ + if (l_likely(status == LUA_OK)) /* no error? */ + status = LUA_YIELD; /* was interrupted by an yield */ + else { /* error */ + StkId func = restorestack(L, ci->u2.funcidx); + L->allowhook = getoah(ci); /* restore 'allowhook' */ + func = luaF_close(L, func, status, 1); /* can yield or raise an error */ + luaD_seterrorobj(L, status, func); + luaD_shrinkstack(L); /* restore stack size in case of overflow */ + setcistrecst(ci, LUA_OK); /* clear original status */ + } + ci->callstatus &= ~CIST_YPCALL; + L->errfunc = ci->u.c.old_errfunc; + /* if it is here, there were errors or yields; unlike 'lua_pcallk', + do not change status */ + return status; +} + + +/* +** Completes the execution of a C function interrupted by an yield. +** The interruption must have happened while the function was either +** closing its tbc variables in 'moveresults' or executing +** 'lua_callk'/'lua_pcallk'. In the first case, it just redoes +** 'luaD_poscall'. In the second case, the call to 'finishpcallk' +** finishes the interrupted execution of 'lua_pcallk'. After that, it +** calls the continuation of the interrupted function and finally it +** completes the job of the 'luaD_call' that called the function. In +** the call to 'adjustresults', we do not know the number of results +** of the function called by 'lua_callk'/'lua_pcallk', so we are +** conservative and use LUA_MULTRET (always adjust). +*/ +static void finishCcall (lua_State *L, CallInfo *ci) { + int n; /* actual number of results from C function */ + if (ci->callstatus & CIST_CLSRET) { /* was closing TBC variable? */ + lua_assert(ci->callstatus & CIST_TBC); + n = ci->u2.nres; /* just redo 'luaD_poscall' */ + /* don't need to reset CIST_CLSRET, as it will be set again anyway */ + } + else { + TStatus status = LUA_YIELD; /* default if there were no errors */ + lua_KFunction kf = ci->u.c.k; /* continuation function */ + /* must have a continuation and must be able to call it */ + lua_assert(kf != NULL && yieldable(L)); + if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */ + status = finishpcallk(L, ci); /* finish it */ + adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */ + lua_unlock(L); + n = (*kf)(L, APIstatus(status), ci->u.c.ctx); /* call continuation */ + lua_lock(L); + api_checknelems(L, n); + } + luaD_poscall(L, ci, n); /* finish 'luaD_call' */ +} + + +/* +** Executes "full continuation" (everything in the stack) of a +** previously interrupted coroutine until the stack is empty (or another +** interruption long-jumps out of the loop). +*/ +static void unroll (lua_State *L, void *ud) { + CallInfo *ci; + UNUSED(ud); + while ((ci = L->ci) != &L->base_ci) { /* something in the stack */ + if (!isLua(ci)) /* C function? */ + finishCcall(L, ci); /* complete its execution */ + else { /* Lua function */ + luaV_finishOp(L); /* finish interrupted instruction */ + luaV_execute(L, ci); /* execute down to higher C 'boundary' */ + } + } +} + + +/* +** Try to find a suspended protected call (a "recover point") for the +** given thread. +*/ +static CallInfo *findpcall (lua_State *L) { + CallInfo *ci; + for (ci = L->ci; ci != NULL; ci = ci->previous) { /* search for a pcall */ + if (ci->callstatus & CIST_YPCALL) + return ci; + } + return NULL; /* no pending pcall */ +} + + +/* +** Signal an error in the call to 'lua_resume', not in the execution +** of the coroutine itself. (Such errors should not be handled by any +** coroutine error handler and should not kill the coroutine.) +*/ +static int resume_error (lua_State *L, const char *msg, int narg) { + api_checkpop(L, narg); + L->top.p -= narg; /* remove args from the stack */ + setsvalue2s(L, L->top.p, luaS_new(L, msg)); /* push error message */ + api_incr_top(L); + lua_unlock(L); + return LUA_ERRRUN; +} + + +/* +** Do the work for 'lua_resume' in protected mode. Most of the work +** depends on the status of the coroutine: initial state, suspended +** inside a hook, or regularly suspended (optionally with a continuation +** function), plus erroneous cases: non-suspended coroutine or dead +** coroutine. +*/ +static void resume (lua_State *L, void *ud) { + int n = *(cast(int*, ud)); /* number of arguments */ + StkId firstArg = L->top.p - n; /* first argument */ + CallInfo *ci = L->ci; + if (L->status == LUA_OK) /* starting a coroutine? */ + ccall(L, firstArg - 1, LUA_MULTRET, 0); /* just call its body */ + else { /* resuming from previous yield */ + lua_assert(L->status == LUA_YIELD); + L->status = LUA_OK; /* mark that it is running (again) */ + if (isLua(ci)) { /* yielded inside a hook? */ + /* undo increment made by 'luaG_traceexec': instruction was not + executed yet */ + lua_assert(ci->callstatus & CIST_HOOKYIELD); + ci->u.l.savedpc--; + L->top.p = firstArg; /* discard arguments */ + luaV_execute(L, ci); /* just continue running Lua code */ + } + else { /* 'common' yield */ + if (ci->u.c.k != NULL) { /* does it have a continuation function? */ + lua_unlock(L); + n = (*ci->u.c.k)(L, LUA_YIELD, ci->u.c.ctx); /* call continuation */ + lua_lock(L); + api_checknelems(L, n); + } + luaD_poscall(L, ci, n); /* finish 'luaD_call' */ + } + unroll(L, NULL); /* run continuation */ + } +} + + +/* +** Unrolls a coroutine in protected mode while there are recoverable +** errors, that is, errors inside a protected call. (Any error +** interrupts 'unroll', and this loop protects it again so it can +** continue.) Stops with a normal end (status == LUA_OK), an yield +** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't +** find a recover point). +*/ +static TStatus precover (lua_State *L, TStatus status) { + CallInfo *ci; + while (errorstatus(status) && (ci = findpcall(L)) != NULL) { + L->ci = ci; /* go down to recovery functions */ + setcistrecst(ci, status); /* status to finish 'pcall' */ + status = luaD_rawrunprotected(L, unroll, NULL); + } + return status; +} + + +LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, + int *nresults) { + TStatus status; + lua_lock(L); + if (L->status == LUA_OK) { /* may be starting a coroutine */ + if (L->ci != &L->base_ci) /* not in base level? */ + return resume_error(L, "cannot resume non-suspended coroutine", nargs); + else if (L->top.p - (L->ci->func.p + 1) == nargs) /* no function? */ + return resume_error(L, "cannot resume dead coroutine", nargs); + } + else if (L->status != LUA_YIELD) /* ended with errors? */ + return resume_error(L, "cannot resume dead coroutine", nargs); + L->nCcalls = (from) ? getCcalls(from) : 0; + if (getCcalls(L) >= LUAI_MAXCCALLS) + return resume_error(L, "C stack overflow", nargs); + L->nCcalls++; + luai_userstateresume(L, nargs); + api_checkpop(L, (L->status == LUA_OK) ? nargs + 1 : nargs); + status = luaD_rawrunprotected(L, resume, &nargs); + /* continue running after recoverable errors */ + status = precover(L, status); + if (l_likely(!errorstatus(status))) + lua_assert(status == L->status); /* normal end or yield */ + else { /* unrecoverable error */ + L->status = status; /* mark thread as 'dead' */ + luaD_seterrorobj(L, status, L->top.p); /* push error message */ + L->ci->top.p = L->top.p; + } + *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield + : cast_int(L->top.p - (L->ci->func.p + 1)); + lua_unlock(L); + return APIstatus(status); +} + + +LUA_API int lua_isyieldable (lua_State *L) { + return yieldable(L); +} + + +LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, + lua_KFunction k) { + CallInfo *ci; + luai_userstateyield(L, nresults); + lua_lock(L); + ci = L->ci; + api_checkpop(L, nresults); + if (l_unlikely(!yieldable(L))) { + if (L != mainthread(G(L))) + luaG_runerror(L, "attempt to yield across a C-call boundary"); + else + luaG_runerror(L, "attempt to yield from outside a coroutine"); + } + L->status = LUA_YIELD; + ci->u2.nyield = nresults; /* save number of results */ + if (isLua(ci)) { /* inside a hook? */ + lua_assert(!isLuacode(ci)); + api_check(L, nresults == 0, "hooks cannot yield values"); + api_check(L, k == NULL, "hooks cannot continue after yielding"); + } + else { + if ((ci->u.c.k = k) != NULL) /* is there a continuation? */ + ci->u.c.ctx = ctx; /* save context */ + luaD_throw(L, LUA_YIELD); + } + lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */ + lua_unlock(L); + return 0; /* return to 'luaD_hook' */ +} + + +/* +** Auxiliary structure to call 'luaF_close' in protected mode. +*/ +struct CloseP { + StkId level; + TStatus status; +}; + + +/* +** Auxiliary function to call 'luaF_close' in protected mode. +*/ +static void closepaux (lua_State *L, void *ud) { + struct CloseP *pcl = cast(struct CloseP *, ud); + luaF_close(L, pcl->level, pcl->status, 0); +} + + +/* +** Calls 'luaF_close' in protected mode. Return the original status +** or, in case of errors, the new status. +*/ +TStatus luaD_closeprotected (lua_State *L, ptrdiff_t level, TStatus status) { + CallInfo *old_ci = L->ci; + lu_byte old_allowhooks = L->allowhook; + for (;;) { /* keep closing upvalues until no more errors */ + struct CloseP pcl; + pcl.level = restorestack(L, level); pcl.status = status; + status = luaD_rawrunprotected(L, &closepaux, &pcl); + if (l_likely(status == LUA_OK)) /* no more errors? */ + return pcl.status; + else { /* an error occurred; restore saved state and repeat */ + L->ci = old_ci; + L->allowhook = old_allowhooks; + } + } +} + + +/* +** Call the C function 'func' in protected mode, restoring basic +** thread information ('allowhook', etc.) and in particular +** its stack level in case of errors. +*/ +TStatus luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, + ptrdiff_t ef) { + TStatus status; + CallInfo *old_ci = L->ci; + lu_byte old_allowhooks = L->allowhook; + ptrdiff_t old_errfunc = L->errfunc; + L->errfunc = ef; + status = luaD_rawrunprotected(L, func, u); + if (l_unlikely(status != LUA_OK)) { /* an error occurred? */ + L->ci = old_ci; + L->allowhook = old_allowhooks; + status = luaD_closeprotected(L, old_top, status); + luaD_seterrorobj(L, status, restorestack(L, old_top)); + luaD_shrinkstack(L); /* restore stack size in case of overflow */ + } + L->errfunc = old_errfunc; + return status; +} + + + +/* +** Execute a protected parser. +*/ +struct SParser { /* data to 'f_parser' */ + ZIO *z; + Mbuffer buff; /* dynamic structure used by the scanner */ + Dyndata dyd; /* dynamic structures used by the parser */ + const char *mode; + const char *name; +}; + + +static void checkmode (lua_State *L, const char *mode, const char *x) { + if (strchr(mode, x[0]) == NULL) { + luaO_pushfstring(L, + "attempt to load a %s chunk (mode is '%s')", x, mode); + luaD_throw(L, LUA_ERRSYNTAX); + } +} + + +static void f_parser (lua_State *L, void *ud) { + LClosure *cl; + struct SParser *p = cast(struct SParser *, ud); + const char *mode = p->mode ? p->mode : "bt"; + int c = zgetc(p->z); /* read first character */ + if (c == LUA_SIGNATURE[0]) { + int fixed = 0; + if (strchr(mode, 'B') != NULL) + fixed = 1; + else + checkmode(L, mode, "binary"); + cl = luaU_undump(L, p->z, p->name, fixed); + } + else { + checkmode(L, mode, "text"); + cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); + } + lua_assert(cl->nupvalues == cl->p->sizeupvalues); + luaF_initupvals(L, cl); +} + + +TStatus luaD_protectedparser (lua_State *L, ZIO *z, const char *name, + const char *mode) { + struct SParser p; + TStatus status; + incnny(L); /* cannot yield during parsing */ + p.z = z; p.name = name; p.mode = mode; + p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0; + p.dyd.gt.arr = NULL; p.dyd.gt.size = 0; + p.dyd.label.arr = NULL; p.dyd.label.size = 0; + luaZ_initbuffer(L, &p.buff); + status = luaD_pcall(L, f_parser, &p, savestack(L, L->top.p), L->errfunc); + luaZ_freebuffer(L, &p.buff); + luaM_freearray(L, p.dyd.actvar.arr, cast_sizet(p.dyd.actvar.size)); + luaM_freearray(L, p.dyd.gt.arr, cast_sizet(p.dyd.gt.size)); + luaM_freearray(L, p.dyd.label.arr, cast_sizet(p.dyd.label.size)); + decnny(L); + return status; +} + + + +``` + +`Lua/ldo.h`: + +```h +/* +** $Id: ldo.h $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + +#ifndef ldo_h +#define ldo_h + + +#include "llimits.h" +#include "lobject.h" +#include "lstate.h" +#include "lzio.h" + + +/* +** Macro to check stack size and grow stack if needed. Parameters +** 'pre'/'pos' allow the macro to preserve a pointer into the +** stack across reallocations, doing the work only when needed. +** It also allows the running of one GC step when the stack is +** reallocated. +** 'condmovestack' is used in heavy tests to force a stack reallocation +** at every check. +*/ + +#if !defined(HARDSTACKTESTS) +#define condmovestack(L,pre,pos) ((void)0) +#else +/* realloc stack keeping its size */ +#define condmovestack(L,pre,pos) \ + { int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; } +#endif + +#define luaD_checkstackaux(L,n,pre,pos) \ + if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \ + { pre; luaD_growstack(L, n, 1); pos; } \ + else { condmovestack(L,pre,pos); } + +/* In general, 'pre'/'pos' are empty (nothing to save) */ +#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0) + + + +#define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p)) +#define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n)) + + +/* macro to check stack size, preserving 'p' */ +#define checkstackp(L,n,p) \ + luaD_checkstackaux(L, n, \ + ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \ + p = restorestack(L, t__)) /* 'pos' part: restore 'p' */ + + +/* +** Maximum depth for nested C calls, syntactical nested non-terminals, +** and other features implemented through recursion in C. (Value must +** fit in a 16-bit unsigned integer. It must also be compatible with +** the size of the C stack.) +*/ +#if !defined(LUAI_MAXCCALLS) +#define LUAI_MAXCCALLS 200 +#endif + + +/* type of protected functions, to be ran by 'runprotected' */ +typedef void (*Pfunc) (lua_State *L, void *ud); + +LUAI_FUNC l_noret luaD_errerr (lua_State *L); +LUAI_FUNC void luaD_seterrorobj (lua_State *L, TStatus errcode, StkId oldtop); +LUAI_FUNC TStatus luaD_protectedparser (lua_State *L, ZIO *z, + const char *name, + const char *mode); +LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, + int fTransfer, int nTransfer); +LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); +LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, + int narg1, int delta); +LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); +LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); +LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); +LUAI_FUNC TStatus luaD_closeprotected (lua_State *L, ptrdiff_t level, + TStatus status); +LUAI_FUNC TStatus luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t oldtop, ptrdiff_t ef); +LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres); +LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror); +LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror); +LUAI_FUNC void luaD_shrinkstack (lua_State *L); +LUAI_FUNC void luaD_inctop (lua_State *L); +LUAI_FUNC int luaD_checkminstack (lua_State *L); + +LUAI_FUNC l_noret luaD_throw (lua_State *L, TStatus errcode); +LUAI_FUNC l_noret luaD_throwbaselevel (lua_State *L, TStatus errcode); +LUAI_FUNC TStatus luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); + +#endif + + +``` + +`Lua/ldump.c`: + +```c +/* +** $Id: ldump.c $ +** save precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#define ldump_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <limits.h> +#include <stddef.h> + +#include "lua.h" + +#include "lapi.h" +#include "lgc.h" +#include "lobject.h" +#include "lstate.h" +#include "ltable.h" +#include "lundump.h" + + +typedef struct { + lua_State *L; + lua_Writer writer; + void *data; + size_t offset; /* current position relative to beginning of dump */ + int strip; + int status; + Table *h; /* table to track saved strings */ + lua_Unsigned nstr; /* counter for counting saved strings */ +} DumpState; + + +/* +** All high-level dumps go through dumpVector; you can change it to +** change the endianness of the result +*/ +#define dumpVector(D,v,n) dumpBlock(D,v,(n)*sizeof((v)[0])) + +#define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char)) + + +/* +** Dump the block of memory pointed by 'b' with given 'size'. +** 'b' should not be NULL, except for the last call signaling the end +** of the dump. +*/ +static void dumpBlock (DumpState *D, const void *b, size_t size) { + if (D->status == 0) { /* do not write anything after an error */ + lua_unlock(D->L); + D->status = (*D->writer)(D->L, b, size, D->data); + lua_lock(D->L); + D->offset += size; + } +} + + +/* +** Dump enough zeros to ensure that current position is a multiple of +** 'align'. +*/ +static void dumpAlign (DumpState *D, unsigned align) { + unsigned padding = align - cast_uint(D->offset % align); + if (padding < align) { /* padding == align means no padding */ + static lua_Integer paddingContent = 0; + lua_assert(align <= sizeof(lua_Integer)); + dumpBlock(D, &paddingContent, padding); + } + lua_assert(D->offset % align == 0); +} + + +#define dumpVar(D,x) dumpVector(D,&x,1) + + +static void dumpByte (DumpState *D, int y) { + lu_byte x = (lu_byte)y; + dumpVar(D, x); +} + + +/* +** size for 'dumpVarint' buffer: each byte can store up to 7 bits. +** (The "+6" rounds up the division.) +*/ +#define DIBS ((l_numbits(lua_Unsigned) + 6) / 7) + +/* +** Dumps an unsigned integer using the MSB Varint encoding +*/ +static void dumpVarint (DumpState *D, lua_Unsigned x) { + lu_byte buff[DIBS]; + unsigned n = 1; + buff[DIBS - 1] = x & 0x7f; /* fill least-significant byte */ + while ((x >>= 7) != 0) /* fill other bytes in reverse order */ + buff[DIBS - (++n)] = cast_byte((x & 0x7f) | 0x80); + dumpVector(D, buff + DIBS - n, n); +} + + +static void dumpSize (DumpState *D, size_t sz) { + dumpVarint(D, cast(lua_Unsigned, sz)); +} + + +static void dumpInt (DumpState *D, int x) { + lua_assert(x >= 0); + dumpVarint(D, cast_uint(x)); +} + + +static void dumpNumber (DumpState *D, lua_Number x) { + dumpVar(D, x); +} + + +/* +** Signed integers are coded to keep small values small. (Coding -1 as +** 0xfff...fff would use too many bytes to save a quite common value.) +** A non-negative x is coded as 2x; a negative x is coded as -2x - 1. +** (0 => 0; -1 => 1; 1 => 2; -2 => 3; 2 => 4; ...) +*/ +static void dumpInteger (DumpState *D, lua_Integer x) { + lua_Unsigned cx = (x >= 0) ? 2u * l_castS2U(x) + : (2u * ~l_castS2U(x)) + 1; + dumpVarint(D, cx); +} + + +/* +** Dump a String. First dump its "size": +** size==0 is followed by an index and means "reuse saved string with +** that index"; index==0 means NULL. +** size>=1 is followed by the string contents with real size==size-1 and +** means that string, which will be saved with the next available index. +** The real size does not include the ending '\0' (which is not dumped), +** so adding 1 to it cannot overflow a size_t. +*/ +static void dumpString (DumpState *D, TString *ts) { + if (ts == NULL) { + dumpVarint(D, 0); /* will "reuse" NULL */ + dumpVarint(D, 0); /* special index for NULL */ + } + else { + TValue idx; + int tag = luaH_getstr(D->h, ts, &idx); + if (!tagisempty(tag)) { /* string already saved? */ + dumpVarint(D, 0); /* reuse a saved string */ + dumpVarint(D, l_castS2U(ivalue(&idx))); /* index of saved string */ + } + else { /* must write and save the string */ + TValue key, value; /* to save the string in the hash */ + size_t size; + const char *s = getlstr(ts, size); + dumpSize(D, size + 1); + dumpVector(D, s, size + 1); /* include ending '\0' */ + D->nstr++; /* one more saved string */ + setsvalue(D->L, &key, ts); /* the string is the key */ + setivalue(&value, l_castU2S(D->nstr)); /* its index is the value */ + luaH_set(D->L, D->h, &key, &value); /* h[ts] = nstr */ + /* integer value does not need barrier */ + } + } +} + + +static void dumpCode (DumpState *D, const Proto *f) { + dumpInt(D, f->sizecode); + dumpAlign(D, sizeof(f->code[0])); + lua_assert(f->code != NULL); + dumpVector(D, f->code, cast_uint(f->sizecode)); +} + + +static void dumpFunction (DumpState *D, const Proto *f); + +static void dumpConstants (DumpState *D, const Proto *f) { + int i; + int n = f->sizek; + dumpInt(D, n); + for (i = 0; i < n; i++) { + const TValue *o = &f->k[i]; + int tt = ttypetag(o); + dumpByte(D, tt); + switch (tt) { + case LUA_VNUMFLT: + dumpNumber(D, fltvalue(o)); + break; + case LUA_VNUMINT: + dumpInteger(D, ivalue(o)); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: + dumpString(D, tsvalue(o)); + break; + default: + lua_assert(tt == LUA_VNIL || tt == LUA_VFALSE || tt == LUA_VTRUE); + } + } +} + + +static void dumpProtos (DumpState *D, const Proto *f) { + int i; + int n = f->sizep; + dumpInt(D, n); + for (i = 0; i < n; i++) + dumpFunction(D, f->p[i]); +} + + +static void dumpUpvalues (DumpState *D, const Proto *f) { + int i, n = f->sizeupvalues; + dumpInt(D, n); + for (i = 0; i < n; i++) { + dumpByte(D, f->upvalues[i].instack); + dumpByte(D, f->upvalues[i].idx); + dumpByte(D, f->upvalues[i].kind); + } +} + + +static void dumpDebug (DumpState *D, const Proto *f) { + int i, n; + n = (D->strip) ? 0 : f->sizelineinfo; + dumpInt(D, n); + if (f->lineinfo != NULL) + dumpVector(D, f->lineinfo, cast_uint(n)); + n = (D->strip) ? 0 : f->sizeabslineinfo; + dumpInt(D, n); + if (n > 0) { + /* 'abslineinfo' is an array of structures of int's */ + dumpAlign(D, sizeof(int)); + dumpVector(D, f->abslineinfo, cast_uint(n)); + } + n = (D->strip) ? 0 : f->sizelocvars; + dumpInt(D, n); + for (i = 0; i < n; i++) { + dumpString(D, f->locvars[i].varname); + dumpInt(D, f->locvars[i].startpc); + dumpInt(D, f->locvars[i].endpc); + } + n = (D->strip) ? 0 : f->sizeupvalues; + dumpInt(D, n); + for (i = 0; i < n; i++) + dumpString(D, f->upvalues[i].name); +} + + +static void dumpFunction (DumpState *D, const Proto *f) { + dumpInt(D, f->linedefined); + dumpInt(D, f->lastlinedefined); + dumpByte(D, f->numparams); + dumpByte(D, f->flag); + dumpByte(D, f->maxstacksize); + dumpCode(D, f); + dumpConstants(D, f); + dumpUpvalues(D, f); + dumpProtos(D, f); + dumpString(D, D->strip ? NULL : f->source); + dumpDebug(D, f); +} + + +#define dumpNumInfo(D, tvar, value) \ + { tvar i = value; dumpByte(D, sizeof(tvar)); dumpVar(D, i); } + + +static void dumpHeader (DumpState *D) { + dumpLiteral(D, LUA_SIGNATURE); + dumpByte(D, LUAC_VERSION); + dumpByte(D, LUAC_FORMAT); + dumpLiteral(D, LUAC_DATA); + dumpNumInfo(D, int, LUAC_INT); + dumpNumInfo(D, Instruction, LUAC_INST); + dumpNumInfo(D, lua_Integer, LUAC_INT); + dumpNumInfo(D, lua_Number, LUAC_NUM); +} + + +/* +** dump Lua function as precompiled chunk +*/ +int luaU_dump (lua_State *L, const Proto *f, lua_Writer w, void *data, + int strip) { + DumpState D; + D.h = luaH_new(L); /* aux. table to keep strings already dumped */ + sethvalue2s(L, L->top.p, D.h); /* anchor it */ + L->top.p++; + D.L = L; + D.writer = w; + D.offset = 0; + D.data = data; + D.strip = strip; + D.status = 0; + D.nstr = 0; + dumpHeader(&D); + dumpByte(&D, f->sizeupvalues); + dumpFunction(&D, f); + dumpBlock(&D, NULL, 0); /* signal end of dump */ + return D.status; +} + + +``` + +`Lua/lfunc.c`: + +```c +/* +** $Id: lfunc.c $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + +#define lfunc_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <stddef.h> + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +CClosure *luaF_newCclosure (lua_State *L, int nupvals) { + GCObject *o = luaC_newobj(L, LUA_VCCL, sizeCclosure(nupvals)); + CClosure *c = gco2ccl(o); + c->nupvalues = cast_byte(nupvals); + return c; +} + + +LClosure *luaF_newLclosure (lua_State *L, int nupvals) { + GCObject *o = luaC_newobj(L, LUA_VLCL, sizeLclosure(nupvals)); + LClosure *c = gco2lcl(o); + c->p = NULL; + c->nupvalues = cast_byte(nupvals); + while (nupvals--) c->upvals[nupvals] = NULL; + return c; +} + + +/* +** fill a closure with new closed upvalues +*/ +void luaF_initupvals (lua_State *L, LClosure *cl) { + int i; + for (i = 0; i < cl->nupvalues; i++) { + GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); + UpVal *uv = gco2upv(o); + uv->v.p = &uv->u.value; /* make it closed */ + setnilvalue(uv->v.p); + cl->upvals[i] = uv; + luaC_objbarrier(L, cl, uv); + } +} + + +/* +** Create a new upvalue at the given level, and link it to the list of +** open upvalues of 'L' after entry 'prev'. +**/ +static UpVal *newupval (lua_State *L, StkId level, UpVal **prev) { + GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal)); + UpVal *uv = gco2upv(o); + UpVal *next = *prev; + uv->v.p = s2v(level); /* current value lives in the stack */ + uv->u.open.next = next; /* link it to list of open upvalues */ + uv->u.open.previous = prev; + if (next) + next->u.open.previous = &uv->u.open.next; + *prev = uv; + if (!isintwups(L)) { /* thread not in list of threads with upvalues? */ + L->twups = G(L)->twups; /* link it to the list */ + G(L)->twups = L; + } + return uv; +} + + +/* +** Find and reuse, or create if it does not exist, an upvalue +** at the given level. +*/ +UpVal *luaF_findupval (lua_State *L, StkId level) { + UpVal **pp = &L->openupval; + UpVal *p; + lua_assert(isintwups(L) || L->openupval == NULL); + while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */ + lua_assert(!isdead(G(L), p)); + if (uplevel(p) == level) /* corresponding upvalue? */ + return p; /* return it */ + pp = &p->u.open.next; + } + /* not found: create a new upvalue after 'pp' */ + return newupval(L, level, pp); +} + + +/* +** Call closing method for object 'obj' with error object 'err'. The +** boolean 'yy' controls whether the call is yieldable. +** (This function assumes EXTRA_STACK.) +*/ +static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) { + StkId top = L->top.p; + StkId func = top; + const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); + setobj2s(L, top++, tm); /* will call metamethod... */ + setobj2s(L, top++, obj); /* with 'self' as the 1st argument */ + if (err != NULL) /* if there was an error... */ + setobj2s(L, top++, err); /* then error object will be 2nd argument */ + L->top.p = top; /* add function and arguments */ + if (yy) + luaD_call(L, func, 0); + else + luaD_callnoyield(L, func, 0); +} + + +/* +** Check whether object at given level has a close metamethod and raise +** an error if not. +*/ +static void checkclosemth (lua_State *L, StkId level) { + const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE); + if (ttisnil(tm)) { /* no metamethod? */ + int idx = cast_int(level - L->ci->func.p); /* variable index */ + const char *vname = luaG_findlocal(L, L->ci, idx, NULL); + if (vname == NULL) vname = "?"; + luaG_runerror(L, "variable '%s' got a non-closable value", vname); + } +} + + +/* +** Prepare and call a closing method. +** If status is CLOSEKTOP, the call to the closing method will be pushed +** at the top of the stack. Otherwise, values can be pushed right after +** the 'level' of the upvalue being closed, as everything after that +** won't be used again. +*/ +static void prepcallclosemth (lua_State *L, StkId level, TStatus status, + int yy) { + TValue *uv = s2v(level); /* value being closed */ + TValue *errobj; + switch (status) { + case LUA_OK: + L->top.p = level + 1; /* call will be at this level */ + /* FALLTHROUGH */ + case CLOSEKTOP: /* don't need to change top */ + errobj = NULL; /* no error object */ + break; + default: /* 'luaD_seterrorobj' will set top to level + 2 */ + errobj = s2v(level + 1); /* error object goes after 'uv' */ + luaD_seterrorobj(L, status, level + 1); /* set error object */ + break; + } + callclosemethod(L, uv, errobj, yy); +} + + +/* Maximum value for deltas in 'tbclist' */ +#define MAXDELTA USHRT_MAX + + +/* +** Insert a variable in the list of to-be-closed variables. +*/ +void luaF_newtbcupval (lua_State *L, StkId level) { + lua_assert(level > L->tbclist.p); + if (l_isfalse(s2v(level))) + return; /* false doesn't need to be closed */ + checkclosemth(L, level); /* value must have a close method */ + while (cast_uint(level - L->tbclist.p) > MAXDELTA) { + L->tbclist.p += MAXDELTA; /* create a dummy node at maximum delta */ + L->tbclist.p->tbclist.delta = 0; + } + level->tbclist.delta = cast(unsigned short, level - L->tbclist.p); + L->tbclist.p = level; +} + + +void luaF_unlinkupval (UpVal *uv) { + lua_assert(upisopen(uv)); + *uv->u.open.previous = uv->u.open.next; + if (uv->u.open.next) + uv->u.open.next->u.open.previous = uv->u.open.previous; +} + + +/* +** Close all upvalues up to the given stack level. +*/ +void luaF_closeupval (lua_State *L, StkId level) { + UpVal *uv; + while ((uv = L->openupval) != NULL && uplevel(uv) >= level) { + TValue *slot = &uv->u.value; /* new position for value */ + lua_assert(uplevel(uv) < L->top.p); + luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */ + setobj(L, slot, uv->v.p); /* move value to upvalue slot */ + uv->v.p = slot; /* now current value lives here */ + if (!iswhite(uv)) { /* neither white nor dead? */ + nw2black(uv); /* closed upvalues cannot be gray */ + luaC_barrier(L, uv, slot); + } + } +} + + +/* +** Remove first element from the tbclist plus its dummy nodes. +*/ +static void poptbclist (lua_State *L) { + StkId tbc = L->tbclist.p; + lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */ + tbc -= tbc->tbclist.delta; + while (tbc > L->stack.p && tbc->tbclist.delta == 0) + tbc -= MAXDELTA; /* remove dummy nodes */ + L->tbclist.p = tbc; +} + + +/* +** Close all upvalues and to-be-closed variables up to the given stack +** level. Return restored 'level'. +*/ +StkId luaF_close (lua_State *L, StkId level, TStatus status, int yy) { + ptrdiff_t levelrel = savestack(L, level); + luaF_closeupval(L, level); /* first, close the upvalues */ + while (L->tbclist.p >= level) { /* traverse tbc's down to that level */ + StkId tbc = L->tbclist.p; /* get variable index */ + poptbclist(L); /* remove it from list */ + prepcallclosemth(L, tbc, status, yy); /* close variable */ + level = restorestack(L, levelrel); + } + return level; +} + + +Proto *luaF_newproto (lua_State *L) { + GCObject *o = luaC_newobj(L, LUA_VPROTO, sizeof(Proto)); + Proto *f = gco2p(o); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->lineinfo = NULL; + f->sizelineinfo = 0; + f->abslineinfo = NULL; + f->sizeabslineinfo = 0; + f->upvalues = NULL; + f->sizeupvalues = 0; + f->numparams = 0; + f->flag = 0; + f->maxstacksize = 0; + f->locvars = NULL; + f->sizelocvars = 0; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + + +lu_mem luaF_protosize (Proto *p) { + lu_mem sz = cast(lu_mem, sizeof(Proto)) + + cast_uint(p->sizep) * sizeof(Proto*) + + cast_uint(p->sizek) * sizeof(TValue) + + cast_uint(p->sizelocvars) * sizeof(LocVar) + + cast_uint(p->sizeupvalues) * sizeof(Upvaldesc); + if (!(p->flag & PF_FIXED)) { + sz += cast_uint(p->sizecode) * sizeof(Instruction); + sz += cast_uint(p->sizelineinfo) * sizeof(lu_byte); + sz += cast_uint(p->sizeabslineinfo) * sizeof(AbsLineInfo); + } + return sz; +} + + +void luaF_freeproto (lua_State *L, Proto *f) { + if (!(f->flag & PF_FIXED)) { + luaM_freearray(L, f->code, cast_sizet(f->sizecode)); + luaM_freearray(L, f->lineinfo, cast_sizet(f->sizelineinfo)); + luaM_freearray(L, f->abslineinfo, cast_sizet(f->sizeabslineinfo)); + } + luaM_freearray(L, f->p, cast_sizet(f->sizep)); + luaM_freearray(L, f->k, cast_sizet(f->sizek)); + luaM_freearray(L, f->locvars, cast_sizet(f->sizelocvars)); + luaM_freearray(L, f->upvalues, cast_sizet(f->sizeupvalues)); + luaM_free(L, f); +} + + +/* +** Look for n-th local variable at line 'line' in function 'func'. +** Returns NULL if not found. +*/ +const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { + int i; + for (i = 0; i<f->sizelocvars && f->locvars[i].startpc <= pc; i++) { + if (pc < f->locvars[i].endpc) { /* is variable active? */ + local_number--; + if (local_number == 0) + return getstr(f->locvars[i].varname); + } + } + return NULL; /* not found */ +} + + +``` + +`Lua/lfunc.h`: + +```h +/* +** $Id: lfunc.h $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + +#ifndef lfunc_h +#define lfunc_h + + +#include "lobject.h" + + +#define sizeCclosure(n) \ + (offsetof(CClosure, upvalue) + sizeof(TValue) * cast_uint(n)) + +#define sizeLclosure(n) \ + (offsetof(LClosure, upvals) + sizeof(UpVal *) * cast_uint(n)) + + +/* test whether thread is in 'twups' list */ +#define isintwups(L) (L->twups != L) + + +/* +** maximum number of upvalues in a closure (both C and Lua). (Value +** must fit in a VM register.) +*/ +#define MAXUPVAL 255 + + +#define upisopen(up) ((up)->v.p != &(up)->u.value) + + +#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v.p)) + + +/* +** maximum number of misses before giving up the cache of closures +** in prototypes +*/ +#define MAXMISS 10 + + + +/* special status to close upvalues preserving the top of the stack */ +#define CLOSEKTOP (LUA_ERRERR + 1) + + +LUAI_FUNC Proto *luaF_newproto (lua_State *L); +LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nupvals); +LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals); +LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); +LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); +LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); +LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level); +LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, TStatus status, int yy); +LUAI_FUNC void luaF_unlinkupval (UpVal *uv); +LUAI_FUNC lu_mem luaF_protosize (Proto *p); +LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); +LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, + int pc); + + +#endif + +``` + +`Lua/lgc.c`: + +```c +/* +** $Id: lgc.c $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#define lgc_c +#define LUA_CORE + +#include "lprefix.h" + +#include <string.h> + + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + +/* +** Maximum number of elements to sweep in each single step. +** (Large enough to dissipate fixed overheads but small enough +** to allow small steps for the collector.) +*/ +#define GCSWEEPMAX 20 + + +/* +** Cost (in work units) of running one finalizer. +*/ +#define CWUFIN 10 + + +/* mask with all color bits */ +#define maskcolors (bitmask(BLACKBIT) | WHITEBITS) + +/* mask with all GC bits */ +#define maskgcbits (maskcolors | AGEBITS) + + +/* macro to erase all color bits then set only the current white bit */ +#define makewhite(g,x) \ + (x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g))) + +/* make an object gray (neither white nor black) */ +#define set2gray(x) resetbits(x->marked, maskcolors) + + +/* make an object black (coming from any color) */ +#define set2black(x) \ + (x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT))) + + +#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) + +#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n))) + + +/* +** Protected access to objects in values +*/ +#define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL) + + +/* +** Access to collectable objects in array part of tables +*/ +#define gcvalarr(t,i) \ + ((*getArrTag(t,i) & BIT_ISCOLLECTABLE) ? getArrVal(t,i)->gc : NULL) + + +#define markvalue(g,o) { checkliveness(mainthread(g),o); \ + if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); } + +#define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); } + +#define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); } + +/* +** mark an object that can be NULL (either because it is really optional, +** or it was stripped as debug info, or inside an uncompleted structure) +*/ +#define markobjectN(g,t) { if (t) markobject(g,t); } + + +static void reallymarkobject (global_State *g, GCObject *o); +static void atomic (lua_State *L); +static void entersweep (lua_State *L); + + +/* +** {====================================================== +** Generic functions +** ======================================================= +*/ + + +/* +** one after last element in a hash array +*/ +#define gnodelast(h) gnode(h, cast_sizet(sizenode(h))) + + +static l_mem objsize (GCObject *o) { + lu_mem res; + switch (o->tt) { + case LUA_VTABLE: { + res = luaH_size(gco2t(o)); + break; + } + case LUA_VLCL: { + LClosure *cl = gco2lcl(o); + res = sizeLclosure(cl->nupvalues); + break; + } + case LUA_VCCL: { + CClosure *cl = gco2ccl(o); + res = sizeCclosure(cl->nupvalues); + break; + } + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + res = sizeudata(u->nuvalue, u->len); + break; + } + case LUA_VPROTO: { + res = luaF_protosize(gco2p(o)); + break; + } + case LUA_VTHREAD: { + res = luaE_threadsize(gco2th(o)); + break; + } + case LUA_VSHRSTR: { + TString *ts = gco2ts(o); + res = sizestrshr(cast_uint(ts->shrlen)); + break; + } + case LUA_VLNGSTR: { + TString *ts = gco2ts(o); + res = luaS_sizelngstr(ts->u.lnglen, ts->shrlen); + break; + } + case LUA_VUPVAL: { + res = sizeof(UpVal); + break; + } + default: res = 0; lua_assert(0); + } + return cast(l_mem, res); +} + + +static GCObject **getgclist (GCObject *o) { + switch (o->tt) { + case LUA_VTABLE: return &gco2t(o)->gclist; + case LUA_VLCL: return &gco2lcl(o)->gclist; + case LUA_VCCL: return &gco2ccl(o)->gclist; + case LUA_VTHREAD: return &gco2th(o)->gclist; + case LUA_VPROTO: return &gco2p(o)->gclist; + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + lua_assert(u->nuvalue > 0); + return &u->gclist; + } + default: lua_assert(0); return 0; + } +} + + +/* +** Link a collectable object 'o' with a known type into the list 'p'. +** (Must be a macro to access the 'gclist' field in different types.) +*/ +#define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p)) + +static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) { + lua_assert(!isgray(o)); /* cannot be in a gray list */ + *pnext = *list; + *list = o; + set2gray(o); /* now it is */ +} + + +/* +** Link a generic collectable object 'o' into the list 'p'. +*/ +#define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p)) + + + +/* +** Clear keys for empty entries in tables. If entry is empty, mark its +** entry as dead. This allows the collection of the key, but keeps its +** entry in the table: its removal could break a chain and could break +** a table traversal. Other places never manipulate dead keys, because +** its associated empty value is enough to signal that the entry is +** logically empty. +*/ +static void clearkey (Node *n) { + lua_assert(isempty(gval(n))); + if (keyiscollectable(n)) + setdeadkey(n); /* unused key; remove it */ +} + + +/* +** tells whether a key or value can be cleared from a weak +** table. Non-collectable objects are never removed from weak +** tables. Strings behave as 'values', so are never removed too. for +** other objects: if really collected, cannot keep them; for objects +** being finalized, keep them in keys, but not in values +*/ +static int iscleared (global_State *g, const GCObject *o) { + if (o == NULL) return 0; /* non-collectable value */ + else if (novariant(o->tt) == LUA_TSTRING) { + markobject(g, o); /* strings are 'values', so are never weak */ + return 0; + } + else return iswhite(o); +} + + +/* +** Barrier that moves collector forward, that is, marks the white object +** 'v' being pointed by the black object 'o'. In the generational +** mode, 'v' must also become old, if 'o' is old; however, it cannot +** be changed directly to OLD, because it may still point to non-old +** objects. So, it is marked as OLD0. In the next cycle it will become +** OLD1, and in the next it will finally become OLD (regular old). By +** then, any object it points to will also be old. If called in the +** incremental sweep phase, it clears the black object to white (sweep +** it) to avoid other barrier calls for this same object. (That cannot +** be done is generational mode, as its sweep does not distinguish +** white from dead.) +*/ +void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) { + global_State *g = G(L); + lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); + if (keepinvariant(g)) { /* must keep invariant? */ + reallymarkobject(g, v); /* restore invariant */ + if (isold(o)) { + lua_assert(!isold(v)); /* white object could not be old */ + setage(v, G_OLD0); /* restore generational invariant */ + } + } + else { /* sweep phase */ + lua_assert(issweepphase(g)); + if (g->gckind != KGC_GENMINOR) /* incremental mode? */ + makewhite(g, o); /* mark 'o' as white to avoid other barriers */ + } +} + + +/* +** barrier that moves collector backward, that is, mark the black object +** pointing to a white object as gray again. +*/ +void luaC_barrierback_ (lua_State *L, GCObject *o) { + global_State *g = G(L); + lua_assert(isblack(o) && !isdead(g, o)); + lua_assert((g->gckind != KGC_GENMINOR) + || (isold(o) && getage(o) != G_TOUCHED1)); + if (getage(o) == G_TOUCHED2) /* already in gray list? */ + set2gray(o); /* make it gray to become touched1 */ + else /* link it in 'grayagain' and paint it gray */ + linkobjgclist(o, g->grayagain); + if (isold(o)) /* generational mode? */ + setage(o, G_TOUCHED1); /* touched in current cycle */ +} + + +void luaC_fix (lua_State *L, GCObject *o) { + global_State *g = G(L); + lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ + set2gray(o); /* they will be gray forever */ + setage(o, G_OLD); /* and old forever */ + g->allgc = o->next; /* remove object from 'allgc' list */ + o->next = g->fixedgc; /* link it to 'fixedgc' list */ + g->fixedgc = o; +} + + +/* +** create a new collectable object (with given type, size, and offset) +** and link it to 'allgc' list. +*/ +GCObject *luaC_newobjdt (lua_State *L, lu_byte tt, size_t sz, size_t offset) { + global_State *g = G(L); + char *p = cast_charp(luaM_newobject(L, novariant(tt), sz)); + GCObject *o = cast(GCObject *, p + offset); + o->marked = luaC_white(g); + o->tt = tt; + o->next = g->allgc; + g->allgc = o; + return o; +} + + +/* +** create a new collectable object with no offset. +*/ +GCObject *luaC_newobj (lua_State *L, lu_byte tt, size_t sz) { + return luaC_newobjdt(L, tt, sz, 0); +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Mark functions +** ======================================================= +*/ + + +/* +** Mark an object. Userdata with no user values, strings, and closed +** upvalues are visited and turned black here. Open upvalues are +** already indirectly linked through their respective threads in the +** 'twups' list, so they don't go to the gray list; nevertheless, they +** are kept gray to avoid barriers, as their values will be revisited +** by the thread or by 'remarkupvals'. Other objects are added to the +** gray list to be visited (and turned black) later. Both userdata and +** upvalues can call this function recursively, but this recursion goes +** for at most two levels: An upvalue cannot refer to another upvalue +** (only closures can), and a userdata's metatable must be a table. +*/ +static void reallymarkobject (global_State *g, GCObject *o) { + g->GCmarked += objsize(o); + switch (o->tt) { + case LUA_VSHRSTR: + case LUA_VLNGSTR: { + set2black(o); /* nothing to visit */ + break; + } + case LUA_VUPVAL: { + UpVal *uv = gco2upv(o); + if (upisopen(uv)) + set2gray(uv); /* open upvalues are kept gray */ + else + set2black(uv); /* closed upvalues are visited here */ + markvalue(g, uv->v.p); /* mark its content */ + break; + } + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + if (u->nuvalue == 0) { /* no user values? */ + markobjectN(g, u->metatable); /* mark its metatable */ + set2black(u); /* nothing else to mark */ + break; + } + /* else... */ + } /* FALLTHROUGH */ + case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE: + case LUA_VTHREAD: case LUA_VPROTO: { + linkobjgclist(o, g->gray); /* to be visited later */ + break; + } + default: lua_assert(0); break; + } +} + + +/* +** mark metamethods for basic types +*/ +static void markmt (global_State *g) { + int i; + for (i=0; i < LUA_NUMTYPES; i++) + markobjectN(g, g->mt[i]); +} + + +/* +** mark all objects in list of being-finalized +*/ +static void markbeingfnz (global_State *g) { + GCObject *o; + for (o = g->tobefnz; o != NULL; o = o->next) + markobject(g, o); +} + + +/* +** For each non-marked thread, simulates a barrier between each open +** upvalue and its value. (If the thread is collected, the value will be +** assigned to the upvalue, but then it can be too late for the barrier +** to act. The "barrier" does not need to check colors: A non-marked +** thread must be young; upvalues cannot be older than their threads; so +** any visited upvalue must be young too.) Also removes the thread from +** the list, as it was already visited. Removes also threads with no +** upvalues, as they have nothing to be checked. (If the thread gets an +** upvalue later, it will be linked in the list again.) +*/ +static void remarkupvals (global_State *g) { + lua_State *thread; + lua_State **p = &g->twups; + while ((thread = *p) != NULL) { + if (!iswhite(thread) && thread->openupval != NULL) + p = &thread->twups; /* keep marked thread with upvalues in the list */ + else { /* thread is not marked or without upvalues */ + UpVal *uv; + lua_assert(!isold(thread) || thread->openupval == NULL); + *p = thread->twups; /* remove thread from the list */ + thread->twups = thread; /* mark that it is out of list */ + for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) { + lua_assert(getage(uv) <= getage(thread)); + if (!iswhite(uv)) { /* upvalue already visited? */ + lua_assert(upisopen(uv) && isgray(uv)); + markvalue(g, uv->v.p); /* mark its value */ + } + } + } + } +} + + +static void cleargraylists (global_State *g) { + g->gray = g->grayagain = NULL; + g->weak = g->allweak = g->ephemeron = NULL; +} + + +/* +** mark root set and reset all gray lists, to start a new collection. +** 'GCmarked' is initialized to count the total number of live bytes +** during a cycle. +*/ +static void restartcollection (global_State *g) { + cleargraylists(g); + g->GCmarked = 0; + markobject(g, mainthread(g)); + markvalue(g, &g->l_registry); + markmt(g); + markbeingfnz(g); /* mark any finalizing object left from previous cycle */ +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Traverse functions +** ======================================================= +*/ + + +/* +** Check whether object 'o' should be kept in the 'grayagain' list for +** post-processing by 'correctgraylist'. (It could put all old objects +** in the list and leave all the work to 'correctgraylist', but it is +** more efficient to avoid adding elements that will be removed.) Only +** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go +** back to a gray list, but then it must become OLD. (That is what +** 'correctgraylist' does when it finds a TOUCHED2 object.) +** This function is a no-op in incremental mode, as objects cannot be +** marked as touched in that mode. +*/ +static void genlink (global_State *g, GCObject *o) { + lua_assert(isblack(o)); + if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */ + linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */ + } /* everything else do not need to be linked back */ + else if (getage(o) == G_TOUCHED2) + setage(o, G_OLD); /* advance age */ +} + + +/* +** Traverse a table with weak values and link it to proper list. During +** propagate phase, keep it in 'grayagain' list, to be revisited in the +** atomic phase. In the atomic phase, if table has any white value, +** put it in 'weak' list, to be cleared; otherwise, call 'genlink' +** to check table age in generational mode. +*/ +static void traverseweakvalue (global_State *g, Table *h) { + Node *n, *limit = gnodelast(h); + /* if there is array part, assume it may have white values (it is not + worth traversing it now just to check) */ + int hasclears = (h->asize > 0); + for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ + if (isempty(gval(n))) /* entry is empty? */ + clearkey(n); /* clear its key */ + else { + lua_assert(!keyisnil(n)); + markkey(g, n); + if (!hasclears && iscleared(g, gcvalueN(gval(n)))) /* a white value? */ + hasclears = 1; /* table will have to be cleared */ + } + } + if (g->gcstate == GCSpropagate) + linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ + else if (hasclears) + linkgclist(h, g->weak); /* has to be cleared later */ + else + genlink(g, obj2gco(h)); +} + + +/* +** Traverse the array part of a table. +*/ +static int traversearray (global_State *g, Table *h) { + unsigned asize = h->asize; + int marked = 0; /* true if some object is marked in this traversal */ + unsigned i; + for (i = 0; i < asize; i++) { + GCObject *o = gcvalarr(h, i); + if (o != NULL && iswhite(o)) { + marked = 1; + reallymarkobject(g, o); + } + } + return marked; +} + + +/* +** Traverse an ephemeron table and link it to proper list. Returns true +** iff any object was marked during this traversal (which implies that +** convergence has to continue). During propagation phase, keep table +** in 'grayagain' list, to be visited again in the atomic phase. In +** the atomic phase, if table has any white->white entry, it has to +** be revisited during ephemeron convergence (as that key may turn +** black). Otherwise, if it has any white key, table has to be cleared +** (in the atomic phase). In generational mode, some tables +** must be kept in some gray list for post-processing; this is done +** by 'genlink'. +*/ +static int traverseephemeron (global_State *g, Table *h, int inv) { + int hasclears = 0; /* true if table has white keys */ + int hasww = 0; /* true if table has entry "white-key -> white-value" */ + unsigned int i; + unsigned int nsize = sizenode(h); + int marked = traversearray(g, h); /* traverse array part */ + /* traverse hash part; if 'inv', traverse descending + (see 'convergeephemerons') */ + for (i = 0; i < nsize; i++) { + Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i); + if (isempty(gval(n))) /* entry is empty? */ + clearkey(n); /* clear its key */ + else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */ + hasclears = 1; /* table must be cleared */ + if (valiswhite(gval(n))) /* value not marked yet? */ + hasww = 1; /* white-white entry */ + } + else if (valiswhite(gval(n))) { /* value not marked yet? */ + marked = 1; + reallymarkobject(g, gcvalue(gval(n))); /* mark it now */ + } + } + /* link table into proper list */ + if (g->gcstate == GCSpropagate) + linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */ + else if (hasww) /* table has white->white entries? */ + linkgclist(h, g->ephemeron); /* have to propagate again */ + else if (hasclears) /* table has white keys? */ + linkgclist(h, g->allweak); /* may have to clean white keys */ + else + genlink(g, obj2gco(h)); /* check whether collector still needs to see it */ + return marked; +} + + +static void traversestrongtable (global_State *g, Table *h) { + Node *n, *limit = gnodelast(h); + traversearray(g, h); + for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */ + if (isempty(gval(n))) /* entry is empty? */ + clearkey(n); /* clear its key */ + else { + lua_assert(!keyisnil(n)); + markkey(g, n); + markvalue(g, gval(n)); + } + } + genlink(g, obj2gco(h)); +} + + +/* +** (result & 1) iff weak values; (result & 2) iff weak keys. +*/ +static int getmode (global_State *g, Table *h) { + const TValue *mode = gfasttm(g, h->metatable, TM_MODE); + if (mode == NULL || !ttisstring(mode)) + return 0; /* ignore non-string modes */ + else { + const char *smode = getstr(tsvalue(mode)); + const char *weakkey = strchr(smode, 'k'); + const char *weakvalue = strchr(smode, 'v'); + return ((weakkey != NULL) << 1) | (weakvalue != NULL); + } +} + + +static l_mem traversetable (global_State *g, Table *h) { + markobjectN(g, h->metatable); + switch (getmode(g, h)) { + case 0: /* not weak */ + traversestrongtable(g, h); + break; + case 1: /* weak values */ + traverseweakvalue(g, h); + break; + case 2: /* weak keys */ + traverseephemeron(g, h, 0); + break; + case 3: /* all weak; nothing to traverse */ + if (g->gcstate == GCSpropagate) + linkgclist(h, g->grayagain); /* must visit again its metatable */ + else + linkgclist(h, g->allweak); /* must clear collected entries */ + break; + } + return cast(l_mem, 1 + 2*sizenode(h) + h->asize); +} + + +static l_mem traverseudata (global_State *g, Udata *u) { + int i; + markobjectN(g, u->metatable); /* mark its metatable */ + for (i = 0; i < u->nuvalue; i++) + markvalue(g, &u->uv[i].uv); + genlink(g, obj2gco(u)); + return 1 + u->nuvalue; +} + + +/* +** Traverse a prototype. (While a prototype is being build, its +** arrays can be larger than needed; the extra slots are filled with +** NULL, so the use of 'markobjectN') +*/ +static l_mem traverseproto (global_State *g, Proto *f) { + int i; + markobjectN(g, f->source); + for (i = 0; i < f->sizek; i++) /* mark literals */ + markvalue(g, &f->k[i]); + for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */ + markobjectN(g, f->upvalues[i].name); + for (i = 0; i < f->sizep; i++) /* mark nested protos */ + markobjectN(g, f->p[i]); + for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */ + markobjectN(g, f->locvars[i].varname); + return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars; +} + + +static l_mem traverseCclosure (global_State *g, CClosure *cl) { + int i; + for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ + markvalue(g, &cl->upvalue[i]); + return 1 + cl->nupvalues; +} + +/* +** Traverse a Lua closure, marking its prototype and its upvalues. +** (Both can be NULL while closure is being created.) +*/ +static l_mem traverseLclosure (global_State *g, LClosure *cl) { + int i; + markobjectN(g, cl->p); /* mark its prototype */ + for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */ + UpVal *uv = cl->upvals[i]; + markobjectN(g, uv); /* mark upvalue */ + } + return 1 + cl->nupvalues; +} + + +/* +** Traverse a thread, marking the elements in the stack up to its top +** and cleaning the rest of the stack in the final traversal. That +** ensures that the entire stack have valid (non-dead) objects. +** Threads have no barriers. In gen. mode, old threads must be visited +** at every cycle, because they might point to young objects. In inc. +** mode, the thread can still be modified before the end of the cycle, +** and therefore it must be visited again in the atomic phase. To ensure +** these visits, threads must return to a gray list if they are not new +** (which can only happen in generational mode) or if the traverse is in +** the propagate phase (which can only happen in incremental mode). +*/ +static l_mem traversethread (global_State *g, lua_State *th) { + UpVal *uv; + StkId o = th->stack.p; + if (isold(th) || g->gcstate == GCSpropagate) + linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ + if (o == NULL) + return 0; /* stack not completely built yet */ + lua_assert(g->gcstate == GCSatomic || + th->openupval == NULL || isintwups(th)); + for (; o < th->top.p; o++) /* mark live elements in the stack */ + markvalue(g, s2v(o)); + for (uv = th->openupval; uv != NULL; uv = uv->u.open.next) + markobject(g, uv); /* open upvalues cannot be collected */ + if (g->gcstate == GCSatomic) { /* final traversal? */ + if (!g->gcemergency) + luaD_shrinkstack(th); /* do not change stack in emergency cycle */ + for (o = th->top.p; o < th->stack_last.p + EXTRA_STACK; o++) + setnilvalue(s2v(o)); /* clear dead stack slice */ + /* 'remarkupvals' may have removed thread from 'twups' list */ + if (!isintwups(th) && th->openupval != NULL) { + th->twups = g->twups; /* link it back to the list */ + g->twups = th; + } + } + return 1 + (th->top.p - th->stack.p); +} + + +/* +** traverse one gray object, turning it to black. Return an estimate +** of the number of slots traversed. +*/ +static l_mem propagatemark (global_State *g) { + GCObject *o = g->gray; + nw2black(o); + g->gray = *getgclist(o); /* remove from 'gray' list */ + switch (o->tt) { + case LUA_VTABLE: return traversetable(g, gco2t(o)); + case LUA_VUSERDATA: return traverseudata(g, gco2u(o)); + case LUA_VLCL: return traverseLclosure(g, gco2lcl(o)); + case LUA_VCCL: return traverseCclosure(g, gco2ccl(o)); + case LUA_VPROTO: return traverseproto(g, gco2p(o)); + case LUA_VTHREAD: return traversethread(g, gco2th(o)); + default: lua_assert(0); return 0; + } +} + + +static void propagateall (global_State *g) { + while (g->gray) + propagatemark(g); +} + + +/* +** Traverse all ephemeron tables propagating marks from keys to values. +** Repeat until it converges, that is, nothing new is marked. 'dir' +** inverts the direction of the traversals, trying to speed up +** convergence on chains in the same table. +*/ +static void convergeephemerons (global_State *g) { + int changed; + int dir = 0; + do { + GCObject *w; + GCObject *next = g->ephemeron; /* get ephemeron list */ + g->ephemeron = NULL; /* tables may return to this list when traversed */ + changed = 0; + while ((w = next) != NULL) { /* for each ephemeron table */ + Table *h = gco2t(w); + next = h->gclist; /* list is rebuilt during loop */ + nw2black(h); /* out of the list (for now) */ + if (traverseephemeron(g, h, dir)) { /* marked some value? */ + propagateall(g); /* propagate changes */ + changed = 1; /* will have to revisit all ephemeron tables */ + } + } + dir = !dir; /* invert direction next time */ + } while (changed); /* repeat until no more changes */ +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Sweep Functions +** ======================================================= +*/ + + +/* +** clear entries with unmarked keys from all weaktables in list 'l' +*/ +static void clearbykeys (global_State *g, GCObject *l) { + for (; l; l = gco2t(l)->gclist) { + Table *h = gco2t(l); + Node *limit = gnodelast(h); + Node *n; + for (n = gnode(h, 0); n < limit; n++) { + if (iscleared(g, gckeyN(n))) /* unmarked key? */ + setempty(gval(n)); /* remove entry */ + if (isempty(gval(n))) /* is entry empty? */ + clearkey(n); /* clear its key */ + } + } +} + + +/* +** clear entries with unmarked values from all weaktables in list 'l' up +** to element 'f' +*/ +static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) { + for (; l != f; l = gco2t(l)->gclist) { + Table *h = gco2t(l); + Node *n, *limit = gnodelast(h); + unsigned int i; + unsigned int asize = h->asize; + for (i = 0; i < asize; i++) { + GCObject *o = gcvalarr(h, i); + if (iscleared(g, o)) /* value was collected? */ + *getArrTag(h, i) = LUA_VEMPTY; /* remove entry */ + } + for (n = gnode(h, 0); n < limit; n++) { + if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */ + setempty(gval(n)); /* remove entry */ + if (isempty(gval(n))) /* is entry empty? */ + clearkey(n); /* clear its key */ + } + } +} + + +static void freeupval (lua_State *L, UpVal *uv) { + if (upisopen(uv)) + luaF_unlinkupval(uv); + luaM_free(L, uv); +} + + +static void freeobj (lua_State *L, GCObject *o) { + assert_code(l_mem newmem = gettotalbytes(G(L)) - objsize(o)); + switch (o->tt) { + case LUA_VPROTO: + luaF_freeproto(L, gco2p(o)); + break; + case LUA_VUPVAL: + freeupval(L, gco2upv(o)); + break; + case LUA_VLCL: { + LClosure *cl = gco2lcl(o); + luaM_freemem(L, cl, sizeLclosure(cl->nupvalues)); + break; + } + case LUA_VCCL: { + CClosure *cl = gco2ccl(o); + luaM_freemem(L, cl, sizeCclosure(cl->nupvalues)); + break; + } + case LUA_VTABLE: + luaH_free(L, gco2t(o)); + break; + case LUA_VTHREAD: + luaE_freethread(L, gco2th(o)); + break; + case LUA_VUSERDATA: { + Udata *u = gco2u(o); + luaM_freemem(L, o, sizeudata(u->nuvalue, u->len)); + break; + } + case LUA_VSHRSTR: { + TString *ts = gco2ts(o); + luaS_remove(L, ts); /* remove it from hash table */ + luaM_freemem(L, ts, sizestrshr(cast_uint(ts->shrlen))); + break; + } + case LUA_VLNGSTR: { + TString *ts = gco2ts(o); + if (ts->shrlen == LSTRMEM) /* must free external string? */ + (*ts->falloc)(ts->ud, ts->contents, ts->u.lnglen + 1, 0); + luaM_freemem(L, ts, luaS_sizelngstr(ts->u.lnglen, ts->shrlen)); + break; + } + default: lua_assert(0); + } + lua_assert(gettotalbytes(G(L)) == newmem); +} + + +/* +** sweep at most 'countin' elements from a list of GCObjects erasing dead +** objects, where a dead object is one marked with the old (non current) +** white; change all non-dead objects back to white (and new), preparing +** for next collection cycle. Return where to continue the traversal or +** NULL if list is finished. +*/ +static GCObject **sweeplist (lua_State *L, GCObject **p, l_mem countin) { + global_State *g = G(L); + int ow = otherwhite(g); + int white = luaC_white(g); /* current white */ + while (*p != NULL && countin-- > 0) { + GCObject *curr = *p; + int marked = curr->marked; + if (isdeadm(ow, marked)) { /* is 'curr' dead? */ + *p = curr->next; /* remove 'curr' from list */ + freeobj(L, curr); /* erase 'curr' */ + } + else { /* change mark to 'white' and age to 'new' */ + curr->marked = cast_byte((marked & ~maskgcbits) | white | G_NEW); + p = &curr->next; /* go to next element */ + } + } + return (*p == NULL) ? NULL : p; +} + + +/* +** sweep a list until a live object (or end of list) +*/ +static GCObject **sweeptolive (lua_State *L, GCObject **p) { + GCObject **old = p; + do { + p = sweeplist(L, p, 1); + } while (p == old); + return p; +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Finalization +** ======================================================= +*/ + +/* +** If possible, shrink string table. +*/ +static void checkSizes (lua_State *L, global_State *g) { + if (!g->gcemergency) { + if (g->strt.nuse < g->strt.size / 4) /* string table too big? */ + luaS_resize(L, g->strt.size / 2); + } +} + + +/* +** Get the next udata to be finalized from the 'tobefnz' list, and +** link it back into the 'allgc' list. +*/ +static GCObject *udata2finalize (global_State *g) { + GCObject *o = g->tobefnz; /* get first element */ + lua_assert(tofinalize(o)); + g->tobefnz = o->next; /* remove it from 'tobefnz' list */ + o->next = g->allgc; /* return it to 'allgc' list */ + g->allgc = o; + resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ + if (issweepphase(g)) + makewhite(g, o); /* "sweep" object */ + else if (getage(o) == G_OLD1) + g->firstold1 = o; /* it is the first OLD1 object in the list */ + return o; +} + + +static void dothecall (lua_State *L, void *ud) { + UNUSED(ud); + luaD_callnoyield(L, L->top.p - 2, 0); +} + + +static void GCTM (lua_State *L) { + global_State *g = G(L); + const TValue *tm; + TValue v; + lua_assert(!g->gcemergency); + setgcovalue(L, &v, udata2finalize(g)); + tm = luaT_gettmbyobj(L, &v, TM_GC); + if (!notm(tm)) { /* is there a finalizer? */ + TStatus status; + lu_byte oldah = L->allowhook; + lu_byte oldgcstp = g->gcstp; + g->gcstp |= GCSTPGC; /* avoid GC steps */ + L->allowhook = 0; /* stop debug hooks during GC metamethod */ + setobj2s(L, L->top.p++, tm); /* push finalizer... */ + setobj2s(L, L->top.p++, &v); /* ... and its argument */ + L->ci->callstatus |= CIST_FIN; /* will run a finalizer */ + status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top.p - 2), 0); + L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ + L->allowhook = oldah; /* restore hooks */ + g->gcstp = oldgcstp; /* restore state */ + if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */ + luaE_warnerror(L, "__gc"); + L->top.p--; /* pops error object */ + } + } +} + + +/* +** call all pending finalizers +*/ +static void callallpendingfinalizers (lua_State *L) { + global_State *g = G(L); + while (g->tobefnz) + GCTM(L); +} + + +/* +** find last 'next' field in list 'p' list (to add elements in its end) +*/ +static GCObject **findlast (GCObject **p) { + while (*p != NULL) + p = &(*p)->next; + return p; +} + + +/* +** Move all unreachable objects (or 'all' objects) that need +** finalization from list 'finobj' to list 'tobefnz' (to be finalized). +** (Note that objects after 'finobjold1' cannot be white, so they +** don't need to be traversed. In incremental mode, 'finobjold1' is NULL, +** so the whole list is traversed.) +*/ +static void separatetobefnz (global_State *g, int all) { + GCObject *curr; + GCObject **p = &g->finobj; + GCObject **lastnext = findlast(&g->tobefnz); + while ((curr = *p) != g->finobjold1) { /* traverse all finalizable objects */ + lua_assert(tofinalize(curr)); + if (!(iswhite(curr) || all)) /* not being collected? */ + p = &curr->next; /* don't bother with it */ + else { + if (curr == g->finobjsur) /* removing 'finobjsur'? */ + g->finobjsur = curr->next; /* correct it */ + *p = curr->next; /* remove 'curr' from 'finobj' list */ + curr->next = *lastnext; /* link at the end of 'tobefnz' list */ + *lastnext = curr; + lastnext = &curr->next; + } + } +} + + +/* +** If pointer 'p' points to 'o', move it to the next element. +*/ +static void checkpointer (GCObject **p, GCObject *o) { + if (o == *p) + *p = o->next; +} + + +/* +** Correct pointers to objects inside 'allgc' list when +** object 'o' is being removed from the list. +*/ +static void correctpointers (global_State *g, GCObject *o) { + checkpointer(&g->survival, o); + checkpointer(&g->old1, o); + checkpointer(&g->reallyold, o); + checkpointer(&g->firstold1, o); +} + + +/* +** if object 'o' has a finalizer, remove it from 'allgc' list (must +** search the list to find it) and link it in 'finobj' list. +*/ +void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) { + global_State *g = G(L); + if (tofinalize(o) || /* obj. is already marked... */ + gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */ + (g->gcstp & GCSTPCLS)) /* or closing state? */ + return; /* nothing to be done */ + else { /* move 'o' to 'finobj' list */ + GCObject **p; + if (issweepphase(g)) { + makewhite(g, o); /* "sweep" object 'o' */ + if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ + g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ + } + else + correctpointers(g, o); + /* search for pointer pointing to 'o' */ + for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } + *p = o->next; /* remove 'o' from 'allgc' list */ + o->next = g->finobj; /* link it in 'finobj' list */ + g->finobj = o; + l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */ + } +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Generational Collector +** ======================================================= +*/ + +/* +** Fields 'GCmarked' and 'GCmajorminor' are used to control the pace and +** the mode of the collector. They play several roles, depending on the +** mode of the collector: +** * KGC_INC: +** GCmarked: number of marked bytes during a cycle. +** GCmajorminor: not used. +** * KGC_GENMINOR +** GCmarked: number of bytes that became old since last major collection. +** GCmajorminor: number of bytes marked in last major collection. +** * KGC_GENMAJOR +** GCmarked: number of bytes that became old since last major collection. +** GCmajorminor: number of bytes marked in last major collection. +*/ + + +/* +** Set the "time" to wait before starting a new incremental cycle; +** cycle will start when number of bytes in use hits the threshold of +** approximately (marked * pause / 100). +*/ +static void setpause (global_State *g) { + l_mem threshold = applygcparam(g, PAUSE, g->GCmarked); + l_mem debt = threshold - gettotalbytes(g); + if (debt < 0) debt = 0; + luaE_setdebt(g, debt); +} + + +/* +** Sweep a list of objects to enter generational mode. Deletes dead +** objects and turns the non dead to old. All non-dead threads---which +** are now old---must be in a gray list. Everything else is not in a +** gray list. Open upvalues are also kept gray. +*/ +static void sweep2old (lua_State *L, GCObject **p) { + GCObject *curr; + global_State *g = G(L); + while ((curr = *p) != NULL) { + if (iswhite(curr)) { /* is 'curr' dead? */ + lua_assert(isdead(g, curr)); + *p = curr->next; /* remove 'curr' from list */ + freeobj(L, curr); /* erase 'curr' */ + } + else { /* all surviving objects become old */ + setage(curr, G_OLD); + if (curr->tt == LUA_VTHREAD) { /* threads must be watched */ + lua_State *th = gco2th(curr); + linkgclist(th, g->grayagain); /* insert into 'grayagain' list */ + } + else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr))) + set2gray(curr); /* open upvalues are always gray */ + else /* everything else is black */ + nw2black(curr); + p = &curr->next; /* go to next element */ + } + } +} + + +/* +** Sweep for generational mode. Delete dead objects. (Because the +** collection is not incremental, there are no "new white" objects +** during the sweep. So, any white object must be dead.) For +** non-dead objects, advance their ages and clear the color of +** new objects. (Old objects keep their colors.) +** The ages of G_TOUCHED1 and G_TOUCHED2 objects cannot be advanced +** here, because these old-generation objects are usually not swept +** here. They will all be advanced in 'correctgraylist'. That function +** will also remove objects turned white here from any gray list. +*/ +static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, + GCObject *limit, GCObject **pfirstold1, + l_mem *paddedold) { + static const lu_byte nextage[] = { + G_SURVIVAL, /* from G_NEW */ + G_OLD1, /* from G_SURVIVAL */ + G_OLD1, /* from G_OLD0 */ + G_OLD, /* from G_OLD1 */ + G_OLD, /* from G_OLD (do not change) */ + G_TOUCHED1, /* from G_TOUCHED1 (do not change) */ + G_TOUCHED2 /* from G_TOUCHED2 (do not change) */ + }; + l_mem addedold = 0; + int white = luaC_white(g); + GCObject *curr; + while ((curr = *p) != limit) { + if (iswhite(curr)) { /* is 'curr' dead? */ + lua_assert(!isold(curr) && isdead(g, curr)); + *p = curr->next; /* remove 'curr' from list */ + freeobj(L, curr); /* erase 'curr' */ + } + else { /* correct mark and age */ + int age = getage(curr); + if (age == G_NEW) { /* new objects go back to white */ + int marked = curr->marked & ~maskgcbits; /* erase GC bits */ + curr->marked = cast_byte(marked | G_SURVIVAL | white); + } + else { /* all other objects will be old, and so keep their color */ + lua_assert(age != G_OLD1); /* advanced in 'markold' */ + setage(curr, nextage[age]); + if (getage(curr) == G_OLD1) { + addedold += objsize(curr); /* bytes becoming old */ + if (*pfirstold1 == NULL) + *pfirstold1 = curr; /* first OLD1 object in the list */ + } + } + p = &curr->next; /* go to next element */ + } + } + *paddedold += addedold; + return p; +} + + +/* +** Correct a list of gray objects. Return a pointer to the last element +** left on the list, so that we can link another list to the end of +** this one. +** Because this correction is done after sweeping, young objects might +** be turned white and still be in the list. They are only removed. +** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list; +** Non-white threads also remain on the list. 'TOUCHED2' objects and +** anything else become regular old, are marked black, and are removed +** from the list. +*/ +static GCObject **correctgraylist (GCObject **p) { + GCObject *curr; + while ((curr = *p) != NULL) { + GCObject **next = getgclist(curr); + if (iswhite(curr)) + goto remove; /* remove all white objects */ + else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */ + lua_assert(isgray(curr)); + nw2black(curr); /* make it black, for next barrier */ + setage(curr, G_TOUCHED2); + goto remain; /* keep it in the list and go to next element */ + } + else if (curr->tt == LUA_VTHREAD) { + lua_assert(isgray(curr)); + goto remain; /* keep non-white threads on the list */ + } + else { /* everything else is removed */ + lua_assert(isold(curr)); /* young objects should be white here */ + if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */ + setage(curr, G_OLD); /* ... to OLD */ + nw2black(curr); /* make object black (to be removed) */ + goto remove; + } + remove: *p = *next; continue; + remain: p = next; continue; + } + return p; +} + + +/* +** Correct all gray lists, coalescing them into 'grayagain'. +*/ +static void correctgraylists (global_State *g) { + GCObject **list = correctgraylist(&g->grayagain); + *list = g->weak; g->weak = NULL; + list = correctgraylist(list); + *list = g->allweak; g->allweak = NULL; + list = correctgraylist(list); + *list = g->ephemeron; g->ephemeron = NULL; + correctgraylist(list); +} + + +/* +** Mark black 'OLD1' objects when starting a new young collection. +** Gray objects are already in some gray list, and so will be visited in +** the atomic step. +*/ +static void markold (global_State *g, GCObject *from, GCObject *to) { + GCObject *p; + for (p = from; p != to; p = p->next) { + if (getage(p) == G_OLD1) { + lua_assert(!iswhite(p)); + setage(p, G_OLD); /* now they are old */ + if (isblack(p)) + reallymarkobject(g, p); + } + } +} + + +/* +** Finish a young-generation collection. +*/ +static void finishgencycle (lua_State *L, global_State *g) { + correctgraylists(g); + checkSizes(L, g); + g->gcstate = GCSpropagate; /* skip restart */ + if (!g->gcemergency && luaD_checkminstack(L)) + callallpendingfinalizers(L); +} + + +/* +** Shifts from a minor collection to major collections. It starts in +** the "sweep all" state to clear all objects, which are mostly black +** in generational mode. +*/ +static void minor2inc (lua_State *L, global_State *g, lu_byte kind) { + g->GCmajorminor = g->GCmarked; /* number of live bytes */ + g->gckind = kind; + g->reallyold = g->old1 = g->survival = NULL; + g->finobjrold = g->finobjold1 = g->finobjsur = NULL; + entersweep(L); /* continue as an incremental cycle */ + /* set a debt equal to the step size */ + luaE_setdebt(g, applygcparam(g, STEPSIZE, 100)); +} + + +/* +** Decide whether to shift to major mode. It shifts if the accumulated +** number of added old bytes (counted in 'GCmarked') is larger than +** 'minormajor'% of the number of lived bytes after the last major +** collection. (This number is kept in 'GCmajorminor'.) +*/ +static int checkminormajor (global_State *g) { + l_mem limit = applygcparam(g, MINORMAJOR, g->GCmajorminor); + if (limit == 0) + return 0; /* special case: 'minormajor' 0 stops major collections */ + return (g->GCmarked >= limit); +} + +/* +** Does a young collection. First, mark 'OLD1' objects. Then does the +** atomic step. Then, check whether to continue in minor mode. If so, +** sweep all lists and advance pointers. Finally, finish the collection. +*/ +static void youngcollection (lua_State *L, global_State *g) { + l_mem addedold1 = 0; + l_mem marked = g->GCmarked; /* preserve 'g->GCmarked' */ + GCObject **psurvival; /* to point to first non-dead survival object */ + GCObject *dummy; /* dummy out parameter to 'sweepgen' */ + lua_assert(g->gcstate == GCSpropagate); + if (g->firstold1) { /* are there regular OLD1 objects? */ + markold(g, g->firstold1, g->reallyold); /* mark them */ + g->firstold1 = NULL; /* no more OLD1 objects (for now) */ + } + markold(g, g->finobj, g->finobjrold); + markold(g, g->tobefnz, NULL); + + atomic(L); /* will lose 'g->marked' */ + + /* sweep nursery and get a pointer to its last live element */ + g->gcstate = GCSswpallgc; + psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1, &addedold1); + /* sweep 'survival' */ + sweepgen(L, g, psurvival, g->old1, &g->firstold1, &addedold1); + g->reallyold = g->old1; + g->old1 = *psurvival; /* 'survival' survivals are old now */ + g->survival = g->allgc; /* all news are survivals */ + + /* repeat for 'finobj' lists */ + dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */ + psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy, &addedold1); + /* sweep 'survival' */ + sweepgen(L, g, psurvival, g->finobjold1, &dummy, &addedold1); + g->finobjrold = g->finobjold1; + g->finobjold1 = *psurvival; /* 'survival' survivals are old now */ + g->finobjsur = g->finobj; /* all news are survivals */ + + sweepgen(L, g, &g->tobefnz, NULL, &dummy, &addedold1); + + /* keep total number of added old1 bytes */ + g->GCmarked = marked + addedold1; + + /* decide whether to shift to major mode */ + if (checkminormajor(g)) { + minor2inc(L, g, KGC_GENMAJOR); /* go to major mode */ + g->GCmarked = 0; /* avoid pause in first major cycle (see 'setpause') */ + } + else + finishgencycle(L, g); /* still in minor mode; finish it */ +} + + +/* +** Clears all gray lists, sweeps objects, and prepare sublists to enter +** generational mode. The sweeps remove dead objects and turn all +** surviving objects to old. Threads go back to 'grayagain'; everything +** else is turned black (not in any gray list). +*/ +static void atomic2gen (lua_State *L, global_State *g) { + cleargraylists(g); + /* sweep all elements making them old */ + g->gcstate = GCSswpallgc; + sweep2old(L, &g->allgc); + /* everything alive now is old */ + g->reallyold = g->old1 = g->survival = g->allgc; + g->firstold1 = NULL; /* there are no OLD1 objects anywhere */ + + /* repeat for 'finobj' lists */ + sweep2old(L, &g->finobj); + g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj; + + sweep2old(L, &g->tobefnz); + + g->gckind = KGC_GENMINOR; + g->GCmajorminor = g->GCmarked; /* "base" for number of bytes */ + g->GCmarked = 0; /* to count the number of added old1 bytes */ + finishgencycle(L, g); +} + + +/* +** Set debt for the next minor collection, which will happen when +** total number of bytes grows 'genminormul'% in relation to +** the base, GCmajorminor, which is the number of bytes being used +** after the last major collection. +*/ +static void setminordebt (global_State *g) { + luaE_setdebt(g, applygcparam(g, MINORMUL, g->GCmajorminor)); +} + + +/* +** Enter generational mode. Must go until the end of an atomic cycle +** to ensure that all objects are correctly marked and weak tables +** are cleared. Then, turn all objects into old and finishes the +** collection. +*/ +static void entergen (lua_State *L, global_State *g) { + luaC_runtilstate(L, GCSpause, 1); /* prepare to start a new cycle */ + luaC_runtilstate(L, GCSpropagate, 1); /* start new cycle */ + atomic(L); /* propagates all and then do the atomic stuff */ + atomic2gen(L, g); + setminordebt(g); /* set debt assuming next cycle will be minor */ +} + + +/* +** Change collector mode to 'newmode'. +*/ +void luaC_changemode (lua_State *L, int newmode) { + global_State *g = G(L); + if (g->gckind == KGC_GENMAJOR) /* doing major collections? */ + g->gckind = KGC_INC; /* already incremental but in name */ + if (newmode != g->gckind) { /* does it need to change? */ + if (newmode == KGC_INC) /* entering incremental mode? */ + minor2inc(L, g, KGC_INC); /* entering incremental mode */ + else { + lua_assert(newmode == KGC_GENMINOR); + entergen(L, g); + } + } +} + + +/* +** Does a full collection in generational mode. +*/ +static void fullgen (lua_State *L, global_State *g) { + minor2inc(L, g, KGC_INC); + entergen(L, g); +} + + +/* +** After an atomic incremental step from a major collection, +** check whether collector could return to minor collections. +** It checks whether the number of bytes 'tobecollected' +** is greater than 'majorminor'% of the number of bytes added +** since the last collection ('addedbytes'). +*/ +static int checkmajorminor (lua_State *L, global_State *g) { + if (g->gckind == KGC_GENMAJOR) { /* generational mode? */ + l_mem numbytes = gettotalbytes(g); + l_mem addedbytes = numbytes - g->GCmajorminor; + l_mem limit = applygcparam(g, MAJORMINOR, addedbytes); + l_mem tobecollected = numbytes - g->GCmarked; + if (tobecollected > limit) { + atomic2gen(L, g); /* return to generational mode */ + setminordebt(g); + return 1; /* exit incremental collection */ + } + } + g->GCmajorminor = g->GCmarked; /* prepare for next collection */ + return 0; /* stay doing incremental collections */ +} + +/* }====================================================== */ + + +/* +** {====================================================== +** GC control +** ======================================================= +*/ + + +/* +** Enter first sweep phase. +** The call to 'sweeptolive' makes the pointer point to an object +** inside the list (instead of to the header), so that the real sweep do +** not need to skip objects created between "now" and the start of the +** real sweep. +*/ +static void entersweep (lua_State *L) { + global_State *g = G(L); + g->gcstate = GCSswpallgc; + lua_assert(g->sweepgc == NULL); + g->sweepgc = sweeptolive(L, &g->allgc); +} + + +/* +** Delete all objects in list 'p' until (but not including) object +** 'limit'. +*/ +static void deletelist (lua_State *L, GCObject *p, GCObject *limit) { + while (p != limit) { + GCObject *next = p->next; + freeobj(L, p); + p = next; + } +} + + +/* +** Call all finalizers of the objects in the given Lua state, and +** then free all objects, except for the main thread. +*/ +void luaC_freeallobjects (lua_State *L) { + global_State *g = G(L); + g->gcstp = GCSTPCLS; /* no extra finalizers after here */ + luaC_changemode(L, KGC_INC); + separatetobefnz(g, 1); /* separate all objects with finalizers */ + lua_assert(g->finobj == NULL); + callallpendingfinalizers(L); + deletelist(L, g->allgc, obj2gco(mainthread(g))); + lua_assert(g->finobj == NULL); /* no new finalizers */ + deletelist(L, g->fixedgc, NULL); /* collect fixed objects */ + lua_assert(g->strt.nuse == 0); +} + + +static void atomic (lua_State *L) { + global_State *g = G(L); + GCObject *origweak, *origall; + GCObject *grayagain = g->grayagain; /* save original list */ + g->grayagain = NULL; + lua_assert(g->ephemeron == NULL && g->weak == NULL); + lua_assert(!iswhite(mainthread(g))); + g->gcstate = GCSatomic; + markobject(g, L); /* mark running thread */ + /* registry and global metatables may be changed by API */ + markvalue(g, &g->l_registry); + markmt(g); /* mark global metatables */ + propagateall(g); /* empties 'gray' list */ + /* remark occasional upvalues of (maybe) dead threads */ + remarkupvals(g); + propagateall(g); /* propagate changes */ + g->gray = grayagain; + propagateall(g); /* traverse 'grayagain' list */ + convergeephemerons(g); + /* at this point, all strongly accessible objects are marked. */ + /* Clear values from weak tables, before checking finalizers */ + clearbyvalues(g, g->weak, NULL); + clearbyvalues(g, g->allweak, NULL); + origweak = g->weak; origall = g->allweak; + separatetobefnz(g, 0); /* separate objects to be finalized */ + markbeingfnz(g); /* mark objects that will be finalized */ + propagateall(g); /* remark, to propagate 'resurrection' */ + convergeephemerons(g); + /* at this point, all resurrected objects are marked. */ + /* remove dead objects from weak tables */ + clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron */ + clearbykeys(g, g->allweak); /* clear keys from all 'allweak' */ + /* clear values from resurrected weak tables */ + clearbyvalues(g, g->weak, origweak); + clearbyvalues(g, g->allweak, origall); + luaS_clearcache(g); + g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ + lua_assert(g->gray == NULL); +} + + +/* +** Do a sweep step. The normal case (not fast) sweeps at most GCSWEEPMAX +** elements. The fast case sweeps the whole list. +*/ +static void sweepstep (lua_State *L, global_State *g, + lu_byte nextstate, GCObject **nextlist, int fast) { + if (g->sweepgc) + g->sweepgc = sweeplist(L, g->sweepgc, fast ? MAX_LMEM : GCSWEEPMAX); + else { /* enter next state */ + g->gcstate = nextstate; + g->sweepgc = nextlist; + } +} + + +/* +** Performs one incremental "step" in an incremental garbage collection. +** For indivisible work, a step goes to the next state. When marking +** (propagating), a step traverses one object. When sweeping, a step +** sweeps GCSWEEPMAX objects, to avoid a big overhead for sweeping +** objects one by one. (Sweeping is inexpensive, no matter the +** object.) When 'fast' is true, 'singlestep' tries to finish a state +** "as fast as possible". In particular, it skips the propagation +** phase and leaves all objects to be traversed by the atomic phase: +** That avoids traversing twice some objects, such as threads and +** weak tables. +*/ + +#define step2pause -3 /* finished collection; entered pause state */ +#define atomicstep -2 /* atomic step */ +#define step2minor -1 /* moved to minor collections */ + + +static l_mem singlestep (lua_State *L, int fast) { + global_State *g = G(L); + l_mem stepresult; + lua_assert(!g->gcstopem); /* collector is not reentrant */ + g->gcstopem = 1; /* no emergency collections while collecting */ + switch (g->gcstate) { + case GCSpause: { + restartcollection(g); + g->gcstate = GCSpropagate; + stepresult = 1; + break; + } + case GCSpropagate: { + if (fast || g->gray == NULL) { + g->gcstate = GCSenteratomic; /* finish propagate phase */ + stepresult = 1; + } + else + stepresult = propagatemark(g); /* traverse one gray object */ + break; + } + case GCSenteratomic: { + atomic(L); + if (checkmajorminor(L, g)) + stepresult = step2minor; + else { + entersweep(L); + stepresult = atomicstep; + } + break; + } + case GCSswpallgc: { /* sweep "regular" objects */ + sweepstep(L, g, GCSswpfinobj, &g->finobj, fast); + stepresult = GCSWEEPMAX; + break; + } + case GCSswpfinobj: { /* sweep objects with finalizers */ + sweepstep(L, g, GCSswptobefnz, &g->tobefnz, fast); + stepresult = GCSWEEPMAX; + break; + } + case GCSswptobefnz: { /* sweep objects to be finalized */ + sweepstep(L, g, GCSswpend, NULL, fast); + stepresult = GCSWEEPMAX; + break; + } + case GCSswpend: { /* finish sweeps */ + checkSizes(L, g); + g->gcstate = GCScallfin; + stepresult = GCSWEEPMAX; + break; + } + case GCScallfin: { /* call finalizers */ + if (g->tobefnz && !g->gcemergency && luaD_checkminstack(L)) { + g->gcstopem = 0; /* ok collections during finalizers */ + GCTM(L); /* call one finalizer */ + stepresult = CWUFIN; + } + else { /* no more finalizers or emergency mode or no enough stack + to run finalizers */ + g->gcstate = GCSpause; /* finish collection */ + stepresult = step2pause; + } + break; + } + default: lua_assert(0); return 0; + } + g->gcstopem = 0; + return stepresult; +} + + +/* +** Advances the garbage collector until it reaches the given state. +** (The option 'fast' is only for testing; in normal code, 'fast' +** here is always true.) +*/ +void luaC_runtilstate (lua_State *L, int state, int fast) { + global_State *g = G(L); + lua_assert(g->gckind == KGC_INC); + while (state != g->gcstate) + singlestep(L, fast); +} + + + +/* +** Performs a basic incremental step. The step size is +** converted from bytes to "units of work"; then the function loops +** running single steps until adding that many units of work or +** finishing a cycle (pause state). Finally, it sets the debt that +** controls when next step will be performed. +*/ +static void incstep (lua_State *L, global_State *g) { + l_mem stepsize = applygcparam(g, STEPSIZE, 100); + l_mem work2do = applygcparam(g, STEPMUL, stepsize / cast_int(sizeof(void*))); + l_mem stres; + int fast = (work2do == 0); /* special case: do a full collection */ + do { /* repeat until enough work */ + stres = singlestep(L, fast); /* perform one single step */ + if (stres == step2minor) /* returned to minor collections? */ + return; /* nothing else to be done here */ + else if (stres == step2pause || (stres == atomicstep && !fast)) + break; /* end of cycle or atomic */ + else + work2do -= stres; + } while (fast || work2do > 0); + if (g->gcstate == GCSpause) + setpause(g); /* pause until next cycle */ + else + luaE_setdebt(g, stepsize); +} + + +#if !defined(luai_tracegc) +#define luai_tracegc(L,f) ((void)0) +#endif + +/* +** Performs a basic GC step if collector is running. (If collector was +** stopped by the user, set a reasonable debt to avoid it being called +** at every single check.) +*/ +void luaC_step (lua_State *L) { + global_State *g = G(L); + lua_assert(!g->gcemergency); + if (!gcrunning(g)) { /* not running? */ + if (g->gcstp & GCSTPUSR) /* stopped by the user? */ + luaE_setdebt(g, 20000); + } + else { + luai_tracegc(L, 1); /* for internal debugging */ + switch (g->gckind) { + case KGC_INC: case KGC_GENMAJOR: + incstep(L, g); + break; + case KGC_GENMINOR: + youngcollection(L, g); + setminordebt(g); + break; + } + luai_tracegc(L, 0); /* for internal debugging */ + } +} + + +/* +** Perform a full collection in incremental mode. +** Before running the collection, check 'keepinvariant'; if it is true, +** there may be some objects marked as black, so the collector has +** to sweep all objects to turn them back to white (as white has not +** changed, nothing will be collected). +*/ +static void fullinc (lua_State *L, global_State *g) { + if (keepinvariant(g)) /* black objects? */ + entersweep(L); /* sweep everything to turn them back to white */ + /* finish any pending sweep phase to start a new cycle */ + luaC_runtilstate(L, GCSpause, 1); + luaC_runtilstate(L, GCScallfin, 1); /* run up to finalizers */ + luaC_runtilstate(L, GCSpause, 1); /* finish collection */ + setpause(g); +} + + +/* +** Performs a full GC cycle; if 'isemergency', set a flag to avoid +** some operations which could change the interpreter state in some +** unexpected ways (running finalizers and shrinking some structures). +*/ +void luaC_fullgc (lua_State *L, int isemergency) { + global_State *g = G(L); + lua_assert(!g->gcemergency); + g->gcemergency = cast_byte(isemergency); /* set flag */ + switch (g->gckind) { + case KGC_GENMINOR: fullgen(L, g); break; + case KGC_INC: fullinc(L, g); break; + case KGC_GENMAJOR: + g->gckind = KGC_INC; + fullinc(L, g); + g->gckind = KGC_GENMAJOR; + break; + } + g->gcemergency = 0; +} + +/* }====================================================== */ + + + +``` + +`Lua/lgc.h`: + +```h +/* +** $Id: lgc.h $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#ifndef lgc_h +#define lgc_h + + +#include <stddef.h> + + +#include "lobject.h" +#include "lstate.h" + +/* +** Collectable objects may have one of three colors: white, which means +** the object is not marked; gray, which means the object is marked, but +** its references may be not marked; and black, which means that the +** object and all its references are marked. The main invariant of the +** garbage collector, while marking objects, is that a black object can +** never point to a white one. Moreover, any gray object must be in a +** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it +** can be visited again before finishing the collection cycle. (Open +** upvalues are an exception to this rule, as they are attached to +** a corresponding thread.) These lists have no meaning when the +** invariant is not being enforced (e.g., sweep phase). +*/ + + +/* +** Possible states of the Garbage Collector +*/ +#define GCSpropagate 0 +#define GCSenteratomic 1 +#define GCSatomic 2 +#define GCSswpallgc 3 +#define GCSswpfinobj 4 +#define GCSswptobefnz 5 +#define GCSswpend 6 +#define GCScallfin 7 +#define GCSpause 8 + + +#define issweepphase(g) \ + (GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend) + + +/* +** macro to tell when main invariant (white objects cannot point to black +** ones) must be kept. During a collection, the sweep phase may break +** the invariant, as objects turned white may point to still-black +** objects. The invariant is restored when sweep ends and all objects +** are white again. +*/ + +#define keepinvariant(g) ((g)->gcstate <= GCSatomic) + + +/* +** some useful bit tricks +*/ +#define resetbits(x,m) ((x) &= cast_byte(~(m))) +#define setbits(x,m) ((x) |= (m)) +#define testbits(x,m) ((x) & (m)) +#define bitmask(b) (1<<(b)) +#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) +#define l_setbit(x,b) setbits(x, bitmask(b)) +#define resetbit(x,b) resetbits(x, bitmask(b)) +#define testbit(x,b) testbits(x, bitmask(b)) + + +/* +** Layout for bit use in 'marked' field. First three bits are +** used for object "age" in generational mode. Last bit is used +** by tests. +*/ +#define WHITE0BIT 3 /* object is white (type 0) */ +#define WHITE1BIT 4 /* object is white (type 1) */ +#define BLACKBIT 5 /* object is black */ +#define FINALIZEDBIT 6 /* object has been marked for finalization */ + +#define TESTBIT 7 + + + +#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) + + +#define iswhite(x) testbits((x)->marked, WHITEBITS) +#define isblack(x) testbit((x)->marked, BLACKBIT) +#define isgray(x) /* neither white nor black */ \ + (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT))) + +#define tofinalize(x) testbit((x)->marked, FINALIZEDBIT) + +#define otherwhite(g) ((g)->currentwhite ^ WHITEBITS) +#define isdeadm(ow,m) ((m) & (ow)) +#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked) + +#define changewhite(x) ((x)->marked ^= WHITEBITS) +#define nw2black(x) \ + check_exp(!iswhite(x), l_setbit((x)->marked, BLACKBIT)) + +#define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS) + + +/* object age in generational mode */ +#define G_NEW 0 /* created in current cycle */ +#define G_SURVIVAL 1 /* created in previous cycle */ +#define G_OLD0 2 /* marked old by frw. barrier in this cycle */ +#define G_OLD1 3 /* first full cycle as old */ +#define G_OLD 4 /* really old object (not to be visited) */ +#define G_TOUCHED1 5 /* old object touched this cycle */ +#define G_TOUCHED2 6 /* old object touched in previous cycle */ + +#define AGEBITS 7 /* all age bits (111) */ + +#define getage(o) ((o)->marked & AGEBITS) +#define setage(o,a) ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a)) +#define isold(o) (getage(o) > G_SURVIVAL) + + +/* +** In generational mode, objects are created 'new'. After surviving one +** cycle, they become 'survival'. Both 'new' and 'survival' can point +** to any other object, as they are traversed at the end of the cycle. +** We call them both 'young' objects. +** If a survival object survives another cycle, it becomes 'old1'. +** 'old1' objects can still point to survival objects (but not to +** new objects), so they still must be traversed. After another cycle +** (that, being old, 'old1' objects will "survive" no matter what) +** finally the 'old1' object becomes really 'old', and then they +** are no more traversed. +** +** To keep its invariants, the generational mode uses the same barriers +** also used by the incremental mode. If a young object is caught in a +** forward barrier, it cannot become old immediately, because it can +** still point to other young objects. Instead, it becomes 'old0', +** which in the next cycle becomes 'old1'. So, 'old0' objects is +** old but can point to new and survival objects; 'old1' is old +** but cannot point to new objects; and 'old' cannot point to any +** young object. +** +** If any old object ('old0', 'old1', 'old') is caught in a back +** barrier, it becomes 'touched1' and goes into a gray list, to be +** visited at the end of the cycle. There it evolves to 'touched2', +** which can point to survivals but not to new objects. In yet another +** cycle then it becomes 'old' again. +** +** The generational mode must also control the colors of objects, +** because of the barriers. While the mutator is running, young objects +** are kept white. 'old', 'old1', and 'touched2' objects are kept black, +** as they cannot point to new objects; exceptions are threads and open +** upvalues, which age to 'old1' and 'old' but are kept gray. 'old0' +** objects may be gray or black, as in the incremental mode. 'touched1' +** objects are kept gray, as they must be visited again at the end of +** the cycle. +*/ + + +/* +** {====================================================== +** Default Values for GC parameters +** ======================================================= +*/ + +/* +** Minor collections will shift to major ones after LUAI_MINORMAJOR% +** bytes become old. +*/ +#define LUAI_MINORMAJOR 70 + +/* +** Major collections will shift to minor ones after a collection +** collects at least LUAI_MAJORMINOR% of the new bytes. +*/ +#define LUAI_MAJORMINOR 50 + +/* +** A young (minor) collection will run after creating LUAI_GENMINORMUL% +** new bytes. +*/ +#define LUAI_GENMINORMUL 20 + + +/* incremental */ + +/* Number of bytes must be LUAI_GCPAUSE% before starting new cycle */ +#define LUAI_GCPAUSE 250 + +/* +** Step multiplier: The collector handles LUAI_GCMUL% work units for +** each new allocated word. (Each "work unit" corresponds roughly to +** sweeping one object or traversing one slot.) +*/ +#define LUAI_GCMUL 200 + +/* How many bytes to allocate before next GC step */ +#define LUAI_GCSTEPSIZE (200 * sizeof(Table)) + + +#define setgcparam(g,p,v) (g->gcparams[LUA_GCP##p] = luaO_codeparam(v)) +#define applygcparam(g,p,x) luaO_applyparam(g->gcparams[LUA_GCP##p], x) + +/* }====================================================== */ + + +/* +** Control when GC is running: +*/ +#define GCSTPUSR 1 /* bit true when GC stopped by user */ +#define GCSTPGC 2 /* bit true when GC stopped by itself */ +#define GCSTPCLS 4 /* bit true when closing Lua state */ +#define gcrunning(g) ((g)->gcstp == 0) + + +/* +** Does one step of collection when debt becomes zero. 'pre'/'pos' +** allows some adjustments to be done only when needed. macro +** 'condchangemem' is used only for heavy tests (forcing a full +** GC cycle on every opportunity) +*/ + +#if !defined(HARDMEMTESTS) +#define condchangemem(L,pre,pos,emg) ((void)0) +#else +#define condchangemem(L,pre,pos,emg) \ + { if (gcrunning(G(L))) { pre; luaC_fullgc(L, emg); pos; } } +#endif + +#define luaC_condGC(L,pre,pos) \ + { if (G(L)->GCdebt <= 0) { pre; luaC_step(L); pos;}; \ + condchangemem(L,pre,pos,0); } + +/* more often than not, 'pre'/'pos' are empty */ +#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0) + + +#define luaC_objbarrier(L,p,o) ( \ + (isblack(p) && iswhite(o)) ? \ + luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) + +#define luaC_barrier(L,p,v) ( \ + iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0)) + +#define luaC_objbarrierback(L,p,o) ( \ + (isblack(p) && iswhite(o)) ? luaC_barrierback_(L,p) : cast_void(0)) + +#define luaC_barrierback(L,p,v) ( \ + iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : cast_void(0)) + +LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); +LUAI_FUNC void luaC_freeallobjects (lua_State *L); +LUAI_FUNC void luaC_step (lua_State *L); +LUAI_FUNC void luaC_runtilstate (lua_State *L, int state, int fast); +LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency); +LUAI_FUNC GCObject *luaC_newobj (lua_State *L, lu_byte tt, size_t sz); +LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, lu_byte tt, size_t sz, + size_t offset); +LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); +LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); +LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); +LUAI_FUNC void luaC_changemode (lua_State *L, int newmode); + + +#endif + +``` + +`Lua/linit.c`: + +```c +/* +** $Id: linit.c $ +** Initialization of libraries for lua.c and other clients +** See Copyright Notice in lua.h +*/ + + +#define linit_c +#define LUA_LIB + + +#include "lprefix.h" + + +#include <stddef.h> + +#include "lua.h" + +#include "lualib.h" +#include "lauxlib.h" +#include "llimits.h" + + +/* +** Standard Libraries. (Must be listed in the same ORDER of their +** respective constants LUA_<libname>K.) +*/ +static const luaL_Reg stdlibs[] = { + {LUA_GNAME, luaopen_base}, + {LUA_LOADLIBNAME, luaopen_package}, + {LUA_COLIBNAME, luaopen_coroutine}, + {LUA_DBLIBNAME, luaopen_debug}, + {LUA_IOLIBNAME, luaopen_io}, + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_OSLIBNAME, luaopen_os}, + {LUA_STRLIBNAME, luaopen_string}, + {LUA_TABLIBNAME, luaopen_table}, + {LUA_UTF8LIBNAME, luaopen_utf8}, + {NULL, NULL} +}; + + +/* +** require and preload selected standard libraries +*/ +LUALIB_API void luaL_openselectedlibs (lua_State *L, int load, int preload) { + int mask; + const luaL_Reg *lib; + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + for (lib = stdlibs, mask = 1; lib->name != NULL; lib++, mask <<= 1) { + if (load & mask) { /* selected? */ + luaL_requiref(L, lib->name, lib->func, 1); /* require library */ + lua_pop(L, 1); /* remove result from the stack */ + } + else if (preload & mask) { /* selected? */ + lua_pushcfunction(L, lib->func); + lua_setfield(L, -2, lib->name); /* add library to PRELOAD table */ + } + } + lua_assert((mask >> 1) == LUA_UTF8LIBK); + lua_pop(L, 1); /* remove PRELOAD table */ +} + + +``` + +`Lua/liolib.c`: + +```c +/* +** $Id: liolib.c $ +** Standard I/O (and system) library +** See Copyright Notice in lua.h +*/ + +#define liolib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include <ctype.h> +#include <errno.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "llimits.h" + + +/* +** Change this macro to accept other modes for 'fopen' besides +** the standard ones. +*/ +#if !defined(l_checkmode) + +/* accepted extensions to 'mode' in 'fopen' */ +#if !defined(L_MODEEXT) +#define L_MODEEXT "b" +#endif + +/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */ +static int l_checkmode (const char *mode) { + return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL && + (*mode != '+' || ((void)(++mode), 1)) && /* skip if char is '+' */ + (strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */ +} + +#endif + +/* +** {====================================================== +** l_popen spawns a new process connected to the current +** one through the file streams. +** ======================================================= +*/ + +#if !defined(l_popen) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + +#define l_popen(L,c,m) (fflush(NULL), popen(c,m)) +#define l_pclose(L,file) (pclose(file)) + +#elif defined(LUA_USE_WINDOWS) /* }{ */ + +#define l_popen(L,c,m) (_popen(c,m)) +#define l_pclose(L,file) (_pclose(file)) + +#if !defined(l_checkmodep) +/* Windows accepts "[rw][bt]?" as valid modes */ +#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && \ + (m[1] == '\0' || ((m[1] == 'b' || m[1] == 't') && m[2] == '\0'))) +#endif + +#else /* }{ */ + +/* ISO C definitions */ +#define l_popen(L,c,m) \ + ((void)c, (void)m, \ + luaL_error(L, "'popen' not supported"), \ + (FILE*)0) +#define l_pclose(L,file) ((void)L, (void)file, -1) + +#endif /* } */ + +#endif /* } */ + + +#if !defined(l_checkmodep) +/* By default, Lua accepts only "r" or "w" as valid modes */ +#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0') +#endif + +/* }====================================================== */ + + +#if !defined(l_getc) /* { */ + +#if defined(LUA_USE_POSIX) +#define l_getc(f) getc_unlocked(f) +#define l_lockfile(f) flockfile(f) +#define l_unlockfile(f) funlockfile(f) +#else +#define l_getc(f) getc(f) +#define l_lockfile(f) ((void)0) +#define l_unlockfile(f) ((void)0) +#endif + +#endif /* } */ + + +/* +** {====================================================== +** l_fseek: configuration for longer offsets +** ======================================================= +*/ + +#if !defined(l_fseek) /* { */ + +#if defined(LUA_USE_POSIX) || defined(LUA_USE_OFF_T) /* { */ + +#include <sys/types.h> + +#define l_fseek(f,o,w) fseeko(f,o,w) +#define l_ftell(f) ftello(f) +#define l_seeknum off_t + +#elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \ + && defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */ + +/* Windows (but not DDK) and Visual C++ 2005 or higher */ +#define l_fseek(f,o,w) _fseeki64(f,o,w) +#define l_ftell(f) _ftelli64(f) +#define l_seeknum __int64 + +#else /* }{ */ + +/* ISO C definitions */ +#define l_fseek(f,o,w) fseek(f,o,w) +#define l_ftell(f) ftell(f) +#define l_seeknum long + +#endif /* } */ + +#endif /* } */ + +/* }====================================================== */ + + + +#define IO_PREFIX "_IO_" +#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1) +#define IO_INPUT (IO_PREFIX "input") +#define IO_OUTPUT (IO_PREFIX "output") + + +typedef luaL_Stream LStream; + + +#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) + +#define isclosed(p) ((p)->closef == NULL) + + +static int io_type (lua_State *L) { + LStream *p; + luaL_checkany(L, 1); + p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE); + if (p == NULL) + luaL_pushfail(L); /* not a file */ + else if (isclosed(p)) + lua_pushliteral(L, "closed file"); + else + lua_pushliteral(L, "file"); + return 1; +} + + +static int f_tostring (lua_State *L) { + LStream *p = tolstream(L); + if (isclosed(p)) + lua_pushliteral(L, "file (closed)"); + else + lua_pushfstring(L, "file (%p)", p->f); + return 1; +} + + +static FILE *tofile (lua_State *L) { + LStream *p = tolstream(L); + if (l_unlikely(isclosed(p))) + luaL_error(L, "attempt to use a closed file"); + lua_assert(p->f); + return p->f; +} + + +/* +** When creating file handles, always creates a 'closed' file handle +** before opening the actual file; so, if there is a memory error, the +** handle is in a consistent state. +*/ +static LStream *newprefile (lua_State *L) { + LStream *p = (LStream *)lua_newuserdatauv(L, sizeof(LStream), 0); + p->closef = NULL; /* mark file handle as 'closed' */ + luaL_setmetatable(L, LUA_FILEHANDLE); + return p; +} + + +/* +** Calls the 'close' function from a file handle. The 'volatile' avoids +** a bug in some versions of the Clang compiler (e.g., clang 3.0 for +** 32 bits). +*/ +static int aux_close (lua_State *L) { + LStream *p = tolstream(L); + volatile lua_CFunction cf = p->closef; + p->closef = NULL; /* mark stream as closed */ + return (*cf)(L); /* close it */ +} + + +static int f_close (lua_State *L) { + tofile(L); /* make sure argument is an open stream */ + return aux_close(L); +} + + +static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) /* no argument? */ + lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use default output */ + return f_close(L); +} + + +static int f_gc (lua_State *L) { + LStream *p = tolstream(L); + if (!isclosed(p) && p->f != NULL) + aux_close(L); /* ignore closed and incompletely open files */ + return 0; +} + + +/* +** function to close regular files +*/ +static int io_fclose (lua_State *L) { + LStream *p = tolstream(L); + errno = 0; + return luaL_fileresult(L, (fclose(p->f) == 0), NULL); +} + + +static LStream *newfile (lua_State *L) { + LStream *p = newprefile(L); + p->f = NULL; + p->closef = &io_fclose; + return p; +} + + +static void opencheck (lua_State *L, const char *fname, const char *mode) { + LStream *p = newfile(L); + p->f = fopen(fname, mode); + if (l_unlikely(p->f == NULL)) + luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno)); +} + + +static int io_open (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + LStream *p = newfile(L); + const char *md = mode; /* to traverse/check mode */ + luaL_argcheck(L, l_checkmode(md), 2, "invalid mode"); + errno = 0; + p->f = fopen(filename, mode); + return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; +} + + +/* +** function to close 'popen' files +*/ +static int io_pclose (lua_State *L) { + LStream *p = tolstream(L); + errno = 0; + return luaL_execresult(L, l_pclose(L, p->f)); +} + + +static int io_popen (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + LStream *p = newprefile(L); + luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode"); + errno = 0; + p->f = l_popen(L, filename, mode); + p->closef = &io_pclose; + return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; +} + + +static int io_tmpfile (lua_State *L) { + LStream *p = newfile(L); + errno = 0; + p->f = tmpfile(); + return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; +} + + +static FILE *getiofile (lua_State *L, const char *findex) { + LStream *p; + lua_getfield(L, LUA_REGISTRYINDEX, findex); + p = (LStream *)lua_touserdata(L, -1); + if (l_unlikely(isclosed(p))) + luaL_error(L, "default %s file is closed", findex + IOPREF_LEN); + return p->f; +} + + +static int g_iofile (lua_State *L, const char *f, const char *mode) { + if (!lua_isnoneornil(L, 1)) { + const char *filename = lua_tostring(L, 1); + if (filename) + opencheck(L, filename, mode); + else { + tofile(L); /* check that it's a valid file handle */ + lua_pushvalue(L, 1); + } + lua_setfield(L, LUA_REGISTRYINDEX, f); + } + /* return current value */ + lua_getfield(L, LUA_REGISTRYINDEX, f); + return 1; +} + + +static int io_input (lua_State *L) { + return g_iofile(L, IO_INPUT, "r"); +} + + +static int io_output (lua_State *L) { + return g_iofile(L, IO_OUTPUT, "w"); +} + + +static int io_readline (lua_State *L); + + +/* +** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit +** in the limit for upvalues of a closure) +*/ +#define MAXARGLINE 250 + +/* +** Auxiliary function to create the iteration function for 'lines'. +** The iteration function is a closure over 'io_readline', with +** the following upvalues: +** 1) The file being read (first value in the stack) +** 2) the number of arguments to read +** 3) a boolean, true iff file has to be closed when finished ('toclose') +** *) a variable number of format arguments (rest of the stack) +*/ +static void aux_lines (lua_State *L, int toclose) { + int n = lua_gettop(L) - 1; /* number of arguments to read */ + luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments"); + lua_pushvalue(L, 1); /* file */ + lua_pushinteger(L, n); /* number of arguments to read */ + lua_pushboolean(L, toclose); /* close/not close file when finished */ + lua_rotate(L, 2, 3); /* move the three values to their positions */ + lua_pushcclosure(L, io_readline, 3 + n); +} + + +static int f_lines (lua_State *L) { + tofile(L); /* check that it's a valid file handle */ + aux_lines(L, 0); + return 1; +} + + +/* +** Return an iteration function for 'io.lines'. If file has to be +** closed, also returns the file itself as a second result (to be +** closed as the state at the exit of a generic for). +*/ +static int io_lines (lua_State *L) { + int toclose; + if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */ + if (lua_isnil(L, 1)) { /* no file name? */ + lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */ + lua_replace(L, 1); /* put it at index 1 */ + tofile(L); /* check that it's a valid file handle */ + toclose = 0; /* do not close it after iteration */ + } + else { /* open a new file */ + const char *filename = luaL_checkstring(L, 1); + opencheck(L, filename, "r"); + lua_replace(L, 1); /* put file at index 1 */ + toclose = 1; /* close it after iteration */ + } + aux_lines(L, toclose); /* push iteration function */ + if (toclose) { + lua_pushnil(L); /* state */ + lua_pushnil(L); /* control */ + lua_pushvalue(L, 1); /* file is the to-be-closed variable (4th result) */ + return 4; + } + else + return 1; +} + + +/* +** {====================================================== +** READ +** ======================================================= +*/ + + +/* maximum length of a numeral */ +#if !defined (L_MAXLENNUM) +#define L_MAXLENNUM 200 +#endif + + +/* auxiliary structure used by 'read_number' */ +typedef struct { + FILE *f; /* file being read */ + int c; /* current character (look ahead) */ + int n; /* number of elements in buffer 'buff' */ + char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */ +} RN; + + +/* +** Add current char to buffer (if not out of space) and read next one +*/ +static int nextc (RN *rn) { + if (l_unlikely(rn->n >= L_MAXLENNUM)) { /* buffer overflow? */ + rn->buff[0] = '\0'; /* invalidate result */ + return 0; /* fail */ + } + else { + rn->buff[rn->n++] = cast_char(rn->c); /* save current char */ + rn->c = l_getc(rn->f); /* read next one */ + return 1; + } +} + + +/* +** Accept current char if it is in 'set' (of size 2) +*/ +static int test2 (RN *rn, const char *set) { + if (rn->c == set[0] || rn->c == set[1]) + return nextc(rn); + else return 0; +} + + +/* +** Read a sequence of (hex)digits +*/ +static int readdigits (RN *rn, int hex) { + int count = 0; + while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn)) + count++; + return count; +} + + +/* +** Read a number: first reads a valid prefix of a numeral into a buffer. +** Then it calls 'lua_stringtonumber' to check whether the format is +** correct and to convert it to a Lua number. +*/ +static int read_number (lua_State *L, FILE *f) { + RN rn; + int count = 0; + int hex = 0; + char decp[2]; + rn.f = f; rn.n = 0; + decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */ + decp[1] = '.'; /* always accept a dot */ + l_lockfile(rn.f); + do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */ + test2(&rn, "-+"); /* optional sign */ + if (test2(&rn, "00")) { + if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */ + else count = 1; /* count initial '0' as a valid digit */ + } + count += readdigits(&rn, hex); /* integral part */ + if (test2(&rn, decp)) /* decimal point? */ + count += readdigits(&rn, hex); /* fractional part */ + if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */ + test2(&rn, "-+"); /* exponent sign */ + readdigits(&rn, 0); /* exponent digits */ + } + ungetc(rn.c, rn.f); /* unread look-ahead char */ + l_unlockfile(rn.f); + rn.buff[rn.n] = '\0'; /* finish string */ + if (l_likely(lua_stringtonumber(L, rn.buff))) + return 1; /* ok, it is a valid number */ + else { /* invalid format */ + lua_pushnil(L); /* "result" to be removed */ + return 0; /* read fails */ + } +} + + +static int test_eof (lua_State *L, FILE *f) { + int c = getc(f); + ungetc(c, f); /* no-op when c == EOF */ + lua_pushliteral(L, ""); + return (c != EOF); +} + + +static int read_line (lua_State *L, FILE *f, int chop) { + luaL_Buffer b; + int c; + luaL_buffinit(L, &b); + do { /* may need to read several chunks to get whole line */ + char *buff = luaL_prepbuffer(&b); /* preallocate buffer space */ + unsigned i = 0; + l_lockfile(f); /* no memory errors can happen inside the lock */ + while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n') + buff[i++] = cast_char(c); /* read up to end of line or buffer limit */ + l_unlockfile(f); + luaL_addsize(&b, i); + } while (c != EOF && c != '\n'); /* repeat until end of line */ + if (!chop && c == '\n') /* want a newline and have one? */ + luaL_addchar(&b, '\n'); /* add ending newline to result */ + luaL_pushresult(&b); /* close buffer */ + /* return ok if read something (either a newline or something else) */ + return (c == '\n' || lua_rawlen(L, -1) > 0); +} + + +static void read_all (lua_State *L, FILE *f) { + size_t nr; + luaL_Buffer b; + luaL_buffinit(L, &b); + do { /* read file in chunks of LUAL_BUFFERSIZE bytes */ + char *p = luaL_prepbuffer(&b); + nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f); + luaL_addsize(&b, nr); + } while (nr == LUAL_BUFFERSIZE); + luaL_pushresult(&b); /* close buffer */ +} + + +static int read_chars (lua_State *L, FILE *f, size_t n) { + size_t nr; /* number of chars actually read */ + char *p; + luaL_Buffer b; + luaL_buffinit(L, &b); + p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */ + nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */ + luaL_addsize(&b, nr); + luaL_pushresult(&b); /* close buffer */ + return (nr > 0); /* true iff read something */ +} + + +static int g_read (lua_State *L, FILE *f, int first) { + int nargs = lua_gettop(L) - 1; + int n, success; + clearerr(f); + errno = 0; + if (nargs == 0) { /* no arguments? */ + success = read_line(L, f, 1); + n = first + 1; /* to return 1 result */ + } + else { + /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = first; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)luaL_checkinteger(L, n); + success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); + } + else { + const char *p = luaL_checkstring(L, n); + if (*p == '*') p++; /* skip optional '*' (for compatibility) */ + switch (*p) { + case 'n': /* number */ + success = read_number(L, f); + break; + case 'l': /* line */ + success = read_line(L, f, 1); + break; + case 'L': /* line with end-of-line */ + success = read_line(L, f, 0); + break; + case 'a': /* file */ + read_all(L, f); /* read entire file */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (ferror(f)) + return luaL_fileresult(L, 0, NULL); + if (!success) { + lua_pop(L, 1); /* remove last result */ + luaL_pushfail(L); /* push nil instead */ + } + return n - first; +} + + +static int io_read (lua_State *L) { + return g_read(L, getiofile(L, IO_INPUT), 1); +} + + +static int f_read (lua_State *L) { + return g_read(L, tofile(L), 2); +} + + +/* +** Iteration function for 'lines'. +*/ +static int io_readline (lua_State *L) { + LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1)); + int i; + int n = (int)lua_tointeger(L, lua_upvalueindex(2)); + if (isclosed(p)) /* file is already closed? */ + return luaL_error(L, "file is already closed"); + lua_settop(L , 1); + luaL_checkstack(L, n, "too many arguments"); + for (i = 1; i <= n; i++) /* push arguments to 'g_read' */ + lua_pushvalue(L, lua_upvalueindex(3 + i)); + n = g_read(L, p->f, 2); /* 'n' is number of results */ + lua_assert(n > 0); /* should return at least a nil */ + if (lua_toboolean(L, -n)) /* read at least one value? */ + return n; /* return them */ + else { /* first result is false: EOF or error */ + if (n > 1) { /* is there error information? */ + /* 2nd result is error message */ + return luaL_error(L, "%s", lua_tostring(L, -n + 1)); + } + if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ + lua_settop(L, 0); /* clear stack */ + lua_pushvalue(L, lua_upvalueindex(1)); /* push file at index 1 */ + aux_close(L); /* close it */ + } + return 0; + } +} + +/* }====================================================== */ + + +static int g_write (lua_State *L, FILE *f, int arg) { + int nargs = lua_gettop(L) - arg; + size_t totalbytes = 0; /* total number of bytes written */ + errno = 0; + for (; nargs--; arg++) { /* for each argument */ + char buff[LUA_N2SBUFFSZ]; + const char *s; + size_t numbytes; /* bytes written in one call to 'fwrite' */ + size_t len = lua_numbertocstring(L, arg, buff); /* try as a number */ + if (len > 0) { /* did conversion work (value was a number)? */ + s = buff; + len--; + } + else /* must be a string */ + s = luaL_checklstring(L, arg, &len); + numbytes = fwrite(s, sizeof(char), len, f); + totalbytes += numbytes; + if (numbytes < len) { /* write error? */ + int n = luaL_fileresult(L, 0, NULL); + lua_pushinteger(L, cast_st2S(totalbytes)); + return n + 1; /* return fail, error msg., error code, and counter */ + } + } + return 1; /* no errors; file handle already on stack top */ +} + + +static int io_write (lua_State *L) { + return g_write(L, getiofile(L, IO_OUTPUT), 1); +} + + +static int f_write (lua_State *L) { + FILE *f = tofile(L); + lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */ + return g_write(L, f, 2); +} + + +static int f_seek (lua_State *L) { + static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, "cur", modenames); + lua_Integer p3 = luaL_optinteger(L, 3, 0); + l_seeknum offset = (l_seeknum)p3; + luaL_argcheck(L, (lua_Integer)offset == p3, 3, + "not an integer in proper range"); + errno = 0; + op = l_fseek(f, offset, mode[op]); + if (l_unlikely(op)) + return luaL_fileresult(L, 0, NULL); /* error */ + else { + lua_pushinteger(L, (lua_Integer)l_ftell(f)); + return 1; + } +} + + +static int f_setvbuf (lua_State *L) { + static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; + static const char *const modenames[] = {"no", "full", "line", NULL}; + FILE *f = tofile(L); + int op = luaL_checkoption(L, 2, NULL, modenames); + lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); + int res; + errno = 0; + res = setvbuf(f, NULL, mode[op], (size_t)sz); + return luaL_fileresult(L, res == 0, NULL); +} + + +static int aux_flush (lua_State *L, FILE *f) { + errno = 0; + return luaL_fileresult(L, fflush(f) == 0, NULL); +} + + +static int f_flush (lua_State *L) { + return aux_flush(L, tofile(L)); +} + + +static int io_flush (lua_State *L) { + return aux_flush(L, getiofile(L, IO_OUTPUT)); +} + + +/* +** functions for 'io' library +*/ +static const luaL_Reg iolib[] = { + {"close", io_close}, + {"flush", io_flush}, + {"input", io_input}, + {"lines", io_lines}, + {"open", io_open}, + {"output", io_output}, + {"popen", io_popen}, + {"read", io_read}, + {"tmpfile", io_tmpfile}, + {"type", io_type}, + {"write", io_write}, + {NULL, NULL} +}; + + +/* +** methods for file handles +*/ +static const luaL_Reg meth[] = { + {"read", f_read}, + {"write", f_write}, + {"lines", f_lines}, + {"flush", f_flush}, + {"seek", f_seek}, + {"close", f_close}, + {"setvbuf", f_setvbuf}, + {NULL, NULL} +}; + + +/* +** metamethods for file handles +*/ +static const luaL_Reg metameth[] = { + {"__index", NULL}, /* placeholder */ + {"__gc", f_gc}, + {"__close", f_gc}, + {"__tostring", f_tostring}, + {NULL, NULL} +}; + + +static void createmeta (lua_State *L) { + luaL_newmetatable(L, LUA_FILEHANDLE); /* metatable for file handles */ + luaL_setfuncs(L, metameth, 0); /* add metamethods to new metatable */ + luaL_newlibtable(L, meth); /* create method table */ + luaL_setfuncs(L, meth, 0); /* add file methods to method table */ + lua_setfield(L, -2, "__index"); /* metatable.__index = method table */ + lua_pop(L, 1); /* pop metatable */ +} + + +/* +** function to (not) close the standard files stdin, stdout, and stderr +*/ +static int io_noclose (lua_State *L) { + LStream *p = tolstream(L); + p->closef = &io_noclose; /* keep file opened */ + luaL_pushfail(L); + lua_pushliteral(L, "cannot close standard file"); + return 2; +} + + +static void createstdfile (lua_State *L, FILE *f, const char *k, + const char *fname) { + LStream *p = newprefile(L); + p->f = f; + p->closef = &io_noclose; + if (k != NULL) { + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */ + } + lua_setfield(L, -2, fname); /* add file to module */ +} + + +LUAMOD_API int luaopen_io (lua_State *L) { + luaL_newlib(L, iolib); /* new module */ + createmeta(L); + /* create (and set) default files */ + createstdfile(L, stdin, IO_INPUT, "stdin"); + createstdfile(L, stdout, IO_OUTPUT, "stdout"); + createstdfile(L, stderr, NULL, "stderr"); + return 1; +} + + +``` + +`Lua/ljumptab.h`: + +```h +/* +** $Id: ljumptab.h $ +** Jump Table for the Lua interpreter +** See Copyright Notice in lua.h +*/ + + +#undef vmdispatch +#undef vmcase +#undef vmbreak + +#define vmdispatch(x) goto *disptab[x]; + +#define vmcase(l) L_##l: + +#define vmbreak vmfetch(); vmdispatch(GET_OPCODE(i)); + + +static const void *const disptab[NUM_OPCODES] = { + +#if 0 +** you can update the following list with this command: +** +** sed -n '/^OP_/!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h +** +#endif + +&&L_OP_MOVE, +&&L_OP_LOADI, +&&L_OP_LOADF, +&&L_OP_LOADK, +&&L_OP_LOADKX, +&&L_OP_LOADFALSE, +&&L_OP_LFALSESKIP, +&&L_OP_LOADTRUE, +&&L_OP_LOADNIL, +&&L_OP_GETUPVAL, +&&L_OP_SETUPVAL, +&&L_OP_GETTABUP, +&&L_OP_GETTABLE, +&&L_OP_GETI, +&&L_OP_GETFIELD, +&&L_OP_SETTABUP, +&&L_OP_SETTABLE, +&&L_OP_SETI, +&&L_OP_SETFIELD, +&&L_OP_NEWTABLE, +&&L_OP_SELF, +&&L_OP_ADDI, +&&L_OP_ADDK, +&&L_OP_SUBK, +&&L_OP_MULK, +&&L_OP_MODK, +&&L_OP_POWK, +&&L_OP_DIVK, +&&L_OP_IDIVK, +&&L_OP_BANDK, +&&L_OP_BORK, +&&L_OP_BXORK, +&&L_OP_SHLI, +&&L_OP_SHRI, +&&L_OP_ADD, +&&L_OP_SUB, +&&L_OP_MUL, +&&L_OP_MOD, +&&L_OP_POW, +&&L_OP_DIV, +&&L_OP_IDIV, +&&L_OP_BAND, +&&L_OP_BOR, +&&L_OP_BXOR, +&&L_OP_SHL, +&&L_OP_SHR, +&&L_OP_MMBIN, +&&L_OP_MMBINI, +&&L_OP_MMBINK, +&&L_OP_UNM, +&&L_OP_BNOT, +&&L_OP_NOT, +&&L_OP_LEN, +&&L_OP_CONCAT, +&&L_OP_CLOSE, +&&L_OP_TBC, +&&L_OP_JMP, +&&L_OP_EQ, +&&L_OP_LT, +&&L_OP_LE, +&&L_OP_EQK, +&&L_OP_EQI, +&&L_OP_LTI, +&&L_OP_LEI, +&&L_OP_GTI, +&&L_OP_GEI, +&&L_OP_TEST, +&&L_OP_TESTSET, +&&L_OP_CALL, +&&L_OP_TAILCALL, +&&L_OP_RETURN, +&&L_OP_RETURN0, +&&L_OP_RETURN1, +&&L_OP_FORLOOP, +&&L_OP_FORPREP, +&&L_OP_TFORPREP, +&&L_OP_TFORCALL, +&&L_OP_TFORLOOP, +&&L_OP_SETLIST, +&&L_OP_CLOSURE, +&&L_OP_VARARG, +&&L_OP_GETVARG, +&&L_OP_ERRNNIL, +&&L_OP_VARARGPREP, +&&L_OP_EXTRAARG + +}; + +``` + +`Lua/llex.c`: + +```c +/* +** $Id: llex.c $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + +#define llex_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <locale.h> +#include <string.h> + +#include "lua.h" + +#include "lctype.h" +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "llex.h" +#include "lobject.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "lzio.h" + + + +#define next(ls) (ls->current = zgetc(ls->z)) + + +/* minimum size for string buffer */ +#if !defined(LUA_MINBUFFER) +#define LUA_MINBUFFER 32 +#endif + + +#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') + + +/* ORDER RESERVED */ +static const char *const luaX_tokens [] = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "global", "goto", "if", + "in", "local", "nil", "not", "or", "repeat", + "return", "then", "true", "until", "while", + "//", "..", "...", "==", ">=", "<=", "~=", + "<<", ">>", "::", "<eof>", + "<number>", "<integer>", "<name>", "<string>" +}; + + +#define save_and_next(ls) (save(ls, ls->current), next(ls)) + + +static l_noret lexerror (LexState *ls, const char *msg, int token); + + +static void save (LexState *ls, int c) { + Mbuffer *b = ls->buff; + if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) { + size_t newsize = luaZ_sizebuffer(b); /* get old size */; + if (newsize >= (MAX_SIZE/3 * 2)) /* larger than MAX_SIZE/1.5 ? */ + lexerror(ls, "lexical element too long", 0); + newsize += (newsize >> 1); /* new size is 1.5 times the old one */ + luaZ_resizebuffer(ls->L, b, newsize); + } + b->buffer[luaZ_bufflen(b)++] = cast_char(c); +} + + +void luaX_init (lua_State *L) { + int i; + TString *e = luaS_newliteral(L, LUA_ENV); /* create env name */ + luaC_fix(L, obj2gco(e)); /* never collect this name */ + for (i=0; i<NUM_RESERVED; i++) { + TString *ts = luaS_new(L, luaX_tokens[i]); + luaC_fix(L, obj2gco(ts)); /* reserved words are never collected */ + ts->extra = cast_byte(i+1); /* reserved word */ + } +} + + +const char *luaX_token2str (LexState *ls, int token) { + if (token < FIRST_RESERVED) { /* single-byte symbols? */ + if (lisprint(token)) + return luaO_pushfstring(ls->L, "'%c'", token); + else /* control character */ + return luaO_pushfstring(ls->L, "'<\\%d>'", token); + } + else { + const char *s = luaX_tokens[token - FIRST_RESERVED]; + if (token < TK_EOS) /* fixed format (symbols and reserved words)? */ + return luaO_pushfstring(ls->L, "'%s'", s); + else /* names, strings, and numerals */ + return s; + } +} + + +static const char *txtToken (LexState *ls, int token) { + switch (token) { + case TK_NAME: case TK_STRING: + case TK_FLT: case TK_INT: + save(ls, '\0'); + return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff)); + default: + return luaX_token2str(ls, token); + } +} + + +static l_noret lexerror (LexState *ls, const char *msg, int token) { + msg = luaG_addinfo(ls->L, msg, ls->source, ls->linenumber); + if (token) + luaO_pushfstring(ls->L, "%s near %s", msg, txtToken(ls, token)); + luaD_throw(ls->L, LUA_ERRSYNTAX); +} + + +l_noret luaX_syntaxerror (LexState *ls, const char *msg) { + lexerror(ls, msg, ls->t.token); +} + + +/* +** Anchors a string in scanner's table so that it will not be collected +** until the end of the compilation; by that time it should be anchored +** somewhere. It also internalizes long strings, ensuring there is only +** one copy of each unique string. +*/ +static TString *anchorstr (LexState *ls, TString *ts) { + lua_State *L = ls->L; + TValue oldts; + int tag = luaH_getstr(ls->h, ts, &oldts); + if (!tagisempty(tag)) /* string already present? */ + return tsvalue(&oldts); /* use stored value */ + else { /* create a new entry */ + TValue *stv = s2v(L->top.p++); /* reserve stack space for string */ + setsvalue(L, stv, ts); /* push (anchor) the string on the stack */ + luaH_set(L, ls->h, stv, stv); /* t[string] = string */ + /* table is not a metatable, so it does not need to invalidate cache */ + luaC_checkGC(L); + L->top.p--; /* remove string from stack */ + return ts; + } +} + + +/* +** Creates a new string and anchors it in scanner's table. +*/ +TString *luaX_newstring (LexState *ls, const char *str, size_t l) { + return anchorstr(ls, luaS_newlstr(ls->L, str, l)); +} + + +/* +** increment line number and skips newline sequence (any of +** \n, \r, \n\r, or \r\n) +*/ +static void inclinenumber (LexState *ls) { + int old = ls->current; + lua_assert(currIsNewline(ls)); + next(ls); /* skip '\n' or '\r' */ + if (currIsNewline(ls) && ls->current != old) + next(ls); /* skip '\n\r' or '\r\n' */ + if (++ls->linenumber >= INT_MAX) + lexerror(ls, "chunk has too many lines", 0); +} + + +void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source, + int firstchar) { + ls->t.token = 0; + ls->L = L; + ls->current = firstchar; + ls->lookahead.token = TK_EOS; /* no look-ahead token */ + ls->z = z; + ls->fs = NULL; + ls->linenumber = 1; + ls->lastline = 1; + ls->source = source; + /* all three strings here ("_ENV", "break", "global") were fixed, + so they cannot be collected */ + ls->envn = luaS_newliteral(L, LUA_ENV); /* get env string */ + ls->brkn = luaS_newliteral(L, "break"); /* get "break" string */ +#if defined(LUA_COMPAT_GLOBAL) + /* compatibility mode: "global" is not a reserved word */ + ls->glbn = luaS_newliteral(L, "global"); /* get "global" string */ + ls->glbn->extra = 0; /* mark it as not reserved */ +#endif + luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ +} + + + +/* +** ======================================================= +** LEXICAL ANALYZER +** ======================================================= +*/ + + +static int check_next1 (LexState *ls, int c) { + if (ls->current == c) { + next(ls); + return 1; + } + else return 0; +} + + +/* +** Check whether current char is in set 'set' (with two chars) and +** saves it +*/ +static int check_next2 (LexState *ls, const char *set) { + lua_assert(set[2] == '\0'); + if (ls->current == set[0] || ls->current == set[1]) { + save_and_next(ls); + return 1; + } + else return 0; +} + + +/* LUA_NUMBER */ +/* +** This function is quite liberal in what it accepts, as 'luaO_str2num' +** will reject ill-formed numerals. Roughly, it accepts the following +** pattern: +** +** %d(%x|%.|([Ee][+-]?))* | 0[Xx](%x|%.|([Pp][+-]?))* +** +** The only tricky part is to accept [+-] only after a valid exponent +** mark, to avoid reading '3-4' or '0xe+1' as a single number. +** +** The caller might have already read an initial dot. +*/ +static int read_numeral (LexState *ls, SemInfo *seminfo) { + TValue obj; + const char *expo = "Ee"; + int first = ls->current; + lua_assert(lisdigit(ls->current)); + save_and_next(ls); + if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */ + expo = "Pp"; + for (;;) { + if (check_next2(ls, expo)) /* exponent mark? */ + check_next2(ls, "-+"); /* optional exponent sign */ + else if (lisxdigit(ls->current) || ls->current == '.') /* '%x|%.' */ + save_and_next(ls); + else break; + } + if (lislalpha(ls->current)) /* is numeral touching a letter? */ + save_and_next(ls); /* force an error */ + save(ls, '\0'); + if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */ + lexerror(ls, "malformed number", TK_FLT); + if (ttisinteger(&obj)) { + seminfo->i = ivalue(&obj); + return TK_INT; + } + else { + lua_assert(ttisfloat(&obj)); + seminfo->r = fltvalue(&obj); + return TK_FLT; + } +} + + +/* +** read a sequence '[=*[' or ']=*]', leaving the last bracket. If +** sequence is well formed, return its number of '='s + 2; otherwise, +** return 1 if it is a single bracket (no '='s and no 2nd bracket); +** otherwise (an unfinished '[==...') return 0. +*/ +static size_t skip_sep (LexState *ls) { + size_t count = 0; + int s = ls->current; + lua_assert(s == '[' || s == ']'); + save_and_next(ls); + while (ls->current == '=') { + save_and_next(ls); + count++; + } + return (ls->current == s) ? count + 2 + : (count == 0) ? 1 + : 0; +} + + +static void read_long_string (LexState *ls, SemInfo *seminfo, size_t sep) { + int line = ls->linenumber; /* initial line (for error message) */ + save_and_next(ls); /* skip 2nd '[' */ + if (currIsNewline(ls)) /* string starts with a newline? */ + inclinenumber(ls); /* skip it */ + for (;;) { + switch (ls->current) { + case EOZ: { /* error */ + const char *what = (seminfo ? "string" : "comment"); + const char *msg = luaO_pushfstring(ls->L, + "unfinished long %s (starting at line %d)", what, line); + lexerror(ls, msg, TK_EOS); + break; /* to avoid warnings */ + } + case ']': { + if (skip_sep(ls) == sep) { + save_and_next(ls); /* skip 2nd ']' */ + goto endloop; + } + break; + } + case '\n': case '\r': { + save(ls, '\n'); + inclinenumber(ls); + if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */ + break; + } + default: { + if (seminfo) save_and_next(ls); + else next(ls); + } + } + } endloop: + if (seminfo) + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + sep, + luaZ_bufflen(ls->buff) - 2 * sep); +} + + +static void esccheck (LexState *ls, int c, const char *msg) { + if (!c) { + if (ls->current != EOZ) + save_and_next(ls); /* add current to buffer for error message */ + lexerror(ls, msg, TK_STRING); + } +} + + +static int gethexa (LexState *ls) { + save_and_next(ls); + esccheck (ls, lisxdigit(ls->current), "hexadecimal digit expected"); + return luaO_hexavalue(ls->current); +} + + +static int readhexaesc (LexState *ls) { + int r = gethexa(ls); + r = (r << 4) + gethexa(ls); + luaZ_buffremove(ls->buff, 2); /* remove saved chars from buffer */ + return r; +} + + +/* +** When reading a UTF-8 escape sequence, save everything to the buffer +** for error reporting in case of errors; 'i' counts the number of +** saved characters, so that they can be removed if case of success. +*/ +static l_uint32 readutf8esc (LexState *ls) { + l_uint32 r; + int i = 4; /* number of chars to be removed: start with #"\u{X" */ + save_and_next(ls); /* skip 'u' */ + esccheck(ls, ls->current == '{', "missing '{'"); + r = cast_uint(gethexa(ls)); /* must have at least one digit */ + while (cast_void(save_and_next(ls)), lisxdigit(ls->current)) { + i++; + esccheck(ls, r <= (0x7FFFFFFFu >> 4), "UTF-8 value too large"); + r = (r << 4) + luaO_hexavalue(ls->current); + } + esccheck(ls, ls->current == '}', "missing '}'"); + next(ls); /* skip '}' */ + luaZ_buffremove(ls->buff, i); /* remove saved chars from buffer */ + return r; +} + + +static void utf8esc (LexState *ls) { + char buff[UTF8BUFFSZ]; + int n = luaO_utf8esc(buff, readutf8esc(ls)); + for (; n > 0; n--) /* add 'buff' to string */ + save(ls, buff[UTF8BUFFSZ - n]); +} + + +static int readdecesc (LexState *ls) { + int i; + int r = 0; /* result accumulator */ + for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */ + r = 10*r + ls->current - '0'; + save_and_next(ls); + } + esccheck(ls, r <= UCHAR_MAX, "decimal escape too large"); + luaZ_buffremove(ls->buff, i); /* remove read digits from buffer */ + return r; +} + + +static void read_string (LexState *ls, int del, SemInfo *seminfo) { + save_and_next(ls); /* keep delimiter (for error messages) */ + while (ls->current != del) { + switch (ls->current) { + case EOZ: + lexerror(ls, "unfinished string", TK_EOS); + break; /* to avoid warnings */ + case '\n': + case '\r': + lexerror(ls, "unfinished string", TK_STRING); + break; /* to avoid warnings */ + case '\\': { /* escape sequences */ + int c; /* final character to be saved */ + save_and_next(ls); /* keep '\\' for error messages */ + switch (ls->current) { + case 'a': c = '\a'; goto read_save; + case 'b': c = '\b'; goto read_save; + case 'f': c = '\f'; goto read_save; + case 'n': c = '\n'; goto read_save; + case 'r': c = '\r'; goto read_save; + case 't': c = '\t'; goto read_save; + case 'v': c = '\v'; goto read_save; + case 'x': c = readhexaesc(ls); goto read_save; + case 'u': utf8esc(ls); goto no_save; + case '\n': case '\r': + inclinenumber(ls); c = '\n'; goto only_save; + case '\\': case '\"': case '\'': + c = ls->current; goto read_save; + case EOZ: goto no_save; /* will raise an error next loop */ + case 'z': { /* zap following span of spaces */ + luaZ_buffremove(ls->buff, 1); /* remove '\\' */ + next(ls); /* skip the 'z' */ + while (lisspace(ls->current)) { + if (currIsNewline(ls)) inclinenumber(ls); + else next(ls); + } + goto no_save; + } + default: { + esccheck(ls, lisdigit(ls->current), "invalid escape sequence"); + c = readdecesc(ls); /* digital escape '\ddd' */ + goto only_save; + } + } + read_save: + next(ls); + /* go through */ + only_save: + luaZ_buffremove(ls->buff, 1); /* remove '\\' */ + save(ls, c); + /* go through */ + no_save: break; + } + default: + save_and_next(ls); + } + } + save_and_next(ls); /* skip delimiter */ + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1, + luaZ_bufflen(ls->buff) - 2); +} + + +static int llex (LexState *ls, SemInfo *seminfo) { + luaZ_resetbuffer(ls->buff); + for (;;) { + switch (ls->current) { + case '\n': case '\r': { /* line breaks */ + inclinenumber(ls); + break; + } + case ' ': case '\f': case '\t': case '\v': { /* spaces */ + next(ls); + break; + } + case '-': { /* '-' or '--' (comment) */ + next(ls); + if (ls->current != '-') return '-'; + /* else is a comment */ + next(ls); + if (ls->current == '[') { /* long comment? */ + size_t sep = skip_sep(ls); + luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */ + if (sep >= 2) { + read_long_string(ls, NULL, sep); /* skip long comment */ + luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */ + break; + } + } + /* else short comment */ + while (!currIsNewline(ls) && ls->current != EOZ) + next(ls); /* skip until end of line (or end of file) */ + break; + } + case '[': { /* long string or simply '[' */ + size_t sep = skip_sep(ls); + if (sep >= 2) { + read_long_string(ls, seminfo, sep); + return TK_STRING; + } + else if (sep == 0) /* '[=...' missing second bracket? */ + lexerror(ls, "invalid long string delimiter", TK_STRING); + return '['; + } + case '=': { + next(ls); + if (check_next1(ls, '=')) return TK_EQ; /* '==' */ + else return '='; + } + case '<': { + next(ls); + if (check_next1(ls, '=')) return TK_LE; /* '<=' */ + else if (check_next1(ls, '<')) return TK_SHL; /* '<<' */ + else return '<'; + } + case '>': { + next(ls); + if (check_next1(ls, '=')) return TK_GE; /* '>=' */ + else if (check_next1(ls, '>')) return TK_SHR; /* '>>' */ + else return '>'; + } + case '/': { + next(ls); + if (check_next1(ls, '/')) return TK_IDIV; /* '//' */ + else return '/'; + } + case '~': { + next(ls); + if (check_next1(ls, '=')) return TK_NE; /* '~=' */ + else return '~'; + } + case ':': { + next(ls); + if (check_next1(ls, ':')) return TK_DBCOLON; /* '::' */ + else return ':'; + } + case '"': case '\'': { /* short literal strings */ + read_string(ls, ls->current, seminfo); + return TK_STRING; + } + case '.': { /* '.', '..', '...', or number */ + save_and_next(ls); + if (check_next1(ls, '.')) { + if (check_next1(ls, '.')) + return TK_DOTS; /* '...' */ + else return TK_CONCAT; /* '..' */ + } + else if (!lisdigit(ls->current)) return '.'; + else return read_numeral(ls, seminfo); + } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + return read_numeral(ls, seminfo); + } + case EOZ: { + return TK_EOS; + } + default: { + if (lislalpha(ls->current)) { /* identifier or reserved word? */ + TString *ts; + do { + save_and_next(ls); + } while (lislalnum(ls->current)); + /* find or create string */ + ts = luaS_newlstr(ls->L, luaZ_buffer(ls->buff), + luaZ_bufflen(ls->buff)); + if (isreserved(ts)) /* reserved word? */ + return ts->extra - 1 + FIRST_RESERVED; + else { + seminfo->ts = anchorstr(ls, ts); + return TK_NAME; + } + } + else { /* single-char tokens ('+', '*', '%', '{', '}', ...) */ + int c = ls->current; + next(ls); + return c; + } + } + } + } +} + + +void luaX_next (LexState *ls) { + ls->lastline = ls->linenumber; + if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ + ls->t = ls->lookahead; /* use this one */ + ls->lookahead.token = TK_EOS; /* and discharge it */ + } + else + ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ +} + + +int luaX_lookahead (LexState *ls) { + lua_assert(ls->lookahead.token == TK_EOS); + ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); + return ls->lookahead.token; +} + + +``` + +`Lua/llex.h`: + +```h +/* +** $Id: llex.h $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + +#ifndef llex_h +#define llex_h + +#include <limits.h> + +#include "lobject.h" +#include "lzio.h" + + +/* +** Single-char tokens (terminal symbols) are represented by their own +** numeric code. Other tokens start at the following value. +*/ +#define FIRST_RESERVED (UCHAR_MAX + 1) + + +#if !defined(LUA_ENV) +#define LUA_ENV "_ENV" +#endif + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER RESERVED" +*/ +enum RESERVED { + /* terminal symbols denoted by reserved words */ + TK_AND = FIRST_RESERVED, TK_BREAK, + TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, + TK_GLOBAL, TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, + TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, + /* other terminal symbols */ + TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, + TK_SHL, TK_SHR, + TK_DBCOLON, TK_EOS, + TK_FLT, TK_INT, TK_NAME, TK_STRING +}; + +/* number of reserved words */ +#define NUM_RESERVED (cast_int(TK_WHILE-FIRST_RESERVED + 1)) + + +typedef union { + lua_Number r; + lua_Integer i; + TString *ts; +} SemInfo; /* semantics information */ + + +typedef struct Token { + int token; + SemInfo seminfo; +} Token; + + +/* state of the scanner plus state of the parser when shared by all + functions */ +typedef struct LexState { + int current; /* current character (charint) */ + int linenumber; /* input line counter */ + int lastline; /* line of last token 'consumed' */ + Token t; /* current token */ + Token lookahead; /* look ahead token */ + struct FuncState *fs; /* current function (parser) */ + struct lua_State *L; + ZIO *z; /* input stream */ + Mbuffer *buff; /* buffer for tokens */ + Table *h; /* to avoid collection/reuse strings */ + struct Dyndata *dyd; /* dynamic structures used by the parser */ + TString *source; /* current source name */ + TString *envn; /* environment variable name */ + TString *brkn; /* "break" name (used as a label) */ + TString *glbn; /* "global" name (when not a reserved word) */ +} LexState; + + +LUAI_FUNC void luaX_init (lua_State *L); +LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, + TString *source, int firstchar); +LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); +LUAI_FUNC void luaX_next (LexState *ls); +LUAI_FUNC int luaX_lookahead (LexState *ls); +LUAI_FUNC l_noret luaX_syntaxerror (LexState *ls, const char *s); +LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); + + +#endif + +``` + +`Lua/llimits.h`: + +```h +/* +** $Id: llimits.h $ +** Limits, basic types, and some other 'installation-dependent' definitions +** See Copyright Notice in lua.h +*/ + +#ifndef llimits_h +#define llimits_h + + +#include <limits.h> +#include <stddef.h> + + +#include "lua.h" + + +#define l_numbits(t) cast_int(sizeof(t) * CHAR_BIT) + +/* +** 'l_mem' is a signed integer big enough to count the total memory +** used by Lua. (It is signed due to the use of debt in several +** computations.) 'lu_mem' is a corresponding unsigned type. Usually, +** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines. +*/ +#if defined(LUAI_MEM) /* { external definitions? */ +typedef LUAI_MEM l_mem; +typedef LUAI_UMEM lu_mem; +#elif LUAI_IS32INT /* }{ */ +typedef ptrdiff_t l_mem; +typedef size_t lu_mem; +#else /* 16-bit ints */ /* }{ */ +typedef long l_mem; +typedef unsigned long lu_mem; +#endif /* } */ + +#define MAX_LMEM \ + cast(l_mem, (cast(lu_mem, 1) << (l_numbits(l_mem) - 1)) - 1) + + +/* chars used as small naturals (so that 'char' is reserved for characters) */ +typedef unsigned char lu_byte; +typedef signed char ls_byte; + + +/* Type for thread status/error codes */ +typedef lu_byte TStatus; + +/* The C API still uses 'int' for status/error codes */ +#define APIstatus(st) cast_int(st) + +/* maximum value for size_t */ +#define MAX_SIZET ((size_t)(~(size_t)0)) + +/* +** Maximum size for strings and userdata visible for Lua; should be +** representable as a lua_Integer and as a size_t. +*/ +#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \ + : cast_sizet(LUA_MAXINTEGER)) + +/* +** test whether an unsigned value is a power of 2 (or zero) +*/ +#define ispow2(x) (((x) & ((x) - 1)) == 0) + + +/* number of chars of a literal string without the ending \0 */ +#define LL(x) (sizeof(x)/sizeof(char) - 1) + + +/* +** conversion of pointer to unsigned integer: this is for hashing only; +** there is no problem if the integer cannot hold the whole pointer +** value. (In strict ISO C this may cause undefined behavior, but no +** actual machine seems to bother.) +*/ +#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 199901L +#include <stdint.h> +#if defined(UINTPTR_MAX) /* even in C99 this type is optional */ +#define L_P2I uintptr_t +#else /* no 'intptr'? */ +#define L_P2I uintmax_t /* use the largest available integer */ +#endif +#else /* C89 option */ +#define L_P2I size_t +#endif + +#define point2uint(p) cast_uint((L_P2I)(p) & UINT_MAX) + + + +/* types of 'usual argument conversions' for lua_Number and lua_Integer */ +typedef LUAI_UACNUMBER l_uacNumber; +typedef LUAI_UACINT l_uacInt; + + +/* +** Internal assertions for in-house debugging +*/ +#if defined LUAI_ASSERT +#undef NDEBUG +#include <assert.h> +#define lua_assert(c) assert(c) +#define assert_code(c) c +#endif + +#if defined(lua_assert) +#else +#define lua_assert(c) ((void)0) +#define assert_code(c) ((void)0) +#endif + +#define check_exp(c,e) (lua_assert(c), (e)) +/* to avoid problems with conditions too long */ +#define lua_longassert(c) assert_code((c) ? (void)0 : lua_assert(0)) + + +/* macro to avoid warnings about unused variables */ +#if !defined(UNUSED) +#define UNUSED(x) ((void)(x)) +#endif + + +/* type casts (a macro highlights casts in the code) */ +#define cast(t, exp) ((t)(exp)) + +#define cast_void(i) cast(void, (i)) +#define cast_voidp(i) cast(void *, (i)) +#define cast_num(i) cast(lua_Number, (i)) +#define cast_int(i) cast(int, (i)) +#define cast_short(i) cast(short, (i)) +#define cast_uint(i) cast(unsigned int, (i)) +#define cast_byte(i) cast(lu_byte, (i)) +#define cast_uchar(i) cast(unsigned char, (i)) +#define cast_char(i) cast(char, (i)) +#define cast_charp(i) cast(char *, (i)) +#define cast_sizet(i) cast(size_t, (i)) +#define cast_Integer(i) cast(lua_Integer, (i)) +#define cast_Inst(i) cast(Instruction, (i)) + + +/* cast a signed lua_Integer to lua_Unsigned */ +#if !defined(l_castS2U) +#define l_castS2U(i) ((lua_Unsigned)(i)) +#endif + +/* +** cast a lua_Unsigned to a signed lua_Integer; this cast is +** not strict ISO C, but two-complement architectures should +** work fine. +*/ +#if !defined(l_castU2S) +#define l_castU2S(i) ((lua_Integer)(i)) +#endif + +/* +** cast a size_t to lua_Integer: These casts are always valid for +** sizes of Lua objects (see MAX_SIZE) +*/ +#define cast_st2S(sz) ((lua_Integer)(sz)) + +/* Cast a ptrdiff_t to size_t, when it is known that the minuend +** comes from the subtrahend (the base) +*/ +#define ct_diff2sz(df) ((size_t)(df)) + +/* ptrdiff_t to lua_Integer */ +#define ct_diff2S(df) cast_st2S(ct_diff2sz(df)) + +/* +** Special type equivalent to '(void*)' for functions (to suppress some +** warnings when converting function pointers) +*/ +typedef void (*voidf)(void); + +/* +** Macro to convert pointer-to-void* to pointer-to-function. This cast +** is undefined according to ISO C, but POSIX assumes that it works. +** (The '__extension__' in gnu compilers is only to avoid warnings.) +*/ +#if defined(__GNUC__) +#define cast_func(p) (__extension__ (voidf)(p)) +#else +#define cast_func(p) ((voidf)(p)) +#endif + + + +/* +** non-return type +*/ +#if !defined(l_noret) + +#if defined(__GNUC__) +#define l_noret void __attribute__((noreturn)) +#elif defined(_MSC_VER) && _MSC_VER >= 1200 +#define l_noret void __declspec(noreturn) +#else +#define l_noret void +#endif + +#endif + + +/* +** Inline functions +*/ +#if !defined(LUA_USE_C89) +#define l_inline inline +#elif defined(__GNUC__) +#define l_inline __inline__ +#else +#define l_inline /* empty */ +#endif + +#define l_sinline static l_inline + + +/* +** An unsigned with (at least) 4 bytes +*/ +#if LUAI_IS32INT +typedef unsigned int l_uint32; +#else +typedef unsigned long l_uint32; +#endif + + +/* +** The luai_num* macros define the primitive operations over numbers. +*/ + +/* floor division (defined as 'floor(a/b)') */ +#if !defined(luai_numidiv) +#define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b))) +#endif + +/* float division */ +#if !defined(luai_numdiv) +#define luai_numdiv(L,a,b) ((a)/(b)) +#endif + +/* +** modulo: defined as 'a - floor(a/b)*b'; the direct computation +** using this definition has several problems with rounding errors, +** so it is better to use 'fmod'. 'fmod' gives the result of +** 'a - trunc(a/b)*b', and therefore must be corrected when +** 'trunc(a/b) ~= floor(a/b)'. That happens when the division has a +** non-integer negative result: non-integer result is equivalent to +** a non-zero remainder 'm'; negative result is equivalent to 'a' and +** 'b' with different signs, or 'm' and 'b' with different signs +** (as the result 'm' of 'fmod' has the same sign of 'a'). +*/ +#if !defined(luai_nummod) +#define luai_nummod(L,a,b,m) \ + { (void)L; (m) = l_mathop(fmod)(a,b); \ + if (((m) > 0) ? (b) < 0 : ((m) < 0 && (b) > 0)) (m) += (b); } +#endif + +/* exponentiation */ +#if !defined(luai_numpow) +#define luai_numpow(L,a,b) \ + ((void)L, (b == 2) ? (a)*(a) : l_mathop(pow)(a,b)) +#endif + +/* the others are quite standard operations */ +#if !defined(luai_numadd) +#define luai_numadd(L,a,b) ((a)+(b)) +#define luai_numsub(L,a,b) ((a)-(b)) +#define luai_nummul(L,a,b) ((a)*(b)) +#define luai_numunm(L,a) (-(a)) +#define luai_numeq(a,b) ((a)==(b)) +#define luai_numlt(a,b) ((a)<(b)) +#define luai_numle(a,b) ((a)<=(b)) +#define luai_numgt(a,b) ((a)>(b)) +#define luai_numge(a,b) ((a)>=(b)) +#define luai_numisnan(a) (!luai_numeq((a), (a))) +#endif + + + +/* +** lua_numbertointeger converts a float number with an integral value +** to an integer, or returns 0 if the float is not within the range of +** a lua_Integer. (The range comparisons are tricky because of +** rounding. The tests here assume a two-complement representation, +** where MININTEGER always has an exact representation as a float; +** MAXINTEGER may not have one, and therefore its conversion to float +** may have an ill-defined value.) +*/ +#define lua_numbertointeger(n,p) \ + ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ + (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ + (*(p) = (LUA_INTEGER)(n), 1)) + + + +/* +** LUAI_FUNC is a mark for all extern functions that are not to be +** exported to outside modules. +** LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables, +** none of which to be exported to outside modules (LUAI_DDEF for +** definitions and LUAI_DDEC for declarations). +** Elf and MACH/gcc (versions 3.2 and later) mark them as "hidden" to +** optimize access when Lua is compiled as a shared library. Not all elf +** targets support this attribute. Unfortunately, gcc does not offer +** a way to check whether the target offers that support, and those +** without support give a warning about it. To avoid these warnings, +** change to the default definition. +*/ +#if !defined(LUAI_FUNC) + +#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + (defined(__ELF__) || defined(__MACH__)) +#define LUAI_FUNC __attribute__((visibility("internal"))) extern +#else +#define LUAI_FUNC extern +#endif + +#define LUAI_DDEC(dec) LUAI_FUNC dec +#define LUAI_DDEF /* empty */ + +#endif + + +/* Give these macros simpler names for internal use */ +#define l_likely(x) luai_likely(x) +#define l_unlikely(x) luai_unlikely(x) + +/* +** {================================================================== +** "Abstraction Layer" for basic report of messages and errors +** =================================================================== +*/ + +/* print a string */ +#if !defined(lua_writestring) +#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) +#endif + +/* print a newline and flush the output */ +#if !defined(lua_writeline) +#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout)) +#endif + +/* print an error message */ +#if !defined(lua_writestringerror) +#define lua_writestringerror(s,p) \ + (fprintf(stderr, (s), (p)), fflush(stderr)) +#endif + +/* }================================================================== */ + +#endif + + +``` + +`Lua/lmathlib.c`: + +```c +/* +** $Id: lmathlib.c $ +** Standard mathematical library +** See Copyright Notice in lua.h +*/ + +#define lmathlib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include <float.h> +#include <limits.h> +#include <math.h> +#include <stdlib.h> +#include <time.h> + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "llimits.h" + + +#undef PI +#define PI (l_mathop(3.141592653589793238462643383279502884)) + + +static int math_abs (lua_State *L) { + if (lua_isinteger(L, 1)) { + lua_Integer n = lua_tointeger(L, 1); + if (n < 0) n = (lua_Integer)(0u - (lua_Unsigned)n); + lua_pushinteger(L, n); + } + else + lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1))); + return 1; +} + + +static int math_sin (lua_State *L) { + lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1))); + return 1; +} + + +static int math_cos (lua_State *L) { + lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1))); + return 1; +} + + +static int math_tan (lua_State *L) { + lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1))); + return 1; +} + + +static int math_asin (lua_State *L) { + lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1))); + return 1; +} + + +static int math_acos (lua_State *L) { + lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1))); + return 1; +} + + +static int math_atan (lua_State *L) { + lua_Number y = luaL_checknumber(L, 1); + lua_Number x = luaL_optnumber(L, 2, 1); + lua_pushnumber(L, l_mathop(atan2)(y, x)); + return 1; +} + + +static int math_toint (lua_State *L) { + int valid; + lua_Integer n = lua_tointegerx(L, 1, &valid); + if (l_likely(valid)) + lua_pushinteger(L, n); + else { + luaL_checkany(L, 1); + luaL_pushfail(L); /* value is not convertible to integer */ + } + return 1; +} + + +static void pushnumint (lua_State *L, lua_Number d) { + lua_Integer n; + if (lua_numbertointeger(d, &n)) /* does 'd' fit in an integer? */ + lua_pushinteger(L, n); /* result is integer */ + else + lua_pushnumber(L, d); /* result is float */ +} + + +static int math_floor (lua_State *L) { + if (lua_isinteger(L, 1)) + lua_settop(L, 1); /* integer is its own floor */ + else { + lua_Number d = l_mathop(floor)(luaL_checknumber(L, 1)); + pushnumint(L, d); + } + return 1; +} + + +static int math_ceil (lua_State *L) { + if (lua_isinteger(L, 1)) + lua_settop(L, 1); /* integer is its own ceiling */ + else { + lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1)); + pushnumint(L, d); + } + return 1; +} + + +static int math_fmod (lua_State *L) { + if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) { + lua_Integer d = lua_tointeger(L, 2); + if ((lua_Unsigned)d + 1u <= 1u) { /* special cases: -1 or 0 */ + luaL_argcheck(L, d != 0, 2, "zero"); + lua_pushinteger(L, 0); /* avoid overflow with 0x80000... / -1 */ + } + else + lua_pushinteger(L, lua_tointeger(L, 1) % d); + } + else + lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1), + luaL_checknumber(L, 2))); + return 1; +} + + +/* +** next function does not use 'modf', avoiding problems with 'double*' +** (which is not compatible with 'float*') when lua_Number is not +** 'double'. +*/ +static int math_modf (lua_State *L) { + if (lua_isinteger(L ,1)) { + lua_settop(L, 1); /* number is its own integer part */ + lua_pushnumber(L, 0); /* no fractional part */ + } + else { + lua_Number n = luaL_checknumber(L, 1); + /* integer part (rounds toward zero) */ + lua_Number ip = (n < 0) ? l_mathop(ceil)(n) : l_mathop(floor)(n); + pushnumint(L, ip); + /* fractional part (test needed for inf/-inf) */ + lua_pushnumber(L, (n == ip) ? l_mathop(0.0) : (n - ip)); + } + return 2; +} + + +static int math_sqrt (lua_State *L) { + lua_pushnumber(L, l_mathop(sqrt)(luaL_checknumber(L, 1))); + return 1; +} + + +static int math_ult (lua_State *L) { + lua_Integer a = luaL_checkinteger(L, 1); + lua_Integer b = luaL_checkinteger(L, 2); + lua_pushboolean(L, (lua_Unsigned)a < (lua_Unsigned)b); + return 1; +} + + +static int math_log (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + lua_Number res; + if (lua_isnoneornil(L, 2)) + res = l_mathop(log)(x); + else { + lua_Number base = luaL_checknumber(L, 2); +#if !defined(LUA_USE_C89) + if (base == l_mathop(2.0)) + res = l_mathop(log2)(x); + else +#endif + if (base == l_mathop(10.0)) + res = l_mathop(log10)(x); + else + res = l_mathop(log)(x)/l_mathop(log)(base); + } + lua_pushnumber(L, res); + return 1; +} + + +static int math_exp (lua_State *L) { + lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1))); + return 1; +} + + +static int math_deg (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI)); + return 1; +} + + +static int math_rad (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0))); + return 1; +} + + +static int math_frexp (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + int ep; + lua_pushnumber(L, l_mathop(frexp)(x, &ep)); + lua_pushinteger(L, ep); + return 2; +} + + +static int math_ldexp (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + int ep = (int)luaL_checkinteger(L, 2); + lua_pushnumber(L, l_mathop(ldexp)(x, ep)); + return 1; +} + + +static int math_min (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int imin = 1; /* index of current minimum value */ + int i; + luaL_argcheck(L, n >= 1, 1, "value expected"); + for (i = 2; i <= n; i++) { + if (lua_compare(L, i, imin, LUA_OPLT)) + imin = i; + } + lua_pushvalue(L, imin); + return 1; +} + + +static int math_max (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int imax = 1; /* index of current maximum value */ + int i; + luaL_argcheck(L, n >= 1, 1, "value expected"); + for (i = 2; i <= n; i++) { + if (lua_compare(L, imax, i, LUA_OPLT)) + imax = i; + } + lua_pushvalue(L, imax); + return 1; +} + + +static int math_type (lua_State *L) { + if (lua_type(L, 1) == LUA_TNUMBER) + lua_pushstring(L, (lua_isinteger(L, 1)) ? "integer" : "float"); + else { + luaL_checkany(L, 1); + luaL_pushfail(L); + } + return 1; +} + + + +/* +** {================================================================== +** Pseudo-Random Number Generator based on 'xoshiro256**'. +** =================================================================== +*/ + +/* +** This code uses lots of shifts. ISO C does not allow shifts greater +** than or equal to the width of the type being shifted, so some shifts +** are written in convoluted ways to match that restriction. For +** preprocessor tests, it assumes a width of 32 bits, so the maximum +** shift there is 31 bits. +*/ + + +/* number of binary digits in the mantissa of a float */ +#define FIGS l_floatatt(MANT_DIG) + +#if FIGS > 64 +/* there are only 64 random bits; use them all */ +#undef FIGS +#define FIGS 64 +#endif + + +/* +** LUA_RAND32 forces the use of 32-bit integers in the implementation +** of the PRN generator (mainly for testing). +*/ +#if !defined(LUA_RAND32) && !defined(Rand64) + +/* try to find an integer type with at least 64 bits */ + +#if ((ULONG_MAX >> 31) >> 31) >= 3 + +/* 'long' has at least 64 bits */ +#define Rand64 unsigned long +#define SRand64 long + +#elif !defined(LUA_USE_C89) && defined(LLONG_MAX) + +/* there is a 'long long' type (which must have at least 64 bits) */ +#define Rand64 unsigned long long +#define SRand64 long long + +#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3 + +/* 'lua_Unsigned' has at least 64 bits */ +#define Rand64 lua_Unsigned +#define SRand64 lua_Integer + +#endif + +#endif + + +#if defined(Rand64) /* { */ + +/* +** Standard implementation, using 64-bit integers. +** If 'Rand64' has more than 64 bits, the extra bits do not interfere +** with the 64 initial bits, except in a right shift. Moreover, the +** final result has to discard the extra bits. +*/ + +/* avoid using extra bits when needed */ +#define trim64(x) ((x) & 0xffffffffffffffffu) + + +/* rotate left 'x' by 'n' bits */ +static Rand64 rotl (Rand64 x, int n) { + return (x << n) | (trim64(x) >> (64 - n)); +} + +static Rand64 nextrand (Rand64 *state) { + Rand64 state0 = state[0]; + Rand64 state1 = state[1]; + Rand64 state2 = state[2] ^ state0; + Rand64 state3 = state[3] ^ state1; + Rand64 res = rotl(state1 * 5, 7) * 9; + state[0] = state0 ^ state3; + state[1] = state1 ^ state2; + state[2] = state2 ^ (state1 << 17); + state[3] = rotl(state3, 45); + return res; +} + + +/* +** Convert bits from a random integer into a float in the +** interval [0,1), getting the higher FIG bits from the +** random unsigned integer and converting that to a float. +** Some old Microsoft compilers cannot cast an unsigned long +** to a floating-point number, so we use a signed long as an +** intermediary. When lua_Number is float or double, the shift ensures +** that 'sx' is non negative; in that case, a good compiler will remove +** the correction. +*/ + +/* must throw out the extra (64 - FIGS) bits */ +#define shift64_FIG (64 - FIGS) + +/* 2^(-FIGS) == 2^-1 / 2^(FIGS-1) */ +#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1))) + +static lua_Number I2d (Rand64 x) { + SRand64 sx = (SRand64)(trim64(x) >> shift64_FIG); + lua_Number res = (lua_Number)(sx) * scaleFIG; + if (sx < 0) + res += l_mathop(1.0); /* correct the two's complement if negative */ + lua_assert(0 <= res && res < 1); + return res; +} + +/* convert a 'Rand64' to a 'lua_Unsigned' */ +#define I2UInt(x) ((lua_Unsigned)trim64(x)) + +/* convert a 'lua_Unsigned' to a 'Rand64' */ +#define Int2I(x) ((Rand64)(x)) + + +#else /* no 'Rand64' }{ */ + +/* +** Use two 32-bit integers to represent a 64-bit quantity. +*/ +typedef struct Rand64 { + l_uint32 h; /* higher half */ + l_uint32 l; /* lower half */ +} Rand64; + + +/* +** If 'l_uint32' has more than 32 bits, the extra bits do not interfere +** with the 32 initial bits, except in a right shift and comparisons. +** Moreover, the final result has to discard the extra bits. +*/ + +/* avoid using extra bits when needed */ +#define trim32(x) ((x) & 0xffffffffu) + + +/* +** basic operations on 'Rand64' values +*/ + +/* build a new Rand64 value */ +static Rand64 packI (l_uint32 h, l_uint32 l) { + Rand64 result; + result.h = h; + result.l = l; + return result; +} + +/* return i << n */ +static Rand64 Ishl (Rand64 i, int n) { + lua_assert(n > 0 && n < 32); + return packI((i.h << n) | (trim32(i.l) >> (32 - n)), i.l << n); +} + +/* i1 ^= i2 */ +static void Ixor (Rand64 *i1, Rand64 i2) { + i1->h ^= i2.h; + i1->l ^= i2.l; +} + +/* return i1 + i2 */ +static Rand64 Iadd (Rand64 i1, Rand64 i2) { + Rand64 result = packI(i1.h + i2.h, i1.l + i2.l); + if (trim32(result.l) < trim32(i1.l)) /* carry? */ + result.h++; + return result; +} + +/* return i * 5 */ +static Rand64 times5 (Rand64 i) { + return Iadd(Ishl(i, 2), i); /* i * 5 == (i << 2) + i */ +} + +/* return i * 9 */ +static Rand64 times9 (Rand64 i) { + return Iadd(Ishl(i, 3), i); /* i * 9 == (i << 3) + i */ +} + +/* return 'i' rotated left 'n' bits */ +static Rand64 rotl (Rand64 i, int n) { + lua_assert(n > 0 && n < 32); + return packI((i.h << n) | (trim32(i.l) >> (32 - n)), + (trim32(i.h) >> (32 - n)) | (i.l << n)); +} + +/* for offsets larger than 32, rotate right by 64 - offset */ +static Rand64 rotl1 (Rand64 i, int n) { + lua_assert(n > 32 && n < 64); + n = 64 - n; + return packI((trim32(i.h) >> n) | (i.l << (32 - n)), + (i.h << (32 - n)) | (trim32(i.l) >> n)); +} + +/* +** implementation of 'xoshiro256**' algorithm on 'Rand64' values +*/ +static Rand64 nextrand (Rand64 *state) { + Rand64 res = times9(rotl(times5(state[1]), 7)); + Rand64 t = Ishl(state[1], 17); + Ixor(&state[2], state[0]); + Ixor(&state[3], state[1]); + Ixor(&state[1], state[2]); + Ixor(&state[0], state[3]); + Ixor(&state[2], t); + state[3] = rotl1(state[3], 45); + return res; +} + + +/* +** Converts a 'Rand64' into a float. +*/ + +/* an unsigned 1 with proper type */ +#define UONE ((l_uint32)1) + + +#if FIGS <= 32 + +/* 2^(-FIGS) */ +#define scaleFIG (l_mathop(0.5) / (UONE << (FIGS - 1))) + +/* +** get up to 32 bits from higher half, shifting right to +** throw out the extra bits. +*/ +static lua_Number I2d (Rand64 x) { + lua_Number h = (lua_Number)(trim32(x.h) >> (32 - FIGS)); + return h * scaleFIG; +} + +#else /* 32 < FIGS <= 64 */ + +/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */ +#define scaleFIG \ + (l_mathop(1.0) / (UONE << 30) / l_mathop(8.0) / (UONE << (FIGS - 33))) + +/* +** use FIGS - 32 bits from lower half, throwing out the other +** (32 - (FIGS - 32)) = (64 - FIGS) bits +*/ +#define shiftLOW (64 - FIGS) + +/* +** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32) +*/ +#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * l_mathop(2.0)) + + +static lua_Number I2d (Rand64 x) { + lua_Number h = (lua_Number)trim32(x.h) * shiftHI; + lua_Number l = (lua_Number)(trim32(x.l) >> shiftLOW); + return (h + l) * scaleFIG; +} + +#endif + + +/* convert a 'Rand64' to a 'lua_Unsigned' */ +static lua_Unsigned I2UInt (Rand64 x) { + return (((lua_Unsigned)trim32(x.h) << 31) << 1) | (lua_Unsigned)trim32(x.l); +} + +/* convert a 'lua_Unsigned' to a 'Rand64' */ +static Rand64 Int2I (lua_Unsigned n) { + return packI((l_uint32)((n >> 31) >> 1), (l_uint32)n); +} + +#endif /* } */ + + +/* +** A state uses four 'Rand64' values. +*/ +typedef struct { + Rand64 s[4]; +} RanState; + + +/* +** Project the random integer 'ran' into the interval [0, n]. +** Because 'ran' has 2^B possible values, the projection can only be +** uniform when the size of the interval is a power of 2 (exact +** division). So, to get a uniform projection into [0, n], we +** first compute 'lim', the smallest Mersenne number not smaller than +** 'n'. We then project 'ran' into the interval [0, lim]. If the result +** is inside [0, n], we are done. Otherwise, we try with another 'ran', +** until we have a result inside the interval. +*/ +static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n, + RanState *state) { + lua_Unsigned lim = n; /* to compute the Mersenne number */ + int sh; /* how much to spread bits to the right in 'lim' */ + /* spread '1' bits in 'lim' until it becomes a Mersenne number */ + for (sh = 1; (lim & (lim + 1)) != 0; sh *= 2) + lim |= (lim >> sh); /* spread '1's to the right */ + while ((ran &= lim) > n) /* project 'ran' into [0..lim] and test */ + ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */ + return ran; +} + + +static int math_random (lua_State *L) { + lua_Integer low, up; + lua_Unsigned p; + RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1)); + Rand64 rv = nextrand(state->s); /* next pseudo-random value */ + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, I2d(rv)); /* float between 0 and 1 */ + return 1; + } + case 1: { /* only upper limit */ + low = 1; + up = luaL_checkinteger(L, 1); + if (up == 0) { /* single 0 as argument? */ + lua_pushinteger(L, l_castU2S(I2UInt(rv))); /* full random integer */ + return 1; + } + break; + } + case 2: { /* lower and upper limits */ + low = luaL_checkinteger(L, 1); + up = luaL_checkinteger(L, 2); + break; + } + default: return luaL_error(L, "wrong number of arguments"); + } + /* random integer in the interval [low, up] */ + luaL_argcheck(L, low <= up, 1, "interval is empty"); + /* project random integer into the interval [0, up - low] */ + p = project(I2UInt(rv), l_castS2U(up) - l_castS2U(low), state); + lua_pushinteger(L, l_castU2S(p + l_castS2U(low))); + return 1; +} + + +static void setseed (lua_State *L, Rand64 *state, + lua_Unsigned n1, lua_Unsigned n2) { + int i; + state[0] = Int2I(n1); + state[1] = Int2I(0xff); /* avoid a zero state */ + state[2] = Int2I(n2); + state[3] = Int2I(0); + for (i = 0; i < 16; i++) + nextrand(state); /* discard initial values to "spread" seed */ + lua_pushinteger(L, l_castU2S(n1)); + lua_pushinteger(L, l_castU2S(n2)); +} + + +static int math_randomseed (lua_State *L) { + RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1)); + lua_Unsigned n1, n2; + if (lua_isnone(L, 1)) { + n1 = luaL_makeseed(L); /* "random" seed */ + n2 = I2UInt(nextrand(state->s)); /* in case seed is not that random... */ + } + else { + n1 = l_castS2U(luaL_checkinteger(L, 1)); + n2 = l_castS2U(luaL_optinteger(L, 2, 0)); + } + setseed(L, state->s, n1, n2); + return 2; /* return seeds */ +} + + +static const luaL_Reg randfuncs[] = { + {"random", math_random}, + {"randomseed", math_randomseed}, + {NULL, NULL} +}; + + +/* +** Register the random functions and initialize their state. +*/ +static void setrandfunc (lua_State *L) { + RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0); + setseed(L, state->s, luaL_makeseed(L), 0); /* initialize with random seed */ + lua_pop(L, 2); /* remove pushed seeds */ + luaL_setfuncs(L, randfuncs, 1); +} + +/* }================================================================== */ + + +/* +** {================================================================== +** Deprecated functions (for compatibility only) +** =================================================================== +*/ +#if defined(LUA_COMPAT_MATHLIB) + +static int math_cosh (lua_State *L) { + lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sinh (lua_State *L) { + lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tanh (lua_State *L) { + lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1))); + return 1; +} + +static int math_pow (lua_State *L) { + lua_Number x = luaL_checknumber(L, 1); + lua_Number y = luaL_checknumber(L, 2); + lua_pushnumber(L, l_mathop(pow)(x, y)); + return 1; +} + +static int math_log10 (lua_State *L) { + lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1))); + return 1; +} + +#endif +/* }================================================================== */ + + + +static const luaL_Reg mathlib[] = { + {"abs", math_abs}, + {"acos", math_acos}, + {"asin", math_asin}, + {"atan", math_atan}, + {"ceil", math_ceil}, + {"cos", math_cos}, + {"deg", math_deg}, + {"exp", math_exp}, + {"tointeger", math_toint}, + {"floor", math_floor}, + {"fmod", math_fmod}, + {"frexp", math_frexp}, + {"ult", math_ult}, + {"ldexp", math_ldexp}, + {"log", math_log}, + {"max", math_max}, + {"min", math_min}, + {"modf", math_modf}, + {"rad", math_rad}, + {"sin", math_sin}, + {"sqrt", math_sqrt}, + {"tan", math_tan}, + {"type", math_type}, +#if defined(LUA_COMPAT_MATHLIB) + {"atan2", math_atan}, + {"cosh", math_cosh}, + {"sinh", math_sinh}, + {"tanh", math_tanh}, + {"pow", math_pow}, + {"log10", math_log10}, +#endif + /* placeholders */ + {"random", NULL}, + {"randomseed", NULL}, + {"pi", NULL}, + {"huge", NULL}, + {"maxinteger", NULL}, + {"mininteger", NULL}, + {NULL, NULL} +}; + + +/* +** Open math library +*/ +LUAMOD_API int luaopen_math (lua_State *L) { + luaL_newlib(L, mathlib); + lua_pushnumber(L, PI); + lua_setfield(L, -2, "pi"); + lua_pushnumber(L, (lua_Number)HUGE_VAL); + lua_setfield(L, -2, "huge"); + lua_pushinteger(L, LUA_MAXINTEGER); + lua_setfield(L, -2, "maxinteger"); + lua_pushinteger(L, LUA_MININTEGER); + lua_setfield(L, -2, "mininteger"); + setrandfunc(L); + return 1; +} + + +``` + +`Lua/lmem.c`: + +```c +/* +** $Id: lmem.c $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + +#define lmem_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <stddef.h> + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +/* +** About the realloc function: +** void *frealloc (void *ud, void *ptr, size_t osize, size_t nsize); +** ('osize' is the old size, 'nsize' is the new size) +** +** - frealloc(ud, p, x, 0) frees the block 'p' and returns NULL. +** Particularly, frealloc(ud, NULL, 0, 0) does nothing, +** which is equivalent to free(NULL) in ISO C. +** +** - frealloc(ud, NULL, x, s) creates a new block of size 's' +** (no matter 'x'). Returns NULL if it cannot create the new block. +** +** - otherwise, frealloc(ud, b, x, y) reallocates the block 'b' from +** size 'x' to size 'y'. Returns NULL if it cannot reallocate the +** block to the new size. +*/ + + +/* +** Macro to call the allocation function. +*/ +#define callfrealloc(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns)) + + +/* +** When an allocation fails, it will try again after an emergency +** collection, except when it cannot run a collection. The GC should +** not be called while the state is not fully built, as the collector +** is not yet fully initialized. Also, it should not be called when +** 'gcstopem' is true, because then the interpreter is in the middle of +** a collection step. +*/ +#define cantryagain(g) (completestate(g) && !g->gcstopem) + + + + +#if defined(EMERGENCYGCTESTS) +/* +** First allocation will fail except when freeing a block (frees never +** fail) and when it cannot try again; this fail will trigger 'tryagain' +** and a full GC cycle at every allocation. +*/ +static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { + if (ns > 0 && cantryagain(g)) + return NULL; /* fail */ + else /* normal allocation */ + return callfrealloc(g, block, os, ns); +} +#else +#define firsttry(g,block,os,ns) callfrealloc(g, block, os, ns) +#endif + + + + + +/* +** {================================================================== +** Functions to allocate/deallocate arrays for the Parser +** =================================================================== +*/ + +/* +** Minimum size for arrays during parsing, to avoid overhead of +** reallocating to size 1, then 2, and then 4. All these arrays +** will be reallocated to exact sizes or erased when parsing ends. +*/ +#define MINSIZEARRAY 4 + + +void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize, + unsigned size_elems, int limit, const char *what) { + void *newblock; + int size = *psize; + if (nelems + 1 <= size) /* does one extra element still fit? */ + return block; /* nothing to be done */ + if (size >= limit / 2) { /* cannot double it? */ + if (l_unlikely(size >= limit)) /* cannot grow even a little? */ + luaG_runerror(L, "too many %s (limit is %d)", what, limit); + size = limit; /* still have at least one free place */ + } + else { + size *= 2; + if (size < MINSIZEARRAY) + size = MINSIZEARRAY; /* minimum size */ + } + lua_assert(nelems + 1 <= size && size <= limit); + /* 'limit' ensures that multiplication will not overflow */ + newblock = luaM_saferealloc_(L, block, cast_sizet(*psize) * size_elems, + cast_sizet(size) * size_elems); + *psize = size; /* update only when everything else is OK */ + return newblock; +} + + +/* +** In prototypes, the size of the array is also its number of +** elements (to save memory). So, if it cannot shrink an array +** to its number of elements, the only option is to raise an +** error. +*/ +void *luaM_shrinkvector_ (lua_State *L, void *block, int *size, + int final_n, unsigned size_elem) { + void *newblock; + size_t oldsize = cast_sizet(*size) * size_elem; + size_t newsize = cast_sizet(final_n) * size_elem; + lua_assert(newsize <= oldsize); + newblock = luaM_saferealloc_(L, block, oldsize, newsize); + *size = final_n; + return newblock; +} + +/* }================================================================== */ + + +l_noret luaM_toobig (lua_State *L) { + luaG_runerror(L, "memory allocation error: block too big"); +} + + +/* +** Free memory +*/ +void luaM_free_ (lua_State *L, void *block, size_t osize) { + global_State *g = G(L); + lua_assert((osize == 0) == (block == NULL)); + callfrealloc(g, block, osize, 0); + g->GCdebt += cast(l_mem, osize); +} + + +/* +** In case of allocation fail, this function will do an emergency +** collection to free some memory and then try the allocation again. +*/ +static void *tryagain (lua_State *L, void *block, + size_t osize, size_t nsize) { + global_State *g = G(L); + if (cantryagain(g)) { + luaC_fullgc(L, 1); /* try to free some memory... */ + return callfrealloc(g, block, osize, nsize); /* try again */ + } + else return NULL; /* cannot run an emergency collection */ +} + + +/* +** Generic allocation routine. +*/ +void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { + void *newblock; + global_State *g = G(L); + lua_assert((osize == 0) == (block == NULL)); + newblock = firsttry(g, block, osize, nsize); + if (l_unlikely(newblock == NULL && nsize > 0)) { + newblock = tryagain(L, block, osize, nsize); + if (newblock == NULL) /* still no memory? */ + return NULL; /* do not update 'GCdebt' */ + } + lua_assert((nsize == 0) == (newblock == NULL)); + g->GCdebt -= cast(l_mem, nsize) - cast(l_mem, osize); + return newblock; +} + + +void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize, + size_t nsize) { + void *newblock = luaM_realloc_(L, block, osize, nsize); + if (l_unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */ + luaM_error(L); + return newblock; +} + + +void *luaM_malloc_ (lua_State *L, size_t size, int tag) { + if (size == 0) + return NULL; /* that's all */ + else { + global_State *g = G(L); + void *newblock = firsttry(g, NULL, cast_sizet(tag), size); + if (l_unlikely(newblock == NULL)) { + newblock = tryagain(L, NULL, cast_sizet(tag), size); + if (newblock == NULL) + luaM_error(L); + } + g->GCdebt -= cast(l_mem, size); + return newblock; + } +} + +``` + +`Lua/lmem.h`: + +```h +/* +** $Id: lmem.h $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + +#ifndef lmem_h +#define lmem_h + + +#include <stddef.h> + +#include "llimits.h" +#include "lua.h" + + +#define luaM_error(L) luaD_throw(L, LUA_ERRMEM) + + +/* +** This macro tests whether it is safe to multiply 'n' by the size of +** type 't' without overflows. Because 'e' is always constant, it avoids +** the runtime division MAX_SIZET/(e). +** (The macro is somewhat complex to avoid warnings: The 'sizeof' +** comparison avoids a runtime comparison when overflow cannot occur. +** The compiler should be able to optimize the real test by itself, but +** when it does it, it may give a warning about "comparison is always +** false due to limited range of data type"; the +1 tricks the compiler, +** avoiding this warning but also this optimization.) +*/ +#define luaM_testsize(n,e) \ + (sizeof(n) >= sizeof(size_t) && cast_sizet((n)) + 1 > MAX_SIZET/(e)) + +#define luaM_checksize(L,n,e) \ + (luaM_testsize(n,e) ? luaM_toobig(L) : cast_void(0)) + + +/* +** Computes the minimum between 'n' and 'MAX_SIZET/sizeof(t)', so that +** the result is not larger than 'n' and cannot overflow a 'size_t' +** when multiplied by the size of type 't'. (Assumes that 'n' is an +** 'int' and that 'int' is not larger than 'size_t'.) +*/ +#define luaM_limitN(n,t) \ + ((cast_sizet(n) <= MAX_SIZET/sizeof(t)) ? (n) : \ + cast_int((MAX_SIZET/sizeof(t)))) + + +/* +** Arrays of chars do not need any test +*/ +#define luaM_reallocvchar(L,b,on,n) \ + cast_charp(luaM_saferealloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char))) + +#define luaM_freemem(L, b, s) luaM_free_(L, (b), (s)) +#define luaM_free(L, b) luaM_free_(L, (b), sizeof(*(b))) +#define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b))) + +#define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0)) +#define luaM_newvector(L,n,t) \ + cast(t*, luaM_malloc_(L, cast_sizet(n)*sizeof(t), 0)) +#define luaM_newvectorchecked(L,n,t) \ + (luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t)) + +#define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag) + +#define luaM_newblock(L, size) luaM_newvector(L, size, char) + +#define luaM_growvector(L,v,nelems,size,t,limit,e) \ + ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \ + luaM_limitN(limit,t),e))) + +#define luaM_reallocvector(L, v,oldn,n,t) \ + (cast(t *, luaM_realloc_(L, v, cast_sizet(oldn) * sizeof(t), \ + cast_sizet(n) * sizeof(t)))) + +#define luaM_shrinkvector(L,v,size,fs,t) \ + ((v)=cast(t *, luaM_shrinkvector_(L, v, &(size), fs, sizeof(t)))) + +LUAI_FUNC l_noret luaM_toobig (lua_State *L); + +/* not to be called directly */ +LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, + size_t size); +LUAI_FUNC void *luaM_saferealloc_ (lua_State *L, void *block, size_t oldsize, + size_t size); +LUAI_FUNC void luaM_free_ (lua_State *L, void *block, size_t osize); +LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int nelems, + int *size, unsigned size_elem, int limit, + const char *what); +LUAI_FUNC void *luaM_shrinkvector_ (lua_State *L, void *block, int *nelem, + int final_n, unsigned size_elem); +LUAI_FUNC void *luaM_malloc_ (lua_State *L, size_t size, int tag); + +#endif + + +``` + +`Lua/loadlib.c`: + +```c +/* +** $Id: loadlib.c $ +** Dynamic library loader for Lua +** See Copyright Notice in lua.h +** +** This module contains an implementation of loadlib for Unix systems +** that have dlfcn, an implementation for Windows, and a stub for other +** systems. +*/ + +#define loadlib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "llimits.h" + + +/* +** LUA_CSUBSEP is the character that replaces dots in submodule names +** when searching for a C loader. +** LUA_LSUBSEP is the character that replaces dots in submodule names +** when searching for a Lua loader. +*/ +#if !defined(LUA_CSUBSEP) +#define LUA_CSUBSEP LUA_DIRSEP +#endif + +#if !defined(LUA_LSUBSEP) +#define LUA_LSUBSEP LUA_DIRSEP +#endif + + +/* prefix for open functions in C libraries */ +#define LUA_POF "luaopen_" + +/* separator for open functions in C libraries */ +#define LUA_OFSEP "_" + + +/* +** key for table in the registry that keeps handles +** for all loaded C libraries +*/ +static const char *const CLIBS = "_CLIBS"; + +#define LIB_FAIL "open" + + +#define setprogdir(L) ((void)0) + + +/* cast void* to a Lua function */ +#define cast_Lfunc(p) cast(lua_CFunction, cast_func(p)) + + +/* +** system-dependent functions +*/ + +/* +** unload library 'lib' +*/ +static void lsys_unloadlib (void *lib); + +/* +** load C library in file 'path'. If 'seeglb', load with all names in +** the library global. +** Returns the library; in case of error, returns NULL plus an +** error string in the stack. +*/ +static void *lsys_load (lua_State *L, const char *path, int seeglb); + +/* +** Try to find a function named 'sym' in library 'lib'. +** Returns the function; in case of error, returns NULL plus an +** error string in the stack. +*/ +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym); + + + + +#if defined(LUA_USE_DLOPEN) /* { */ +/* +** {======================================================================== +** This is an implementation of loadlib based on the dlfcn interface, +** which is available in all POSIX systems. +** ========================================================================= +*/ + +#include <dlfcn.h> + + +static void lsys_unloadlib (void *lib) { + dlclose(lib); +} + + +static void *lsys_load (lua_State *L, const char *path, int seeglb) { + void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL)); + if (l_unlikely(lib == NULL)) + lua_pushstring(L, dlerror()); + return lib; +} + + +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = cast_Lfunc(dlsym(lib, sym)); + if (l_unlikely(f == NULL)) + lua_pushstring(L, dlerror()); + return f; +} + +/* }====================================================== */ + + + +#elif defined(LUA_DL_DLL) /* }{ */ +/* +** {====================================================================== +** This is an implementation of loadlib for Windows using native functions. +** ======================================================================= +*/ + +#include <windows.h> + + +/* +** optional flags for LoadLibraryEx +*/ +#if !defined(LUA_LLE_FLAGS) +#define LUA_LLE_FLAGS 0 +#endif + + +#undef setprogdir + + +/* +** Replace in the path (on the top of the stack) any occurrence +** of LUA_EXEC_DIR with the executable's path. +*/ +static void setprogdir (lua_State *L) { + char buff[MAX_PATH + 1]; + char *lb; + DWORD nsize = sizeof(buff)/sizeof(char); + DWORD n = GetModuleFileNameA(NULL, buff, nsize); /* get exec. name */ + if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) + luaL_error(L, "unable to get ModuleFileName"); + else { + *lb = '\0'; /* cut name on the last '\\' to get the path */ + luaL_gsub(L, lua_tostring(L, -1), LUA_EXEC_DIR, buff); + lua_remove(L, -2); /* remove original string */ + } +} + + + + +static void pusherror (lua_State *L) { + int error = GetLastError(); + char buffer[128]; + if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, 0, buffer, sizeof(buffer)/sizeof(char), NULL)) + lua_pushstring(L, buffer); + else + lua_pushfstring(L, "system error %d\n", error); +} + +static void lsys_unloadlib (void *lib) { + FreeLibrary((HMODULE)lib); +} + + +static void *lsys_load (lua_State *L, const char *path, int seeglb) { + HMODULE lib = LoadLibraryExA(path, NULL, LUA_LLE_FLAGS); + (void)(seeglb); /* not used: symbols are 'global' by default */ + if (lib == NULL) pusherror(L); + return lib; +} + + +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = cast_Lfunc(GetProcAddress((HMODULE)lib, sym)); + if (f == NULL) pusherror(L); + return f; +} + +/* }====================================================== */ + + +#else /* }{ */ +/* +** {====================================================== +** Fallback for other systems +** ======================================================= +*/ + +#undef LIB_FAIL +#define LIB_FAIL "absent" + + +#define DLMSG "dynamic libraries not enabled; check your Lua installation" + + +static void lsys_unloadlib (void *lib) { + (void)(lib); /* not used */ +} + + +static void *lsys_load (lua_State *L, const char *path, int seeglb) { + (void)(path); (void)(seeglb); /* not used */ + lua_pushliteral(L, DLMSG); + return NULL; +} + + +static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { + (void)(lib); (void)(sym); /* not used */ + lua_pushliteral(L, DLMSG); + return NULL; +} + +/* }====================================================== */ +#endif /* } */ + + +/* +** {================================================================== +** Set Paths +** =================================================================== +*/ + +/* +** LUA_PATH_VAR and LUA_CPATH_VAR are the names of the environment +** variables that Lua check to set its paths. +*/ +#if !defined(LUA_PATH_VAR) +#define LUA_PATH_VAR "LUA_PATH" +#endif + +#if !defined(LUA_CPATH_VAR) +#define LUA_CPATH_VAR "LUA_CPATH" +#endif + + + +/* +** return registry.LUA_NOENV as a boolean +*/ +static int noenv (lua_State *L) { + int b; + lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); + b = lua_toboolean(L, -1); + lua_pop(L, 1); /* remove value */ + return b; +} + + +/* +** Set a path. (If using the default path, assume it is a string +** literal in C and create it as an external string.) +*/ +static void setpath (lua_State *L, const char *fieldname, + const char *envname, + const char *dft) { + const char *dftmark; + const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX); + const char *path = getenv(nver); /* try versioned name */ + if (path == NULL) /* no versioned environment variable? */ + path = getenv(envname); /* try unversioned name */ + if (path == NULL || noenv(L)) /* no environment variable? */ + lua_pushexternalstring(L, dft, strlen(dft), NULL, NULL); /* use default */ + else if ((dftmark = strstr(path, LUA_PATH_SEP LUA_PATH_SEP)) == NULL) + lua_pushstring(L, path); /* nothing to change */ + else { /* path contains a ";;": insert default path in its place */ + size_t len = strlen(path); + luaL_Buffer b; + luaL_buffinit(L, &b); + if (path < dftmark) { /* is there a prefix before ';;'? */ + luaL_addlstring(&b, path, ct_diff2sz(dftmark - path)); /* add it */ + luaL_addchar(&b, *LUA_PATH_SEP); + } + luaL_addstring(&b, dft); /* add default */ + if (dftmark < path + len - 2) { /* is there a suffix after ';;'? */ + luaL_addchar(&b, *LUA_PATH_SEP); + luaL_addlstring(&b, dftmark + 2, ct_diff2sz((path + len - 2) - dftmark)); + } + luaL_pushresult(&b); + } + setprogdir(L); + lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */ + lua_pop(L, 1); /* pop versioned variable name ('nver') */ +} + +/* }================================================================== */ + + +/* +** External strings created by DLLs may need the DLL code to be +** deallocated. This implies that a DLL can only be unloaded after all +** its strings were deallocated. To ensure that, we create a 'library +** string' to represent each DLL, and when this string is deallocated +** it closes its corresponding DLL. +** (The string itself is irrelevant; its userdata is the DLL pointer.) +*/ + + +/* +** return registry.CLIBS[path] +*/ +static void *checkclib (lua_State *L, const char *path) { + void *plib; + lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); + lua_getfield(L, -1, path); + plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */ + lua_pop(L, 2); /* pop CLIBS table and 'plib' */ + return plib; +} + + +/* +** Deallocate function for library strings. +** Unload the DLL associated with the string being deallocated. +*/ +static void *freelib (void *ud, void *ptr, size_t osize, size_t nsize) { + /* string itself is irrelevant and static */ + (void)ptr; (void)osize; (void)nsize; + lsys_unloadlib(ud); /* unload library represented by the string */ + return NULL; +} + + +/* +** Create a library string that, when deallocated, will unload 'plib' +*/ +static void createlibstr (lua_State *L, void *plib) { + /* common content for all library strings */ + static const char dummy[] = "01234567890"; + lua_pushexternalstring(L, dummy, sizeof(dummy) - 1, freelib, plib); +} + + +/* +** registry.CLIBS[path] = plib -- for queries. +** Also create a reference to strlib, so that the library string will +** only be collected when registry.CLIBS is collected. +*/ +static void addtoclib (lua_State *L, const char *path, void *plib) { + lua_getfield(L, LUA_REGISTRYINDEX, CLIBS); + lua_pushlightuserdata(L, plib); + lua_setfield(L, -2, path); /* CLIBS[path] = plib */ + createlibstr(L, plib); + luaL_ref(L, -2); /* keep library string in CLIBS */ + lua_pop(L, 1); /* pop CLIBS table */ +} + + +/* error codes for 'lookforfunc' */ +#define ERRLIB 1 +#define ERRFUNC 2 + +/* +** Look for a C function named 'sym' in a dynamically loaded library +** 'path'. +** First, check whether the library is already loaded; if not, try +** to load it. +** Then, if 'sym' is '*', return true (as library has been loaded). +** Otherwise, look for symbol 'sym' in the library and push a +** C function with that symbol. +** Return 0 with 'true' or a function in the stack; in case of +** errors, return an error code with an error message in the stack. +*/ +static int lookforfunc (lua_State *L, const char *path, const char *sym) { + void *reg = checkclib(L, path); /* check loaded C libraries */ + if (reg == NULL) { /* must load library? */ + reg = lsys_load(L, path, *sym == '*'); /* global symbols if 'sym'=='*' */ + if (reg == NULL) return ERRLIB; /* unable to load library */ + addtoclib(L, path, reg); + } + if (*sym == '*') { /* loading only library (no function)? */ + lua_pushboolean(L, 1); /* return 'true' */ + return 0; /* no errors */ + } + else { + lua_CFunction f = lsys_sym(L, reg, sym); + if (f == NULL) + return ERRFUNC; /* unable to find function */ + lua_pushcfunction(L, f); /* else create new function */ + return 0; /* no errors */ + } +} + + +static int ll_loadlib (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + const char *init = luaL_checkstring(L, 2); + int stat = lookforfunc(L, path, init); + if (l_likely(stat == 0)) /* no errors? */ + return 1; /* return the loaded function */ + else { /* error; error message is on stack top */ + luaL_pushfail(L); + lua_insert(L, -2); + lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init"); + return 3; /* return fail, error message, and where */ + } +} + + + +/* +** {====================================================== +** 'require' function +** ======================================================= +*/ + + +static int readable (const char *filename) { + FILE *f = fopen(filename, "r"); /* try to open file */ + if (f == NULL) return 0; /* open failed */ + fclose(f); + return 1; +} + + +/* +** Get the next name in '*path' = 'name1;name2;name3;...', changing +** the ending ';' to '\0' to create a zero-terminated string. Return +** NULL when list ends. +*/ +static const char *getnextfilename (char **path, char *end) { + char *sep; + char *name = *path; + if (name == end) + return NULL; /* no more names */ + else if (*name == '\0') { /* from previous iteration? */ + *name = *LUA_PATH_SEP; /* restore separator */ + name++; /* skip it */ + } + sep = strchr(name, *LUA_PATH_SEP); /* find next separator */ + if (sep == NULL) /* separator not found? */ + sep = end; /* name goes until the end */ + *sep = '\0'; /* finish file name */ + *path = sep; /* will start next search from here */ + return name; +} + + +/* +** Given a path such as ";blabla.so;blublu.so", pushes the string +** +** no file 'blabla.so' +** no file 'blublu.so' +*/ +static void pusherrornotfound (lua_State *L, const char *path) { + luaL_Buffer b; + luaL_buffinit(L, &b); + luaL_addstring(&b, "no file '"); + luaL_addgsub(&b, path, LUA_PATH_SEP, "'\n\tno file '"); + luaL_addstring(&b, "'"); + luaL_pushresult(&b); +} + + +static const char *searchpath (lua_State *L, const char *name, + const char *path, + const char *sep, + const char *dirsep) { + luaL_Buffer buff; + char *pathname; /* path with name inserted */ + char *endpathname; /* its end */ + const char *filename; + /* separator is non-empty and appears in 'name'? */ + if (*sep != '\0' && strchr(name, *sep) != NULL) + name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */ + luaL_buffinit(L, &buff); + /* add path to the buffer, replacing marks ('?') with the file name */ + luaL_addgsub(&buff, path, LUA_PATH_MARK, name); + luaL_addchar(&buff, '\0'); + pathname = luaL_buffaddr(&buff); /* writable list of file names */ + endpathname = pathname + luaL_bufflen(&buff) - 1; + while ((filename = getnextfilename(&pathname, endpathname)) != NULL) { + if (readable(filename)) /* does file exist and is readable? */ + return lua_pushstring(L, filename); /* save and return name */ + } + luaL_pushresult(&buff); /* push path to create error message */ + pusherrornotfound(L, lua_tostring(L, -1)); /* create error message */ + return NULL; /* not found */ +} + + +static int ll_searchpath (lua_State *L) { + const char *f = searchpath(L, luaL_checkstring(L, 1), + luaL_checkstring(L, 2), + luaL_optstring(L, 3, "."), + luaL_optstring(L, 4, LUA_DIRSEP)); + if (f != NULL) return 1; + else { /* error message is on top of the stack */ + luaL_pushfail(L); + lua_insert(L, -2); + return 2; /* return fail + error message */ + } +} + + +static const char *findfile (lua_State *L, const char *name, + const char *pname, + const char *dirsep) { + const char *path; + lua_getfield(L, lua_upvalueindex(1), pname); + path = lua_tostring(L, -1); + if (l_unlikely(path == NULL)) + luaL_error(L, "'package.%s' must be a string", pname); + return searchpath(L, name, path, ".", dirsep); +} + + +static int checkload (lua_State *L, int stat, const char *filename) { + if (l_likely(stat)) { /* module loaded successfully? */ + lua_pushstring(L, filename); /* will be 2nd argument to module */ + return 2; /* return open function and file name */ + } + else + return luaL_error(L, "error loading module '%s' from file '%s':\n\t%s", + lua_tostring(L, 1), filename, lua_tostring(L, -1)); +} + + +static int searcher_Lua (lua_State *L) { + const char *filename; + const char *name = luaL_checkstring(L, 1); + filename = findfile(L, name, "path", LUA_LSUBSEP); + if (filename == NULL) return 1; /* module not found in this path */ + return checkload(L, (luaL_loadfile(L, filename) == LUA_OK), filename); +} + + +/* +** Try to find a load function for module 'modname' at file 'filename'. +** First, change '.' to '_' in 'modname'; then, if 'modname' has +** the form X-Y (that is, it has an "ignore mark"), build a function +** name "luaopen_X" and look for it. (For compatibility, if that +** fails, it also tries "luaopen_Y".) If there is no ignore mark, +** look for a function named "luaopen_modname". +*/ +static int loadfunc (lua_State *L, const char *filename, const char *modname) { + const char *openfunc; + const char *mark; + modname = luaL_gsub(L, modname, ".", LUA_OFSEP); + mark = strchr(modname, *LUA_IGMARK); + if (mark) { + int stat; + openfunc = lua_pushlstring(L, modname, ct_diff2sz(mark - modname)); + openfunc = lua_pushfstring(L, LUA_POF"%s", openfunc); + stat = lookforfunc(L, filename, openfunc); + if (stat != ERRFUNC) return stat; + modname = mark + 1; /* else go ahead and try old-style name */ + } + openfunc = lua_pushfstring(L, LUA_POF"%s", modname); + return lookforfunc(L, filename, openfunc); +} + + +static int searcher_C (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + const char *filename = findfile(L, name, "cpath", LUA_CSUBSEP); + if (filename == NULL) return 1; /* module not found in this path */ + return checkload(L, (loadfunc(L, filename, name) == 0), filename); +} + + +static int searcher_Croot (lua_State *L) { + const char *filename; + const char *name = luaL_checkstring(L, 1); + const char *p = strchr(name, '.'); + int stat; + if (p == NULL) return 0; /* is root */ + lua_pushlstring(L, name, ct_diff2sz(p - name)); + filename = findfile(L, lua_tostring(L, -1), "cpath", LUA_CSUBSEP); + if (filename == NULL) return 1; /* root not found */ + if ((stat = loadfunc(L, filename, name)) != 0) { + if (stat != ERRFUNC) + return checkload(L, 0, filename); /* real error */ + else { /* open function not found */ + lua_pushfstring(L, "no module '%s' in file '%s'", name, filename); + return 1; + } + } + lua_pushstring(L, filename); /* will be 2nd argument to module */ + return 2; +} + + +static int searcher_preload (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */ + lua_pushfstring(L, "no field package.preload['%s']", name); + return 1; + } + else { + lua_pushliteral(L, ":preload:"); + return 2; + } +} + + +static void findloader (lua_State *L, const char *name) { + int i; + luaL_Buffer msg; /* to build error message */ + /* push 'package.searchers' to index 3 in the stack */ + if (l_unlikely(lua_getfield(L, lua_upvalueindex(1), "searchers") + != LUA_TTABLE)) + luaL_error(L, "'package.searchers' must be a table"); + luaL_buffinit(L, &msg); + luaL_addstring(&msg, "\n\t"); /* error-message prefix for first message */ + /* iterate over available searchers to find a loader */ + for (i = 1; ; i++) { + if (l_unlikely(lua_rawgeti(L, 3, i) == LUA_TNIL)) { /* no more searchers? */ + lua_pop(L, 1); /* remove nil */ + luaL_buffsub(&msg, 2); /* remove last prefix */ + luaL_pushresult(&msg); /* create error message */ + luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1)); + } + lua_pushstring(L, name); + lua_call(L, 1, 2); /* call it */ + if (lua_isfunction(L, -2)) /* did it find a loader? */ + return; /* module loader found */ + else if (lua_isstring(L, -2)) { /* searcher returned error message? */ + lua_pop(L, 1); /* remove extra return */ + luaL_addvalue(&msg); /* concatenate error message */ + luaL_addstring(&msg, "\n\t"); /* prefix for next message */ + } + else /* no error message */ + lua_pop(L, 2); /* remove both returns */ + } +} + + +static int ll_require (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + lua_settop(L, 1); /* LOADED table will be at index 2 */ + lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_getfield(L, 2, name); /* LOADED[name] */ + if (lua_toboolean(L, -1)) /* is it there? */ + return 1; /* package is already loaded */ + /* else must load package */ + lua_pop(L, 1); /* remove 'getfield' result */ + findloader(L, name); + lua_rotate(L, -2, 1); /* function <-> loader data */ + lua_pushvalue(L, 1); /* name is 1st argument to module loader */ + lua_pushvalue(L, -3); /* loader data is 2nd argument */ + /* stack: ...; loader data; loader function; mod. name; loader data */ + lua_call(L, 2, 1); /* run loader to load module */ + /* stack: ...; loader data; result from loader */ + if (!lua_isnil(L, -1)) /* non-nil return? */ + lua_setfield(L, 2, name); /* LOADED[name] = returned value */ + else + lua_pop(L, 1); /* pop nil */ + if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */ + lua_pushboolean(L, 1); /* use true as result */ + lua_copy(L, -1, -2); /* replace loader result */ + lua_setfield(L, 2, name); /* LOADED[name] = true */ + } + lua_rotate(L, -2, 1); /* loader data <-> module result */ + return 2; /* return module result and loader data */ +} + +/* }====================================================== */ + + + + +static const luaL_Reg pk_funcs[] = { + {"loadlib", ll_loadlib}, + {"searchpath", ll_searchpath}, + /* placeholders */ + {"preload", NULL}, + {"cpath", NULL}, + {"path", NULL}, + {"searchers", NULL}, + {"loaded", NULL}, + {NULL, NULL} +}; + + +static const luaL_Reg ll_funcs[] = { + {"require", ll_require}, + {NULL, NULL} +}; + + +static void createsearcherstable (lua_State *L) { + static const lua_CFunction searchers[] = { + searcher_preload, + searcher_Lua, + searcher_C, + searcher_Croot, + NULL + }; + int i; + /* create 'searchers' table */ + lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0); + /* fill it with predefined searchers */ + for (i=0; searchers[i] != NULL; i++) { + lua_pushvalue(L, -2); /* set 'package' as upvalue for all searchers */ + lua_pushcclosure(L, searchers[i], 1); + lua_rawseti(L, -2, i+1); + } + lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */ +} + + +LUAMOD_API int luaopen_package (lua_State *L) { + luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */ + lua_pop(L, 1); /* will not use it now */ + luaL_newlib(L, pk_funcs); /* create 'package' table */ + createsearcherstable(L); + /* set paths */ + setpath(L, "path", LUA_PATH_VAR, LUA_PATH_DEFAULT); + setpath(L, "cpath", LUA_CPATH_VAR, LUA_CPATH_DEFAULT); + /* store config information */ + lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATH_SEP "\n" LUA_PATH_MARK "\n" + LUA_EXEC_DIR "\n" LUA_IGMARK "\n"); + lua_setfield(L, -2, "config"); + /* set field 'loaded' */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_setfield(L, -2, "loaded"); + /* set field 'preload' */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + lua_setfield(L, -2, "preload"); + lua_pushglobaltable(L); + lua_pushvalue(L, -2); /* set 'package' as upvalue for next lib */ + luaL_setfuncs(L, ll_funcs, 1); /* open lib into global table */ + lua_pop(L, 1); /* pop global table */ + return 1; /* return 'package' table */ +} + + +``` + +`Lua/lobject.c`: + +```c +/* +** $Id: lobject.c $ +** Some generic functions over Lua objects +** See Copyright Notice in lua.h +*/ + +#define lobject_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <float.h> +#include <locale.h> +#include <math.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lua.h" + +#include "lctype.h" +#include "ldebug.h" +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "lvm.h" + + +/* +** Computes ceil(log2(x)), which is the smallest integer n such that +** x <= (1 << n). +*/ +lu_byte luaO_ceillog2 (unsigned int x) { + static const lu_byte log_2[256] = { /* log_2[i - 1] = ceil(log2(i)) */ + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + }; + int l = 0; + x--; + while (x >= 256) { l += 8; x >>= 8; } + return cast_byte(l + log_2[x]); +} + +/* +** Encodes 'p'% as a floating-point byte, represented as (eeeexxxx). +** The exponent is represented using excess-7. Mimicking IEEE 754, the +** representation normalizes the number when possible, assuming an extra +** 1 before the mantissa (xxxx) and adding one to the exponent (eeee) +** to signal that. So, the real value is (1xxxx) * 2^(eeee - 7 - 1) if +** eeee != 0, and (xxxx) * 2^-7 otherwise (subnormal numbers). +*/ +lu_byte luaO_codeparam (unsigned int p) { + if (p >= (cast(lu_mem, 0x1F) << (0xF - 7 - 1)) * 100u) /* overflow? */ + return 0xFF; /* return maximum value */ + else { + p = (cast(l_uint32, p) * 128 + 99) / 100; /* round up the division */ + if (p < 0x10) { /* subnormal number? */ + /* exponent bits are already zero; nothing else to do */ + return cast_byte(p); + } + else { /* p >= 0x10 implies ceil(log2(p + 1)) >= 5 */ + /* preserve 5 bits in 'p' */ + unsigned log = luaO_ceillog2(p + 1) - 5u; + return cast_byte(((p >> log) - 0x10) | ((log + 1) << 4)); + } + } +} + + +/* +** Computes 'p' times 'x', where 'p' is a floating-point byte. Roughly, +** we have to multiply 'x' by the mantissa and then shift accordingly to +** the exponent. If the exponent is positive, both the multiplication +** and the shift increase 'x', so we have to care only about overflows. +** For negative exponents, however, multiplying before the shift keeps +** more significant bits, as long as the multiplication does not +** overflow, so we check which order is best. +*/ +l_mem luaO_applyparam (lu_byte p, l_mem x) { + int m = p & 0xF; /* mantissa */ + int e = (p >> 4); /* exponent */ + if (e > 0) { /* normalized? */ + e--; /* correct exponent */ + m += 0x10; /* correct mantissa; maximum value is 0x1F */ + } + e -= 7; /* correct excess-7 */ + if (e >= 0) { + if (x < (MAX_LMEM / 0x1F) >> e) /* no overflow? */ + return (x * m) << e; /* order doesn't matter here */ + else /* real overflow */ + return MAX_LMEM; + } + else { /* negative exponent */ + e = -e; + if (x < MAX_LMEM / 0x1F) /* multiplication cannot overflow? */ + return (x * m) >> e; /* multiplying first gives more precision */ + else if ((x >> e) < MAX_LMEM / 0x1F) /* cannot overflow after shift? */ + return (x >> e) * m; + else /* real overflow */ + return MAX_LMEM; + } +} + + +static lua_Integer intarith (lua_State *L, int op, lua_Integer v1, + lua_Integer v2) { + switch (op) { + case LUA_OPADD: return intop(+, v1, v2); + case LUA_OPSUB:return intop(-, v1, v2); + case LUA_OPMUL:return intop(*, v1, v2); + case LUA_OPMOD: return luaV_mod(L, v1, v2); + case LUA_OPIDIV: return luaV_idiv(L, v1, v2); + case LUA_OPBAND: return intop(&, v1, v2); + case LUA_OPBOR: return intop(|, v1, v2); + case LUA_OPBXOR: return intop(^, v1, v2); + case LUA_OPSHL: return luaV_shiftl(v1, v2); + case LUA_OPSHR: return luaV_shiftr(v1, v2); + case LUA_OPUNM: return intop(-, 0, v1); + case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1); + default: lua_assert(0); return 0; + } +} + + +static lua_Number numarith (lua_State *L, int op, lua_Number v1, + lua_Number v2) { + switch (op) { + case LUA_OPADD: return luai_numadd(L, v1, v2); + case LUA_OPSUB: return luai_numsub(L, v1, v2); + case LUA_OPMUL: return luai_nummul(L, v1, v2); + case LUA_OPDIV: return luai_numdiv(L, v1, v2); + case LUA_OPPOW: return luai_numpow(L, v1, v2); + case LUA_OPIDIV: return luai_numidiv(L, v1, v2); + case LUA_OPUNM: return luai_numunm(L, v1); + case LUA_OPMOD: return luaV_modf(L, v1, v2); + default: lua_assert(0); return 0; + } +} + + +int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2, + TValue *res) { + switch (op) { + case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: + case LUA_OPSHL: case LUA_OPSHR: + case LUA_OPBNOT: { /* operate only on integers */ + lua_Integer i1; lua_Integer i2; + if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) { + setivalue(res, intarith(L, op, i1, i2)); + return 1; + } + else return 0; /* fail */ + } + case LUA_OPDIV: case LUA_OPPOW: { /* operate only on floats */ + lua_Number n1; lua_Number n2; + if (tonumberns(p1, n1) && tonumberns(p2, n2)) { + setfltvalue(res, numarith(L, op, n1, n2)); + return 1; + } + else return 0; /* fail */ + } + default: { /* other operations */ + lua_Number n1; lua_Number n2; + if (ttisinteger(p1) && ttisinteger(p2)) { + setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2))); + return 1; + } + else if (tonumberns(p1, n1) && tonumberns(p2, n2)) { + setfltvalue(res, numarith(L, op, n1, n2)); + return 1; + } + else return 0; /* fail */ + } + } +} + + +void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2, + StkId res) { + if (!luaO_rawarith(L, op, p1, p2, s2v(res))) { + /* could not perform raw operation; try metamethod */ + luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD)); + } +} + + +lu_byte luaO_hexavalue (int c) { + lua_assert(lisxdigit(c)); + if (lisdigit(c)) return cast_byte(c - '0'); + else return cast_byte((ltolower(c) - 'a') + 10); +} + + +static int isneg (const char **s) { + if (**s == '-') { (*s)++; return 1; } + else if (**s == '+') (*s)++; + return 0; +} + + + +/* +** {================================================================== +** Lua's implementation for 'lua_strx2number' +** =================================================================== +*/ + +#if !defined(lua_strx2number) + +/* maximum number of significant digits to read (to avoid overflows + even with single floats) */ +#define MAXSIGDIG 30 + +/* +** convert a hexadecimal numeric string to a number, following +** C99 specification for 'strtod' +*/ +static lua_Number lua_strx2number (const char *s, char **endptr) { + int dot = lua_getlocaledecpoint(); + lua_Number r = l_mathop(0.0); /* result (accumulator) */ + int sigdig = 0; /* number of significant digits */ + int nosigdig = 0; /* number of non-significant digits */ + int e = 0; /* exponent correction */ + int neg; /* 1 if number is negative */ + int hasdot = 0; /* true after seen a dot */ + *endptr = cast_charp(s); /* nothing is valid yet */ + while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ + neg = isneg(&s); /* check sign */ + if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */ + return l_mathop(0.0); /* invalid format (no '0x') */ + for (s += 2; ; s++) { /* skip '0x' and read numeral */ + if (*s == dot) { + if (hasdot) break; /* second dot? stop loop */ + else hasdot = 1; + } + else if (lisxdigit(cast_uchar(*s))) { + if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */ + nosigdig++; + else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */ + r = (r * l_mathop(16.0)) + luaO_hexavalue(*s); + else e++; /* too many digits; ignore, but still count for exponent */ + if (hasdot) e--; /* decimal digit? correct exponent */ + } + else break; /* neither a dot nor a digit */ + } + if (nosigdig + sigdig == 0) /* no digits? */ + return l_mathop(0.0); /* invalid format */ + *endptr = cast_charp(s); /* valid up to here */ + e *= 4; /* each digit multiplies/divides value by 2^4 */ + if (*s == 'p' || *s == 'P') { /* exponent part? */ + int exp1 = 0; /* exponent value */ + int neg1; /* exponent sign */ + s++; /* skip 'p' */ + neg1 = isneg(&s); /* sign */ + if (!lisdigit(cast_uchar(*s))) + return l_mathop(0.0); /* invalid; must have at least one digit */ + while (lisdigit(cast_uchar(*s))) /* read exponent */ + exp1 = exp1 * 10 + *(s++) - '0'; + if (neg1) exp1 = -exp1; + e += exp1; + *endptr = cast_charp(s); /* valid up to here */ + } + if (neg) r = -r; + return l_mathop(ldexp)(r, e); +} + +#endif +/* }====================================================== */ + + +/* maximum length of a numeral to be converted to a number */ +#if !defined (L_MAXLENNUM) +#define L_MAXLENNUM 200 +#endif + +/* +** Convert string 's' to a Lua number (put in 'result'). Return NULL on +** fail or the address of the ending '\0' on success. ('mode' == 'x') +** means a hexadecimal numeral. +*/ +static const char *l_str2dloc (const char *s, lua_Number *result, int mode) { + char *endptr; + *result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */ + : lua_str2number(s, &endptr); + if (endptr == s) return NULL; /* nothing recognized? */ + while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */ + return (*endptr == '\0') ? endptr : NULL; /* OK iff no trailing chars */ +} + + +/* +** Convert string 's' to a Lua number (put in 'result') handling the +** current locale. +** This function accepts both the current locale or a dot as the radix +** mark. If the conversion fails, it may mean number has a dot but +** locale accepts something else. In that case, the code copies 's' +** to a buffer (because 's' is read-only), changes the dot to the +** current locale radix mark, and tries to convert again. +** The variable 'mode' checks for special characters in the string: +** - 'n' means 'inf' or 'nan' (which should be rejected) +** - 'x' means a hexadecimal numeral +** - '.' just optimizes the search for the common case (no special chars) +*/ +static const char *l_str2d (const char *s, lua_Number *result) { + const char *endptr; + const char *pmode = strpbrk(s, ".xXnN"); /* look for special chars */ + int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0; + if (mode == 'n') /* reject 'inf' and 'nan' */ + return NULL; + endptr = l_str2dloc(s, result, mode); /* try to convert */ + if (endptr == NULL) { /* failed? may be a different locale */ + char buff[L_MAXLENNUM + 1]; + const char *pdot = strchr(s, '.'); + if (pdot == NULL || strlen(s) > L_MAXLENNUM) + return NULL; /* string too long or no dot; fail */ + strcpy(buff, s); /* copy string to buffer */ + buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */ + endptr = l_str2dloc(buff, result, mode); /* try again */ + if (endptr != NULL) + endptr = s + (endptr - buff); /* make relative to 's' */ + } + return endptr; +} + + +#define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10) +#define MAXLASTD cast_int(LUA_MAXINTEGER % 10) + +static const char *l_str2int (const char *s, lua_Integer *result) { + lua_Unsigned a = 0; + int empty = 1; + int neg; + while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */ + neg = isneg(&s); + if (s[0] == '0' && + (s[1] == 'x' || s[1] == 'X')) { /* hex? */ + s += 2; /* skip '0x' */ + for (; lisxdigit(cast_uchar(*s)); s++) { + a = a * 16 + luaO_hexavalue(*s); + empty = 0; + } + } + else { /* decimal */ + for (; lisdigit(cast_uchar(*s)); s++) { + int d = *s - '0'; + if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */ + return NULL; /* do not accept it (as integer) */ + a = a * 10 + cast_uint(d); + empty = 0; + } + } + while (lisspace(cast_uchar(*s))) s++; /* skip trailing spaces */ + if (empty || *s != '\0') return NULL; /* something wrong in the numeral */ + else { + *result = l_castU2S((neg) ? 0u - a : a); + return s; + } +} + + +size_t luaO_str2num (const char *s, TValue *o) { + lua_Integer i; lua_Number n; + const char *e; + if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */ + setivalue(o, i); + } + else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */ + setfltvalue(o, n); + } + else + return 0; /* conversion failed */ + return ct_diff2sz(e - s) + 1; /* success; return string size */ +} + + +int luaO_utf8esc (char *buff, l_uint32 x) { + int n = 1; /* number of bytes put in buffer (backwards) */ + lua_assert(x <= 0x7FFFFFFFu); + if (x < 0x80) /* ASCII? */ + buff[UTF8BUFFSZ - 1] = cast_char(x); + else { /* need continuation bytes */ + unsigned int mfb = 0x3f; /* maximum that fits in first byte */ + do { /* add continuation bytes */ + buff[UTF8BUFFSZ - (n++)] = cast_char(0x80 | (x & 0x3f)); + x >>= 6; /* remove added bits */ + mfb >>= 1; /* now there is one less bit available in first byte */ + } while (x > mfb); /* still needs continuation byte? */ + buff[UTF8BUFFSZ - n] = cast_char((~mfb << 1) | x); /* add first byte */ + } + return n; +} + + +/* +** The size of the buffer for the conversion of a number to a string +** 'LUA_N2SBUFFSZ' must be enough to accommodate both LUA_INTEGER_FMT +** and LUA_NUMBER_FMT. For a long long int, this is 19 digits plus a +** sign and a final '\0', adding to 21. For a long double, it can go to +** a sign, the dot, an exponent letter, an exponent sign, 4 exponent +** digits, the final '\0', plus the significant digits, which are +** approximately the *_DIG attribute. +*/ +#if LUA_N2SBUFFSZ < (20 + l_floatatt(DIG)) +#error "invalid value for LUA_N2SBUFFSZ" +#endif + + +/* +** Convert a float to a string, adding it to a buffer. First try with +** a not too large number of digits, to avoid noise (for instance, +** 1.1 going to "1.1000000000000001"). If that lose precision, so +** that reading the result back gives a different number, then do the +** conversion again with extra precision. Moreover, if the numeral looks +** like an integer (without a decimal point or an exponent), add ".0" to +** its end. +*/ +static int tostringbuffFloat (lua_Number n, char *buff) { + /* first conversion */ + int len = l_sprintf(buff, LUA_N2SBUFFSZ, LUA_NUMBER_FMT, + (LUAI_UACNUMBER)n); + lua_Number check = lua_str2number(buff, NULL); /* read it back */ + if (check != n) { /* not enough precision? */ + /* convert again with more precision */ + len = l_sprintf(buff, LUA_N2SBUFFSZ, LUA_NUMBER_FMT_N, + (LUAI_UACNUMBER)n); + } + /* looks like an integer? */ + if (buff[strspn(buff, "-0123456789")] == '\0') { + buff[len++] = lua_getlocaledecpoint(); + buff[len++] = '0'; /* adds '.0' to result */ + } + return len; +} + + +/* +** Convert a number object to a string, adding it to a buffer. +*/ +unsigned luaO_tostringbuff (const TValue *obj, char *buff) { + int len; + lua_assert(ttisnumber(obj)); + if (ttisinteger(obj)) + len = lua_integer2str(buff, LUA_N2SBUFFSZ, ivalue(obj)); + else + len = tostringbuffFloat(fltvalue(obj), buff); + lua_assert(len < LUA_N2SBUFFSZ); + return cast_uint(len); +} + + +/* +** Convert a number object to a Lua string, replacing the value at 'obj' +*/ +void luaO_tostring (lua_State *L, TValue *obj) { + char buff[LUA_N2SBUFFSZ]; + unsigned len = luaO_tostringbuff(obj, buff); + setsvalue(L, obj, luaS_newlstr(L, buff, len)); +} + + + + +/* +** {================================================================== +** 'luaO_pushvfstring' +** =================================================================== +*/ + +/* +** Size for buffer space used by 'luaO_pushvfstring'. It should be +** (LUA_IDSIZE + LUA_N2SBUFFSZ) + a minimal space for basic messages, +** so that 'luaG_addinfo' can work directly on the static buffer. +*/ +#define BUFVFS cast_uint(LUA_IDSIZE + LUA_N2SBUFFSZ + 95) + +/* +** Buffer used by 'luaO_pushvfstring'. 'err' signals an error while +** building result (memory error [1] or buffer overflow [2]). +*/ +typedef struct BuffFS { + lua_State *L; + char *b; + size_t buffsize; + size_t blen; /* length of string in 'buff' */ + int err; + char space[BUFVFS]; /* initial buffer */ +} BuffFS; + + +static void initbuff (lua_State *L, BuffFS *buff) { + buff->L = L; + buff->b = buff->space; + buff->buffsize = sizeof(buff->space); + buff->blen = 0; + buff->err = 0; +} + + +/* +** Push final result from 'luaO_pushvfstring'. This function may raise +** errors explicitly or through memory errors, so it must run protected. +*/ +static void pushbuff (lua_State *L, void *ud) { + BuffFS *buff = cast(BuffFS*, ud); + switch (buff->err) { + case 1: /* memory error */ + luaD_throw(L, LUA_ERRMEM); + break; + case 2: /* length overflow: Add "..." at the end of result */ + if (buff->buffsize - buff->blen < 3) + strcpy(buff->b + buff->blen - 3, "..."); /* 'blen' must be > 3 */ + else { /* there is enough space left for the "..." */ + strcpy(buff->b + buff->blen, "..."); + buff->blen += 3; + } + /* FALLTHROUGH */ + default: { /* no errors, but it can raise one creating the new string */ + TString *ts = luaS_newlstr(L, buff->b, buff->blen); + setsvalue2s(L, L->top.p, ts); + L->top.p++; + } + } +} + + +static const char *clearbuff (BuffFS *buff) { + lua_State *L = buff->L; + const char *res; + if (luaD_rawrunprotected(L, pushbuff, buff) != LUA_OK) /* errors? */ + res = NULL; /* error message is on the top of the stack */ + else + res = getstr(tsvalue(s2v(L->top.p - 1))); + if (buff->b != buff->space) /* using dynamic buffer? */ + luaM_freearray(L, buff->b, buff->buffsize); /* free it */ + return res; +} + + +static void addstr2buff (BuffFS *buff, const char *str, size_t slen) { + size_t left = buff->buffsize - buff->blen; /* space left in the buffer */ + if (buff->err) /* do nothing else after an error */ + return; + if (slen > left) { /* new string doesn't fit into current buffer? */ + if (slen > ((MAX_SIZE/2) - buff->blen)) { /* overflow? */ + memcpy(buff->b + buff->blen, str, left); /* copy what it can */ + buff->blen = buff->buffsize; + buff->err = 2; /* doesn't add anything else */ + return; + } + else { + size_t newsize = buff->buffsize + slen; /* limited to MAX_SIZE/2 */ + char *newb = + (buff->b == buff->space) /* still using static space? */ + ? luaM_reallocvector(buff->L, NULL, 0, newsize, char) + : luaM_reallocvector(buff->L, buff->b, buff->buffsize, newsize, + char); + if (newb == NULL) { /* allocation error? */ + buff->err = 1; /* signal a memory error */ + return; + } + if (buff->b == buff->space) /* new buffer (not reallocated)? */ + memcpy(newb, buff->b, buff->blen); /* copy previous content */ + buff->b = newb; /* set new (larger) buffer... */ + buff->buffsize = newsize; /* ...and its new size */ + } + } + memcpy(buff->b + buff->blen, str, slen); /* copy new content */ + buff->blen += slen; +} + + +/* +** Add a numeral to the buffer. +*/ +static void addnum2buff (BuffFS *buff, TValue *num) { + char numbuff[LUA_N2SBUFFSZ]; + unsigned len = luaO_tostringbuff(num, numbuff); + addstr2buff(buff, numbuff, len); +} + + +/* +** this function handles only '%d', '%c', '%f', '%p', '%s', and '%%' + conventional formats, plus Lua-specific '%I' and '%U' +*/ +const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { + BuffFS buff; /* holds last part of the result */ + const char *e; /* points to next '%' */ + initbuff(L, &buff); + while ((e = strchr(fmt, '%')) != NULL) { + addstr2buff(&buff, fmt, ct_diff2sz(e - fmt)); /* add 'fmt' up to '%' */ + switch (*(e + 1)) { /* conversion specifier */ + case 's': { /* zero-terminated string */ + const char *s = va_arg(argp, char *); + if (s == NULL) s = "(null)"; + addstr2buff(&buff, s, strlen(s)); + break; + } + case 'c': { /* an 'int' as a character */ + char c = cast_char(va_arg(argp, int)); + addstr2buff(&buff, &c, sizeof(char)); + break; + } + case 'd': { /* an 'int' */ + TValue num; + setivalue(&num, va_arg(argp, int)); + addnum2buff(&buff, &num); + break; + } + case 'I': { /* a 'lua_Integer' */ + TValue num; + setivalue(&num, cast_Integer(va_arg(argp, l_uacInt))); + addnum2buff(&buff, &num); + break; + } + case 'f': { /* a 'lua_Number' */ + TValue num; + setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber))); + addnum2buff(&buff, &num); + break; + } + case 'p': { /* a pointer */ + char bf[LUA_N2SBUFFSZ]; /* enough space for '%p' */ + void *p = va_arg(argp, void *); + int len = lua_pointer2str(bf, LUA_N2SBUFFSZ, p); + addstr2buff(&buff, bf, cast_uint(len)); + break; + } + case 'U': { /* an 'unsigned long' as a UTF-8 sequence */ + char bf[UTF8BUFFSZ]; + unsigned long arg = va_arg(argp, unsigned long); + int len = luaO_utf8esc(bf, cast(l_uint32, arg)); + addstr2buff(&buff, bf + UTF8BUFFSZ - len, cast_uint(len)); + break; + } + case '%': { + addstr2buff(&buff, "%", 1); + break; + } + default: { + addstr2buff(&buff, e, 2); /* keep unknown format in the result */ + break; + } + } + fmt = e + 2; /* skip '%' and the specifier */ + } + addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */ + return clearbuff(&buff); /* empty buffer into a new string */ +} + + +const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) { + const char *msg; + va_list argp; + va_start(argp, fmt); + msg = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + if (msg == NULL) /* error? */ + luaD_throw(L, LUA_ERRMEM); + return msg; +} + +/* }================================================================== */ + + +#define RETS "..." +#define PRE "[string \"" +#define POS "\"]" + +#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) ) + +void luaO_chunkid (char *out, const char *source, size_t srclen) { + size_t bufflen = LUA_IDSIZE; /* free space in buffer */ + if (*source == '=') { /* 'literal' source */ + if (srclen <= bufflen) /* small enough? */ + memcpy(out, source + 1, srclen * sizeof(char)); + else { /* truncate it */ + addstr(out, source + 1, bufflen - 1); + *out = '\0'; + } + } + else if (*source == '@') { /* file name */ + if (srclen <= bufflen) /* small enough? */ + memcpy(out, source + 1, srclen * sizeof(char)); + else { /* add '...' before rest of name */ + addstr(out, RETS, LL(RETS)); + bufflen -= LL(RETS); + memcpy(out, source + 1 + srclen - bufflen, bufflen * sizeof(char)); + } + } + else { /* string; format as [string "source"] */ + const char *nl = strchr(source, '\n'); /* find first new line (if any) */ + addstr(out, PRE, LL(PRE)); /* add prefix */ + bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */ + if (srclen < bufflen && nl == NULL) { /* small one-line source? */ + addstr(out, source, srclen); /* keep it */ + } + else { + if (nl != NULL) + srclen = ct_diff2sz(nl - source); /* stop at first newline */ + if (srclen > bufflen) srclen = bufflen; + addstr(out, source, srclen); + addstr(out, RETS, LL(RETS)); + } + memcpy(out, POS, (LL(POS) + 1) * sizeof(char)); + } +} + + +``` + +`Lua/lobject.h`: + +```h +/* +** $Id: lobject.h $ +** Type definitions for Lua objects +** See Copyright Notice in lua.h +*/ + + +#ifndef lobject_h +#define lobject_h + + +#include <stdarg.h> + + +#include "llimits.h" +#include "lua.h" + + +/* +** Extra types for collectable non-values +*/ +#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */ +#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */ +#define LUA_TDEADKEY (LUA_NUMTYPES+2) /* removed keys in tables */ + + + +/* +** number of all possible types (including LUA_TNONE but excluding DEADKEY) +*/ +#define LUA_TOTALTYPES (LUA_TPROTO + 2) + + +/* +** tags for Tagged Values have the following use of bits: +** bits 0-3: actual tag (a LUA_T* constant) +** bits 4-5: variant bits +** bit 6: whether value is collectable +*/ + +/* add variant bits to a type */ +#define makevariant(t,v) ((t) | ((v) << 4)) + + + +/* +** Union of all Lua values +*/ +typedef union Value { + struct GCObject *gc; /* collectable objects */ + void *p; /* light userdata */ + lua_CFunction f; /* light C functions */ + lua_Integer i; /* integer numbers */ + lua_Number n; /* float numbers */ + /* not used, but may avoid warnings for uninitialized value */ + lu_byte ub; +} Value; + + +/* +** Tagged Values. This is the basic representation of values in Lua: +** an actual value plus a tag with its type. +*/ + +#define TValuefields Value value_; lu_byte tt_ + +typedef struct TValue { + TValuefields; +} TValue; + + +#define val_(o) ((o)->value_) +#define valraw(o) (val_(o)) + + +/* raw type tag of a TValue */ +#define rawtt(o) ((o)->tt_) + +/* tag with no variants (bits 0-3) */ +#define novariant(t) ((t) & 0x0F) + +/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */ +#define withvariant(t) ((t) & 0x3F) +#define ttypetag(o) withvariant(rawtt(o)) + +/* type of a TValue */ +#define ttype(o) (novariant(rawtt(o))) + + +/* Macros to test type */ +#define checktag(o,t) (rawtt(o) == (t)) +#define checktype(o,t) (ttype(o) == (t)) + + +/* Macros for internal tests */ + +/* collectable object has the same tag as the original value */ +#define righttt(obj) (ttypetag(obj) == gcvalue(obj)->tt) + +/* +** Any value being manipulated by the program either is non +** collectable, or the collectable object has the right tag +** and it is not dead. The option 'L == NULL' allows other +** macros using this one to be used where L is not available. +*/ +#define checkliveness(L,obj) \ + ((void)L, lua_longassert(!iscollectable(obj) || \ + (righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj)))))) + + +/* Macros to set values */ + +/* set a value's tag */ +#define settt_(o,t) ((o)->tt_=(t)) + + +/* main macro to copy values (from 'obj2' to 'obj1') */ +#define setobj(L,obj1,obj2) \ + { TValue *io1=(obj1); const TValue *io2=(obj2); \ + io1->value_ = io2->value_; settt_(io1, io2->tt_); \ + checkliveness(L,io1); lua_assert(!isnonstrictnil(io1)); } + +/* +** Different types of assignments, according to source and destination. +** (They are mostly equal now, but may be different in the future.) +*/ + +/* from stack to stack */ +#define setobjs2s(L,o1,o2) setobj(L,s2v(o1),s2v(o2)) +/* to stack (not from same stack) */ +#define setobj2s(L,o1,o2) setobj(L,s2v(o1),o2) +/* from table to same table */ +#define setobjt2t setobj +/* to new object */ +#define setobj2n setobj +/* to table */ +#define setobj2t setobj + + +/* +** Entries in a Lua stack. Field 'tbclist' forms a list of all +** to-be-closed variables active in this stack. Dummy entries are +** used when the distance between two tbc variables does not fit +** in an unsigned short. They are represented by delta==0, and +** their real delta is always the maximum value that fits in +** that field. +*/ +typedef union StackValue { + TValue val; + struct { + TValuefields; + unsigned short delta; + } tbclist; +} StackValue; + + +/* index to stack elements */ +typedef StackValue *StkId; + + +/* +** When reallocating the stack, change all pointers to the stack into +** proper offsets. +*/ +typedef union { + StkId p; /* actual pointer */ + ptrdiff_t offset; /* used while the stack is being reallocated */ +} StkIdRel; + + +/* convert a 'StackValue' to a 'TValue' */ +#define s2v(o) (&(o)->val) + + + +/* +** {================================================================== +** Nil +** =================================================================== +*/ + +/* Standard nil */ +#define LUA_VNIL makevariant(LUA_TNIL, 0) + +/* Empty slot (which might be different from a slot containing nil) */ +#define LUA_VEMPTY makevariant(LUA_TNIL, 1) + +/* Value returned for a key not found in a table (absent key) */ +#define LUA_VABSTKEY makevariant(LUA_TNIL, 2) + +/* Special variant to signal that a fast get is accessing a non-table */ +#define LUA_VNOTABLE makevariant(LUA_TNIL, 3) + + +/* macro to test for (any kind of) nil */ +#define ttisnil(v) checktype((v), LUA_TNIL) + +/* +** Macro to test the result of a table access. Formally, it should +** distinguish between LUA_VEMPTY/LUA_VABSTKEY/LUA_VNOTABLE and +** other tags. As currently nil is equivalent to LUA_VEMPTY, it is +** simpler to just test whether the value is nil. +*/ +#define tagisempty(tag) (novariant(tag) == LUA_TNIL) + + +/* macro to test for a standard nil */ +#define ttisstrictnil(o) checktag((o), LUA_VNIL) + + +#define setnilvalue(obj) settt_(obj, LUA_VNIL) + + +#define isabstkey(v) checktag((v), LUA_VABSTKEY) + + +/* +** macro to detect non-standard nils (used only in assertions) +*/ +#define isnonstrictnil(v) (ttisnil(v) && !ttisstrictnil(v)) + + +/* +** By default, entries with any kind of nil are considered empty. +** (In any definition, values associated with absent keys must also +** be accepted as empty.) +*/ +#define isempty(v) ttisnil(v) + + +/* macro defining a value corresponding to an absent key */ +#define ABSTKEYCONSTANT {NULL}, LUA_VABSTKEY + + +/* mark an entry as empty */ +#define setempty(v) settt_(v, LUA_VEMPTY) + + + +/* }================================================================== */ + + +/* +** {================================================================== +** Booleans +** =================================================================== +*/ + + +#define LUA_VFALSE makevariant(LUA_TBOOLEAN, 0) +#define LUA_VTRUE makevariant(LUA_TBOOLEAN, 1) + +#define ttisboolean(o) checktype((o), LUA_TBOOLEAN) +#define ttisfalse(o) checktag((o), LUA_VFALSE) +#define ttistrue(o) checktag((o), LUA_VTRUE) + + +#define l_isfalse(o) (ttisfalse(o) || ttisnil(o)) +#define tagisfalse(t) ((t) == LUA_VFALSE || novariant(t) == LUA_TNIL) + + + +#define setbfvalue(obj) settt_(obj, LUA_VFALSE) +#define setbtvalue(obj) settt_(obj, LUA_VTRUE) + +/* }================================================================== */ + + +/* +** {================================================================== +** Threads +** =================================================================== +*/ + +#define LUA_VTHREAD makevariant(LUA_TTHREAD, 0) + +#define ttisthread(o) checktag((o), ctb(LUA_VTHREAD)) + +#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc)) + +#define setthvalue(L,obj,x) \ + { TValue *io = (obj); lua_State *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTHREAD)); \ + checkliveness(L,io); } + +#define setthvalue2s(L,o,t) setthvalue(L,s2v(o),t) + +/* }================================================================== */ + + +/* +** {================================================================== +** Collectable Objects +** =================================================================== +*/ + +/* +** Common Header for all collectable objects (in macro form, to be +** included in other objects) +*/ +#define CommonHeader struct GCObject *next; lu_byte tt; lu_byte marked + + +/* Common type for all collectable objects */ +typedef struct GCObject { + CommonHeader; +} GCObject; + + +/* Bit mark for collectable types */ +#define BIT_ISCOLLECTABLE (1 << 6) + +#define iscollectable(o) (rawtt(o) & BIT_ISCOLLECTABLE) + +/* mark a tag as collectable */ +#define ctb(t) ((t) | BIT_ISCOLLECTABLE) + +#define gcvalue(o) check_exp(iscollectable(o), val_(o).gc) + +#define gcvalueraw(v) ((v).gc) + +#define setgcovalue(L,obj,x) \ + { TValue *io = (obj); GCObject *i_g=(x); \ + val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); } + +/* }================================================================== */ + + +/* +** {================================================================== +** Numbers +** =================================================================== +*/ + +/* Variant tags for numbers */ +#define LUA_VNUMINT makevariant(LUA_TNUMBER, 0) /* integer numbers */ +#define LUA_VNUMFLT makevariant(LUA_TNUMBER, 1) /* float numbers */ + +#define ttisnumber(o) checktype((o), LUA_TNUMBER) +#define ttisfloat(o) checktag((o), LUA_VNUMFLT) +#define ttisinteger(o) checktag((o), LUA_VNUMINT) + +#define nvalue(o) check_exp(ttisnumber(o), \ + (ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o))) +#define fltvalue(o) check_exp(ttisfloat(o), val_(o).n) +#define ivalue(o) check_exp(ttisinteger(o), val_(o).i) + +#define fltvalueraw(v) ((v).n) +#define ivalueraw(v) ((v).i) + +#define setfltvalue(obj,x) \ + { TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_VNUMFLT); } + +#define chgfltvalue(obj,x) \ + { TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); } + +#define setivalue(obj,x) \ + { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_VNUMINT); } + +#define chgivalue(obj,x) \ + { TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); } + +/* }================================================================== */ + + +/* +** {================================================================== +** Strings +** =================================================================== +*/ + +/* Variant tags for strings */ +#define LUA_VSHRSTR makevariant(LUA_TSTRING, 0) /* short strings */ +#define LUA_VLNGSTR makevariant(LUA_TSTRING, 1) /* long strings */ + +#define ttisstring(o) checktype((o), LUA_TSTRING) +#define ttisshrstring(o) checktag((o), ctb(LUA_VSHRSTR)) +#define ttislngstring(o) checktag((o), ctb(LUA_VLNGSTR)) + +#define tsvalueraw(v) (gco2ts((v).gc)) + +#define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc)) + +#define setsvalue(L,obj,x) \ + { TValue *io = (obj); TString *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \ + checkliveness(L,io); } + +/* set a string to the stack */ +#define setsvalue2s(L,o,s) setsvalue(L,s2v(o),s) + +/* set a string to a new object */ +#define setsvalue2n setsvalue + + +/* Kinds of long strings (stored in 'shrlen') */ +#define LSTRREG -1 /* regular long string */ +#define LSTRFIX -2 /* fixed external long string */ +#define LSTRMEM -3 /* external long string with deallocation */ + + +/* +** Header for a string value. +*/ +typedef struct TString { + CommonHeader; + lu_byte extra; /* reserved words for short strings; "has hash" for longs */ + ls_byte shrlen; /* length for short strings, negative for long strings */ + unsigned int hash; + union { + size_t lnglen; /* length for long strings */ + struct TString *hnext; /* linked list for hash table */ + } u; + char *contents; /* pointer to content in long strings */ + lua_Alloc falloc; /* deallocation function for external strings */ + void *ud; /* user data for external strings */ +} TString; + + +#define strisshr(ts) ((ts)->shrlen >= 0) +#define isextstr(ts) (ttislngstring(ts) && tsvalue(ts)->shrlen != LSTRREG) + + +/* +** Get the actual string (array of bytes) from a 'TString'. (Generic +** version and specialized versions for long and short strings.) +*/ +#define rawgetshrstr(ts) (cast_charp(&(ts)->contents)) +#define getshrstr(ts) check_exp(strisshr(ts), rawgetshrstr(ts)) +#define getlngstr(ts) check_exp(!strisshr(ts), (ts)->contents) +#define getstr(ts) (strisshr(ts) ? rawgetshrstr(ts) : (ts)->contents) + + +/* get string length from 'TString *ts' */ +#define tsslen(ts) \ + (strisshr(ts) ? cast_sizet((ts)->shrlen) : (ts)->u.lnglen) + +/* +** Get string and length */ +#define getlstr(ts, len) \ + (strisshr(ts) \ + ? (cast_void((len) = cast_sizet((ts)->shrlen)), rawgetshrstr(ts)) \ + : (cast_void((len) = (ts)->u.lnglen), (ts)->contents)) + +/* }================================================================== */ + + +/* +** {================================================================== +** Userdata +** =================================================================== +*/ + + +/* +** Light userdata should be a variant of userdata, but for compatibility +** reasons they are also different types. +*/ +#define LUA_VLIGHTUSERDATA makevariant(LUA_TLIGHTUSERDATA, 0) + +#define LUA_VUSERDATA makevariant(LUA_TUSERDATA, 0) + +#define ttislightuserdata(o) checktag((o), LUA_VLIGHTUSERDATA) +#define ttisfulluserdata(o) checktag((o), ctb(LUA_VUSERDATA)) + +#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p) +#define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc)) + +#define pvalueraw(v) ((v).p) + +#define setpvalue(obj,x) \ + { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_VLIGHTUSERDATA); } + +#define setuvalue(L,obj,x) \ + { TValue *io = (obj); Udata *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VUSERDATA)); \ + checkliveness(L,io); } + + +/* Ensures that addresses after this type are always fully aligned. */ +typedef union UValue { + TValue uv; + LUAI_MAXALIGN; /* ensures maximum alignment for udata bytes */ +} UValue; + + +/* +** Header for userdata with user values; +** memory area follows the end of this structure. +*/ +typedef struct Udata { + CommonHeader; + unsigned short nuvalue; /* number of user values */ + size_t len; /* number of bytes */ + struct Table *metatable; + GCObject *gclist; + UValue uv[1]; /* user values */ +} Udata; + + +/* +** Header for userdata with no user values. These userdata do not need +** to be gray during GC, and therefore do not need a 'gclist' field. +** To simplify, the code always use 'Udata' for both kinds of userdata, +** making sure it never accesses 'gclist' on userdata with no user values. +** This structure here is used only to compute the correct size for +** this representation. (The 'bindata' field in its end ensures correct +** alignment for binary data following this header.) +*/ +typedef struct Udata0 { + CommonHeader; + unsigned short nuvalue; /* number of user values */ + size_t len; /* number of bytes */ + struct Table *metatable; + union {LUAI_MAXALIGN;} bindata; +} Udata0; + + +/* compute the offset of the memory area of a userdata */ +#define udatamemoffset(nuv) \ + ((nuv) == 0 ? offsetof(Udata0, bindata) \ + : offsetof(Udata, uv) + (sizeof(UValue) * (nuv))) + +/* get the address of the memory block inside 'Udata' */ +#define getudatamem(u) (cast_charp(u) + udatamemoffset((u)->nuvalue)) + +/* compute the size of a userdata */ +#define sizeudata(nuv,nb) (udatamemoffset(nuv) + (nb)) + +/* }================================================================== */ + + +/* +** {================================================================== +** Prototypes +** =================================================================== +*/ + +#define LUA_VPROTO makevariant(LUA_TPROTO, 0) + + +typedef l_uint32 Instruction; + + +/* +** Description of an upvalue for function prototypes +*/ +typedef struct Upvaldesc { + TString *name; /* upvalue name (for debug information) */ + lu_byte instack; /* whether it is in stack (register) */ + lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ + lu_byte kind; /* kind of corresponding variable */ +} Upvaldesc; + + +/* +** Description of a local variable for function prototypes +** (used for debug information) +*/ +typedef struct LocVar { + TString *varname; + int startpc; /* first point where variable is active */ + int endpc; /* first point where variable is dead */ +} LocVar; + + +/* +** Associates the absolute line source for a given instruction ('pc'). +** The array 'lineinfo' gives, for each instruction, the difference in +** lines from the previous instruction. When that difference does not +** fit into a byte, Lua saves the absolute line for that instruction. +** (Lua also saves the absolute line periodically, to speed up the +** computation of a line number: we can use binary search in the +** absolute-line array, but we must traverse the 'lineinfo' array +** linearly to compute a line.) +*/ +typedef struct AbsLineInfo { + int pc; + int line; +} AbsLineInfo; + + +/* +** Flags in Prototypes +*/ +#define PF_VAHID 1 /* function has hidden vararg arguments */ +#define PF_VATAB 2 /* function has vararg table */ +#define PF_FIXED 4 /* prototype has parts in fixed memory */ + +/* a vararg function either has hidden args. or a vararg table */ +#define isvararg(p) ((p)->flag & (PF_VAHID | PF_VATAB)) + +/* +** mark that a function needs a vararg table. (The flag PF_VAHID will +** be cleared later.) +*/ +#define needvatab(p) ((p)->flag |= PF_VATAB) + +/* +** Function Prototypes +*/ +typedef struct Proto { + CommonHeader; + lu_byte numparams; /* number of fixed (named) parameters */ + lu_byte flag; + lu_byte maxstacksize; /* number of registers needed by this function */ + int sizeupvalues; /* size of 'upvalues' */ + int sizek; /* size of 'k' */ + int sizecode; + int sizelineinfo; + int sizep; /* size of 'p' */ + int sizelocvars; + int sizeabslineinfo; /* size of 'abslineinfo' */ + int linedefined; /* debug information */ + int lastlinedefined; /* debug information */ + TValue *k; /* constants used by the function */ + Instruction *code; /* opcodes */ + struct Proto **p; /* functions defined inside the function */ + Upvaldesc *upvalues; /* upvalue information */ + ls_byte *lineinfo; /* information about source lines (debug information) */ + AbsLineInfo *abslineinfo; /* idem */ + LocVar *locvars; /* information about local variables (debug information) */ + TString *source; /* used for debug information */ + GCObject *gclist; +} Proto; + +/* }================================================================== */ + + +/* +** {================================================================== +** Functions +** =================================================================== +*/ + +#define LUA_VUPVAL makevariant(LUA_TUPVAL, 0) + + +/* Variant tags for functions */ +#define LUA_VLCL makevariant(LUA_TFUNCTION, 0) /* Lua closure */ +#define LUA_VLCF makevariant(LUA_TFUNCTION, 1) /* light C function */ +#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */ + +#define ttisfunction(o) checktype(o, LUA_TFUNCTION) +#define ttisLclosure(o) checktag((o), ctb(LUA_VLCL)) +#define ttislcf(o) checktag((o), LUA_VLCF) +#define ttisCclosure(o) checktag((o), ctb(LUA_VCCL)) +#define ttisclosure(o) (ttisLclosure(o) || ttisCclosure(o)) + + +#define isLfunction(o) ttisLclosure(o) + +#define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc)) +#define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc)) +#define fvalue(o) check_exp(ttislcf(o), val_(o).f) +#define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc)) + +#define fvalueraw(v) ((v).f) + +#define setclLvalue(L,obj,x) \ + { TValue *io = (obj); LClosure *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VLCL)); \ + checkliveness(L,io); } + +#define setclLvalue2s(L,o,cl) setclLvalue(L,s2v(o),cl) + +#define setfvalue(obj,x) \ + { TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_VLCF); } + +#define setclCvalue(L,obj,x) \ + { TValue *io = (obj); CClosure *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VCCL)); \ + checkliveness(L,io); } + + +/* +** Upvalues for Lua closures +*/ +typedef struct UpVal { + CommonHeader; + union { + TValue *p; /* points to stack or to its own value */ + ptrdiff_t offset; /* used while the stack is being reallocated */ + } v; + union { + struct { /* (when open) */ + struct UpVal *next; /* linked list */ + struct UpVal **previous; + } open; + TValue value; /* the value (when closed) */ + } u; +} UpVal; + + + +#define ClosureHeader \ + CommonHeader; lu_byte nupvalues; GCObject *gclist + +typedef struct CClosure { + ClosureHeader; + lua_CFunction f; + TValue upvalue[1]; /* list of upvalues */ +} CClosure; + + +typedef struct LClosure { + ClosureHeader; + struct Proto *p; + UpVal *upvals[1]; /* list of upvalues */ +} LClosure; + + +typedef union Closure { + CClosure c; + LClosure l; +} Closure; + + +#define getproto(o) (clLvalue(o)->p) + +/* }================================================================== */ + + +/* +** {================================================================== +** Tables +** =================================================================== +*/ + +#define LUA_VTABLE makevariant(LUA_TTABLE, 0) + +#define ttistable(o) checktag((o), ctb(LUA_VTABLE)) + +#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc)) + +#define sethvalue(L,obj,x) \ + { TValue *io = (obj); Table *x_ = (x); \ + val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTABLE)); \ + checkliveness(L,io); } + +#define sethvalue2s(L,o,h) sethvalue(L,s2v(o),h) + + +/* +** Nodes for Hash tables: A pack of two TValue's (key-value pairs) +** plus a 'next' field to link colliding entries. The distribution +** of the key's fields ('key_tt' and 'key_val') not forming a proper +** 'TValue' allows for a smaller size for 'Node' both in 4-byte +** and 8-byte alignments. +*/ +typedef union Node { + struct NodeKey { + TValuefields; /* fields for value */ + lu_byte key_tt; /* key type */ + int next; /* for chaining */ + Value key_val; /* key value */ + } u; + TValue i_val; /* direct access to node's value as a proper 'TValue' */ +} Node; + + +/* copy a value into a key */ +#define setnodekey(node,obj) \ + { Node *n_=(node); const TValue *io_=(obj); \ + n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; } + + +/* copy a value from a key */ +#define getnodekey(L,obj,node) \ + { TValue *io_=(obj); const Node *n_=(node); \ + io_->value_ = n_->u.key_val; io_->tt_ = n_->u.key_tt; \ + checkliveness(L,io_); } + + + +typedef struct Table { + CommonHeader; + lu_byte flags; /* 1<<p means tagmethod(p) is not present */ + lu_byte lsizenode; /* log2 of number of slots of 'node' array */ + unsigned int asize; /* number of slots in 'array' array */ + Value *array; /* array part */ + Node *node; + struct Table *metatable; + GCObject *gclist; +} Table; + + +/* +** Macros to manipulate keys inserted in nodes +*/ +#define keytt(node) ((node)->u.key_tt) +#define keyval(node) ((node)->u.key_val) + +#define keyisnil(node) (keytt(node) == LUA_TNIL) +#define keyisinteger(node) (keytt(node) == LUA_VNUMINT) +#define keyival(node) (keyval(node).i) +#define keyisshrstr(node) (keytt(node) == ctb(LUA_VSHRSTR)) +#define keystrval(node) (gco2ts(keyval(node).gc)) + +#define setnilkey(node) (keytt(node) = LUA_TNIL) + +#define keyiscollectable(n) (keytt(n) & BIT_ISCOLLECTABLE) + +#define gckey(n) (keyval(n).gc) +#define gckeyN(n) (keyiscollectable(n) ? gckey(n) : NULL) + + +/* +** Dead keys in tables have the tag DEADKEY but keep their original +** gcvalue. This distinguishes them from regular keys but allows them to +** be found when searched in a special way. ('next' needs that to find +** keys removed from a table during a traversal.) +*/ +#define setdeadkey(node) (keytt(node) = LUA_TDEADKEY) +#define keyisdead(node) (keytt(node) == LUA_TDEADKEY) + +/* }================================================================== */ + + + +/* +** 'module' operation for hashing (size is always a power of 2) +*/ +#define lmod(s,size) \ + (check_exp((size&(size-1))==0, (cast_uint(s) & cast_uint((size)-1)))) + + +#define twoto(x) (1u<<(x)) +#define sizenode(t) (twoto((t)->lsizenode)) + + +/* size of buffer for 'luaO_utf8esc' function */ +#define UTF8BUFFSZ 8 + + +/* macro to call 'luaO_pushvfstring' correctly */ +#define pushvfstring(L, argp, fmt, msg) \ + { va_start(argp, fmt); \ + msg = luaO_pushvfstring(L, fmt, argp); \ + va_end(argp); \ + if (msg == NULL) luaD_throw(L, LUA_ERRMEM); /* only after 'va_end' */ } + + +LUAI_FUNC int luaO_utf8esc (char *buff, l_uint32 x); +LUAI_FUNC lu_byte luaO_ceillog2 (unsigned int x); +LUAI_FUNC lu_byte luaO_codeparam (unsigned int p); +LUAI_FUNC l_mem luaO_applyparam (lu_byte p, l_mem x); + +LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1, + const TValue *p2, TValue *res); +LUAI_FUNC void luaO_arith (lua_State *L, int op, const TValue *p1, + const TValue *p2, StkId res); +LUAI_FUNC size_t luaO_str2num (const char *s, TValue *o); +LUAI_FUNC unsigned luaO_tostringbuff (const TValue *obj, char *buff); +LUAI_FUNC lu_byte luaO_hexavalue (int c); +LUAI_FUNC void luaO_tostring (lua_State *L, TValue *obj); +LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, + va_list argp); +LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); +LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t srclen); + + +#endif + + +``` + +`Lua/lopcodes.c`: + +```c +/* +** $Id: lopcodes.c $ +** Opcodes for Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#define lopcodes_c +#define LUA_CORE + +#include "lprefix.h" + + +#include "lopcodes.h" + + +#define opmode(mm,ot,it,t,a,m) \ + (((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m)) + + +/* ORDER OP */ + +LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { +/* MM OT IT T A mode opcode */ + opmode(0, 0, 0, 0, 1, iABC) /* OP_MOVE */ + ,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADI */ + ,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADF */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADK */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADKX */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADFALSE */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LFALSESKIP */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADTRUE */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADNIL */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETUPVAL */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETUPVAL */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABUP */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABLE */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETFIELD */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABUP */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABLE */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETI */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETFIELD */ + ,opmode(0, 0, 0, 0, 1, ivABC) /* OP_NEWTABLE */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SELF */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUBK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MULK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MODK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_POWK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIVK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIVK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MOD */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_POW */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIV */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIV */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BAND */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BOR */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXOR */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHL */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHR */ + ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBIN */ + ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINI */ + ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINK */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_UNM */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BNOT */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NOT */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LEN */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_CONCAT */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_CLOSE */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TBC */ + ,opmode(0, 0, 0, 0, 0, isJ) /* OP_JMP */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQ */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LT */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LE */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQK */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LTI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LEI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_GTI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_GEI */ + ,opmode(0, 0, 0, 1, 0, iABC) /* OP_TEST */ + ,opmode(0, 0, 0, 1, 1, iABC) /* OP_TESTSET */ + ,opmode(0, 1, 1, 0, 1, iABC) /* OP_CALL */ + ,opmode(0, 1, 1, 0, 1, iABC) /* OP_TAILCALL */ + ,opmode(0, 0, 1, 0, 0, iABC) /* OP_RETURN */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN0 */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN1 */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORLOOP */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORPREP */ + ,opmode(0, 0, 0, 0, 0, iABx) /* OP_TFORPREP */ + ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TFORCALL */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_TFORLOOP */ + ,opmode(0, 0, 1, 0, 0, ivABC) /* OP_SETLIST */ + ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ + ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ + ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETVARG */ + ,opmode(0, 0, 0, 0, 0, iABx) /* OP_ERRNNIL */ + ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ + ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ +}; + + + +/* +** Check whether instruction sets top for next instruction, that is, +** it results in multiple values. +*/ +int luaP_isOT (Instruction i) { + OpCode op = GET_OPCODE(i); + switch (op) { + case OP_TAILCALL: return 1; + default: + return testOTMode(op) && GETARG_C(i) == 0; + } +} + + +/* +** Check whether instruction uses top from previous instruction, that is, +** it accepts multiple results. +*/ +int luaP_isIT (Instruction i) { + OpCode op = GET_OPCODE(i); + switch (op) { + case OP_SETLIST: + return testITMode(GET_OPCODE(i)) && GETARG_vB(i) == 0; + default: + return testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0; + } +} + + +``` + +`Lua/lopcodes.h`: + +```h +/* +** $Id: lopcodes.h $ +** Opcodes for Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lopcodes_h +#define lopcodes_h + +#include "llimits.h" +#include "lobject.h" + + +/*=========================================================================== + We assume that instructions are unsigned 32-bit integers. + All instructions have an opcode in the first 7 bits. + Instructions can have the following formats: + + 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 + 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +iABC C(8) | B(8) |k| A(8) | Op(7) | +ivABC vC(10) | vB(6) |k| A(8) | Op(7) | +iABx Bx(17) | A(8) | Op(7) | +iAsBx sBx (signed)(17) | A(8) | Op(7) | +iAx Ax(25) | Op(7) | +isJ sJ (signed)(25) | Op(7) | + + ('v' stands for "variant", 's' for "signed", 'x' for "extended".) + A signed argument is represented in excess K: The represented value is + the written unsigned value minus K, where K is half (rounded down) the + maximum value for the corresponding unsigned argument. +===========================================================================*/ + + +/* basic instruction formats */ +enum OpMode {iABC, ivABC, iABx, iAsBx, iAx, isJ}; + + +/* +** size and position of opcode arguments. +*/ +#define SIZE_C 8 +#define SIZE_vC 10 +#define SIZE_B 8 +#define SIZE_vB 6 +#define SIZE_Bx (SIZE_C + SIZE_B + 1) +#define SIZE_A 8 +#define SIZE_Ax (SIZE_Bx + SIZE_A) +#define SIZE_sJ (SIZE_Bx + SIZE_A) + +#define SIZE_OP 7 + +#define POS_OP 0 + +#define POS_A (POS_OP + SIZE_OP) +#define POS_k (POS_A + SIZE_A) +#define POS_B (POS_k + 1) +#define POS_vB (POS_k + 1) +#define POS_C (POS_B + SIZE_B) +#define POS_vC (POS_vB + SIZE_vB) + +#define POS_Bx POS_k + +#define POS_Ax POS_A + +#define POS_sJ POS_A + + +/* +** limits for opcode arguments. +** we use (signed) 'int' to manipulate most arguments, +** so they must fit in ints. +*/ + +/* +** Check whether type 'int' has at least 'b' + 1 bits. +** 'b' < 32; +1 for the sign bit. +*/ +#define L_INTHASBITS(b) ((UINT_MAX >> (b)) >= 1) + + +#if L_INTHASBITS(SIZE_Bx) +#define MAXARG_Bx ((1<<SIZE_Bx)-1) +#else +#define MAXARG_Bx INT_MAX +#endif + +#define OFFSET_sBx (MAXARG_Bx>>1) /* 'sBx' is signed */ + + +#if L_INTHASBITS(SIZE_Ax) +#define MAXARG_Ax ((1<<SIZE_Ax)-1) +#else +#define MAXARG_Ax INT_MAX +#endif + +#if L_INTHASBITS(SIZE_sJ) +#define MAXARG_sJ ((1 << SIZE_sJ) - 1) +#else +#define MAXARG_sJ INT_MAX +#endif + +#define OFFSET_sJ (MAXARG_sJ >> 1) + + +#define MAXARG_A ((1<<SIZE_A)-1) +#define MAXARG_B ((1<<SIZE_B)-1) +#define MAXARG_vB ((1<<SIZE_vB)-1) +#define MAXARG_C ((1<<SIZE_C)-1) +#define MAXARG_vC ((1<<SIZE_vC)-1) +#define OFFSET_sC (MAXARG_C >> 1) + +#define int2sC(i) ((i) + OFFSET_sC) +#define sC2int(i) ((i) - OFFSET_sC) + + +/* creates a mask with 'n' 1 bits at position 'p' */ +#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p)) + +/* creates a mask with 'n' 0 bits at position 'p' */ +#define MASK0(n,p) (~MASK1(n,p)) + +/* +** the following macros help to manipulate instructions +*/ + +#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0))) +#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ + ((cast_Inst(o)<<POS_OP)&MASK1(SIZE_OP,POS_OP)))) + +#define checkopm(i,m) (getOpMode(GET_OPCODE(i)) == m) + + +#define getarg(i,pos,size) (cast_int(((i)>>(pos)) & MASK1(size,0))) +#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \ + ((cast_Inst(v)<<pos)&MASK1(size,pos)))) + +#define GETARG_A(i) getarg(i, POS_A, SIZE_A) +#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A) + +#define GETARG_B(i) \ + check_exp(checkopm(i, iABC), getarg(i, POS_B, SIZE_B)) +#define GETARG_vB(i) \ + check_exp(checkopm(i, ivABC), getarg(i, POS_vB, SIZE_vB)) +#define GETARG_sB(i) sC2int(GETARG_B(i)) +#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B) +#define SETARG_vB(i,v) setarg(i, v, POS_vB, SIZE_vB) + +#define GETARG_C(i) \ + check_exp(checkopm(i, iABC), getarg(i, POS_C, SIZE_C)) +#define GETARG_vC(i) \ + check_exp(checkopm(i, ivABC), getarg(i, POS_vC, SIZE_vC)) +#define GETARG_sC(i) sC2int(GETARG_C(i)) +#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C) +#define SETARG_vC(i,v) setarg(i, v, POS_vC, SIZE_vC) + +#define TESTARG_k(i) (cast_int(((i) & (1u << POS_k)))) +#define GETARG_k(i) getarg(i, POS_k, 1) +#define SETARG_k(i,v) setarg(i, v, POS_k, 1) + +#define GETARG_Bx(i) check_exp(checkopm(i, iABx), getarg(i, POS_Bx, SIZE_Bx)) +#define SETARG_Bx(i,v) setarg(i, v, POS_Bx, SIZE_Bx) + +#define GETARG_Ax(i) check_exp(checkopm(i, iAx), getarg(i, POS_Ax, SIZE_Ax)) +#define SETARG_Ax(i,v) setarg(i, v, POS_Ax, SIZE_Ax) + +#define GETARG_sBx(i) \ + check_exp(checkopm(i, iAsBx), getarg(i, POS_Bx, SIZE_Bx) - OFFSET_sBx) +#define SETARG_sBx(i,b) SETARG_Bx((i),cast_uint((b)+OFFSET_sBx)) + +#define GETARG_sJ(i) \ + check_exp(checkopm(i, isJ), getarg(i, POS_sJ, SIZE_sJ) - OFFSET_sJ) +#define SETARG_sJ(i,j) \ + setarg(i, cast_uint((j)+OFFSET_sJ), POS_sJ, SIZE_sJ) + + +#define CREATE_ABCk(o,a,b,c,k) ((cast_Inst(o)<<POS_OP) \ + | (cast_Inst(a)<<POS_A) \ + | (cast_Inst(b)<<POS_B) \ + | (cast_Inst(c)<<POS_C) \ + | (cast_Inst(k)<<POS_k)) + +#define CREATE_vABCk(o,a,b,c,k) ((cast_Inst(o)<<POS_OP) \ + | (cast_Inst(a)<<POS_A) \ + | (cast_Inst(b)<<POS_vB) \ + | (cast_Inst(c)<<POS_vC) \ + | (cast_Inst(k)<<POS_k)) + +#define CREATE_ABx(o,a,bc) ((cast_Inst(o)<<POS_OP) \ + | (cast_Inst(a)<<POS_A) \ + | (cast_Inst(bc)<<POS_Bx)) + +#define CREATE_Ax(o,a) ((cast_Inst(o)<<POS_OP) \ + | (cast_Inst(a)<<POS_Ax)) + +#define CREATE_sJ(o,j,k) ((cast_Inst(o) << POS_OP) \ + | (cast_Inst(j) << POS_sJ) \ + | (cast_Inst(k) << POS_k)) + + +#if !defined(MAXINDEXRK) /* (for debugging only) */ +#define MAXINDEXRK MAXARG_B +#endif + + +/* +** Maximum size for the stack of a Lua function. It must fit in 8 bits. +** The highest valid register is one less than this value. +*/ +#define MAX_FSTACK MAXARG_A + +/* +** Invalid register (one more than last valid register). +*/ +#define NO_REG MAX_FSTACK + + + +/* +** R[x] - register +** K[x] - constant (in constant table) +** RK(x) == if k(i) then K[x] else R[x] +*/ + + +/* +** Grep "ORDER OP" if you change this enum. +** See "Notes" below for more information about some instructions. +*/ + +typedef enum { +/*---------------------------------------------------------------------- + name args description +------------------------------------------------------------------------*/ +OP_MOVE,/* A B R[A] := R[B] */ +OP_LOADI,/* A sBx R[A] := sBx */ +OP_LOADF,/* A sBx R[A] := (lua_Number)sBx */ +OP_LOADK,/* A Bx R[A] := K[Bx] */ +OP_LOADKX,/* A R[A] := K[extra arg] */ +OP_LOADFALSE,/* A R[A] := false */ +OP_LFALSESKIP,/*A R[A] := false; pc++ */ +OP_LOADTRUE,/* A R[A] := true */ +OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */ +OP_GETUPVAL,/* A B R[A] := UpValue[B] */ +OP_SETUPVAL,/* A B UpValue[B] := R[A] */ + +OP_GETTABUP,/* A B C R[A] := UpValue[B][K[C]:shortstring] */ +OP_GETTABLE,/* A B C R[A] := R[B][R[C]] */ +OP_GETI,/* A B C R[A] := R[B][C] */ +OP_GETFIELD,/* A B C R[A] := R[B][K[C]:shortstring] */ + +OP_SETTABUP,/* A B C UpValue[A][K[B]:shortstring] := RK(C) */ +OP_SETTABLE,/* A B C R[A][R[B]] := RK(C) */ +OP_SETI,/* A B C R[A][B] := RK(C) */ +OP_SETFIELD,/* A B C R[A][K[B]:shortstring] := RK(C) */ + +OP_NEWTABLE,/* A vB vC k R[A] := {} */ + +OP_SELF,/* A B C R[A+1] := R[B]; R[A] := R[B][K[C]:shortstring] */ + +OP_ADDI,/* A B sC R[A] := R[B] + sC */ + +OP_ADDK,/* A B C R[A] := R[B] + K[C]:number */ +OP_SUBK,/* A B C R[A] := R[B] - K[C]:number */ +OP_MULK,/* A B C R[A] := R[B] * K[C]:number */ +OP_MODK,/* A B C R[A] := R[B] % K[C]:number */ +OP_POWK,/* A B C R[A] := R[B] ^ K[C]:number */ +OP_DIVK,/* A B C R[A] := R[B] / K[C]:number */ +OP_IDIVK,/* A B C R[A] := R[B] // K[C]:number */ + +OP_BANDK,/* A B C R[A] := R[B] & K[C]:integer */ +OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */ +OP_BXORK,/* A B C R[A] := R[B] ~ K[C]:integer */ + +OP_SHLI,/* A B sC R[A] := sC << R[B] */ +OP_SHRI,/* A B sC R[A] := R[B] >> sC */ + +OP_ADD,/* A B C R[A] := R[B] + R[C] */ +OP_SUB,/* A B C R[A] := R[B] - R[C] */ +OP_MUL,/* A B C R[A] := R[B] * R[C] */ +OP_MOD,/* A B C R[A] := R[B] % R[C] */ +OP_POW,/* A B C R[A] := R[B] ^ R[C] */ +OP_DIV,/* A B C R[A] := R[B] / R[C] */ +OP_IDIV,/* A B C R[A] := R[B] // R[C] */ + +OP_BAND,/* A B C R[A] := R[B] & R[C] */ +OP_BOR,/* A B C R[A] := R[B] | R[C] */ +OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */ +OP_SHL,/* A B C R[A] := R[B] << R[C] */ +OP_SHR,/* A B C R[A] := R[B] >> R[C] */ + +OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] */ +OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */ +OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */ + +OP_UNM,/* A B R[A] := -R[B] */ +OP_BNOT,/* A B R[A] := ~R[B] */ +OP_NOT,/* A B R[A] := not R[B] */ +OP_LEN,/* A B R[A] := #R[B] (length operator) */ + +OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */ + +OP_CLOSE,/* A close all upvalues >= R[A] */ +OP_TBC,/* A mark variable A "to be closed" */ +OP_JMP,/* sJ pc += sJ */ +OP_EQ,/* A B k if ((R[A] == R[B]) ~= k) then pc++ */ +OP_LT,/* A B k if ((R[A] < R[B]) ~= k) then pc++ */ +OP_LE,/* A B k if ((R[A] <= R[B]) ~= k) then pc++ */ + +OP_EQK,/* A B k if ((R[A] == K[B]) ~= k) then pc++ */ +OP_EQI,/* A sB k if ((R[A] == sB) ~= k) then pc++ */ +OP_LTI,/* A sB k if ((R[A] < sB) ~= k) then pc++ */ +OP_LEI,/* A sB k if ((R[A] <= sB) ~= k) then pc++ */ +OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */ +OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */ + +OP_TEST,/* A k if (not R[A] == k) then pc++ */ +OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] */ + +OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */ +OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */ + +OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] */ +OP_RETURN0,/* return */ +OP_RETURN1,/* A return R[A] */ + +OP_FORLOOP,/* A Bx update counters; if loop continues then pc-=Bx; */ +OP_FORPREP,/* A Bx <check values and prepare counters>; + if not to run then pc+=Bx+1; */ + +OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */ +OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */ +OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */ + +OP_SETLIST,/* A vB vC k R[A][vC+i] := R[A+i], 1 <= i <= vB */ + +OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */ + +OP_VARARG,/* A B C k R[A], ..., R[A+C-2] = varargs */ + +OP_GETVARG, /* A B C R[A] := R[B][R[C]], R[B] is vararg parameter */ + +OP_ERRNNIL,/* A Bx raise error if R[A] ~= nil (K[Bx - 1] is global name)*/ + +OP_VARARGPREP,/* (adjust varargs) */ + +OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ +} OpCode; + + +#define NUM_OPCODES ((int)(OP_EXTRAARG) + 1) + + + +/*=========================================================================== + Notes: + + (*) Opcode OP_LFALSESKIP is used to convert a condition to a boolean + value, in a code equivalent to (not cond ? false : true). (It + produces false and skips the next instruction producing true.) + + (*) Opcodes OP_MMBIN and variants follow each arithmetic and + bitwise opcode. If the operation succeeds, it skips this next + opcode. Otherwise, this opcode calls the corresponding metamethod. + + (*) Opcode OP_TESTSET is used in short-circuit expressions that need + both to jump and to produce a value, such as (a = b or c). + + (*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then + 'top' is set to last_result+1, so next open instruction (OP_CALL, + OP_RETURN*, OP_SETLIST) may use 'top'. + + (*) In OP_VARARG, if (C == 0) then use actual number of varargs and + set top (like in OP_CALL with C == 0). 'k' means function has a + vararg table, which is in R[B]. + + (*) In OP_RETURN, if (B == 0) then return up to 'top'. + + (*) In OP_LOADKX and OP_NEWTABLE, the next instruction is always + OP_EXTRAARG. + + (*) In OP_SETLIST, if (B == 0) then real B = 'top'; if k, then + real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the + bits of C). + + (*) In OP_NEWTABLE, vB is log2 of the hash size (which is always a + power of 2) plus 1, or zero for size zero. If not k, the array size + is vC. Otherwise, the array size is EXTRAARG _ vC. + + (*) In OP_ERRNNIL, (Bx == 0) means index of global name doesn't + fit in Bx. (So, that name is not available for the error message.) + + (*) For comparisons, k specifies what condition the test should accept + (true or false). + + (*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped + (the constant is the first operand). + + (*) All comparison and test instructions assume that the instruction + being skipped (pc++) is a jump. + + (*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the + function builds upvalues, which may need to be closed. C > 0 means + the function has hidden vararg arguments, so that its 'func' must be + corrected before returning; in this case, (C - 1) is its number of + fixed parameters. + + (*) In comparisons with an immediate operand, C signals whether the + original operand was a float. (It must be corrected in case of + metamethods.) + +===========================================================================*/ + + +/* +** masks for instruction properties. The format is: +** bits 0-2: op mode +** bit 3: instruction set register A +** bit 4: operator is a test (next instruction must be a jump) +** bit 5: instruction uses 'L->top' set by previous instruction (when B == 0) +** bit 6: instruction sets 'L->top' for next instruction (when C == 0) +** bit 7: instruction is an MM instruction (call a metamethod) +*/ + +LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];) + +#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7)) +#define testAMode(m) (luaP_opmodes[m] & (1 << 3)) +#define testTMode(m) (luaP_opmodes[m] & (1 << 4)) +#define testITMode(m) (luaP_opmodes[m] & (1 << 5)) +#define testOTMode(m) (luaP_opmodes[m] & (1 << 6)) +#define testMMMode(m) (luaP_opmodes[m] & (1 << 7)) + + +LUAI_FUNC int luaP_isOT (Instruction i); +LUAI_FUNC int luaP_isIT (Instruction i); + + +#endif + +``` + +`Lua/lopnames.h`: + +```h +/* +** $Id: lopnames.h $ +** Opcode names +** See Copyright Notice in lua.h +*/ + +#if !defined(lopnames_h) +#define lopnames_h + +#include <stddef.h> + + +/* ORDER OP */ + +static const char *const opnames[] = { + "MOVE", + "LOADI", + "LOADF", + "LOADK", + "LOADKX", + "LOADFALSE", + "LFALSESKIP", + "LOADTRUE", + "LOADNIL", + "GETUPVAL", + "SETUPVAL", + "GETTABUP", + "GETTABLE", + "GETI", + "GETFIELD", + "SETTABUP", + "SETTABLE", + "SETI", + "SETFIELD", + "NEWTABLE", + "SELF", + "ADDI", + "ADDK", + "SUBK", + "MULK", + "MODK", + "POWK", + "DIVK", + "IDIVK", + "BANDK", + "BORK", + "BXORK", + "SHLI", + "SHRI", + "ADD", + "SUB", + "MUL", + "MOD", + "POW", + "DIV", + "IDIV", + "BAND", + "BOR", + "BXOR", + "SHL", + "SHR", + "MMBIN", + "MMBINI", + "MMBINK", + "UNM", + "BNOT", + "NOT", + "LEN", + "CONCAT", + "CLOSE", + "TBC", + "JMP", + "EQ", + "LT", + "LE", + "EQK", + "EQI", + "LTI", + "LEI", + "GTI", + "GEI", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "RETURN0", + "RETURN1", + "FORLOOP", + "FORPREP", + "TFORPREP", + "TFORCALL", + "TFORLOOP", + "SETLIST", + "CLOSURE", + "VARARG", + "GETVARG", + "ERRNNIL", + "VARARGPREP", + "EXTRAARG", + NULL +}; + +#endif + + +``` + +`Lua/loslib.c`: + +```c +/* +** $Id: loslib.c $ +** Standard Operating System library +** See Copyright Notice in lua.h +*/ + +#define loslib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include <errno.h> +#include <locale.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "llimits.h" + + +/* +** {================================================================== +** List of valid conversion specifiers for the 'strftime' function; +** options are grouped by length; group of length 2 start with '||'. +** =================================================================== +*/ +#if !defined(LUA_STRFTIMEOPTIONS) /* { */ + +#if defined(LUA_USE_WINDOWS) +#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \ + "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */ +#elif defined(LUA_USE_C89) /* C89 (only 1-char options) */ +#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%" +#else /* C99 specification */ +#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \ + "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */ +#endif + +#endif /* } */ +/* }================================================================== */ + + +/* +** {================================================================== +** Configuration for time-related stuff +** =================================================================== +*/ + +/* +** type to represent time_t in Lua +*/ +#if !defined(LUA_NUMTIME) /* { */ + +#define l_timet lua_Integer +#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t)) +#define l_gettime(L,arg) luaL_checkinteger(L, arg) + +#else /* }{ */ + +#define l_timet lua_Number +#define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t)) +#define l_gettime(L,arg) luaL_checknumber(L, arg) + +#endif /* } */ + + +#if !defined(l_gmtime) /* { */ +/* +** By default, Lua uses gmtime/localtime, except when POSIX is available, +** where it uses gmtime_r/localtime_r +*/ + +#if defined(LUA_USE_POSIX) /* { */ + +#define l_gmtime(t,r) gmtime_r(t,r) +#define l_localtime(t,r) localtime_r(t,r) + +#else /* }{ */ + +/* ISO C definitions */ +#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t)) +#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t)) + +#endif /* } */ + +#endif /* } */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Configuration for 'tmpnam': +** By default, Lua uses tmpnam except when POSIX is available, where +** it uses mkstemp. +** =================================================================== +*/ +#if !defined(lua_tmpnam) /* { */ + +#if defined(LUA_USE_POSIX) /* { */ + +#include <unistd.h> + +#define LUA_TMPNAMBUFSIZE 32 + +#if !defined(LUA_TMPNAMTEMPLATE) +#define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX" +#endif + +#define lua_tmpnam(b,e) { \ + strcpy(b, LUA_TMPNAMTEMPLATE); \ + e = mkstemp(b); \ + if (e != -1) close(e); \ + e = (e == -1); } + +#else /* }{ */ + +/* ISO C definitions */ +#define LUA_TMPNAMBUFSIZE L_tmpnam +#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } + +#endif /* } */ + +#endif /* } */ +/* }================================================================== */ + + +#if !defined(l_system) +#if defined(LUA_USE_IOS) +/* Despite claiming to be ISO C, iOS does not implement 'system'. */ +#define l_system(cmd) ((cmd) == NULL ? 0 : -1) +#else +#define l_system(cmd) system(cmd) /* default definition */ +#endif +#endif + + +static int os_execute (lua_State *L) { + const char *cmd = luaL_optstring(L, 1, NULL); + int stat; + errno = 0; + stat = l_system(cmd); + if (cmd != NULL) + return luaL_execresult(L, stat); + else { + lua_pushboolean(L, stat); /* true if there is a shell */ + return 1; + } +} + + +static int os_remove (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + errno = 0; + return luaL_fileresult(L, remove(filename) == 0, filename); +} + + +static int os_rename (lua_State *L) { + const char *fromname = luaL_checkstring(L, 1); + const char *toname = luaL_checkstring(L, 2); + errno = 0; + return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); +} + + +static int os_tmpname (lua_State *L) { + char buff[LUA_TMPNAMBUFSIZE]; + int err; + lua_tmpnam(buff, err); + if (l_unlikely(err)) + return luaL_error(L, "unable to generate a unique filename"); + lua_pushstring(L, buff); + return 1; +} + + +static int os_getenv (lua_State *L) { + lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ + return 1; +} + + +static int os_clock (lua_State *L) { + lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); + return 1; +} + + +/* +** {====================================================== +** Time/Date operations +** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, +** wday=%w+1, yday=%j, isdst=? } +** ======================================================= +*/ + +/* +** About the overflow check: an overflow cannot occur when time +** is represented by a lua_Integer, because either lua_Integer is +** large enough to represent all int fields or it is not large enough +** to represent a time that cause a field to overflow. However, if +** times are represented as doubles and lua_Integer is int, then the +** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900 +** to compute the year. +*/ +static void setfield (lua_State *L, const char *key, int value, int delta) { + #if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX) + if (l_unlikely(value > LUA_MAXINTEGER - delta)) + luaL_error(L, "field '%s' is out-of-bound", key); + #endif + lua_pushinteger(L, (lua_Integer)value + delta); + lua_setfield(L, -2, key); +} + + +static void setboolfield (lua_State *L, const char *key, int value) { + if (value < 0) /* undefined? */ + return; /* does not set field */ + lua_pushboolean(L, value); + lua_setfield(L, -2, key); +} + + +/* +** Set all fields from structure 'tm' in the table on top of the stack +*/ +static void setallfields (lua_State *L, struct tm *stm) { + setfield(L, "year", stm->tm_year, 1900); + setfield(L, "month", stm->tm_mon, 1); + setfield(L, "day", stm->tm_mday, 0); + setfield(L, "hour", stm->tm_hour, 0); + setfield(L, "min", stm->tm_min, 0); + setfield(L, "sec", stm->tm_sec, 0); + setfield(L, "yday", stm->tm_yday, 1); + setfield(L, "wday", stm->tm_wday, 1); + setboolfield(L, "isdst", stm->tm_isdst); +} + + +static int getboolfield (lua_State *L, const char *key) { + int res; + res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1); + lua_pop(L, 1); + return res; +} + + +static int getfield (lua_State *L, const char *key, int d, int delta) { + int isnum; + int t = lua_getfield(L, -1, key); /* get field and its type */ + lua_Integer res = lua_tointegerx(L, -1, &isnum); + if (!isnum) { /* field is not an integer? */ + if (l_unlikely(t != LUA_TNIL)) /* some other value? */ + return luaL_error(L, "field '%s' is not an integer", key); + else if (l_unlikely(d < 0)) /* absent field; no default? */ + return luaL_error(L, "field '%s' missing in date table", key); + res = d; + } + else { + if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res)) + return luaL_error(L, "field '%s' is out-of-bound", key); + res -= delta; + } + lua_pop(L, 1); + return (int)res; +} + + +static const char *checkoption (lua_State *L, const char *conv, + size_t convlen, char *buff) { + const char *option = LUA_STRFTIMEOPTIONS; + unsigned oplen = 1; /* length of options being checked */ + for (; *option != '\0' && oplen <= convlen; option += oplen) { + if (*option == '|') /* next block? */ + oplen++; /* will check options with next length (+1) */ + else if (memcmp(conv, option, oplen) == 0) { /* match? */ + memcpy(buff, conv, oplen); /* copy valid option to buffer */ + buff[oplen] = '\0'; + return conv + oplen; /* return next item */ + } + } + luaL_argerror(L, 1, + lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv)); + return conv; /* to avoid warnings */ +} + + +static time_t l_checktime (lua_State *L, int arg) { + l_timet t = l_gettime(L, arg); + luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds"); + return (time_t)t; +} + + +/* maximum size for an individual 'strftime' item */ +#define SIZETIMEFMT 250 + + +static int os_date (lua_State *L) { + size_t slen; + const char *s = luaL_optlstring(L, 1, "%c", &slen); + time_t t = luaL_opt(L, l_checktime, 2, time(NULL)); + const char *se = s + slen; /* 's' end */ + struct tm tmr, *stm; + if (*s == '!') { /* UTC? */ + stm = l_gmtime(&t, &tmr); + s++; /* skip '!' */ + } + else + stm = l_localtime(&t, &tmr); + if (stm == NULL) /* invalid date? */ + return luaL_error(L, + "date result cannot be represented in this installation"); + if (strcmp(s, "*t") == 0) { + lua_createtable(L, 0, 9); /* 9 = number of fields */ + setallfields(L, stm); + } + else { + char cc[4]; /* buffer for individual conversion specifiers */ + luaL_Buffer b; + cc[0] = '%'; + luaL_buffinit(L, &b); + while (s < se) { + if (*s != '%') /* not a conversion specifier? */ + luaL_addchar(&b, *s++); + else { + size_t reslen; + char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT); + s++; /* skip '%' */ + /* copy specifier to 'cc' */ + s = checkoption(L, s, ct_diff2sz(se - s), cc + 1); + reslen = strftime(buff, SIZETIMEFMT, cc, stm); + luaL_addsize(&b, reslen); + } + } + luaL_pushresult(&b); + } + return 1; +} + + +static int os_time (lua_State *L) { + time_t t; + if (lua_isnoneornil(L, 1)) /* called without args? */ + t = time(NULL); /* get current time */ + else { + struct tm ts; + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); /* make sure table is at the top */ + ts.tm_year = getfield(L, "year", -1, 1900); + ts.tm_mon = getfield(L, "month", -1, 1); + ts.tm_mday = getfield(L, "day", -1, 0); + ts.tm_hour = getfield(L, "hour", 12, 0); + ts.tm_min = getfield(L, "min", 0, 0); + ts.tm_sec = getfield(L, "sec", 0, 0); + ts.tm_isdst = getboolfield(L, "isdst"); + t = mktime(&ts); + setallfields(L, &ts); /* update fields with normalized values */ + } + if (t != (time_t)(l_timet)t || t == (time_t)(-1)) + return luaL_error(L, + "time result cannot be represented in this installation"); + l_pushtime(L, t); + return 1; +} + + +static int os_difftime (lua_State *L) { + time_t t1 = l_checktime(L, 1); + time_t t2 = l_checktime(L, 2); + lua_pushnumber(L, (lua_Number)difftime(t1, t2)); + return 1; +} + +/* }====================================================== */ + + +static int os_setlocale (lua_State *L) { + static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, + LC_NUMERIC, LC_TIME}; + static const char *const catnames[] = {"all", "collate", "ctype", "monetary", + "numeric", "time", NULL}; + const char *l = luaL_optstring(L, 1, NULL); + int op = luaL_checkoption(L, 2, "all", catnames); + lua_pushstring(L, setlocale(cat[op], l)); + return 1; +} + + +static int os_exit (lua_State *L) { + int status; + if (lua_isboolean(L, 1)) + status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE); + else + status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS); + if (lua_toboolean(L, 2)) + lua_close(L); + if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */ + return 0; +} + + +static const luaL_Reg syslib[] = { + {"clock", os_clock}, + {"date", os_date}, + {"difftime", os_difftime}, + {"execute", os_execute}, + {"exit", os_exit}, + {"getenv", os_getenv}, + {"remove", os_remove}, + {"rename", os_rename}, + {"setlocale", os_setlocale}, + {"time", os_time}, + {"tmpname", os_tmpname}, + {NULL, NULL} +}; + +/* }====================================================== */ + + + +LUAMOD_API int luaopen_os (lua_State *L) { + luaL_newlib(L, syslib); + return 1; +} + + +``` + +`Lua/lparser.c`: + +```c +/* +** $Id: lparser.c $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + +#define lparser_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <limits.h> +#include <string.h> + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" + + + +/* maximum number of variable declarations per function (must be + smaller than 250, due to the bytecode format) */ +#define MAXVARS 200 + + +#define hasmultret(k) ((k) == VCALL || (k) == VVARARG) + + +/* because all strings are unified by the scanner, the parser + can use pointer equality for string equality */ +#define eqstr(a,b) ((a) == (b)) + + +/* +** nodes for block list (list of active blocks) +*/ +typedef struct BlockCnt { + struct BlockCnt *previous; /* chain */ + int firstlabel; /* index of first label in this block */ + int firstgoto; /* index of first pending goto in this block */ + short nactvar; /* number of active declarations at block entry */ + lu_byte upval; /* true if some variable in the block is an upvalue */ + lu_byte isloop; /* 1 if 'block' is a loop; 2 if it has pending breaks */ + lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */ +} BlockCnt; + + + +/* +** prototypes for recursive non-terminal functions +*/ +static void statement (LexState *ls); +static void expr (LexState *ls, expdesc *v); + + +static l_noret error_expected (LexState *ls, int token) { + luaX_syntaxerror(ls, + luaO_pushfstring(ls->L, "%s expected", luaX_token2str(ls, token))); +} + + +static l_noret errorlimit (FuncState *fs, int limit, const char *what) { + lua_State *L = fs->ls->L; + const char *msg; + int line = fs->f->linedefined; + const char *where = (line == 0) + ? "main function" + : luaO_pushfstring(L, "function at line %d", line); + msg = luaO_pushfstring(L, "too many %s (limit is %d) in %s", + what, limit, where); + luaX_syntaxerror(fs->ls, msg); +} + + +void luaY_checklimit (FuncState *fs, int v, int l, const char *what) { + if (l_unlikely(v > l)) errorlimit(fs, l, what); +} + + +/* +** Test whether next token is 'c'; if so, skip it. +*/ +static int testnext (LexState *ls, int c) { + if (ls->t.token == c) { + luaX_next(ls); + return 1; + } + else return 0; +} + + +/* +** Check that next token is 'c'. +*/ +static void check (LexState *ls, int c) { + if (ls->t.token != c) + error_expected(ls, c); +} + + +/* +** Check that next token is 'c' and skip it. +*/ +static void checknext (LexState *ls, int c) { + check(ls, c); + luaX_next(ls); +} + + +#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } + + +/* +** Check that next token is 'what' and skip it. In case of error, +** raise an error that the expected 'what' should match a 'who' +** in line 'where' (if that is not the current line). +*/ +static void check_match (LexState *ls, int what, int who, int where) { + if (l_unlikely(!testnext(ls, what))) { + if (where == ls->linenumber) /* all in the same line? */ + error_expected(ls, what); /* do not need a complex message */ + else { + luaX_syntaxerror(ls, luaO_pushfstring(ls->L, + "%s expected (to close %s at line %d)", + luaX_token2str(ls, what), luaX_token2str(ls, who), where)); + } + } +} + + +static TString *str_checkname (LexState *ls) { + TString *ts; + check(ls, TK_NAME); + ts = ls->t.seminfo.ts; + luaX_next(ls); + return ts; +} + + +static void init_exp (expdesc *e, expkind k, int i) { + e->f = e->t = NO_JUMP; + e->k = k; + e->u.info = i; +} + + +static void codestring (expdesc *e, TString *s) { + e->f = e->t = NO_JUMP; + e->k = VKSTR; + e->u.strval = s; +} + + +static void codename (LexState *ls, expdesc *e) { + codestring(e, str_checkname(ls)); +} + + +/* +** Register a new local variable in the active 'Proto' (for debug +** information). +*/ +static short registerlocalvar (LexState *ls, FuncState *fs, + TString *varname) { + Proto *f = fs->f; + int oldsize = f->sizelocvars; + luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars, + LocVar, SHRT_MAX, "local variables"); + while (oldsize < f->sizelocvars) + f->locvars[oldsize++].varname = NULL; + f->locvars[fs->ndebugvars].varname = varname; + f->locvars[fs->ndebugvars].startpc = fs->pc; + luaC_objbarrier(ls->L, f, varname); + return fs->ndebugvars++; +} + + +/* +** Create a new variable with the given 'name' and given 'kind'. +** Return its index in the function. +*/ +static int new_varkind (LexState *ls, TString *name, lu_byte kind) { + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Dyndata *dyd = ls->dyd; + Vardesc *var; + luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, + dyd->actvar.size, Vardesc, SHRT_MAX, "variable declarations"); + var = &dyd->actvar.arr[dyd->actvar.n++]; + var->vd.kind = kind; /* default */ + var->vd.name = name; + return dyd->actvar.n - 1 - fs->firstlocal; +} + + +/* +** Create a new local variable with the given 'name' and regular kind. +*/ +static int new_localvar (LexState *ls, TString *name) { + return new_varkind(ls, name, VDKREG); +} + +#define new_localvarliteral(ls,v) \ + new_localvar(ls, \ + luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1)); + + + +/* +** Return the "variable description" (Vardesc) of a given variable. +** (Unless noted otherwise, all variables are referred to by their +** compiler indices.) +*/ +static Vardesc *getlocalvardesc (FuncState *fs, int vidx) { + return &fs->ls->dyd->actvar.arr[fs->firstlocal + vidx]; +} + + +/* +** Convert 'nvar', a compiler index level, to its corresponding +** register. For that, search for the highest variable below that level +** that is in a register and uses its register index ('ridx') plus one. +*/ +static lu_byte reglevel (FuncState *fs, int nvar) { + while (nvar-- > 0) { + Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */ + if (varinreg(vd)) /* is in a register? */ + return cast_byte(vd->vd.ridx + 1); + } + return 0; /* no variables in registers */ +} + + +/* +** Return the number of variables in the register stack for the given +** function. +*/ +lu_byte luaY_nvarstack (FuncState *fs) { + return reglevel(fs, fs->nactvar); +} + + +/* +** Get the debug-information entry for current variable 'vidx'. +*/ +static LocVar *localdebuginfo (FuncState *fs, int vidx) { + Vardesc *vd = getlocalvardesc(fs, vidx); + if (!varinreg(vd)) + return NULL; /* no debug info. for constants */ + else { + int idx = vd->vd.pidx; + lua_assert(idx < fs->ndebugvars); + return &fs->f->locvars[idx]; + } +} + + +/* +** Create an expression representing variable 'vidx' +*/ +static void init_var (FuncState *fs, expdesc *e, int vidx) { + e->f = e->t = NO_JUMP; + e->k = VLOCAL; + e->u.var.vidx = cast_short(vidx); + e->u.var.ridx = getlocalvardesc(fs, vidx)->vd.ridx; +} + + +/* +** Raises an error if variable described by 'e' is read only; moreover, +** if 'e' is t[exp] where t is the vararg parameter, change it to index +** a real table. (Virtual vararg tables cannot be changed.) +*/ +static void check_readonly (LexState *ls, expdesc *e) { + FuncState *fs = ls->fs; + TString *varname = NULL; /* to be set if variable is const */ + switch (e->k) { + case VCONST: { + varname = ls->dyd->actvar.arr[e->u.info].vd.name; + break; + } + case VLOCAL: case VVARGVAR: { + Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); + if (vardesc->vd.kind != VDKREG) /* not a regular variable? */ + varname = vardesc->vd.name; + break; + } + case VUPVAL: { + Upvaldesc *up = &fs->f->upvalues[e->u.info]; + if (up->kind != VDKREG) + varname = up->name; + break; + } + case VVARGIND: { + needvatab(fs->f); /* function will need a vararg table */ + e->k = VINDEXED; + } /* FALLTHROUGH */ + case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */ + if (e->u.ind.ro) /* read-only? */ + varname = tsvalue(&fs->f->k[e->u.ind.keystr]); + break; + } + default: + lua_assert(e->k == VINDEXI); /* this one doesn't need any check */ + return; /* integer index cannot be read-only */ + } + if (varname) + luaK_semerror(ls, "attempt to assign to const variable '%s'", + getstr(varname)); +} + + +/* +** Start the scope for the last 'nvars' created variables. +*/ +static void adjustlocalvars (LexState *ls, int nvars) { + FuncState *fs = ls->fs; + int reglevel = luaY_nvarstack(fs); + int i; + for (i = 0; i < nvars; i++) { + int vidx = fs->nactvar++; + Vardesc *var = getlocalvardesc(fs, vidx); + var->vd.ridx = cast_byte(reglevel++); + var->vd.pidx = registerlocalvar(ls, fs, var->vd.name); + luaY_checklimit(fs, reglevel, MAXVARS, "local variables"); + } +} + + +/* +** Close the scope for all variables up to level 'tolevel'. +** (debug info.) +*/ +static void removevars (FuncState *fs, int tolevel) { + fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel); + while (fs->nactvar > tolevel) { + LocVar *var = localdebuginfo(fs, --fs->nactvar); + if (var) /* does it have debug information? */ + var->endpc = fs->pc; + } +} + + +/* +** Search the upvalues of the function 'fs' for one +** with the given 'name'. +*/ +static int searchupvalue (FuncState *fs, TString *name) { + int i; + Upvaldesc *up = fs->f->upvalues; + for (i = 0; i < fs->nups; i++) { + if (eqstr(up[i].name, name)) return i; + } + return -1; /* not found */ +} + + +static Upvaldesc *allocupvalue (FuncState *fs) { + Proto *f = fs->f; + int oldsize = f->sizeupvalues; + luaY_checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues"); + luaM_growvector(fs->ls->L, f->upvalues, fs->nups, f->sizeupvalues, + Upvaldesc, MAXUPVAL, "upvalues"); + while (oldsize < f->sizeupvalues) + f->upvalues[oldsize++].name = NULL; + return &f->upvalues[fs->nups++]; +} + + +static int newupvalue (FuncState *fs, TString *name, expdesc *v) { + Upvaldesc *up = allocupvalue(fs); + FuncState *prev = fs->prev; + if (v->k == VLOCAL) { + up->instack = 1; + up->idx = v->u.var.ridx; + up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind; + lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name)); + } + else { + up->instack = 0; + up->idx = cast_byte(v->u.info); + up->kind = prev->f->upvalues[v->u.info].kind; + lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name)); + } + up->name = name; + luaC_objbarrier(fs->ls->L, fs->f, name); + return fs->nups - 1; +} + + +/* +** Look for an active variable with the name 'n' in the +** function 'fs'. If found, initialize 'var' with it and return +** its expression kind; otherwise return -1. While searching, +** var->u.info==-1 means that the preambular global declaration is +** active (the default while there is no other global declaration); +** var->u.info==-2 means there is no active collective declaration +** (some previous global declaration but no collective declaration); +** and var->u.info>=0 points to the inner-most (the first one found) +** collective declaration, if there is one. +*/ +static int searchvar (FuncState *fs, TString *n, expdesc *var) { + int i; + for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { + Vardesc *vd = getlocalvardesc(fs, i); + if (varglobal(vd)) { /* global declaration? */ + if (vd->vd.name == NULL) { /* collective declaration? */ + if (var->u.info < 0) /* no previous collective declaration? */ + var->u.info = fs->firstlocal + i; /* this is the first one */ + } + else { /* global name */ + if (eqstr(n, vd->vd.name)) { /* found? */ + init_exp(var, VGLOBAL, fs->firstlocal + i); + return VGLOBAL; + } + else if (var->u.info == -1) /* active preambular declaration? */ + var->u.info = -2; /* invalidate preambular declaration */ + } + } + else if (eqstr(n, vd->vd.name)) { /* found? */ + if (vd->vd.kind == RDKCTC) /* compile-time constant? */ + init_exp(var, VCONST, fs->firstlocal + i); + else { /* local variable */ + init_var(fs, var, i); + if (vd->vd.kind == RDKVAVAR) /* vararg parameter? */ + var->k = VVARGVAR; + } + return cast_int(var->k); + } + } + return -1; /* not found */ +} + + +/* +** Mark block where variable at given level was defined +** (to emit close instructions later). +*/ +static void markupval (FuncState *fs, int level) { + BlockCnt *bl = fs->bl; + while (bl->nactvar > level) + bl = bl->previous; + bl->upval = 1; + fs->needclose = 1; +} + + +/* +** Mark that current block has a to-be-closed variable. +*/ +static void marktobeclosed (FuncState *fs) { + BlockCnt *bl = fs->bl; + bl->upval = 1; + bl->insidetbc = 1; + fs->needclose = 1; +} + + +/* +** Find a variable with the given name 'n'. If it is an upvalue, add +** this upvalue into all intermediate functions. If it is a global, set +** 'var' as 'void' as a flag. +*/ +static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { + int v = searchvar(fs, n, var); /* look up variables at current level */ + if (v >= 0) { /* found? */ + if (!base) { + if (var->k == VVARGVAR) /* vararg parameter? */ + luaK_vapar2local(fs, var); /* change it to a regular local */ + if (var->k == VLOCAL) + markupval(fs, var->u.var.vidx); /* will be used as an upvalue */ + } + /* else nothing else to be done */ + } + else { /* not found at current level; try upvalues */ + int idx = searchupvalue(fs, n); /* try existing upvalues */ + if (idx < 0) { /* not found? */ + if (fs->prev != NULL) /* more levels? */ + singlevaraux(fs->prev, n, var, 0); /* try upper levels */ + if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */ + idx = newupvalue(fs, n, var); /* will be a new upvalue */ + else /* it is a global or a constant */ + return; /* don't need to do anything at this level */ + } + init_exp(var, VUPVAL, idx); /* new or old upvalue */ + } +} + + +static void buildglobal (LexState *ls, TString *varname, expdesc *var) { + FuncState *fs = ls->fs; + expdesc key; + init_exp(var, VGLOBAL, -1); /* global by default */ + singlevaraux(fs, ls->envn, var, 1); /* get environment variable */ + if (var->k == VGLOBAL) + luaK_semerror(ls, "%s is global when accessing variable '%s'", + LUA_ENV, getstr(varname)); + luaK_exp2anyregup(fs, var); /* _ENV could be a constant */ + codestring(&key, varname); /* key is variable name */ + luaK_indexed(fs, var, &key); /* 'var' represents _ENV[varname] */ +} + + +/* +** Find a variable with the given name 'n', handling global variables +** too. +*/ +static void buildvar (LexState *ls, TString *varname, expdesc *var) { + FuncState *fs = ls->fs; + init_exp(var, VGLOBAL, -1); /* global by default */ + singlevaraux(fs, varname, var, 1); + if (var->k == VGLOBAL) { /* global name? */ + int info = var->u.info; + /* global by default in the scope of a global declaration? */ + if (info == -2) + luaK_semerror(ls, "variable '%s' not declared", getstr(varname)); + buildglobal(ls, varname, var); + if (info != -1 && ls->dyd->actvar.arr[info].vd.kind == GDKCONST) + var->u.ind.ro = 1; /* mark variable as read-only */ + else /* anyway must be a global */ + lua_assert(info == -1 || ls->dyd->actvar.arr[info].vd.kind == GDKREG); + } +} + + +static void singlevar (LexState *ls, expdesc *var) { + buildvar(ls, str_checkname(ls), var); +} + + +/* +** Adjust the number of results from an expression list 'e' with 'nexps' +** expressions to 'nvars' values. +*/ +static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { + FuncState *fs = ls->fs; + int needed = nvars - nexps; /* extra values needed */ + luaK_checkstack(fs, needed); + if (hasmultret(e->k)) { /* last expression has multiple returns? */ + int extra = needed + 1; /* discount last expression itself */ + if (extra < 0) + extra = 0; + luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ + } + else { + if (e->k != VVOID) /* at least one expression? */ + luaK_exp2nextreg(fs, e); /* close last expression */ + if (needed > 0) /* missing values? */ + luaK_nil(fs, fs->freereg, needed); /* complete with nils */ + } + if (needed > 0) + luaK_reserveregs(fs, needed); /* registers for extra values */ + else /* adding 'needed' is actually a subtraction */ + fs->freereg = cast_byte(fs->freereg + needed); /* remove extra values */ +} + + +#define enterlevel(ls) luaE_incCstack(ls->L) + + +#define leavelevel(ls) ((ls)->L->nCcalls--) + + +/* +** Generates an error that a goto jumps into the scope of some +** variable declaration. +*/ +static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { + TString *tsname = getlocalvardesc(ls->fs, gt->nactvar)->vd.name; + const char *varname = (tsname != NULL) ? getstr(tsname) : "*"; + luaK_semerror(ls, + "<goto %s> at line %d jumps into the scope of '%s'", + getstr(gt->name), gt->line, varname); /* raise the error */ +} + + +/* +** Closes the goto at index 'g' to given 'label' and removes it +** from the list of pending gotos. +** If it jumps into the scope of some variable, raises an error. +** The goto needs a CLOSE if it jumps out of a block with upvalues, +** or out of the scope of some variable and the block has upvalues +** (signaled by parameter 'bup'). +*/ +static void closegoto (LexState *ls, int g, Labeldesc *label, int bup) { + int i; + FuncState *fs = ls->fs; + Labellist *gl = &ls->dyd->gt; /* list of gotos */ + Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */ + lua_assert(eqstr(gt->name, label->name)); + if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */ + jumpscopeerror(ls, gt); + if (gt->close || + (label->nactvar < gt->nactvar && bup)) { /* needs close? */ + lu_byte stklevel = reglevel(fs, label->nactvar); + /* move jump to CLOSE position */ + fs->f->code[gt->pc + 1] = fs->f->code[gt->pc]; + /* put CLOSE instruction at original position */ + fs->f->code[gt->pc] = CREATE_ABCk(OP_CLOSE, stklevel, 0, 0, 0); + gt->pc++; /* must point to jump instruction */ + } + luaK_patchlist(ls->fs, gt->pc, label->pc); /* goto jumps to label */ + for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */ + gl->arr[i] = gl->arr[i + 1]; + gl->n--; +} + + +/* +** Search for an active label with the given name, starting at +** index 'ilb' (so that it can search for all labels in current block +** or all labels in current function). +*/ +static Labeldesc *findlabel (LexState *ls, TString *name, int ilb) { + Dyndata *dyd = ls->dyd; + for (; ilb < dyd->label.n; ilb++) { + Labeldesc *lb = &dyd->label.arr[ilb]; + if (eqstr(lb->name, name)) /* correct label? */ + return lb; + } + return NULL; /* label not found */ +} + + +/* +** Adds a new label/goto in the corresponding list. +*/ +static int newlabelentry (LexState *ls, Labellist *l, TString *name, + int line, int pc) { + int n = l->n; + luaM_growvector(ls->L, l->arr, n, l->size, + Labeldesc, SHRT_MAX, "labels/gotos"); + l->arr[n].name = name; + l->arr[n].line = line; + l->arr[n].nactvar = ls->fs->nactvar; + l->arr[n].close = 0; + l->arr[n].pc = pc; + l->n = n + 1; + return n; +} + + +/* +** Create an entry for the goto and the code for it. As it is not known +** at this point whether the goto may need a CLOSE, the code has a jump +** followed by an CLOSE. (As the CLOSE comes after the jump, it is a +** dead instruction; it works as a placeholder.) When the goto is closed +** against a label, if it needs a CLOSE, the two instructions swap +** positions, so that the CLOSE comes before the jump. +*/ +static int newgotoentry (LexState *ls, TString *name, int line) { + FuncState *fs = ls->fs; + int pc = luaK_jump(fs); /* create jump */ + luaK_codeABC(fs, OP_CLOSE, 0, 1, 0); /* spaceholder, marked as dead */ + return newlabelentry(ls, &ls->dyd->gt, name, line, pc); +} + + +/* +** Create a new label with the given 'name' at the given 'line'. +** 'last' tells whether label is the last non-op statement in its +** block. Solves all pending gotos to this new label and adds +** a close instruction if necessary. +** Returns true iff it added a close instruction. +*/ +static void createlabel (LexState *ls, TString *name, int line, int last) { + FuncState *fs = ls->fs; + Labellist *ll = &ls->dyd->label; + int l = newlabelentry(ls, ll, name, line, luaK_getlabel(fs)); + if (last) { /* label is last no-op statement in the block? */ + /* assume that locals are already out of scope */ + ll->arr[l].nactvar = fs->bl->nactvar; + } +} + + +/* +** Traverse the pending gotos of the finishing block checking whether +** each match some label of that block. Those that do not match are +** "exported" to the outer block, to be solved there. In particular, +** its 'nactvar' is updated with the level of the inner block, +** as the variables of the inner block are now out of scope. +*/ +static void solvegotos (FuncState *fs, BlockCnt *bl) { + LexState *ls = fs->ls; + Labellist *gl = &ls->dyd->gt; + int outlevel = reglevel(fs, bl->nactvar); /* level outside the block */ + int igt = bl->firstgoto; /* first goto in the finishing block */ + while (igt < gl->n) { /* for each pending goto */ + Labeldesc *gt = &gl->arr[igt]; + /* search for a matching label in the current block */ + Labeldesc *lb = findlabel(ls, gt->name, bl->firstlabel); + if (lb != NULL) /* found a match? */ + closegoto(ls, igt, lb, bl->upval); /* close and remove goto */ + else { /* adjust 'goto' for outer block */ + /* block has variables to be closed and goto escapes the scope of + some variable? */ + if (bl->upval && reglevel(fs, gt->nactvar) > outlevel) + gt->close = 1; /* jump may need a close */ + gt->nactvar = bl->nactvar; /* correct level for outer block */ + igt++; /* go to next goto */ + } + } + ls->dyd->label.n = bl->firstlabel; /* remove local labels */ +} + + +static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { + bl->isloop = isloop; + bl->nactvar = fs->nactvar; + bl->firstlabel = fs->ls->dyd->label.n; + bl->firstgoto = fs->ls->dyd->gt.n; + bl->upval = 0; + /* inherit 'insidetbc' from enclosing block */ + bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc); + bl->previous = fs->bl; /* link block in function's block list */ + fs->bl = bl; + lua_assert(fs->freereg == luaY_nvarstack(fs)); +} + + +/* +** generates an error for an undefined 'goto'. +*/ +static l_noret undefgoto (LexState *ls, Labeldesc *gt) { + /* breaks are checked when created, cannot be undefined */ + lua_assert(!eqstr(gt->name, ls->brkn)); + luaK_semerror(ls, "no visible label '%s' for <goto> at line %d", + getstr(gt->name), gt->line); +} + + +static void leaveblock (FuncState *fs) { + BlockCnt *bl = fs->bl; + LexState *ls = fs->ls; + lu_byte stklevel = reglevel(fs, bl->nactvar); /* level outside block */ + if (bl->previous && bl->upval) /* need a 'close'? */ + luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0); + fs->freereg = stklevel; /* free registers */ + removevars(fs, bl->nactvar); /* remove block locals */ + lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */ + if (bl->isloop == 2) /* has to fix pending breaks? */ + createlabel(ls, ls->brkn, 0, 0); + solvegotos(fs, bl); + if (bl->previous == NULL) { /* was it the last block? */ + if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */ + undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ + } + fs->bl = bl->previous; /* current block now is previous one */ +} + + +/* +** adds a new prototype into list of prototypes +*/ +static Proto *addprototype (LexState *ls) { + Proto *clp; + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Proto *f = fs->f; /* prototype of current function */ + if (fs->np >= f->sizep) { + int oldsize = f->sizep; + luaM_growvector(L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "functions"); + while (oldsize < f->sizep) + f->p[oldsize++] = NULL; + } + f->p[fs->np++] = clp = luaF_newproto(L); + luaC_objbarrier(L, f, clp); + return clp; +} + + +/* +** codes instruction to create new closure in parent function. +** The OP_CLOSURE instruction uses the last available register, +** so that, if it invokes the GC, the GC knows which registers +** are in use at that time. + +*/ +static void codeclosure (LexState *ls, expdesc *v) { + FuncState *fs = ls->fs->prev; + init_exp(v, VRELOC, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1)); + luaK_exp2nextreg(fs, v); /* fix it at the last register */ +} + + +static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) { + lua_State *L = ls->L; + Proto *f = fs->f; + fs->prev = ls->fs; /* linked list of funcstates */ + fs->ls = ls; + ls->fs = fs; + fs->pc = 0; + fs->previousline = f->linedefined; + fs->iwthabs = 0; + fs->lasttarget = 0; + fs->freereg = 0; + fs->nk = 0; + fs->nabslineinfo = 0; + fs->np = 0; + fs->nups = 0; + fs->ndebugvars = 0; + fs->nactvar = 0; + fs->needclose = 0; + fs->firstlocal = ls->dyd->actvar.n; + fs->firstlabel = ls->dyd->label.n; + fs->bl = NULL; + f->source = ls->source; + luaC_objbarrier(L, f, f->source); + f->maxstacksize = 2; /* registers 0/1 are always valid */ + fs->kcache = luaH_new(L); /* create table for function */ + sethvalue2s(L, L->top.p, fs->kcache); /* anchor it */ + luaD_inctop(L); + enterblock(fs, bl, 0); +} + + +static void close_func (LexState *ls) { + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Proto *f = fs->f; + luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */ + leaveblock(fs); + lua_assert(fs->bl == NULL); + luaK_finish(fs); + luaM_shrinkvector(L, f->code, f->sizecode, fs->pc, Instruction); + luaM_shrinkvector(L, f->lineinfo, f->sizelineinfo, fs->pc, ls_byte); + luaM_shrinkvector(L, f->abslineinfo, f->sizeabslineinfo, + fs->nabslineinfo, AbsLineInfo); + luaM_shrinkvector(L, f->k, f->sizek, fs->nk, TValue); + luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *); + luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar); + luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc); + ls->fs = fs->prev; + L->top.p--; /* pop kcache table */ + luaC_checkGC(L); +} + + +/* +** {====================================================================== +** GRAMMAR RULES +** ======================================================================= +*/ + + +/* +** check whether current token is in the follow set of a block. +** 'until' closes syntactical blocks, but do not close scope, +** so it is handled in separate. +*/ +static int block_follow (LexState *ls, int withuntil) { + switch (ls->t.token) { + case TK_ELSE: case TK_ELSEIF: + case TK_END: case TK_EOS: + return 1; + case TK_UNTIL: return withuntil; + default: return 0; + } +} + + +static void statlist (LexState *ls) { + /* statlist -> { stat [';'] } */ + while (!block_follow(ls, 1)) { + if (ls->t.token == TK_RETURN) { + statement(ls); + return; /* 'return' must be last statement */ + } + statement(ls); + } +} + + +static void fieldsel (LexState *ls, expdesc *v) { + /* fieldsel -> ['.' | ':'] NAME */ + FuncState *fs = ls->fs; + expdesc key; + luaK_exp2anyregup(fs, v); + luaX_next(ls); /* skip the dot or colon */ + codename(ls, &key); + luaK_indexed(fs, v, &key); +} + + +static void yindex (LexState *ls, expdesc *v) { + /* index -> '[' expr ']' */ + luaX_next(ls); /* skip the '[' */ + expr(ls, v); + luaK_exp2val(ls->fs, v); + checknext(ls, ']'); +} + + +/* +** {====================================================================== +** Rules for Constructors +** ======================================================================= +*/ + +typedef struct ConsControl { + expdesc v; /* last list item read */ + expdesc *t; /* table descriptor */ + int nh; /* total number of 'record' elements */ + int na; /* number of array elements already stored */ + int tostore; /* number of array elements pending to be stored */ + int maxtostore; /* maximum number of pending elements */ +} ConsControl; + + +/* +** Maximum number of elements in a constructor, to control the following: +** * counter overflows; +** * overflows in 'extra' for OP_NEWTABLE and OP_SETLIST; +** * overflows when adding multiple returns in OP_SETLIST. +*/ +#define MAX_CNST (INT_MAX/2) +#if MAX_CNST/(MAXARG_vC + 1) > MAXARG_Ax +#undef MAX_CNST +#define MAX_CNST (MAXARG_Ax * (MAXARG_vC + 1)) +#endif + + +static void recfield (LexState *ls, ConsControl *cc) { + /* recfield -> (NAME | '['exp']') = exp */ + FuncState *fs = ls->fs; + lu_byte reg = ls->fs->freereg; + expdesc tab, key, val; + if (ls->t.token == TK_NAME) + codename(ls, &key); + else /* ls->t.token == '[' */ + yindex(ls, &key); + cc->nh++; + checknext(ls, '='); + tab = *cc->t; + luaK_indexed(fs, &tab, &key); + expr(ls, &val); + luaK_storevar(fs, &tab, &val); + fs->freereg = reg; /* free registers */ +} + + +static void closelistfield (FuncState *fs, ConsControl *cc) { + lua_assert(cc->tostore > 0); + luaK_exp2nextreg(fs, &cc->v); + cc->v.k = VVOID; + if (cc->tostore >= cc->maxtostore) { + luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */ + cc->na += cc->tostore; + cc->tostore = 0; /* no more items pending */ + } +} + + +static void lastlistfield (FuncState *fs, ConsControl *cc) { + if (cc->tostore == 0) return; + if (hasmultret(cc->v.k)) { + luaK_setmultret(fs, &cc->v); + luaK_setlist(fs, cc->t->u.info, cc->na, LUA_MULTRET); + cc->na--; /* do not count last expression (unknown number of elements) */ + } + else { + if (cc->v.k != VVOID) + luaK_exp2nextreg(fs, &cc->v); + luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); + } + cc->na += cc->tostore; +} + + +static void listfield (LexState *ls, ConsControl *cc) { + /* listfield -> exp */ + expr(ls, &cc->v); + cc->tostore++; +} + + +static void field (LexState *ls, ConsControl *cc) { + /* field -> listfield | recfield */ + switch(ls->t.token) { + case TK_NAME: { /* may be 'listfield' or 'recfield' */ + if (luaX_lookahead(ls) != '=') /* expression? */ + listfield(ls, cc); + else + recfield(ls, cc); + break; + } + case '[': { + recfield(ls, cc); + break; + } + default: { + listfield(ls, cc); + break; + } + } +} + + +/* +** Compute a limit for how many registers a constructor can use before +** emitting a 'SETLIST' instruction, based on how many registers are +** available. +*/ +static int maxtostore (FuncState *fs) { + int numfreeregs = MAX_FSTACK - fs->freereg; + if (numfreeregs >= 160) /* "lots" of registers? */ + return numfreeregs / 5; /* use up to 1/5 of them */ + else if (numfreeregs >= 80) /* still "enough" registers? */ + return 10; /* one 'SETLIST' instruction for each 10 values */ + else /* save registers for potential more nesting */ + return 1; +} + + +static void constructor (LexState *ls, expdesc *t) { + /* constructor -> '{' [ field { sep field } [sep] ] '}' + sep -> ',' | ';' */ + FuncState *fs = ls->fs; + int line = ls->linenumber; + int pc = luaK_codevABCk(fs, OP_NEWTABLE, 0, 0, 0, 0); + ConsControl cc; + luaK_code(fs, 0); /* space for extra arg. */ + cc.na = cc.nh = cc.tostore = 0; + cc.t = t; + init_exp(t, VNONRELOC, fs->freereg); /* table will be at stack top */ + luaK_reserveregs(fs, 1); + init_exp(&cc.v, VVOID, 0); /* no value (yet) */ + checknext(ls, '{' /*}*/); + cc.maxtostore = maxtostore(fs); + do { + if (ls->t.token == /*{*/ '}') break; + if (cc.v.k != VVOID) /* is there a previous list item? */ + closelistfield(fs, &cc); /* close it */ + field(ls, &cc); + luaY_checklimit(fs, cc.tostore + cc.na + cc.nh, MAX_CNST, + "items in a constructor"); + } while (testnext(ls, ',') || testnext(ls, ';')); + check_match(ls, /*{*/ '}', '{' /*}*/, line); + lastlistfield(fs, &cc); + luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh); +} + +/* }====================================================================== */ + + +static void setvararg (FuncState *fs) { + fs->f->flag |= PF_VAHID; /* by default, use hidden vararg arguments */ + luaK_codeABC(fs, OP_VARARGPREP, 0, 0, 0); +} + + +static void parlist (LexState *ls) { + /* parlist -> [ {NAME ','} (NAME | '...') ] */ + FuncState *fs = ls->fs; + Proto *f = fs->f; + int nparams = 0; + int varargk = 0; + if (ls->t.token != ')') { /* is 'parlist' not empty? */ + do { + switch (ls->t.token) { + case TK_NAME: { + new_localvar(ls, str_checkname(ls)); + nparams++; + break; + } + case TK_DOTS: { + varargk = 1; + luaX_next(ls); /* skip '...' */ + if (ls->t.token == TK_NAME) + new_varkind(ls, str_checkname(ls), RDKVAVAR); + else + new_localvarliteral(ls, "(vararg table)"); + break; + } + default: luaX_syntaxerror(ls, "<name> or '...' expected"); + } + } while (!varargk && testnext(ls, ',')); + } + adjustlocalvars(ls, nparams); + f->numparams = cast_byte(fs->nactvar); + if (varargk) { + setvararg(fs); /* declared vararg */ + adjustlocalvars(ls, 1); /* vararg parameter */ + } + /* reserve registers for parameters (plus vararg parameter, if present) */ + luaK_reserveregs(fs, fs->nactvar); +} + + +static void body (LexState *ls, expdesc *e, int ismethod, int line) { + /* body -> '(' parlist ')' block END */ + FuncState new_fs; + BlockCnt bl; + new_fs.f = addprototype(ls); + new_fs.f->linedefined = line; + open_func(ls, &new_fs, &bl); + checknext(ls, '('); + if (ismethod) { + new_localvarliteral(ls, "self"); /* create 'self' parameter */ + adjustlocalvars(ls, 1); + } + parlist(ls); + checknext(ls, ')'); + statlist(ls); + new_fs.f->lastlinedefined = ls->linenumber; + check_match(ls, TK_END, TK_FUNCTION, line); + codeclosure(ls, e); + close_func(ls); +} + + +static int explist (LexState *ls, expdesc *v) { + /* explist -> expr { ',' expr } */ + int n = 1; /* at least one expression */ + expr(ls, v); + while (testnext(ls, ',')) { + luaK_exp2nextreg(ls->fs, v); + expr(ls, v); + n++; + } + return n; +} + + +static void funcargs (LexState *ls, expdesc *f) { + FuncState *fs = ls->fs; + expdesc args; + int base, nparams; + int line = ls->linenumber; + switch (ls->t.token) { + case '(': { /* funcargs -> '(' [ explist ] ')' */ + luaX_next(ls); + if (ls->t.token == ')') /* arg list is empty? */ + args.k = VVOID; + else { + explist(ls, &args); + if (hasmultret(args.k)) + luaK_setmultret(fs, &args); + } + check_match(ls, ')', '(', line); + break; + } + case '{' /*}*/: { /* funcargs -> constructor */ + constructor(ls, &args); + break; + } + case TK_STRING: { /* funcargs -> STRING */ + codestring(&args, ls->t.seminfo.ts); + luaX_next(ls); /* must use 'seminfo' before 'next' */ + break; + } + default: { + luaX_syntaxerror(ls, "function arguments expected"); + } + } + lua_assert(f->k == VNONRELOC); + base = f->u.info; /* base register for call */ + if (hasmultret(args.k)) + nparams = LUA_MULTRET; /* open call */ + else { + if (args.k != VVOID) + luaK_exp2nextreg(fs, &args); /* close last argument */ + nparams = fs->freereg - (base+1); + } + init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); + luaK_fixline(fs, line); + /* call removes function and arguments and leaves one result (unless + changed later) */ + fs->freereg = cast_byte(base + 1); +} + + + + +/* +** {====================================================================== +** Expression parsing +** ======================================================================= +*/ + + +static void primaryexp (LexState *ls, expdesc *v) { + /* primaryexp -> NAME | '(' expr ')' */ + switch (ls->t.token) { + case '(': { + int line = ls->linenumber; + luaX_next(ls); + expr(ls, v); + check_match(ls, ')', '(', line); + luaK_dischargevars(ls->fs, v); + return; + } + case TK_NAME: { + singlevar(ls, v); + return; + } + default: { + luaX_syntaxerror(ls, "unexpected symbol"); + } + } +} + + +static void suffixedexp (LexState *ls, expdesc *v) { + /* suffixedexp -> + primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */ + FuncState *fs = ls->fs; + primaryexp(ls, v); + for (;;) { + switch (ls->t.token) { + case '.': { /* fieldsel */ + fieldsel(ls, v); + break; + } + case '[': { /* '[' exp ']' */ + expdesc key; + luaK_exp2anyregup(fs, v); + yindex(ls, &key); + luaK_indexed(fs, v, &key); + break; + } + case ':': { /* ':' NAME funcargs */ + expdesc key; + luaX_next(ls); + codename(ls, &key); + luaK_self(fs, v, &key); + funcargs(ls, v); + break; + } + case '(': case TK_STRING: case '{' /*}*/: { /* funcargs */ + luaK_exp2nextreg(fs, v); + funcargs(ls, v); + break; + } + default: return; + } + } +} + + +static void simpleexp (LexState *ls, expdesc *v) { + /* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... | + constructor | FUNCTION body | suffixedexp */ + switch (ls->t.token) { + case TK_FLT: { + init_exp(v, VKFLT, 0); + v->u.nval = ls->t.seminfo.r; + break; + } + case TK_INT: { + init_exp(v, VKINT, 0); + v->u.ival = ls->t.seminfo.i; + break; + } + case TK_STRING: { + codestring(v, ls->t.seminfo.ts); + break; + } + case TK_NIL: { + init_exp(v, VNIL, 0); + break; + } + case TK_TRUE: { + init_exp(v, VTRUE, 0); + break; + } + case TK_FALSE: { + init_exp(v, VFALSE, 0); + break; + } + case TK_DOTS: { /* vararg */ + FuncState *fs = ls->fs; + check_condition(ls, isvararg(fs->f), + "cannot use '...' outside a vararg function"); + init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, fs->f->numparams, 1)); + break; + } + case '{' /*}*/: { /* constructor */ + constructor(ls, v); + return; + } + case TK_FUNCTION: { + luaX_next(ls); + body(ls, v, 0, ls->linenumber); + return; + } + default: { + suffixedexp(ls, v); + return; + } + } + luaX_next(ls); +} + + +static UnOpr getunopr (int op) { + switch (op) { + case TK_NOT: return OPR_NOT; + case '-': return OPR_MINUS; + case '~': return OPR_BNOT; + case '#': return OPR_LEN; + default: return OPR_NOUNOPR; + } +} + + +static BinOpr getbinopr (int op) { + switch (op) { + case '+': return OPR_ADD; + case '-': return OPR_SUB; + case '*': return OPR_MUL; + case '%': return OPR_MOD; + case '^': return OPR_POW; + case '/': return OPR_DIV; + case TK_IDIV: return OPR_IDIV; + case '&': return OPR_BAND; + case '|': return OPR_BOR; + case '~': return OPR_BXOR; + case TK_SHL: return OPR_SHL; + case TK_SHR: return OPR_SHR; + case TK_CONCAT: return OPR_CONCAT; + case TK_NE: return OPR_NE; + case TK_EQ: return OPR_EQ; + case '<': return OPR_LT; + case TK_LE: return OPR_LE; + case '>': return OPR_GT; + case TK_GE: return OPR_GE; + case TK_AND: return OPR_AND; + case TK_OR: return OPR_OR; + default: return OPR_NOBINOPR; + } +} + + +/* +** Priority table for binary operators. +*/ +static const struct { + lu_byte left; /* left priority for each binary operator */ + lu_byte right; /* right priority */ +} priority[] = { /* ORDER OPR */ + {10, 10}, {10, 10}, /* '+' '-' */ + {11, 11}, {11, 11}, /* '*' '%' */ + {14, 13}, /* '^' (right associative) */ + {11, 11}, {11, 11}, /* '/' '//' */ + {6, 6}, {4, 4}, {5, 5}, /* '&' '|' '~' */ + {7, 7}, {7, 7}, /* '<<' '>>' */ + {9, 8}, /* '..' (right associative) */ + {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */ + {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */ + {2, 2}, {1, 1} /* and, or */ +}; + +#define UNARY_PRIORITY 12 /* priority for unary operators */ + + +/* +** subexpr -> (simpleexp | unop subexpr) { binop subexpr } +** where 'binop' is any binary operator with a priority higher than 'limit' +*/ +static BinOpr subexpr (LexState *ls, expdesc *v, int limit) { + BinOpr op; + UnOpr uop; + enterlevel(ls); + uop = getunopr(ls->t.token); + if (uop != OPR_NOUNOPR) { /* prefix (unary) operator? */ + int line = ls->linenumber; + luaX_next(ls); /* skip operator */ + subexpr(ls, v, UNARY_PRIORITY); + luaK_prefix(ls->fs, uop, v, line); + } + else simpleexp(ls, v); + /* expand while operators have priorities higher than 'limit' */ + op = getbinopr(ls->t.token); + while (op != OPR_NOBINOPR && priority[op].left > limit) { + expdesc v2; + BinOpr nextop; + int line = ls->linenumber; + luaX_next(ls); /* skip operator */ + luaK_infix(ls->fs, op, v); + /* read sub-expression with higher priority */ + nextop = subexpr(ls, &v2, priority[op].right); + luaK_posfix(ls->fs, op, v, &v2, line); + op = nextop; + } + leavelevel(ls); + return op; /* return first untreated operator */ +} + + +static void expr (LexState *ls, expdesc *v) { + subexpr(ls, v, 0); +} + +/* }==================================================================== */ + + + +/* +** {====================================================================== +** Rules for Statements +** ======================================================================= +*/ + + +static void block (LexState *ls) { + /* block -> statlist */ + FuncState *fs = ls->fs; + BlockCnt bl; + enterblock(fs, &bl, 0); + statlist(ls); + leaveblock(fs); +} + + +/* +** structure to chain all variables in the left-hand side of an +** assignment +*/ +struct LHS_assign { + struct LHS_assign *prev; + expdesc v; /* variable (global, local, upvalue, or indexed) */ +}; + + +/* +** check whether, in an assignment to an upvalue/local variable, the +** upvalue/local variable is begin used in a previous assignment to a +** table. If so, save original upvalue/local value in a safe place and +** use this safe copy in the previous assignment. +*/ +static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { + FuncState *fs = ls->fs; + lu_byte extra = fs->freereg; /* eventual position to save local variable */ + int conflict = 0; + for (; lh; lh = lh->prev) { /* check all previous assignments */ + if (vkisindexed(lh->v.k)) { /* assignment to table field? */ + if (lh->v.k == VINDEXUP) { /* is table an upvalue? */ + if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) { + conflict = 1; /* table is the upvalue being assigned now */ + lh->v.k = VINDEXSTR; + lh->v.u.ind.t = extra; /* assignment will use safe copy */ + } + } + else { /* table is a register */ + if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.ridx) { + conflict = 1; /* table is the local being assigned now */ + lh->v.u.ind.t = extra; /* assignment will use safe copy */ + } + /* is index the local being assigned? */ + if (lh->v.k == VINDEXED && v->k == VLOCAL && + lh->v.u.ind.idx == v->u.var.ridx) { + conflict = 1; + lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */ + } + } + } + } + if (conflict) { + /* copy upvalue/local value to a temporary (in position 'extra') */ + if (v->k == VLOCAL) + luaK_codeABC(fs, OP_MOVE, extra, v->u.var.ridx, 0); + else + luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0); + luaK_reserveregs(fs, 1); + } +} + + +/* Create code to store the "top" register in 'var' */ +static void storevartop (FuncState *fs, expdesc *var) { + expdesc e; + init_exp(&e, VNONRELOC, fs->freereg - 1); + luaK_storevar(fs, var, &e); /* will also free the top register */ +} + + +/* +** Parse and compile a multiple assignment. The first "variable" +** (a 'suffixedexp') was already read by the caller. +** +** assignment -> suffixedexp restassign +** restassign -> ',' suffixedexp restassign | '=' explist +*/ +static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) { + expdesc e; + check_condition(ls, vkisvar(lh->v.k), "syntax error"); + check_readonly(ls, &lh->v); + if (testnext(ls, ',')) { /* restassign -> ',' suffixedexp restassign */ + struct LHS_assign nv; + nv.prev = lh; + suffixedexp(ls, &nv.v); + if (!vkisindexed(nv.v.k)) + check_conflict(ls, lh, &nv.v); + enterlevel(ls); /* control recursion depth */ + restassign(ls, &nv, nvars+1); + leavelevel(ls); + } + else { /* restassign -> '=' explist */ + int nexps; + checknext(ls, '='); + nexps = explist(ls, &e); + if (nexps != nvars) + adjust_assign(ls, nvars, nexps, &e); + else { + luaK_setoneret(ls->fs, &e); /* close last expression */ + luaK_storevar(ls->fs, &lh->v, &e); + return; /* avoid default */ + } + } + storevartop(ls->fs, &lh->v); /* default assignment */ +} + + +static int cond (LexState *ls) { + /* cond -> exp */ + expdesc v; + expr(ls, &v); /* read condition */ + if (v.k == VNIL) v.k = VFALSE; /* 'falses' are all equal here */ + luaK_goiftrue(ls->fs, &v); + return v.f; +} + + +static void gotostat (LexState *ls, int line) { + TString *name = str_checkname(ls); /* label's name */ + newgotoentry(ls, name, line); +} + + +/* +** Break statement. Semantically equivalent to "goto break". +*/ +static void breakstat (LexState *ls, int line) { + BlockCnt *bl; /* to look for an enclosing loop */ + for (bl = ls->fs->bl; bl != NULL; bl = bl->previous) { + if (bl->isloop) /* found one? */ + goto ok; + } + luaX_syntaxerror(ls, "break outside loop"); + ok: + bl->isloop = 2; /* signal that block has pending breaks */ + luaX_next(ls); /* skip break */ + newgotoentry(ls, ls->brkn, line); +} + + +/* +** Check whether there is already a label with the given 'name' at +** current function. +*/ +static void checkrepeated (LexState *ls, TString *name) { + Labeldesc *lb = findlabel(ls, name, ls->fs->firstlabel); + if (l_unlikely(lb != NULL)) /* already defined? */ + luaK_semerror(ls, "label '%s' already defined on line %d", + getstr(name), lb->line); /* error */ +} + + +static void labelstat (LexState *ls, TString *name, int line) { + /* label -> '::' NAME '::' */ + checknext(ls, TK_DBCOLON); /* skip double colon */ + while (ls->t.token == ';' || ls->t.token == TK_DBCOLON) + statement(ls); /* skip other no-op statements */ + checkrepeated(ls, name); /* check for repeated labels */ + createlabel(ls, name, line, block_follow(ls, 0)); +} + + +static void whilestat (LexState *ls, int line) { + /* whilestat -> WHILE cond DO block END */ + FuncState *fs = ls->fs; + int whileinit; + int condexit; + BlockCnt bl; + luaX_next(ls); /* skip WHILE */ + whileinit = luaK_getlabel(fs); + condexit = cond(ls); + enterblock(fs, &bl, 1); + checknext(ls, TK_DO); + block(ls); + luaK_jumpto(fs, whileinit); + check_match(ls, TK_END, TK_WHILE, line); + leaveblock(fs); + luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ +} + + +static void repeatstat (LexState *ls, int line) { + /* repeatstat -> REPEAT block UNTIL cond */ + int condexit; + FuncState *fs = ls->fs; + int repeat_init = luaK_getlabel(fs); + BlockCnt bl1, bl2; + enterblock(fs, &bl1, 1); /* loop block */ + enterblock(fs, &bl2, 0); /* scope block */ + luaX_next(ls); /* skip REPEAT */ + statlist(ls); + check_match(ls, TK_UNTIL, TK_REPEAT, line); + condexit = cond(ls); /* read condition (inside scope block) */ + leaveblock(fs); /* finish scope */ + if (bl2.upval) { /* upvalues? */ + int exit = luaK_jump(fs); /* normal exit must jump over fix */ + luaK_patchtohere(fs, condexit); /* repetition must close upvalues */ + luaK_codeABC(fs, OP_CLOSE, reglevel(fs, bl2.nactvar), 0, 0); + condexit = luaK_jump(fs); /* repeat after closing upvalues */ + luaK_patchtohere(fs, exit); /* normal exit comes to here */ + } + luaK_patchlist(fs, condexit, repeat_init); /* close the loop */ + leaveblock(fs); /* finish loop */ +} + + +/* +** Read an expression and generate code to put its results in next +** stack slot. +** +*/ +static void exp1 (LexState *ls) { + expdesc e; + expr(ls, &e); + luaK_exp2nextreg(ls->fs, &e); + lua_assert(e.k == VNONRELOC); +} + + +/* +** Fix for instruction at position 'pc' to jump to 'dest'. +** (Jump addresses are relative in Lua). 'back' true means +** a back jump. +*/ +static void fixforjump (FuncState *fs, int pc, int dest, int back) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest - (pc + 1); + if (back) + offset = -offset; + if (l_unlikely(offset > MAXARG_Bx)) + luaX_syntaxerror(fs->ls, "control structure too long"); + SETARG_Bx(*jmp, offset); +} + + +/* +** Generate code for a 'for' loop. +*/ +static void forbody (LexState *ls, int base, int line, int nvars, int isgen) { + /* forbody -> DO block */ + static const OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP}; + static const OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP}; + BlockCnt bl; + FuncState *fs = ls->fs; + int prep, endfor; + checknext(ls, TK_DO); + prep = luaK_codeABx(fs, forprep[isgen], base, 0); + fs->freereg--; /* both 'forprep' remove one register from the stack */ + enterblock(fs, &bl, 0); /* scope for declared variables */ + adjustlocalvars(ls, nvars); + luaK_reserveregs(fs, nvars); + block(ls); + leaveblock(fs); /* end of scope for declared variables */ + fixforjump(fs, prep, luaK_getlabel(fs), 0); + if (isgen) { /* generic for? */ + luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars); + luaK_fixline(fs, line); + } + endfor = luaK_codeABx(fs, forloop[isgen], base, 0); + fixforjump(fs, endfor, prep + 1, 1); + luaK_fixline(fs, line); +} + + +static void fornum (LexState *ls, TString *varname, int line) { + /* fornum -> NAME = exp,exp[,exp] forbody */ + FuncState *fs = ls->fs; + int base = fs->freereg; + new_localvarliteral(ls, "(for state)"); + new_localvarliteral(ls, "(for state)"); + new_varkind(ls, varname, RDKCONST); /* control variable */ + checknext(ls, '='); + exp1(ls); /* initial value */ + checknext(ls, ','); + exp1(ls); /* limit */ + if (testnext(ls, ',')) + exp1(ls); /* optional step */ + else { /* default step = 1 */ + luaK_int(fs, fs->freereg, 1); + luaK_reserveregs(fs, 1); + } + adjustlocalvars(ls, 2); /* start scope for internal variables */ + forbody(ls, base, line, 1, 0); +} + + +static void forlist (LexState *ls, TString *indexname) { + /* forlist -> NAME {,NAME} IN explist forbody */ + FuncState *fs = ls->fs; + expdesc e; + int nvars = 4; /* function, state, closing, control */ + int line; + int base = fs->freereg; + /* create internal variables */ + new_localvarliteral(ls, "(for state)"); /* iterator function */ + new_localvarliteral(ls, "(for state)"); /* state */ + new_localvarliteral(ls, "(for state)"); /* closing var. (after swap) */ + new_varkind(ls, indexname, RDKCONST); /* control variable */ + /* other declared variables */ + while (testnext(ls, ',')) { + new_localvar(ls, str_checkname(ls)); + nvars++; + } + checknext(ls, TK_IN); + line = ls->linenumber; + adjust_assign(ls, 4, explist(ls, &e), &e); + adjustlocalvars(ls, 3); /* start scope for internal variables */ + marktobeclosed(fs); /* last internal var. must be closed */ + luaK_checkstack(fs, 2); /* extra space to call iterator */ + forbody(ls, base, line, nvars - 3, 1); +} + + +static void forstat (LexState *ls, int line) { + /* forstat -> FOR (fornum | forlist) END */ + FuncState *fs = ls->fs; + TString *varname; + BlockCnt bl; + enterblock(fs, &bl, 1); /* scope for loop and control variables */ + luaX_next(ls); /* skip 'for' */ + varname = str_checkname(ls); /* first variable name */ + switch (ls->t.token) { + case '=': fornum(ls, varname, line); break; + case ',': case TK_IN: forlist(ls, varname); break; + default: luaX_syntaxerror(ls, "'=' or 'in' expected"); + } + check_match(ls, TK_END, TK_FOR, line); + leaveblock(fs); /* loop scope ('break' jumps to this point) */ +} + + +static void test_then_block (LexState *ls, int *escapelist) { + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + FuncState *fs = ls->fs; + int condtrue; + luaX_next(ls); /* skip IF or ELSEIF */ + condtrue = cond(ls); /* read condition */ + checknext(ls, TK_THEN); + block(ls); /* 'then' part */ + if (ls->t.token == TK_ELSE || + ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */ + luaK_concat(fs, escapelist, luaK_jump(fs)); /* must jump over it */ + luaK_patchtohere(fs, condtrue); +} + + +static void ifstat (LexState *ls, int line) { + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ + FuncState *fs = ls->fs; + int escapelist = NO_JUMP; /* exit list for finished parts */ + test_then_block(ls, &escapelist); /* IF cond THEN block */ + while (ls->t.token == TK_ELSEIF) + test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */ + if (testnext(ls, TK_ELSE)) + block(ls); /* 'else' part */ + check_match(ls, TK_END, TK_IF, line); + luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */ +} + + +static void localfunc (LexState *ls) { + expdesc b; + FuncState *fs = ls->fs; + int fvar = fs->nactvar; /* function's variable index */ + new_localvar(ls, str_checkname(ls)); /* new local variable */ + adjustlocalvars(ls, 1); /* enter its scope */ + body(ls, &b, 0, ls->linenumber); /* function created in next register */ + /* debug information will only see the variable after this point! */ + localdebuginfo(fs, fvar)->startpc = fs->pc; +} + + +static lu_byte getvarattribute (LexState *ls, lu_byte df) { + /* attrib -> ['<' NAME '>'] */ + if (testnext(ls, '<')) { + TString *ts = str_checkname(ls); + const char *attr = getstr(ts); + checknext(ls, '>'); + if (strcmp(attr, "const") == 0) + return RDKCONST; /* read-only variable */ + else if (strcmp(attr, "close") == 0) + return RDKTOCLOSE; /* to-be-closed variable */ + else + luaK_semerror(ls, "unknown attribute '%s'", attr); + } + return df; /* return default value */ +} + + +static void checktoclose (FuncState *fs, int level) { + if (level != -1) { /* is there a to-be-closed variable? */ + marktobeclosed(fs); + luaK_codeABC(fs, OP_TBC, reglevel(fs, level), 0, 0); + } +} + + +static void localstat (LexState *ls) { + /* stat -> LOCAL NAME attrib { ',' NAME attrib } ['=' explist] */ + FuncState *fs = ls->fs; + int toclose = -1; /* index of to-be-closed variable (if any) */ + Vardesc *var; /* last variable */ + int vidx; /* index of last variable */ + int nvars = 0; + int nexps; + expdesc e; + /* get prefixed attribute (if any); default is regular local variable */ + lu_byte defkind = getvarattribute(ls, VDKREG); + do { /* for each variable */ + TString *vname = str_checkname(ls); /* get its name */ + lu_byte kind = getvarattribute(ls, defkind); /* postfixed attribute */ + vidx = new_varkind(ls, vname, kind); /* predeclare it */ + if (kind == RDKTOCLOSE) { /* to-be-closed? */ + if (toclose != -1) /* one already present? */ + luaK_semerror(ls, "multiple to-be-closed variables in local list"); + toclose = fs->nactvar + nvars; + } + nvars++; + } while (testnext(ls, ',')); + if (testnext(ls, '=')) /* initialization? */ + nexps = explist(ls, &e); + else { + e.k = VVOID; + nexps = 0; + } + var = getlocalvardesc(fs, vidx); /* retrieve last variable */ + if (nvars == nexps && /* no adjustments? */ + var->vd.kind == RDKCONST && /* last variable is const? */ + luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */ + var->vd.kind = RDKCTC; /* variable is a compile-time constant */ + adjustlocalvars(ls, nvars - 1); /* exclude last variable */ + fs->nactvar++; /* but count it */ + } + else { + adjust_assign(ls, nvars, nexps, &e); + adjustlocalvars(ls, nvars); + } + checktoclose(fs, toclose); +} + + +static lu_byte getglobalattribute (LexState *ls, lu_byte df) { + lu_byte kind = getvarattribute(ls, df); + switch (kind) { + case RDKTOCLOSE: + luaK_semerror(ls, "global variables cannot be to-be-closed"); + return kind; /* to avoid warnings */ + case RDKCONST: + return GDKCONST; /* adjust kind for global variable */ + default: + return kind; + } +} + + +static void checkglobal (LexState *ls, TString *varname, int line) { + FuncState *fs = ls->fs; + expdesc var; + int k; + buildglobal(ls, varname, &var); /* create global variable in 'var' */ + k = var.u.ind.keystr; /* index of global name in 'k' */ + luaK_codecheckglobal(fs, &var, k, line); +} + + +/* +** Recursively traverse list of globals to be initalized. When +** going, generate table description for the global. In the end, +** after all indices have been generated, read list of initializing +** expressions. When returning, generate the assignment of the value on +** the stack to the corresponding table description. 'n' is the variable +** being handled, range [0, nvars - 1]. +*/ +static void initglobal (LexState *ls, int nvars, int firstidx, int n, + int line) { + if (n == nvars) { /* traversed all variables? */ + expdesc e; + int nexps = explist(ls, &e); /* read list of expressions */ + adjust_assign(ls, nvars, nexps, &e); + } + else { /* handle variable 'n' */ + FuncState *fs = ls->fs; + expdesc var; + TString *varname = getlocalvardesc(fs, firstidx + n)->vd.name; + buildglobal(ls, varname, &var); /* create global variable in 'var' */ + enterlevel(ls); /* control recursion depth */ + initglobal(ls, nvars, firstidx, n + 1, line); + leavelevel(ls); + checkglobal(ls, varname, line); + storevartop(fs, &var); + } +} + + +static void globalnames (LexState *ls, lu_byte defkind) { + FuncState *fs = ls->fs; + int nvars = 0; + int lastidx; /* index of last registered variable */ + do { /* for each name */ + TString *vname = str_checkname(ls); + lu_byte kind = getglobalattribute(ls, defkind); + lastidx = new_varkind(ls, vname, kind); + nvars++; + } while (testnext(ls, ',')); + if (testnext(ls, '=')) /* initialization? */ + initglobal(ls, nvars, lastidx - nvars + 1, 0, ls->linenumber); + fs->nactvar = cast_short(fs->nactvar + nvars); /* activate declaration */ +} + + +static void globalstat (LexState *ls) { + /* globalstat -> (GLOBAL) attrib '*' + globalstat -> (GLOBAL) attrib NAME attrib {',' NAME attrib} */ + FuncState *fs = ls->fs; + /* get prefixed attribute (if any); default is regular global variable */ + lu_byte defkind = getglobalattribute(ls, GDKREG); + if (!testnext(ls, '*')) + globalnames(ls, defkind); + else { + /* use NULL as name to represent '*' entries */ + new_varkind(ls, NULL, defkind); + fs->nactvar++; /* activate declaration */ + } +} + + +static void globalfunc (LexState *ls, int line) { + /* globalfunc -> (GLOBAL FUNCTION) NAME body */ + expdesc var, b; + FuncState *fs = ls->fs; + TString *fname = str_checkname(ls); + new_varkind(ls, fname, GDKREG); /* declare global variable */ + fs->nactvar++; /* enter its scope */ + buildglobal(ls, fname, &var); + body(ls, &b, 0, ls->linenumber); /* compile and return closure in 'b' */ + checkglobal(ls, fname, line); + luaK_storevar(fs, &var, &b); + luaK_fixline(fs, line); /* definition "happens" in the first line */ +} + + +static void globalstatfunc (LexState *ls, int line) { + /* stat -> GLOBAL globalfunc | GLOBAL globalstat */ + luaX_next(ls); /* skip 'global' */ + if (testnext(ls, TK_FUNCTION)) + globalfunc(ls, line); + else + globalstat(ls); +} + + +static int funcname (LexState *ls, expdesc *v) { + /* funcname -> NAME {fieldsel} [':' NAME] */ + int ismethod = 0; + singlevar(ls, v); + while (ls->t.token == '.') + fieldsel(ls, v); + if (ls->t.token == ':') { + ismethod = 1; + fieldsel(ls, v); + } + return ismethod; +} + + +static void funcstat (LexState *ls, int line) { + /* funcstat -> FUNCTION funcname body */ + int ismethod; + expdesc v, b; + luaX_next(ls); /* skip FUNCTION */ + ismethod = funcname(ls, &v); + check_readonly(ls, &v); + body(ls, &b, ismethod, line); + luaK_storevar(ls->fs, &v, &b); + luaK_fixline(ls->fs, line); /* definition "happens" in the first line */ +} + + +static void exprstat (LexState *ls) { + /* stat -> func | assignment */ + FuncState *fs = ls->fs; + struct LHS_assign v; + suffixedexp(ls, &v.v); + if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */ + v.prev = NULL; + restassign(ls, &v, 1); + } + else { /* stat -> func */ + Instruction *inst; + check_condition(ls, v.v.k == VCALL, "syntax error"); + inst = &getinstruction(fs, &v.v); + SETARG_C(*inst, 1); /* call statement uses no results */ + } +} + + +static void retstat (LexState *ls) { + /* stat -> RETURN [explist] [';'] */ + FuncState *fs = ls->fs; + expdesc e; + int nret; /* number of values being returned */ + int first = luaY_nvarstack(fs); /* first slot to be returned */ + if (block_follow(ls, 1) || ls->t.token == ';') + nret = 0; /* return no values */ + else { + nret = explist(ls, &e); /* optional return values */ + if (hasmultret(e.k)) { + luaK_setmultret(fs, &e); + if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */ + SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL); + lua_assert(GETARG_A(getinstruction(fs,&e)) == luaY_nvarstack(fs)); + } + nret = LUA_MULTRET; /* return all values */ + } + else { + if (nret == 1) /* only one single value? */ + first = luaK_exp2anyreg(fs, &e); /* can use original slot */ + else { /* values must go to the top of the stack */ + luaK_exp2nextreg(fs, &e); + lua_assert(nret == fs->freereg - first); + } + } + } + luaK_ret(fs, first, nret); + testnext(ls, ';'); /* skip optional semicolon */ +} + + +static void statement (LexState *ls) { + int line = ls->linenumber; /* may be needed for error messages */ + enterlevel(ls); + switch (ls->t.token) { + case ';': { /* stat -> ';' (empty statement) */ + luaX_next(ls); /* skip ';' */ + break; + } + case TK_IF: { /* stat -> ifstat */ + ifstat(ls, line); + break; + } + case TK_WHILE: { /* stat -> whilestat */ + whilestat(ls, line); + break; + } + case TK_DO: { /* stat -> DO block END */ + luaX_next(ls); /* skip DO */ + block(ls); + check_match(ls, TK_END, TK_DO, line); + break; + } + case TK_FOR: { /* stat -> forstat */ + forstat(ls, line); + break; + } + case TK_REPEAT: { /* stat -> repeatstat */ + repeatstat(ls, line); + break; + } + case TK_FUNCTION: { /* stat -> funcstat */ + funcstat(ls, line); + break; + } + case TK_LOCAL: { /* stat -> localstat */ + luaX_next(ls); /* skip LOCAL */ + if (testnext(ls, TK_FUNCTION)) /* local function? */ + localfunc(ls); + else + localstat(ls); + break; + } + case TK_GLOBAL: { /* stat -> globalstatfunc */ + globalstatfunc(ls, line); + break; + } + case TK_DBCOLON: { /* stat -> label */ + luaX_next(ls); /* skip double colon */ + labelstat(ls, str_checkname(ls), line); + break; + } + case TK_RETURN: { /* stat -> retstat */ + luaX_next(ls); /* skip RETURN */ + retstat(ls); + break; + } + case TK_BREAK: { /* stat -> breakstat */ + breakstat(ls, line); + break; + } + case TK_GOTO: { /* stat -> 'goto' NAME */ + luaX_next(ls); /* skip 'goto' */ + gotostat(ls, line); + break; + } +#if defined(LUA_COMPAT_GLOBAL) + case TK_NAME: { + /* compatibility code to parse global keyword when "global" + is not reserved */ + if (ls->t.seminfo.ts == ls->glbn) { /* current = "global"? */ + int lk = luaX_lookahead(ls); + if (lk == '<' || lk == TK_NAME || lk == '*' || lk == TK_FUNCTION) { + /* 'global <attrib>' or 'global name' or 'global *' or + 'global function' */ + globalstatfunc(ls, line); + break; + } + } /* else... */ + } +#endif + /* FALLTHROUGH */ + default: { /* stat -> func | assignment */ + exprstat(ls); + break; + } + } + lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && + ls->fs->freereg >= luaY_nvarstack(ls->fs)); + ls->fs->freereg = luaY_nvarstack(ls->fs); /* free registers */ + leavelevel(ls); +} + +/* }====================================================================== */ + +/* }====================================================================== */ + + +/* +** compiles the main function, which is a regular vararg function with an +** upvalue named LUA_ENV +*/ +static void mainfunc (LexState *ls, FuncState *fs) { + BlockCnt bl; + Upvaldesc *env; + open_func(ls, fs, &bl); + setvararg(fs); /* main function is always vararg */ + env = allocupvalue(fs); /* ...set environment upvalue */ + env->instack = 1; + env->idx = 0; + env->kind = VDKREG; + env->name = ls->envn; + luaC_objbarrier(ls->L, fs->f, env->name); + luaX_next(ls); /* read first token */ + statlist(ls); /* parse main body */ + check(ls, TK_EOS); + close_func(ls); +} + + +LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + Dyndata *dyd, const char *name, int firstchar) { + LexState lexstate; + FuncState funcstate; + LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */ + setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */ + luaD_inctop(L); + lexstate.h = luaH_new(L); /* create table for scanner */ + sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */ + luaD_inctop(L); + funcstate.f = cl->p = luaF_newproto(L); + luaC_objbarrier(L, cl, cl->p); + funcstate.f->source = luaS_new(L, name); /* create and anchor TString */ + luaC_objbarrier(L, funcstate.f, funcstate.f->source); + lexstate.buff = buff; + lexstate.dyd = dyd; + dyd->actvar.n = dyd->gt.n = dyd->label.n = 0; + luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar); + mainfunc(&lexstate, &funcstate); + lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs); + /* all scopes should be correctly finished */ + lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0); + L->top.p--; /* remove scanner's table */ + return cl; /* closure is on the stack, too */ +} + + +``` + +`Lua/lparser.h`: + +```h +/* +** $Id: lparser.h $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + +#ifndef lparser_h +#define lparser_h + +#include "llimits.h" +#include "lobject.h" +#include "lzio.h" + + +/* +** Expression and variable descriptor. +** Code generation for variables and expressions can be delayed to allow +** optimizations; An 'expdesc' structure describes a potentially-delayed +** variable/expression. It has a description of its "main" value plus a +** list of conditional jumps that can also produce its value (generated +** by short-circuit operators 'and'/'or'). +*/ + +/* kinds of variables/expressions */ +typedef enum { + VVOID, /* when 'expdesc' describes the last expression of a list, + this kind means an empty list (so, no expression) */ + VNIL, /* constant nil */ + VTRUE, /* constant true */ + VFALSE, /* constant false */ + VK, /* constant in 'k'; info = index of constant in 'k' */ + VKFLT, /* floating constant; nval = numerical float value */ + VKINT, /* integer constant; ival = numerical integer value */ + VKSTR, /* string constant; strval = TString address; + (string is fixed by the scanner) */ + VNONRELOC, /* expression has its value in a fixed register; + info = result register */ + VLOCAL, /* local variable; var.ridx = register index; + var.vidx = relative index in 'actvar.arr' */ + VVARGVAR, /* vararg parameter; var.ridx = register index; + var.vidx = relative index in 'actvar.arr' */ + VGLOBAL, /* global variable; + info = relative index in 'actvar.arr' (or -1 for + implicit declaration) */ + VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ + VCONST, /* compile-time <const> variable; + info = absolute index in 'actvar.arr' */ + VINDEXED, /* indexed variable; + ind.t = table register; + ind.idx = key's R index; + ind.ro = true if it represents a read-only global; + ind.keystr = if key is a string, index in 'k' of that string; + -1 if key is not a string */ + VVARGIND, /* indexed vararg parameter; + ind.* as in VINDEXED */ + VINDEXUP, /* indexed upvalue; + ind.idx = key's K index; + ind.* as in VINDEXED */ + VINDEXI, /* indexed variable with constant integer; + ind.t = table register; + ind.idx = key's value */ + VINDEXSTR, /* indexed variable with literal string; + ind.idx = key's K index; + ind.* as in VINDEXED */ + VJMP, /* expression is a test/comparison; + info = pc of corresponding jump instruction */ + VRELOC, /* expression can put result in any register; + info = instruction pc */ + VCALL, /* expression is a function call; info = instruction pc */ + VVARARG /* vararg expression; info = instruction pc */ +} expkind; + + +#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR) +#define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR) + + +typedef struct expdesc { + expkind k; + union { + lua_Integer ival; /* for VKINT */ + lua_Number nval; /* for VKFLT */ + TString *strval; /* for VKSTR */ + int info; /* for generic use */ + struct { /* for indexed variables */ + short idx; /* index (R or "long" K) */ + lu_byte t; /* table (register or upvalue) */ + lu_byte ro; /* true if variable is read-only */ + int keystr; /* index in 'k' of string key, or -1 if not a string */ + } ind; + struct { /* for local variables */ + lu_byte ridx; /* register holding the variable */ + short vidx; /* index in 'actvar.arr' */ + } var; + } u; + int t; /* patch list of 'exit when true' */ + int f; /* patch list of 'exit when false' */ +} expdesc; + + +/* kinds of variables */ +#define VDKREG 0 /* regular local */ +#define RDKCONST 1 /* local constant */ +#define RDKVAVAR 2 /* vararg parameter */ +#define RDKTOCLOSE 3 /* to-be-closed */ +#define RDKCTC 4 /* local compile-time constant */ +#define GDKREG 5 /* regular global */ +#define GDKCONST 6 /* global constant */ + +/* variables that live in registers */ +#define varinreg(v) ((v)->vd.kind <= RDKTOCLOSE) + +/* test for global variables */ +#define varglobal(v) ((v)->vd.kind >= GDKREG) + + +/* description of an active variable */ +typedef union Vardesc { + struct { + TValuefields; /* constant value (if it is a compile-time constant) */ + lu_byte kind; + lu_byte ridx; /* register holding the variable */ + short pidx; /* index of the variable in the Proto's 'locvars' array */ + TString *name; /* variable name */ + } vd; + TValue k; /* constant value (if any) */ +} Vardesc; + + + +/* description of pending goto statements and label statements */ +typedef struct Labeldesc { + TString *name; /* label identifier */ + int pc; /* position in code */ + int line; /* line where it appeared */ + short nactvar; /* number of active variables in that position */ + lu_byte close; /* true for goto that escapes upvalues */ +} Labeldesc; + + +/* list of labels or gotos */ +typedef struct Labellist { + Labeldesc *arr; /* array */ + int n; /* number of entries in use */ + int size; /* array size */ +} Labellist; + + +/* dynamic structures used by the parser */ +typedef struct Dyndata { + struct { /* list of all active local variables */ + Vardesc *arr; + int n; + int size; + } actvar; + Labellist gt; /* list of pending gotos */ + Labellist label; /* list of active labels */ +} Dyndata; + + +/* control of blocks */ +struct BlockCnt; /* defined in lparser.c */ + + +/* state needed to generate code for a given function */ +typedef struct FuncState { + Proto *f; /* current function header */ + struct FuncState *prev; /* enclosing function */ + struct LexState *ls; /* lexical state */ + struct BlockCnt *bl; /* chain of current blocks */ + Table *kcache; /* cache for reusing constants */ + int pc; /* next position to code (equivalent to 'ncode') */ + int lasttarget; /* 'label' of last 'jump label' */ + int previousline; /* last line that was saved in 'lineinfo' */ + int nk; /* number of elements in 'k' */ + int np; /* number of elements in 'p' */ + int nabslineinfo; /* number of elements in 'abslineinfo' */ + int firstlocal; /* index of first local var (in Dyndata array) */ + int firstlabel; /* index of first label (in 'dyd->label->arr') */ + short ndebugvars; /* number of elements in 'f->locvars' */ + short nactvar; /* number of active variable declarations */ + lu_byte nups; /* number of upvalues */ + lu_byte freereg; /* first free register */ + lu_byte iwthabs; /* instructions issued since last absolute line info */ + lu_byte needclose; /* function needs to close upvalues when returning */ +} FuncState; + + +LUAI_FUNC lu_byte luaY_nvarstack (FuncState *fs); +LUAI_FUNC void luaY_checklimit (FuncState *fs, int v, int l, + const char *what); +LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + Dyndata *dyd, const char *name, int firstchar); + + +#endif + +``` + +`Lua/lprefix.h`: + +```h +/* +** $Id: lprefix.h $ +** Definitions for Lua code that must come before any other header file +** See Copyright Notice in lua.h +*/ + +#ifndef lprefix_h +#define lprefix_h + + +/* +** Allows POSIX/XSI stuff +*/ +#if !defined(LUA_USE_C89) /* { */ + +#if !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE 600 +#elif _XOPEN_SOURCE == 0 +#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ +#endif + +/* +** Allows manipulation of large files in gcc and some other compilers +*/ +#if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) +#define _LARGEFILE_SOURCE 1 +#define _FILE_OFFSET_BITS 64 +#endif + +#endif /* } */ + + +/* +** Windows stuff +*/ +#if defined(_WIN32) /* { */ + +#if !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ +#endif + +#endif /* } */ + +#endif + + +``` + +`Lua/lstate.c`: + +```c +/* +** $Id: lstate.c $ +** Global State +** See Copyright Notice in lua.h +*/ + +#define lstate_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <stddef.h> +#include <string.h> + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + + +#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l))) + + +/* +** these macros allow user-specific actions when a thread is +** created/deleted +*/ +#if !defined(luai_userstateopen) +#define luai_userstateopen(L) ((void)L) +#endif + +#if !defined(luai_userstateclose) +#define luai_userstateclose(L) ((void)L) +#endif + +#if !defined(luai_userstatethread) +#define luai_userstatethread(L,L1) ((void)L) +#endif + +#if !defined(luai_userstatefree) +#define luai_userstatefree(L,L1) ((void)L) +#endif + + +/* +** set GCdebt to a new value keeping the real number of allocated +** objects (GCtotalobjs - GCdebt) invariant and avoiding overflows in +** 'GCtotalobjs'. +*/ +void luaE_setdebt (global_State *g, l_mem debt) { + l_mem tb = gettotalbytes(g); + lua_assert(tb > 0); + if (debt > MAX_LMEM - tb) + debt = MAX_LMEM - tb; /* will make GCtotalbytes == MAX_LMEM */ + g->GCtotalbytes = tb + debt; + g->GCdebt = debt; +} + + +CallInfo *luaE_extendCI (lua_State *L) { + CallInfo *ci; + lua_assert(L->ci->next == NULL); + ci = luaM_new(L, CallInfo); + lua_assert(L->ci->next == NULL); + L->ci->next = ci; + ci->previous = L->ci; + ci->next = NULL; + ci->u.l.trap = 0; + L->nci++; + return ci; +} + + +/* +** free all CallInfo structures not in use by a thread +*/ +static void freeCI (lua_State *L) { + CallInfo *ci = L->ci; + CallInfo *next = ci->next; + ci->next = NULL; + while ((ci = next) != NULL) { + next = ci->next; + luaM_free(L, ci); + L->nci--; + } +} + + +/* +** free half of the CallInfo structures not in use by a thread, +** keeping the first one. +*/ +void luaE_shrinkCI (lua_State *L) { + CallInfo *ci = L->ci->next; /* first free CallInfo */ + CallInfo *next; + if (ci == NULL) + return; /* no extra elements */ + while ((next = ci->next) != NULL) { /* two extra elements? */ + CallInfo *next2 = next->next; /* next's next */ + ci->next = next2; /* remove next from the list */ + L->nci--; + luaM_free(L, next); /* free next */ + if (next2 == NULL) + break; /* no more elements */ + else { + next2->previous = ci; + ci = next2; /* continue */ + } + } +} + + +/* +** Called when 'getCcalls(L)' larger or equal to LUAI_MAXCCALLS. +** If equal, raises an overflow error. If value is larger than +** LUAI_MAXCCALLS (which means it is handling an overflow) but +** not much larger, does not report an error (to allow overflow +** handling to work). +*/ +void luaE_checkcstack (lua_State *L) { + if (getCcalls(L) == LUAI_MAXCCALLS) + luaG_runerror(L, "C stack overflow"); + else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11)) + luaD_errerr(L); /* error while handling stack error */ +} + + +LUAI_FUNC void luaE_incCstack (lua_State *L) { + L->nCcalls++; + if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) + luaE_checkcstack(L); +} + + +static void resetCI (lua_State *L) { + CallInfo *ci = L->ci = &L->base_ci; + ci->func.p = L->stack.p; + setnilvalue(s2v(ci->func.p)); /* 'function' entry for basic 'ci' */ + ci->top.p = ci->func.p + 1 + LUA_MINSTACK; /* +1 for 'function' entry */ + ci->u.c.k = NULL; + ci->callstatus = CIST_C; + L->status = LUA_OK; + L->errfunc = 0; /* stack unwind can "throw away" the error function */ +} + + +static void stack_init (lua_State *L1, lua_State *L) { + int i; + /* initialize stack array */ + L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue); + L1->tbclist.p = L1->stack.p; + for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) + setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */ + L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE; + /* initialize first ci */ + resetCI(L1); + L1->top.p = L1->stack.p + 1; /* +1 for 'function' entry */ +} + + +static void freestack (lua_State *L) { + if (L->stack.p == NULL) + return; /* stack not completely built yet */ + L->ci = &L->base_ci; /* free the entire 'ci' list */ + freeCI(L); + lua_assert(L->nci == 0); + /* free stack */ + luaM_freearray(L, L->stack.p, cast_sizet(stacksize(L) + EXTRA_STACK)); +} + + +/* +** Create registry table and its predefined values +*/ +static void init_registry (lua_State *L, global_State *g) { + /* create registry */ + TValue aux; + Table *registry = luaH_new(L); + sethvalue(L, &g->l_registry, registry); + luaH_resize(L, registry, LUA_RIDX_LAST, 0); + /* registry[1] = false */ + setbfvalue(&aux); + luaH_setint(L, registry, 1, &aux); + /* registry[LUA_RIDX_MAINTHREAD] = L */ + setthvalue(L, &aux, L); + luaH_setint(L, registry, LUA_RIDX_MAINTHREAD, &aux); + /* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */ + sethvalue(L, &aux, luaH_new(L)); + luaH_setint(L, registry, LUA_RIDX_GLOBALS, &aux); +} + + +/* +** open parts of the state that may cause memory-allocation errors. +*/ +static void f_luaopen (lua_State *L, void *ud) { + global_State *g = G(L); + UNUSED(ud); + stack_init(L, L); /* init stack */ + init_registry(L, g); + luaS_init(L); + luaT_init(L); + luaX_init(L); + g->gcstp = 0; /* allow gc */ + setnilvalue(&g->nilvalue); /* now state is complete */ + luai_userstateopen(L); +} + + +/* +** preinitialize a thread with consistent values without allocating +** any memory (to avoid errors) +*/ +static void preinit_thread (lua_State *L, global_State *g) { + G(L) = g; + L->stack.p = NULL; + L->ci = NULL; + L->nci = 0; + L->twups = L; /* thread has no upvalues */ + L->nCcalls = 0; + L->errorJmp = NULL; + L->hook = NULL; + L->hookmask = 0; + L->basehookcount = 0; + L->allowhook = 1; + resethookcount(L); + L->openupval = NULL; + L->status = LUA_OK; + L->errfunc = 0; + L->oldpc = 0; + L->base_ci.previous = L->base_ci.next = NULL; +} + + +lu_mem luaE_threadsize (lua_State *L) { + lu_mem sz = cast(lu_mem, sizeof(LX)) + + cast_uint(L->nci) * sizeof(CallInfo); + if (L->stack.p != NULL) + sz += cast_uint(stacksize(L) + EXTRA_STACK) * sizeof(StackValue); + return sz; +} + + +static void close_state (lua_State *L) { + global_State *g = G(L); + if (!completestate(g)) /* closing a partially built state? */ + luaC_freeallobjects(L); /* just collect its objects */ + else { /* closing a fully built state */ + resetCI(L); + luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */ + L->top.p = L->stack.p + 1; /* empty the stack to run finalizers */ + luaC_freeallobjects(L); /* collect all objects */ + luai_userstateclose(L); + } + luaM_freearray(L, G(L)->strt.hash, cast_sizet(G(L)->strt.size)); + freestack(L); + lua_assert(gettotalbytes(g) == sizeof(global_State)); + (*g->frealloc)(g->ud, g, sizeof(global_State), 0); /* free main block */ +} + + +LUA_API lua_State *lua_newthread (lua_State *L) { + global_State *g = G(L); + GCObject *o; + lua_State *L1; + lua_lock(L); + luaC_checkGC(L); + /* create new thread */ + o = luaC_newobjdt(L, LUA_TTHREAD, sizeof(LX), offsetof(LX, l)); + L1 = gco2th(o); + /* anchor it on L stack */ + setthvalue2s(L, L->top.p, L1); + api_incr_top(L); + preinit_thread(L1, g); + L1->hookmask = L->hookmask; + L1->basehookcount = L->basehookcount; + L1->hook = L->hook; + resethookcount(L1); + /* initialize L1 extra space */ + memcpy(lua_getextraspace(L1), lua_getextraspace(mainthread(g)), + LUA_EXTRASPACE); + luai_userstatethread(L, L1); + stack_init(L1, L); /* init stack */ + lua_unlock(L); + return L1; +} + + +void luaE_freethread (lua_State *L, lua_State *L1) { + LX *l = fromstate(L1); + luaF_closeupval(L1, L1->stack.p); /* close all upvalues */ + lua_assert(L1->openupval == NULL); + luai_userstatefree(L, L1); + freestack(L1); + luaM_free(L, l); +} + + +TStatus luaE_resetthread (lua_State *L, TStatus status) { + resetCI(L); + if (status == LUA_YIELD) + status = LUA_OK; + status = luaD_closeprotected(L, 1, status); + if (status != LUA_OK) /* errors? */ + luaD_seterrorobj(L, status, L->stack.p + 1); + else + L->top.p = L->stack.p + 1; + luaD_reallocstack(L, cast_int(L->ci->top.p - L->stack.p), 0); + return status; +} + + +LUA_API int lua_closethread (lua_State *L, lua_State *from) { + TStatus status; + lua_lock(L); + L->nCcalls = (from) ? getCcalls(from) : 0; + status = luaE_resetthread(L, L->status); + if (L == from) /* closing itself? */ + luaD_throwbaselevel(L, status); + lua_unlock(L); + return APIstatus(status); +} + + +LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud, unsigned seed) { + int i; + lua_State *L; + global_State *g = cast(global_State*, + (*f)(ud, NULL, LUA_TTHREAD, sizeof(global_State))); + if (g == NULL) return NULL; + L = &g->mainth.l; + L->tt = LUA_VTHREAD; + g->currentwhite = bitmask(WHITE0BIT); + L->marked = luaC_white(g); + preinit_thread(L, g); + g->allgc = obj2gco(L); /* by now, only object is the main thread */ + L->next = NULL; + incnny(L); /* main thread is always non yieldable */ + g->frealloc = f; + g->ud = ud; + g->warnf = NULL; + g->ud_warn = NULL; + g->seed = seed; + g->gcstp = GCSTPGC; /* no GC while building state */ + g->strt.size = g->strt.nuse = 0; + g->strt.hash = NULL; + setnilvalue(&g->l_registry); + g->panic = NULL; + g->gcstate = GCSpause; + g->gckind = KGC_INC; + g->gcstopem = 0; + g->gcemergency = 0; + g->finobj = g->tobefnz = g->fixedgc = NULL; + g->firstold1 = g->survival = g->old1 = g->reallyold = NULL; + g->finobjsur = g->finobjold1 = g->finobjrold = NULL; + g->sweepgc = NULL; + g->gray = g->grayagain = NULL; + g->weak = g->ephemeron = g->allweak = NULL; + g->twups = NULL; + g->GCtotalbytes = sizeof(global_State); + g->GCmarked = 0; + g->GCdebt = 0; + setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ + setgcparam(g, PAUSE, LUAI_GCPAUSE); + setgcparam(g, STEPMUL, LUAI_GCMUL); + setgcparam(g, STEPSIZE, LUAI_GCSTEPSIZE); + setgcparam(g, MINORMUL, LUAI_GENMINORMUL); + setgcparam(g, MINORMAJOR, LUAI_MINORMAJOR); + setgcparam(g, MAJORMINOR, LUAI_MAJORMINOR); + for (i=0; i < LUA_NUMTYPES; i++) g->mt[i] = NULL; + if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { + /* memory allocation error: free partial state */ + close_state(L); + L = NULL; + } + return L; +} + + +LUA_API void lua_close (lua_State *L) { + lua_lock(L); + L = mainthread(G(L)); /* only the main thread can be closed */ + close_state(L); +} + + +void luaE_warning (lua_State *L, const char *msg, int tocont) { + lua_WarnFunction wf = G(L)->warnf; + if (wf != NULL) + wf(G(L)->ud_warn, msg, tocont); +} + + +/* +** Generate a warning from an error message +*/ +void luaE_warnerror (lua_State *L, const char *where) { + TValue *errobj = s2v(L->top.p - 1); /* error object */ + const char *msg = (ttisstring(errobj)) + ? getstr(tsvalue(errobj)) + : "error object is not a string"; + /* produce warning "error in %s (%s)" (where, msg) */ + luaE_warning(L, "error in ", 1); + luaE_warning(L, where, 1); + luaE_warning(L, " (", 1); + luaE_warning(L, msg, 1); + luaE_warning(L, ")", 0); +} + + +``` + +`Lua/lstate.h`: + +```h +/* +** $Id: lstate.h $ +** Global State +** See Copyright Notice in lua.h +*/ + +#ifndef lstate_h +#define lstate_h + +#include "lua.h" + + +/* Some header files included here need this definition */ +typedef struct CallInfo CallInfo; + + +#include "lobject.h" +#include "ltm.h" +#include "lzio.h" + + +/* +** Some notes about garbage-collected objects: All objects in Lua must +** be kept somehow accessible until being freed, so all objects always +** belong to one (and only one) of these lists, using field 'next' of +** the 'CommonHeader' for the link: +** +** 'allgc': all objects not marked for finalization; +** 'finobj': all objects marked for finalization; +** 'tobefnz': all objects ready to be finalized; +** 'fixedgc': all objects that are not to be collected (currently +** only small strings, such as reserved words). +** +** For the generational collector, some of these lists have marks for +** generations. Each mark points to the first element in the list for +** that particular generation; that generation goes until the next mark. +** +** 'allgc' -> 'survival': new objects; +** 'survival' -> 'old': objects that survived one collection; +** 'old1' -> 'reallyold': objects that became old in last collection; +** 'reallyold' -> NULL: objects old for more than one cycle. +** +** 'finobj' -> 'finobjsur': new objects marked for finalization; +** 'finobjsur' -> 'finobjold1': survived """"; +** 'finobjold1' -> 'finobjrold': just old """"; +** 'finobjrold' -> NULL: really old """". +** +** All lists can contain elements older than their main ages, due +** to 'luaC_checkfinalizer' and 'udata2finalize', which move +** objects between the normal lists and the "marked for finalization" +** lists. Moreover, barriers can age young objects in young lists as +** OLD0, which then become OLD1. However, a list never contains +** elements younger than their main ages. +** +** The generational collector also uses a pointer 'firstold1', which +** points to the first OLD1 object in the list. It is used to optimize +** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc' +** and 'reallyold', but often the list has no OLD1 objects or they are +** after 'old1'.) Note the difference between it and 'old1': +** 'firstold1': no OLD1 objects before this point; there can be all +** ages after it. +** 'old1': no objects younger than OLD1 after this point. +*/ + +/* +** Moreover, there is another set of lists that control gray objects. +** These lists are linked by fields 'gclist'. (All objects that +** can become gray have such a field. The field is not the same +** in all objects, but it always has this name.) Any gray object +** must belong to one of these lists, and all objects in these lists +** must be gray (with two exceptions explained below): +** +** 'gray': regular gray objects, still waiting to be visited. +** 'grayagain': objects that must be revisited at the atomic phase. +** That includes +** - black objects got in a write barrier; +** - all kinds of weak tables during propagation phase; +** - all threads. +** 'weak': tables with weak values to be cleared; +** 'ephemeron': ephemeron tables with white->white entries; +** 'allweak': tables with weak keys and/or weak values to be cleared. +** +** The exceptions to that "gray rule" are: +** - TOUCHED2 objects in generational mode stay in a gray list (because +** they must be visited again at the end of the cycle), but they are +** marked black because assignments to them must activate barriers (to +** move them back to TOUCHED1). +** - Open upvalues are kept gray to avoid barriers, but they stay out +** of gray lists. (They don't even have a 'gclist' field.) +*/ + + + +/* +** About 'nCcalls': This count has two parts: the lower 16 bits counts +** the number of recursive invocations in the C stack; the higher +** 16 bits counts the number of non-yieldable calls in the stack. +** (They are together so that we can change and save both with one +** instruction.) +*/ + + +/* true if this thread does not have non-yieldable calls in the stack */ +#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0) + +/* real number of C calls */ +#define getCcalls(L) ((L)->nCcalls & 0xffff) + + +/* Increment the number of non-yieldable calls */ +#define incnny(L) ((L)->nCcalls += 0x10000) + +/* Decrement the number of non-yieldable calls */ +#define decnny(L) ((L)->nCcalls -= 0x10000) + +/* Non-yieldable call increment */ +#define nyci (0x10000 | 1) + + + + +struct lua_longjmp; /* defined in ldo.c */ + + +/* +** Atomic type (relative to signals) to better ensure that 'lua_sethook' +** is thread safe +*/ +#if !defined(l_signalT) +#include <signal.h> +#define l_signalT sig_atomic_t +#endif + + +/* +** Extra stack space to handle TM calls and some other extras. This +** space is not included in 'stack_last'. It is used only to avoid stack +** checks, either because the element will be promptly popped or because +** there will be a stack check soon after the push. Function frames +** never use this extra space, so it does not need to be kept clean. +*/ +#define EXTRA_STACK 5 + + +/* +** Size of cache for strings in the API. 'N' is the number of +** sets (better be a prime) and "M" is the size of each set. +** (M == 1 makes a direct cache.) +*/ +#if !defined(STRCACHE_N) +#define STRCACHE_N 53 +#define STRCACHE_M 2 +#endif + + +#define BASIC_STACK_SIZE (2*LUA_MINSTACK) + +#define stacksize(th) cast_int((th)->stack_last.p - (th)->stack.p) + + +/* kinds of Garbage Collection */ +#define KGC_INC 0 /* incremental gc */ +#define KGC_GENMINOR 1 /* generational gc in minor (regular) mode */ +#define KGC_GENMAJOR 2 /* generational in major mode */ + + +typedef struct stringtable { + TString **hash; /* array of buckets (linked lists of strings) */ + int nuse; /* number of elements */ + int size; /* number of buckets */ +} stringtable; + + +/* +** Information about a call. +** About union 'u': +** - field 'l' is used only for Lua functions; +** - field 'c' is used only for C functions. +** About union 'u2': +** - field 'funcidx' is used only by C functions while doing a +** protected call; +** - field 'nyield' is used only while a function is "doing" an +** yield (from the yield until the next resume); +** - field 'nres' is used only while closing tbc variables when +** returning from a function; +*/ +struct CallInfo { + StkIdRel func; /* function index in the stack */ + StkIdRel top; /* top for this function */ + struct CallInfo *previous, *next; /* dynamic call link */ + union { + struct { /* only for Lua functions */ + const Instruction *savedpc; + volatile l_signalT trap; /* function is tracing lines/counts */ + int nextraargs; /* # of extra arguments in vararg functions */ + } l; + struct { /* only for C functions */ + lua_KFunction k; /* continuation in case of yields */ + ptrdiff_t old_errfunc; + lua_KContext ctx; /* context info. in case of yields */ + } c; + } u; + union { + int funcidx; /* called-function index */ + int nyield; /* number of values yielded */ + int nres; /* number of values returned */ + } u2; + l_uint32 callstatus; +}; + + +/* +** Maximum expected number of results from a function +** (must fit in CIST_NRESULTS). +*/ +#define MAXRESULTS 250 + + +/* +** Bits in CallInfo status +*/ +/* bits 0-7 are the expected number of results from this function + 1 */ +#define CIST_NRESULTS 0xffu + +/* bits 8-11 count call metamethods (and their extra arguments) */ +#define CIST_CCMT 8 /* the offset, not the mask */ +#define MAX_CCMT (0xfu << CIST_CCMT) + +/* Bits 12-14 are used for CIST_RECST (see below) */ +#define CIST_RECST 12 /* the offset, not the mask */ + +/* call is running a C function (still in first 16 bits) */ +#define CIST_C (1u << (CIST_RECST + 3)) +/* call is on a fresh "luaV_execute" frame */ +#define CIST_FRESH (cast(l_uint32, CIST_C) << 1) +/* function is closing tbc variables */ +#define CIST_CLSRET (CIST_FRESH << 1) +/* function has tbc variables to close */ +#define CIST_TBC (CIST_CLSRET << 1) +/* original value of 'allowhook' */ +#define CIST_OAH (CIST_TBC << 1) +/* call is running a debug hook */ +#define CIST_HOOKED (CIST_OAH << 1) +/* doing a yieldable protected call */ +#define CIST_YPCALL (CIST_HOOKED << 1) +/* call was tail called */ +#define CIST_TAIL (CIST_YPCALL << 1) +/* last hook called yielded */ +#define CIST_HOOKYIELD (CIST_TAIL << 1) +/* function "called" a finalizer */ +#define CIST_FIN (CIST_HOOKYIELD << 1) + + +#define get_nresults(cs) (cast_int((cs) & CIST_NRESULTS) - 1) + +/* +** Field CIST_RECST stores the "recover status", used to keep the error +** status while closing to-be-closed variables in coroutines, so that +** Lua can correctly resume after an yield from a __close method called +** because of an error. (Three bits are enough for error status.) +*/ +#define getcistrecst(ci) (((ci)->callstatus >> CIST_RECST) & 7) +#define setcistrecst(ci,st) \ + check_exp(((st) & 7) == (st), /* status must fit in three bits */ \ + ((ci)->callstatus = ((ci)->callstatus & ~(7u << CIST_RECST)) \ + | (cast(l_uint32, st) << CIST_RECST))) + + +/* active function is a Lua function */ +#define isLua(ci) (!((ci)->callstatus & CIST_C)) + +/* call is running Lua code (not a hook) */ +#define isLuacode(ci) (!((ci)->callstatus & (CIST_C | CIST_HOOKED))) + + +#define setoah(ci,v) \ + ((ci)->callstatus = ((v) ? (ci)->callstatus | CIST_OAH \ + : (ci)->callstatus & ~CIST_OAH)) +#define getoah(ci) (((ci)->callstatus & CIST_OAH) ? 1 : 0) + + +/* +** 'per thread' state +*/ +struct lua_State { + CommonHeader; + lu_byte allowhook; + TStatus status; + StkIdRel top; /* first free slot in the stack */ + struct global_State *l_G; + CallInfo *ci; /* call info for current function */ + StkIdRel stack_last; /* end of stack (last element + 1) */ + StkIdRel stack; /* stack base */ + UpVal *openupval; /* list of open upvalues in this stack */ + StkIdRel tbclist; /* list of to-be-closed variables */ + GCObject *gclist; + struct lua_State *twups; /* list of threads with open upvalues */ + struct lua_longjmp *errorJmp; /* current error recover point */ + CallInfo base_ci; /* CallInfo for first level (C host) */ + volatile lua_Hook hook; + ptrdiff_t errfunc; /* current error handling function (stack index) */ + l_uint32 nCcalls; /* number of nested non-yieldable or C calls */ + int oldpc; /* last pc traced */ + int nci; /* number of items in 'ci' list */ + int basehookcount; + int hookcount; + volatile l_signalT hookmask; + struct { /* info about transferred values (for call/return hooks) */ + int ftransfer; /* offset of first value transferred */ + int ntransfer; /* number of values transferred */ + } transferinfo; +}; + + +/* +** thread state + extra space +*/ +typedef struct LX { + lu_byte extra_[LUA_EXTRASPACE]; + lua_State l; +} LX; + + +/* +** 'global state', shared by all threads of this state +*/ +typedef struct global_State { + lua_Alloc frealloc; /* function to reallocate memory */ + void *ud; /* auxiliary data to 'frealloc' */ + l_mem GCtotalbytes; /* number of bytes currently allocated + debt */ + l_mem GCdebt; /* bytes counted but not yet allocated */ + l_mem GCmarked; /* number of objects marked in a GC cycle */ + l_mem GCmajorminor; /* auxiliary counter to control major-minor shifts */ + stringtable strt; /* hash table for strings */ + TValue l_registry; + TValue nilvalue; /* a nil value */ + unsigned int seed; /* randomized seed for hashes */ + lu_byte gcparams[LUA_GCPN]; + lu_byte currentwhite; + lu_byte gcstate; /* state of garbage collector */ + lu_byte gckind; /* kind of GC running */ + lu_byte gcstopem; /* stops emergency collections */ + lu_byte gcstp; /* control whether GC is running */ + lu_byte gcemergency; /* true if this is an emergency collection */ + GCObject *allgc; /* list of all collectable objects */ + GCObject **sweepgc; /* current position of sweep in list */ + GCObject *finobj; /* list of collectable objects with finalizers */ + GCObject *gray; /* list of gray objects */ + GCObject *grayagain; /* list of objects to be traversed atomically */ + GCObject *weak; /* list of tables with weak values */ + GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ + GCObject *allweak; /* list of all-weak tables */ + GCObject *tobefnz; /* list of userdata to be GC */ + GCObject *fixedgc; /* list of objects not to be collected */ + /* fields for generational collector */ + GCObject *survival; /* start of objects that survived one GC cycle */ + GCObject *old1; /* start of old1 objects */ + GCObject *reallyold; /* objects more than one cycle old ("really old") */ + GCObject *firstold1; /* first OLD1 object in the list (if any) */ + GCObject *finobjsur; /* list of survival objects with finalizers */ + GCObject *finobjold1; /* list of old1 objects with finalizers */ + GCObject *finobjrold; /* list of really old objects with finalizers */ + struct lua_State *twups; /* list of threads with open upvalues */ + lua_CFunction panic; /* to be called in unprotected errors */ + TString *memerrmsg; /* message for memory-allocation errors */ + TString *tmname[TM_N]; /* array with tag-method names */ + struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */ + TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */ + lua_WarnFunction warnf; /* warning function */ + void *ud_warn; /* auxiliary data to 'warnf' */ + LX mainth; /* main thread of this state */ +} global_State; + + +#define G(L) (L->l_G) +#define mainthread(G) (&(G)->mainth.l) + +/* +** 'g->nilvalue' being a nil value flags that the state was completely +** build. +*/ +#define completestate(g) ttisnil(&g->nilvalue) + + +/* +** Union of all collectable objects (only for conversions) +** ISO C99, 6.5.2.3 p.5: +** "if a union contains several structures that share a common initial +** sequence [...], and if the union object currently contains one +** of these structures, it is permitted to inspect the common initial +** part of any of them anywhere that a declaration of the complete type +** of the union is visible." +*/ +union GCUnion { + GCObject gc; /* common header */ + struct TString ts; + struct Udata u; + union Closure cl; + struct Table h; + struct Proto p; + struct lua_State th; /* thread */ + struct UpVal upv; +}; + + +/* +** ISO C99, 6.7.2.1 p.14: +** "A pointer to a union object, suitably converted, points to each of +** its members [...], and vice versa." +*/ +#define cast_u(o) cast(union GCUnion *, (o)) + +/* macros to convert a GCObject into a specific value */ +#define gco2ts(o) \ + check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts)) +#define gco2u(o) check_exp((o)->tt == LUA_VUSERDATA, &((cast_u(o))->u)) +#define gco2lcl(o) check_exp((o)->tt == LUA_VLCL, &((cast_u(o))->cl.l)) +#define gco2ccl(o) check_exp((o)->tt == LUA_VCCL, &((cast_u(o))->cl.c)) +#define gco2cl(o) \ + check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl)) +#define gco2t(o) check_exp((o)->tt == LUA_VTABLE, &((cast_u(o))->h)) +#define gco2p(o) check_exp((o)->tt == LUA_VPROTO, &((cast_u(o))->p)) +#define gco2th(o) check_exp((o)->tt == LUA_VTHREAD, &((cast_u(o))->th)) +#define gco2upv(o) check_exp((o)->tt == LUA_VUPVAL, &((cast_u(o))->upv)) + + +/* +** macro to convert a Lua object into a GCObject +*/ +#define obj2gco(v) \ + check_exp(novariant((v)->tt) >= LUA_TSTRING, &(cast_u(v)->gc)) + + +/* actual number of total memory allocated */ +#define gettotalbytes(g) ((g)->GCtotalbytes - (g)->GCdebt) + + +LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); +LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); +LUAI_FUNC lu_mem luaE_threadsize (lua_State *L); +LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); +LUAI_FUNC void luaE_shrinkCI (lua_State *L); +LUAI_FUNC void luaE_checkcstack (lua_State *L); +LUAI_FUNC void luaE_incCstack (lua_State *L); +LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont); +LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where); +LUAI_FUNC TStatus luaE_resetthread (lua_State *L, TStatus status); + + +#endif + + +``` + +`Lua/lstring.c`: + +```c +/* +** $Id: lstring.c $ +** String table (keeps all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + +#define lstring_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <string.h> + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" + + +/* +** Maximum size for string table. +*/ +#define MAXSTRTB cast_int(luaM_limitN(INT_MAX, TString*)) + +/* +** Initial size for the string table (must be power of 2). +** The Lua core alone registers ~50 strings (reserved words + +** metaevent keys + a few others). Libraries would typically add +** a few dozens more. +*/ +#if !defined(MINSTRTABSIZE) +#define MINSTRTABSIZE 128 +#endif + + +/* +** generic equality for strings +*/ +int luaS_eqstr (TString *a, TString *b) { + size_t len1, len2; + const char *s1 = getlstr(a, len1); + const char *s2 = getlstr(b, len2); + return ((len1 == len2) && /* equal length and ... */ + (memcmp(s1, s2, len1) == 0)); /* equal contents */ +} + + +static unsigned luaS_hash (const char *str, size_t l, unsigned seed) { + unsigned int h = seed ^ cast_uint(l); + for (; l > 0; l--) + h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1])); + return h; +} + + +unsigned luaS_hashlongstr (TString *ts) { + lua_assert(ts->tt == LUA_VLNGSTR); + if (ts->extra == 0) { /* no hash? */ + size_t len = ts->u.lnglen; + ts->hash = luaS_hash(getlngstr(ts), len, ts->hash); + ts->extra = 1; /* now it has its hash */ + } + return ts->hash; +} + + +static void tablerehash (TString **vect, int osize, int nsize) { + int i; + for (i = osize; i < nsize; i++) /* clear new elements */ + vect[i] = NULL; + for (i = 0; i < osize; i++) { /* rehash old part of the array */ + TString *p = vect[i]; + vect[i] = NULL; + while (p) { /* for each string in the list */ + TString *hnext = p->u.hnext; /* save next */ + unsigned int h = lmod(p->hash, nsize); /* new position */ + p->u.hnext = vect[h]; /* chain it into array */ + vect[h] = p; + p = hnext; + } + } +} + + +/* +** Resize the string table. If allocation fails, keep the current size. +** (This can degrade performance, but any non-zero size should work +** correctly.) +*/ +void luaS_resize (lua_State *L, int nsize) { + stringtable *tb = &G(L)->strt; + int osize = tb->size; + TString **newvect; + if (nsize < osize) /* shrinking table? */ + tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */ + newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString*); + if (l_unlikely(newvect == NULL)) { /* reallocation failed? */ + if (nsize < osize) /* was it shrinking table? */ + tablerehash(tb->hash, nsize, osize); /* restore to original size */ + /* leave table as it was */ + } + else { /* allocation succeeded */ + tb->hash = newvect; + tb->size = nsize; + if (nsize > osize) + tablerehash(newvect, osize, nsize); /* rehash for new size */ + } +} + + +/* +** Clear API string cache. (Entries cannot be empty, so fill them with +** a non-collectable string.) +*/ +void luaS_clearcache (global_State *g) { + int i, j; + for (i = 0; i < STRCACHE_N; i++) + for (j = 0; j < STRCACHE_M; j++) { + if (iswhite(g->strcache[i][j])) /* will entry be collected? */ + g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */ + } +} + + +/* +** Initialize the string table and the string cache +*/ +void luaS_init (lua_State *L) { + global_State *g = G(L); + int i, j; + stringtable *tb = &G(L)->strt; + tb->hash = luaM_newvector(L, MINSTRTABSIZE, TString*); + tablerehash(tb->hash, 0, MINSTRTABSIZE); /* clear array */ + tb->size = MINSTRTABSIZE; + /* pre-create memory-error message */ + g->memerrmsg = luaS_newliteral(L, MEMERRMSG); + luaC_fix(L, obj2gco(g->memerrmsg)); /* it should never be collected */ + for (i = 0; i < STRCACHE_N; i++) /* fill cache with valid strings */ + for (j = 0; j < STRCACHE_M; j++) + g->strcache[i][j] = g->memerrmsg; +} + + +size_t luaS_sizelngstr (size_t len, int kind) { + switch (kind) { + case LSTRREG: /* regular long string */ + /* don't need 'falloc'/'ud', but need space for content */ + return offsetof(TString, falloc) + (len + 1) * sizeof(char); + case LSTRFIX: /* fixed external long string */ + /* don't need 'falloc'/'ud' */ + return offsetof(TString, falloc); + default: /* external long string with deallocation */ + lua_assert(kind == LSTRMEM); + return sizeof(TString); + } +} + + +/* +** creates a new string object +*/ +static TString *createstrobj (lua_State *L, size_t totalsize, lu_byte tag, + unsigned h) { + TString *ts; + GCObject *o; + o = luaC_newobj(L, tag, totalsize); + ts = gco2ts(o); + ts->hash = h; + ts->extra = 0; + return ts; +} + + +TString *luaS_createlngstrobj (lua_State *L, size_t l) { + size_t totalsize = luaS_sizelngstr(l, LSTRREG); + TString *ts = createstrobj(L, totalsize, LUA_VLNGSTR, G(L)->seed); + ts->u.lnglen = l; + ts->shrlen = LSTRREG; /* signals that it is a regular long string */ + ts->contents = cast_charp(ts) + offsetof(TString, falloc); + ts->contents[l] = '\0'; /* ending 0 */ + return ts; +} + + +void luaS_remove (lua_State *L, TString *ts) { + stringtable *tb = &G(L)->strt; + TString **p = &tb->hash[lmod(ts->hash, tb->size)]; + while (*p != ts) /* find previous element */ + p = &(*p)->u.hnext; + *p = (*p)->u.hnext; /* remove element from its list */ + tb->nuse--; +} + + +static void growstrtab (lua_State *L, stringtable *tb) { + if (l_unlikely(tb->nuse == INT_MAX)) { /* too many strings? */ + luaC_fullgc(L, 1); /* try to free some... */ + if (tb->nuse == INT_MAX) /* still too many? */ + luaM_error(L); /* cannot even create a message... */ + } + if (tb->size <= MAXSTRTB / 2) /* can grow string table? */ + luaS_resize(L, tb->size * 2); +} + + +/* +** Checks whether short string exists and reuses it or creates a new one. +*/ +static TString *internshrstr (lua_State *L, const char *str, size_t l) { + TString *ts; + global_State *g = G(L); + stringtable *tb = &g->strt; + unsigned int h = luaS_hash(str, l, g->seed); + TString **list = &tb->hash[lmod(h, tb->size)]; + lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */ + for (ts = *list; ts != NULL; ts = ts->u.hnext) { + if (l == cast_uint(ts->shrlen) && + (memcmp(str, getshrstr(ts), l * sizeof(char)) == 0)) { + /* found! */ + if (isdead(g, ts)) /* dead (but not collected yet)? */ + changewhite(ts); /* resurrect it */ + return ts; + } + } + /* else must create a new string */ + if (tb->nuse >= tb->size) { /* need to grow string table? */ + growstrtab(L, tb); + list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */ + } + ts = createstrobj(L, sizestrshr(l), LUA_VSHRSTR, h); + ts->shrlen = cast(ls_byte, l); + getshrstr(ts)[l] = '\0'; /* ending 0 */ + memcpy(getshrstr(ts), str, l * sizeof(char)); + ts->u.hnext = *list; + *list = ts; + tb->nuse++; + return ts; +} + + +/* +** new string (with explicit length) +*/ +TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { + if (l <= LUAI_MAXSHORTLEN) /* short string? */ + return internshrstr(L, str, l); + else { + TString *ts; + if (l_unlikely(l * sizeof(char) >= (MAX_SIZE - sizeof(TString)))) + luaM_toobig(L); + ts = luaS_createlngstrobj(L, l); + memcpy(getlngstr(ts), str, l * sizeof(char)); + return ts; + } +} + + +/* +** Create or reuse a zero-terminated string, first checking in the +** cache (using the string address as a key). The cache can contain +** only zero-terminated strings, so it is safe to use 'strcmp' to +** check hits. +*/ +TString *luaS_new (lua_State *L, const char *str) { + unsigned int i = point2uint(str) % STRCACHE_N; /* hash */ + int j; + TString **p = G(L)->strcache[i]; + for (j = 0; j < STRCACHE_M; j++) { + if (strcmp(str, getstr(p[j])) == 0) /* hit? */ + return p[j]; /* that is it */ + } + /* normal route */ + for (j = STRCACHE_M - 1; j > 0; j--) + p[j] = p[j - 1]; /* move out last element */ + /* new element is first in the list */ + p[0] = luaS_newlstr(L, str, strlen(str)); + return p[0]; +} + + +Udata *luaS_newudata (lua_State *L, size_t s, unsigned short nuvalue) { + Udata *u; + int i; + GCObject *o; + if (l_unlikely(s > MAX_SIZE - udatamemoffset(nuvalue))) + luaM_toobig(L); + o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s)); + u = gco2u(o); + u->len = s; + u->nuvalue = nuvalue; + u->metatable = NULL; + for (i = 0; i < nuvalue; i++) + setnilvalue(&u->uv[i].uv); + return u; +} + + +struct NewExt { + ls_byte kind; + const char *s; + size_t len; + TString *ts; /* output */ +}; + + +static void f_newext (lua_State *L, void *ud) { + struct NewExt *ne = cast(struct NewExt *, ud); + size_t size = luaS_sizelngstr(0, ne->kind); + ne->ts = createstrobj(L, size, LUA_VLNGSTR, G(L)->seed); +} + + +TString *luaS_newextlstr (lua_State *L, + const char *s, size_t len, lua_Alloc falloc, void *ud) { + struct NewExt ne; + if (!falloc) { + ne.kind = LSTRFIX; + f_newext(L, &ne); /* just create header */ + } + else { + ne.kind = LSTRMEM; + if (luaD_rawrunprotected(L, f_newext, &ne) != LUA_OK) { /* mem. error? */ + (*falloc)(ud, cast_voidp(s), len + 1, 0); /* free external string */ + luaM_error(L); /* re-raise memory error */ + } + ne.ts->falloc = falloc; + ne.ts->ud = ud; + } + ne.ts->shrlen = ne.kind; + ne.ts->u.lnglen = len; + ne.ts->contents = cast_charp(s); + return ne.ts; +} + + +/* +** Normalize an external string: If it is short, internalize it. +*/ +TString *luaS_normstr (lua_State *L, TString *ts) { + size_t len = ts->u.lnglen; + if (len > LUAI_MAXSHORTLEN) + return ts; /* long string; keep the original */ + else { + const char *str = getlngstr(ts); + return internshrstr(L, str, len); + } +} + + +``` + +`Lua/lstring.h`: + +```h +/* +** $Id: lstring.h $ +** String table (keep all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + +#ifndef lstring_h +#define lstring_h + +#include "lgc.h" +#include "lobject.h" +#include "lstate.h" + + +/* +** Memory-allocation error message must be preallocated (it cannot +** be created after memory is exhausted) +*/ +#define MEMERRMSG "not enough memory" + + +/* +** Maximum length for short strings, that is, strings that are +** internalized. (Cannot be smaller than reserved words or tags for +** metamethods, as these strings must be internalized; +** #("function") = 8, #("__newindex") = 10.) +*/ +#if !defined(LUAI_MAXSHORTLEN) +#define LUAI_MAXSHORTLEN 40 +#endif + + +/* +** Size of a short TString: Size of the header plus space for the string +** itself (including final '\0'). +*/ +#define sizestrshr(l) \ + (offsetof(TString, contents) + ((l) + 1) * sizeof(char)) + + +#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ + (sizeof(s)/sizeof(char))-1)) + + +/* +** test whether a string is a reserved word +*/ +#define isreserved(s) (strisshr(s) && (s)->extra > 0) + + +/* +** equality for short strings, which are always internalized +*/ +#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b)) + + +LUAI_FUNC unsigned luaS_hashlongstr (TString *ts); +LUAI_FUNC int luaS_eqstr (TString *a, TString *b); +LUAI_FUNC void luaS_resize (lua_State *L, int newsize); +LUAI_FUNC void luaS_clearcache (global_State *g); +LUAI_FUNC void luaS_init (lua_State *L); +LUAI_FUNC void luaS_remove (lua_State *L, TString *ts); +LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, + unsigned short nuvalue); +LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); +LUAI_FUNC TString *luaS_new (lua_State *L, const char *str); +LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l); +LUAI_FUNC TString *luaS_newextlstr (lua_State *L, + const char *s, size_t len, lua_Alloc falloc, void *ud); +LUAI_FUNC size_t luaS_sizelngstr (size_t len, int kind); +LUAI_FUNC TString *luaS_normstr (lua_State *L, TString *ts); + +#endif + +``` + +`Lua/lstrlib.c`: + +```c +/* +** $Id: lstrlib.c $ +** Standard library for string operations and pattern-matching +** See Copyright Notice in lua.h +*/ + +#define lstrlib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include <ctype.h> +#include <float.h> +#include <limits.h> +#include <locale.h> +#include <math.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "llimits.h" + + +/* +** maximum number of captures that a pattern can do during +** pattern-matching. This limit is arbitrary, but must fit in +** an unsigned char. +*/ +#if !defined(LUA_MAXCAPTURES) +#define LUA_MAXCAPTURES 32 +#endif + + +static int str_len (lua_State *L) { + size_t l; + luaL_checklstring(L, 1, &l); + lua_pushinteger(L, (lua_Integer)l); + return 1; +} + + +/* +** translate a relative initial string position +** (negative means back from end): clip result to [1, inf). +** The length of any string in Lua must fit in a lua_Integer, +** so there are no overflows in the casts. +** The inverted comparison avoids a possible overflow +** computing '-pos'. +*/ +static size_t posrelatI (lua_Integer pos, size_t len) { + if (pos > 0) + return (size_t)pos; + else if (pos == 0) + return 1; + else if (pos < -(lua_Integer)len) /* inverted comparison */ + return 1; /* clip to 1 */ + else return len + (size_t)pos + 1; +} + + +/* +** Gets an optional ending string position from argument 'arg', +** with default value 'def'. +** Negative means back from end: clip result to [0, len] +*/ +static size_t getendpos (lua_State *L, int arg, lua_Integer def, + size_t len) { + lua_Integer pos = luaL_optinteger(L, arg, def); + if (pos > (lua_Integer)len) + return len; + else if (pos >= 0) + return (size_t)pos; + else if (pos < -(lua_Integer)len) + return 0; + else return len + (size_t)pos + 1; +} + + +static int str_sub (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + size_t start = posrelatI(luaL_checkinteger(L, 2), l); + size_t end = getendpos(L, 3, -1, l); + if (start <= end) + lua_pushlstring(L, s + start - 1, (end - start) + 1); + else lua_pushliteral(L, ""); + return 1; +} + + +static int str_reverse (lua_State *L) { + size_t l, i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + char *p = luaL_buffinitsize(L, &b, l); + for (i = 0; i < l; i++) + p[i] = s[l - i - 1]; + luaL_pushresultsize(&b, l); + return 1; +} + + +static int str_lower (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + char *p = luaL_buffinitsize(L, &b, l); + for (i=0; i<l; i++) + p[i] = cast_char(tolower(cast_uchar(s[i]))); + luaL_pushresultsize(&b, l); + return 1; +} + + +static int str_upper (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + char *p = luaL_buffinitsize(L, &b, l); + for (i=0; i<l; i++) + p[i] = cast_char(toupper(cast_uchar(s[i]))); + luaL_pushresultsize(&b, l); + return 1; +} + + +/* +** MAX_SIZE is limited both by size_t and lua_Integer. +** When x <= MAX_SIZE, x can be safely cast to size_t or lua_Integer. +*/ +static int str_rep (lua_State *L) { + size_t len, lsep; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer n = luaL_checkinteger(L, 2); + const char *sep = luaL_optlstring(L, 3, "", &lsep); + if (n <= 0) + lua_pushliteral(L, ""); + else if (l_unlikely(len > MAX_SIZE - lsep || + cast_st2S(len + lsep) > cast_st2S(MAX_SIZE) / n)) + return luaL_error(L, "resulting string too large"); + else { + size_t totallen = (cast_sizet(n) * (len + lsep)) - lsep; + luaL_Buffer b; + char *p = luaL_buffinitsize(L, &b, totallen); + while (n-- > 1) { /* first n-1 copies (followed by separator) */ + memcpy(p, s, len * sizeof(char)); p += len; + if (lsep > 0) { /* empty 'memcpy' is not that cheap */ + memcpy(p, sep, lsep * sizeof(char)); p += lsep; + } + } + memcpy(p, s, len * sizeof(char)); /* last copy without separator */ + luaL_pushresultsize(&b, totallen); + } + return 1; +} + + +static int str_byte (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + lua_Integer pi = luaL_optinteger(L, 2, 1); + size_t posi = posrelatI(pi, l); + size_t pose = getendpos(L, 3, pi, l); + int n, i; + if (posi > pose) return 0; /* empty interval; return no values */ + if (l_unlikely(pose - posi >= (size_t)INT_MAX)) /* arithmetic overflow? */ + return luaL_error(L, "string slice too long"); + n = (int)(pose - posi) + 1; + luaL_checkstack(L, n, "string slice too long"); + for (i=0; i<n; i++) + lua_pushinteger(L, cast_uchar(s[posi + cast_uint(i) - 1])); + return n; +} + + +static int str_char (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + luaL_Buffer b; + char *p = luaL_buffinitsize(L, &b, cast_uint(n)); + for (i=1; i<=n; i++) { + lua_Unsigned c = (lua_Unsigned)luaL_checkinteger(L, i); + luaL_argcheck(L, c <= (lua_Unsigned)UCHAR_MAX, i, "value out of range"); + p[i - 1] = cast_char(cast_uchar(c)); + } + luaL_pushresultsize(&b, cast_uint(n)); + return 1; +} + + +/* +** Buffer to store the result of 'string.dump'. It must be initialized +** after the call to 'lua_dump', to ensure that the function is on the +** top of the stack when 'lua_dump' is called. ('luaL_buffinit' might +** push stuff.) +*/ +struct str_Writer { + int init; /* true iff buffer has been initialized */ + luaL_Buffer B; +}; + + +static int writer (lua_State *L, const void *b, size_t size, void *ud) { + struct str_Writer *state = (struct str_Writer *)ud; + if (!state->init) { + state->init = 1; + luaL_buffinit(L, &state->B); + } + if (b == NULL) { /* finishing dump? */ + luaL_pushresult(&state->B); /* push result */ + lua_replace(L, 1); /* move it to reserved slot */ + } + else + luaL_addlstring(&state->B, (const char *)b, size); + return 0; +} + + +static int str_dump (lua_State *L) { + struct str_Writer state; + int strip = lua_toboolean(L, 2); + luaL_argcheck(L, lua_type(L, 1) == LUA_TFUNCTION && !lua_iscfunction(L, 1), + 1, "Lua function expected"); + /* ensure function is on the top of the stack and vacate slot 1 */ + lua_pushvalue(L, 1); + state.init = 0; + lua_dump(L, writer, &state, strip); + lua_settop(L, 1); /* leave final result on top */ + return 1; +} + + + +/* +** {====================================================== +** METAMETHODS +** ======================================================= +*/ + +#if defined(LUA_NOCVTS2N) /* { */ + +/* no coercion from strings to numbers */ + +static const luaL_Reg stringmetamethods[] = { + {"__index", NULL}, /* placeholder */ + {NULL, NULL} +}; + +#else /* }{ */ + +static int tonum (lua_State *L, int arg) { + if (lua_type(L, arg) == LUA_TNUMBER) { /* already a number? */ + lua_pushvalue(L, arg); + return 1; + } + else { /* check whether it is a numerical string */ + size_t len; + const char *s = lua_tolstring(L, arg, &len); + return (s != NULL && lua_stringtonumber(L, s) == len + 1); + } +} + + +/* +** To be here, either the first operand was a string or the first +** operand didn't have a corresponding metamethod. (Otherwise, that +** other metamethod would have been called.) So, if this metamethod +** doesn't work, the only other option would be for the second +** operand to have a different metamethod. +*/ +static void trymt (lua_State *L, const char *mtkey, const char *opname) { + lua_settop(L, 2); /* back to the original arguments */ + if (l_unlikely(lua_type(L, 2) == LUA_TSTRING || + !luaL_getmetafield(L, 2, mtkey))) + luaL_error(L, "attempt to %s a '%s' with a '%s'", opname, + luaL_typename(L, -2), luaL_typename(L, -1)); + lua_insert(L, -3); /* put metamethod before arguments */ + lua_call(L, 2, 1); /* call metamethod */ +} + + +static int arith (lua_State *L, int op, const char *mtname) { + if (tonum(L, 1) && tonum(L, 2)) + lua_arith(L, op); /* result will be on the top */ + else + trymt(L, mtname, mtname + 2); + return 1; +} + + +static int arith_add (lua_State *L) { + return arith(L, LUA_OPADD, "__add"); +} + +static int arith_sub (lua_State *L) { + return arith(L, LUA_OPSUB, "__sub"); +} + +static int arith_mul (lua_State *L) { + return arith(L, LUA_OPMUL, "__mul"); +} + +static int arith_mod (lua_State *L) { + return arith(L, LUA_OPMOD, "__mod"); +} + +static int arith_pow (lua_State *L) { + return arith(L, LUA_OPPOW, "__pow"); +} + +static int arith_div (lua_State *L) { + return arith(L, LUA_OPDIV, "__div"); +} + +static int arith_idiv (lua_State *L) { + return arith(L, LUA_OPIDIV, "__idiv"); +} + +static int arith_unm (lua_State *L) { + return arith(L, LUA_OPUNM, "__unm"); +} + + +static const luaL_Reg stringmetamethods[] = { + {"__add", arith_add}, + {"__sub", arith_sub}, + {"__mul", arith_mul}, + {"__mod", arith_mod}, + {"__pow", arith_pow}, + {"__div", arith_div}, + {"__idiv", arith_idiv}, + {"__unm", arith_unm}, + {"__index", NULL}, /* placeholder */ + {NULL, NULL} +}; + +#endif /* } */ + +/* }====================================================== */ + +/* +** {====================================================== +** PATTERN MATCHING +** ======================================================= +*/ + + +#define CAP_UNFINISHED (-1) +#define CAP_POSITION (-2) + + +typedef struct MatchState { + const char *src_init; /* init of source string */ + const char *src_end; /* end ('\0') of source string */ + const char *p_end; /* end ('\0') of pattern */ + lua_State *L; + int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ + int level; /* total number of captures (finished or unfinished) */ + struct { + const char *init; + ptrdiff_t len; /* length or special value (CAP_*) */ + } capture[LUA_MAXCAPTURES]; +} MatchState; + + +/* recursive function */ +static const char *match (MatchState *ms, const char *s, const char *p); + + +/* maximum recursion depth for 'match' */ +#if !defined(MAXCCALLS) +#define MAXCCALLS 200 +#endif + + +#define L_ESC '%' +#define SPECIALS "^$*+?.([%-" + + +static int check_capture (MatchState *ms, int l) { + l -= '1'; + if (l_unlikely(l < 0 || l >= ms->level || + ms->capture[l].len == CAP_UNFINISHED)) + return luaL_error(ms->L, "invalid capture index %%%d", l + 1); + return l; +} + + +static int capture_to_close (MatchState *ms) { + int level = ms->level; + for (level--; level>=0; level--) + if (ms->capture[level].len == CAP_UNFINISHED) return level; + return luaL_error(ms->L, "invalid pattern capture"); +} + + +static const char *classend (MatchState *ms, const char *p) { + switch (*p++) { + case L_ESC: { + if (l_unlikely(p == ms->p_end)) + luaL_error(ms->L, "malformed pattern (ends with '%%')"); + return p+1; + } + case '[': { + if (*p == '^') p++; + do { /* look for a ']' */ + if (l_unlikely(p == ms->p_end)) + luaL_error(ms->L, "malformed pattern (missing ']')"); + if (*(p++) == L_ESC && p < ms->p_end) + p++; /* skip escapes (e.g. '%]') */ + } while (*p != ']'); + return p+1; + } + default: { + return p; + } + } +} + + +static int match_class (int c, int cl) { + int res; + switch (tolower(cl)) { + case 'a' : res = isalpha(c); break; + case 'c' : res = iscntrl(c); break; + case 'd' : res = isdigit(c); break; + case 'g' : res = isgraph(c); break; + case 'l' : res = islower(c); break; + case 'p' : res = ispunct(c); break; + case 's' : res = isspace(c); break; + case 'u' : res = isupper(c); break; + case 'w' : res = isalnum(c); break; + case 'x' : res = isxdigit(c); break; + case 'z' : res = (c == 0); break; /* deprecated option */ + default: return (cl == c); + } + return (islower(cl) ? res : !res); +} + + +static int matchbracketclass (int c, const char *p, const char *ec) { + int sig = 1; + if (*(p+1) == '^') { + sig = 0; + p++; /* skip the '^' */ + } + while (++p < ec) { + if (*p == L_ESC) { + p++; + if (match_class(c, cast_uchar(*p))) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < ec)) { + p+=2; + if (cast_uchar(*(p-2)) <= c && c <= cast_uchar(*p)) + return sig; + } + else if (cast_uchar(*p) == c) return sig; + } + return !sig; +} + + +static int singlematch (MatchState *ms, const char *s, const char *p, + const char *ep) { + if (s >= ms->src_end) + return 0; + else { + int c = cast_uchar(*s); + switch (*p) { + case '.': return 1; /* matches any char */ + case L_ESC: return match_class(c, cast_uchar(*(p+1))); + case '[': return matchbracketclass(c, p, ep-1); + default: return (cast_uchar(*p) == c); + } + } +} + + +static const char *matchbalance (MatchState *ms, const char *s, + const char *p) { + if (l_unlikely(p >= ms->p_end - 1)) + luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')"); + if (*s != *p) return NULL; + else { + int b = *p; + int e = *(p+1); + int cont = 1; + while (++s < ms->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } + else if (*s == b) cont++; + } + } + return NULL; /* string ends out of balance */ +} + + +static const char *max_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + ptrdiff_t i = 0; /* counts maximum expand for item */ + while (singlematch(ms, s + i, p, ep)) + i++; + /* keeps trying to match with the maximum repetitions */ + while (i>=0) { + const char *res = match(ms, (s+i), ep+1); + if (res) return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + + +static const char *min_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + for (;;) { + const char *res = match(ms, s, ep+1); + if (res != NULL) + return res; + else if (singlematch(ms, s, p, ep)) + s++; /* try with one more repetition */ + else return NULL; + } +} + + +static const char *start_capture (MatchState *ms, const char *s, + const char *p, int what) { + const char *res; + int level = ms->level; + if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level+1; + if ((res=match(ms, s, p)) == NULL) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + + +static const char *end_capture (MatchState *ms, const char *s, + const char *p) { + int l = capture_to_close(ms); + const char *res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ((res = match(ms, s, p)) == NULL) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + + +static const char *match_capture (MatchState *ms, const char *s, int l) { + size_t len; + l = check_capture(ms, l); + len = cast_sizet(ms->capture[l].len); + if ((size_t)(ms->src_end-s) >= len && + memcmp(ms->capture[l].init, s, len) == 0) + return s+len; + else return NULL; +} + + +static const char *match (MatchState *ms, const char *s, const char *p) { + if (l_unlikely(ms->matchdepth-- == 0)) + luaL_error(ms->L, "pattern too complex"); + init: /* using goto to optimize tail recursion */ + if (p != ms->p_end) { /* end of pattern? */ + switch (*p) { + case '(': { /* start capture */ + if (*(p + 1) == ')') /* position capture? */ + s = start_capture(ms, s, p + 2, CAP_POSITION); + else + s = start_capture(ms, s, p + 1, CAP_UNFINISHED); + break; + } + case ')': { /* end capture */ + s = end_capture(ms, s, p + 1); + break; + } + case '$': { + if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */ + goto dflt; /* no; go to default */ + s = (s == ms->src_end) ? s : NULL; /* check end of string */ + break; + } + case L_ESC: { /* escaped sequences not in the format class[*+?-]? */ + switch (*(p + 1)) { + case 'b': { /* balanced string? */ + s = matchbalance(ms, s, p + 2); + if (s != NULL) { + p += 4; goto init; /* return match(ms, s, p + 4); */ + } /* else fail (s == NULL) */ + break; + } + case 'f': { /* frontier? */ + const char *ep; char previous; + p += 2; + if (l_unlikely(*p != '[')) + luaL_error(ms->L, "missing '[' after '%%f' in pattern"); + ep = classend(ms, p); /* points to what is next */ + previous = (s == ms->src_init) ? '\0' : *(s - 1); + if (!matchbracketclass(cast_uchar(previous), p, ep - 1) && + matchbracketclass(cast_uchar(*s), p, ep - 1)) { + p = ep; goto init; /* return match(ms, s, ep); */ + } + s = NULL; /* match failed */ + break; + } + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': { /* capture results (%0-%9)? */ + s = match_capture(ms, s, cast_uchar(*(p + 1))); + if (s != NULL) { + p += 2; goto init; /* return match(ms, s, p + 2) */ + } + break; + } + default: goto dflt; + } + break; + } + default: dflt: { /* pattern class plus optional suffix */ + const char *ep = classend(ms, p); /* points to optional suffix */ + /* does not match at least once? */ + if (!singlematch(ms, s, p, ep)) { + if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */ + p = ep + 1; goto init; /* return match(ms, s, ep + 1); */ + } + else /* '+' or no suffix */ + s = NULL; /* fail */ + } + else { /* matched once */ + switch (*ep) { /* handle optional suffix */ + case '?': { /* optional */ + const char *res; + if ((res = match(ms, s + 1, ep + 1)) != NULL) + s = res; + else { + p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */ + } + break; + } + case '+': /* 1 or more repetitions */ + s++; /* 1 match already done */ + /* FALLTHROUGH */ + case '*': /* 0 or more repetitions */ + s = max_expand(ms, s, p, ep); + break; + case '-': /* 0 or more repetitions (minimum) */ + s = min_expand(ms, s, p, ep); + break; + default: /* no suffix */ + s++; p = ep; goto init; /* return match(ms, s + 1, ep); */ + } + } + break; + } + } + } + ms->matchdepth++; + return s; +} + + + +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative 'l1' */ + else { + const char *init; /* to search for a '*s2' inside 's1' */ + l2--; /* 1st char will be checked by 'memchr' */ + l1 = l1-l2; /* 's2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct 'l1' and 's1' to try again */ + l1 -= ct_diff2sz(init - s1); + s1 = init; + } + } + return NULL; /* not found */ + } +} + + +/* +** get information about the i-th capture. If there are no captures +** and 'i==0', return information about the whole match, which +** is the range 's'..'e'. If the capture is a string, return +** its length and put its address in '*cap'. If it is an integer +** (a position), push it on the stack and return CAP_POSITION. +*/ +static ptrdiff_t get_onecapture (MatchState *ms, int i, const char *s, + const char *e, const char **cap) { + if (i >= ms->level) { + if (l_unlikely(i != 0)) + luaL_error(ms->L, "invalid capture index %%%d", i + 1); + *cap = s; + return (e - s); + } + else { + ptrdiff_t capl = ms->capture[i].len; + *cap = ms->capture[i].init; + if (l_unlikely(capl == CAP_UNFINISHED)) + luaL_error(ms->L, "unfinished capture"); + else if (capl == CAP_POSITION) + lua_pushinteger(ms->L, + ct_diff2S(ms->capture[i].init - ms->src_init) + 1); + return capl; + } +} + + +/* +** Push the i-th capture on the stack. +*/ +static void push_onecapture (MatchState *ms, int i, const char *s, + const char *e) { + const char *cap; + ptrdiff_t l = get_onecapture(ms, i, s, e, &cap); + if (l != CAP_POSITION) + lua_pushlstring(ms->L, cap, cast_sizet(l)); + /* else position was already pushed */ +} + + +static int push_captures (MatchState *ms, const char *s, const char *e) { + int i; + int nlevels = (ms->level == 0 && s) ? 1 : ms->level; + luaL_checkstack(ms->L, nlevels, "too many captures"); + for (i = 0; i < nlevels; i++) + push_onecapture(ms, i, s, e); + return nlevels; /* number of strings pushed */ +} + + +/* check whether pattern has no special characters */ +static int nospecials (const char *p, size_t l) { + size_t upto = 0; + do { + if (strpbrk(p + upto, SPECIALS)) + return 0; /* pattern has a special character */ + upto += strlen(p + upto) + 1; /* may have more after \0 */ + } while (upto <= l); + return 1; /* no special chars found */ +} + + +static void prepstate (MatchState *ms, lua_State *L, + const char *s, size_t ls, const char *p, size_t lp) { + ms->L = L; + ms->matchdepth = MAXCCALLS; + ms->src_init = s; + ms->src_end = s + ls; + ms->p_end = p + lp; +} + + +static void reprepstate (MatchState *ms) { + ms->level = 0; + lua_assert(ms->matchdepth == MAXCCALLS); +} + + +static int str_find_aux (lua_State *L, int find) { + size_t ls, lp; + const char *s = luaL_checklstring(L, 1, &ls); + const char *p = luaL_checklstring(L, 2, &lp); + size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1; + if (init > ls) { /* start after string's end? */ + luaL_pushfail(L); /* cannot find anything */ + return 1; + } + /* explicit request or no special characters? */ + if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) { + /* do a plain search */ + const char *s2 = lmemfind(s + init, ls - init, p, lp); + if (s2) { + lua_pushinteger(L, ct_diff2S(s2 - s) + 1); + lua_pushinteger(L, cast_st2S(ct_diff2sz(s2 - s) + lp)); + return 2; + } + } + else { + MatchState ms; + const char *s1 = s + init; + int anchor = (*p == '^'); + if (anchor) { + p++; lp--; /* skip anchor character */ + } + prepstate(&ms, L, s, ls, p, lp); + do { + const char *res; + reprepstate(&ms); + if ((res=match(&ms, s1, p)) != NULL) { + if (find) { + lua_pushinteger(L, ct_diff2S(s1 - s) + 1); /* start */ + lua_pushinteger(L, ct_diff2S(res - s)); /* end */ + return push_captures(&ms, NULL, 0) + 2; + } + else + return push_captures(&ms, s1, res); + } + } while (s1++ < ms.src_end && !anchor); + } + luaL_pushfail(L); /* not found */ + return 1; +} + + +static int str_find (lua_State *L) { + return str_find_aux(L, 1); +} + + +static int str_match (lua_State *L) { + return str_find_aux(L, 0); +} + + +/* state for 'gmatch' */ +typedef struct GMatchState { + const char *src; /* current position */ + const char *p; /* pattern */ + const char *lastmatch; /* end of last match */ + MatchState ms; /* match state */ +} GMatchState; + + +static int gmatch_aux (lua_State *L) { + GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3)); + const char *src; + gm->ms.L = L; + for (src = gm->src; src <= gm->ms.src_end; src++) { + const char *e; + reprepstate(&gm->ms); + if ((e = match(&gm->ms, src, gm->p)) != NULL && e != gm->lastmatch) { + gm->src = gm->lastmatch = e; + return push_captures(&gm->ms, src, e); + } + } + return 0; /* not found */ +} + + +static int gmatch (lua_State *L) { + size_t ls, lp; + const char *s = luaL_checklstring(L, 1, &ls); + const char *p = luaL_checklstring(L, 2, &lp); + size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1; + GMatchState *gm; + lua_settop(L, 2); /* keep strings on closure to avoid being collected */ + gm = (GMatchState *)lua_newuserdatauv(L, sizeof(GMatchState), 0); + if (init > ls) /* start after string's end? */ + init = ls + 1; /* avoid overflows in 's + init' */ + prepstate(&gm->ms, L, s, ls, p, lp); + gm->src = s + init; gm->p = p; gm->lastmatch = NULL; + lua_pushcclosure(L, gmatch_aux, 3); + return 1; +} + + +static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e) { + size_t l; + lua_State *L = ms->L; + const char *news = lua_tolstring(L, 3, &l); + const char *p; + while ((p = (char *)memchr(news, L_ESC, l)) != NULL) { + luaL_addlstring(b, news, ct_diff2sz(p - news)); + p++; /* skip ESC */ + if (*p == L_ESC) /* '%%' */ + luaL_addchar(b, *p); + else if (*p == '0') /* '%0' */ + luaL_addlstring(b, s, ct_diff2sz(e - s)); + else if (isdigit(cast_uchar(*p))) { /* '%n' */ + const char *cap; + ptrdiff_t resl = get_onecapture(ms, *p - '1', s, e, &cap); + if (resl == CAP_POSITION) + luaL_addvalue(b); /* add position to accumulated result */ + else + luaL_addlstring(b, cap, cast_sizet(resl)); + } + else + luaL_error(L, "invalid use of '%c' in replacement string", L_ESC); + l -= ct_diff2sz(p + 1 - news); + news = p + 1; + } + luaL_addlstring(b, news, l); +} + + +/* +** Add the replacement value to the string buffer 'b'. +** Return true if the original string was changed. (Function calls and +** table indexing resulting in nil or false do not change the subject.) +*/ +static int add_value (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e, int tr) { + lua_State *L = ms->L; + switch (tr) { + case LUA_TFUNCTION: { /* call the function */ + int n; + lua_pushvalue(L, 3); /* push the function */ + n = push_captures(ms, s, e); /* all captures as arguments */ + lua_call(L, n, 1); /* call it */ + break; + } + case LUA_TTABLE: { /* index the table */ + push_onecapture(ms, 0, s, e); /* first capture is the index */ + lua_gettable(L, 3); + break; + } + default: { /* LUA_TNUMBER or LUA_TSTRING */ + add_s(ms, b, s, e); /* add value to the buffer */ + return 1; /* something changed */ + } + } + if (!lua_toboolean(L, -1)) { /* nil or false? */ + lua_pop(L, 1); /* remove value */ + luaL_addlstring(b, s, ct_diff2sz(e - s)); /* keep original text */ + return 0; /* no changes */ + } + else if (l_unlikely(!lua_isstring(L, -1))) + return luaL_error(L, "invalid replacement value (a %s)", + luaL_typename(L, -1)); + else { + luaL_addvalue(b); /* add result to accumulator */ + return 1; /* something changed */ + } +} + + +static int str_gsub (lua_State *L) { + size_t srcl, lp; + const char *src = luaL_checklstring(L, 1, &srcl); /* subject */ + const char *p = luaL_checklstring(L, 2, &lp); /* pattern */ + const char *lastmatch = NULL; /* end of last match */ + int tr = lua_type(L, 3); /* replacement type */ + /* max replacements */ + lua_Integer max_s = luaL_optinteger(L, 4, cast_st2S(srcl) + 1); + int anchor = (*p == '^'); + lua_Integer n = 0; /* replacement count */ + int changed = 0; /* change flag */ + MatchState ms; + luaL_Buffer b; + luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || + tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, + "string/function/table"); + luaL_buffinit(L, &b); + if (anchor) { + p++; lp--; /* skip anchor character */ + } + prepstate(&ms, L, src, srcl, p, lp); + while (n < max_s) { + const char *e; + reprepstate(&ms); /* (re)prepare state for new match */ + if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */ + n++; + changed = add_value(&ms, &b, src, e, tr) | changed; + src = lastmatch = e; + } + else if (src < ms.src_end) /* otherwise, skip one character */ + luaL_addchar(&b, *src++); + else break; /* end of subject */ + if (anchor) break; + } + if (!changed) /* no changes? */ + lua_pushvalue(L, 1); /* return original string */ + else { /* something changed */ + luaL_addlstring(&b, src, ct_diff2sz(ms.src_end - src)); + luaL_pushresult(&b); /* create and return new string */ + } + lua_pushinteger(L, n); /* number of substitutions */ + return 2; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** STRING FORMAT +** ======================================================= +*/ + +#if !defined(lua_number2strx) /* { */ + +/* +** Hexadecimal floating-point formatter +*/ + +#define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char)) + + +/* +** Number of bits that goes into the first digit. It can be any value +** between 1 and 4; the following definition tries to align the number +** to nibble boundaries by making what is left after that first digit a +** multiple of 4. +*/ +#define L_NBFD ((l_floatatt(MANT_DIG) - 1)%4 + 1) + + +/* +** Add integer part of 'x' to buffer and return new 'x' +*/ +static lua_Number adddigit (char *buff, unsigned n, lua_Number x) { + lua_Number dd = l_mathop(floor)(x); /* get integer part from 'x' */ + int d = (int)dd; + buff[n] = cast_char(d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */ + return x - dd; /* return what is left */ +} + + +static int num2straux (char *buff, unsigned sz, lua_Number x) { + /* if 'inf' or 'NaN', format it like '%g' */ + if (x != x || x == (lua_Number)HUGE_VAL || x == -(lua_Number)HUGE_VAL) + return l_sprintf(buff, sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)x); + else if (x == 0) { /* can be -0... */ + /* create "0" or "-0" followed by exponent */ + return l_sprintf(buff, sz, LUA_NUMBER_FMT "x0p+0", (LUAI_UACNUMBER)x); + } + else { + int e; + lua_Number m = l_mathop(frexp)(x, &e); /* 'x' fraction and exponent */ + unsigned n = 0; /* character count */ + if (m < 0) { /* is number negative? */ + buff[n++] = '-'; /* add sign */ + m = -m; /* make it positive */ + } + buff[n++] = '0'; buff[n++] = 'x'; /* add "0x" */ + m = adddigit(buff, n++, m * (1 << L_NBFD)); /* add first digit */ + e -= L_NBFD; /* this digit goes before the radix point */ + if (m > 0) { /* more digits? */ + buff[n++] = lua_getlocaledecpoint(); /* add radix point */ + do { /* add as many digits as needed */ + m = adddigit(buff, n++, m * 16); + } while (m > 0); + } + n += cast_uint(l_sprintf(buff + n, sz - n, "p%+d", e)); /* add exponent */ + lua_assert(n < sz); + return cast_int(n); + } +} + + +static int lua_number2strx (lua_State *L, char *buff, unsigned sz, + const char *fmt, lua_Number x) { + int n = num2straux(buff, sz, x); + if (fmt[SIZELENMOD] == 'A') { + int i; + for (i = 0; i < n; i++) + buff[i] = cast_char(toupper(cast_uchar(buff[i]))); + } + else if (l_unlikely(fmt[SIZELENMOD] != 'a')) + return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented"); + return n; +} + +#endif /* } */ + + +/* +** Maximum size for items formatted with '%f'. This size is produced +** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.', +** and '\0') + number of decimal digits to represent maxfloat (which +** is maximum exponent + 1). (99+3+1, adding some extra, 110) +*/ +#define MAX_ITEMF (110 + l_floatatt(MAX_10_EXP)) + + +/* +** All formats except '%f' do not need that large limit. The other +** float formats use exponents, so that they fit in the 99 limit for +** significant digits; 's' for large strings and 'q' add items directly +** to the buffer; all integer formats also fit in the 99 limit. The +** worst case are floats: they may need 99 significant digits, plus +** '0x', '-', '.', 'e+XXXX', and '\0'. Adding some extra, 120. +*/ +#define MAX_ITEM 120 + + +/* valid flags in a format specification */ +#if !defined(L_FMTFLAGSF) + +/* valid flags for a, A, e, E, f, F, g, and G conversions */ +#define L_FMTFLAGSF "-+#0 " + +/* valid flags for o, x, and X conversions */ +#define L_FMTFLAGSX "-#0" + +/* valid flags for d and i conversions */ +#define L_FMTFLAGSI "-+0 " + +/* valid flags for u conversions */ +#define L_FMTFLAGSU "-0" + +/* valid flags for c, p, and s conversions */ +#define L_FMTFLAGSC "-" + +#endif + + +/* +** Maximum size of each format specification (such as "%-099.99d"): +** Initial '%', flags (up to 5), width (2), period, precision (2), +** length modifier (8), conversion specifier, and final '\0', plus some +** extra. +*/ +#define MAX_FORMAT 32 + + +static void addquoted (luaL_Buffer *b, const char *s, size_t len) { + luaL_addchar(b, '"'); + while (len--) { + if (*s == '"' || *s == '\\' || *s == '\n') { + luaL_addchar(b, '\\'); + luaL_addchar(b, *s); + } + else if (iscntrl(cast_uchar(*s))) { + char buff[10]; + if (!isdigit(cast_uchar(*(s+1)))) + l_sprintf(buff, sizeof(buff), "\\%d", (int)cast_uchar(*s)); + else + l_sprintf(buff, sizeof(buff), "\\%03d", (int)cast_uchar(*s)); + luaL_addstring(b, buff); + } + else + luaL_addchar(b, *s); + s++; + } + luaL_addchar(b, '"'); +} + + +/* +** Serialize a floating-point number in such a way that it can be +** scanned back by Lua. Use hexadecimal format for "common" numbers +** (to preserve precision); inf, -inf, and NaN are handled separately. +** (NaN cannot be expressed as a numeral, so we write '(0/0)' for it.) +*/ +static int quotefloat (lua_State *L, char *buff, lua_Number n) { + const char *s; /* for the fixed representations */ + if (n == (lua_Number)HUGE_VAL) /* inf? */ + s = "1e9999"; + else if (n == -(lua_Number)HUGE_VAL) /* -inf? */ + s = "-1e9999"; + else if (n != n) /* NaN? */ + s = "(0/0)"; + else { /* format number as hexadecimal */ + int nb = lua_number2strx(L, buff, MAX_ITEM, + "%" LUA_NUMBER_FRMLEN "a", n); + /* ensures that 'buff' string uses a dot as the radix character */ + if (memchr(buff, '.', cast_uint(nb)) == NULL) { /* no dot? */ + char point = lua_getlocaledecpoint(); /* try locale point */ + char *ppoint = (char *)memchr(buff, point, cast_uint(nb)); + if (ppoint) *ppoint = '.'; /* change it to a dot */ + } + return nb; + } + /* for the fixed representations */ + return l_sprintf(buff, MAX_ITEM, "%s", s); +} + + +static void addliteral (lua_State *L, luaL_Buffer *b, int arg) { + switch (lua_type(L, arg)) { + case LUA_TSTRING: { + size_t len; + const char *s = lua_tolstring(L, arg, &len); + addquoted(b, s, len); + break; + } + case LUA_TNUMBER: { + char *buff = luaL_prepbuffsize(b, MAX_ITEM); + int nb; + if (!lua_isinteger(L, arg)) /* float? */ + nb = quotefloat(L, buff, lua_tonumber(L, arg)); + else { /* integers */ + lua_Integer n = lua_tointeger(L, arg); + const char *format = (n == LUA_MININTEGER) /* corner case? */ + ? "0x%" LUA_INTEGER_FRMLEN "x" /* use hex */ + : LUA_INTEGER_FMT; /* else use default format */ + nb = l_sprintf(buff, MAX_ITEM, format, (LUAI_UACINT)n); + } + luaL_addsize(b, cast_uint(nb)); + break; + } + case LUA_TNIL: case LUA_TBOOLEAN: { + luaL_tolstring(L, arg, NULL); + luaL_addvalue(b); + break; + } + default: { + luaL_argerror(L, arg, "value has no literal form"); + } + } +} + + +static const char *get2digits (const char *s) { + if (isdigit(cast_uchar(*s))) { + s++; + if (isdigit(cast_uchar(*s))) s++; /* (2 digits at most) */ + } + return s; +} + + +/* +** Check whether a conversion specification is valid. When called, +** first character in 'form' must be '%' and last character must +** be a valid conversion specifier. 'flags' are the accepted flags; +** 'precision' signals whether to accept a precision. +*/ +static void checkformat (lua_State *L, const char *form, const char *flags, + int precision) { + const char *spec = form + 1; /* skip '%' */ + spec += strspn(spec, flags); /* skip flags */ + if (*spec != '0') { /* a width cannot start with '0' */ + spec = get2digits(spec); /* skip width */ + if (*spec == '.' && precision) { + spec++; + spec = get2digits(spec); /* skip precision */ + } + } + if (!isalpha(cast_uchar(*spec))) /* did not go to the end? */ + luaL_error(L, "invalid conversion specification: '%s'", form); +} + + +/* +** Get a conversion specification and copy it to 'form'. +** Return the address of its last character. +*/ +static const char *getformat (lua_State *L, const char *strfrmt, + char *form) { + /* spans flags, width, and precision ('0' is included as a flag) */ + size_t len = strspn(strfrmt, L_FMTFLAGSF "123456789."); + len++; /* adds following character (should be the specifier) */ + /* still needs space for '%', '\0', plus a length modifier */ + if (len >= MAX_FORMAT - 10) + luaL_error(L, "invalid format (too long)"); + *(form++) = '%'; + memcpy(form, strfrmt, len * sizeof(char)); + *(form + len) = '\0'; + return strfrmt + len - 1; +} + + +/* +** add length modifier into formats +*/ +static void addlenmod (char *form, const char *lenmod) { + size_t l = strlen(form); + size_t lm = strlen(lenmod); + char spec = form[l - 1]; + strcpy(form + l - 1, lenmod); + form[l + lm - 1] = spec; + form[l + lm] = '\0'; +} + + +static int str_format (lua_State *L) { + int top = lua_gettop(L); + int arg = 1; + size_t sfl; + const char *strfrmt = luaL_checklstring(L, arg, &sfl); + const char *strfrmt_end = strfrmt+sfl; + const char *flags; + luaL_Buffer b; + luaL_buffinit(L, &b); + while (strfrmt < strfrmt_end) { + if (*strfrmt != L_ESC) + luaL_addchar(&b, *strfrmt++); + else if (*++strfrmt == L_ESC) + luaL_addchar(&b, *strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT]; /* to store the format ('%...') */ + unsigned maxitem = MAX_ITEM; /* maximum length for the result */ + char *buff = luaL_prepbuffsize(&b, maxitem); /* to put result */ + int nb = 0; /* number of bytes in result */ + if (++arg > top) + return luaL_argerror(L, arg, "no value"); + strfrmt = getformat(L, strfrmt, form); + switch (*strfrmt++) { + case 'c': { + checkformat(L, form, L_FMTFLAGSC, 0); + nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg)); + break; + } + case 'd': case 'i': + flags = L_FMTFLAGSI; + goto intcase; + case 'u': + flags = L_FMTFLAGSU; + goto intcase; + case 'o': case 'x': case 'X': + flags = L_FMTFLAGSX; + intcase: { + lua_Integer n = luaL_checkinteger(L, arg); + checkformat(L, form, flags, 1); + addlenmod(form, LUA_INTEGER_FRMLEN); + nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n); + break; + } + case 'a': case 'A': + checkformat(L, form, L_FMTFLAGSF, 1); + addlenmod(form, LUA_NUMBER_FRMLEN); + nb = lua_number2strx(L, buff, maxitem, form, + luaL_checknumber(L, arg)); + break; + case 'f': + maxitem = MAX_ITEMF; /* extra space for '%f' */ + buff = luaL_prepbuffsize(&b, maxitem); + /* FALLTHROUGH */ + case 'e': case 'E': case 'g': case 'G': { + lua_Number n = luaL_checknumber(L, arg); + checkformat(L, form, L_FMTFLAGSF, 1); + addlenmod(form, LUA_NUMBER_FRMLEN); + nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n); + break; + } + case 'p': { + const void *p = lua_topointer(L, arg); + checkformat(L, form, L_FMTFLAGSC, 0); + if (p == NULL) { /* avoid calling 'printf' with argument NULL */ + p = "(null)"; /* result */ + form[strlen(form) - 1] = 's'; /* format it as a string */ + } + nb = l_sprintf(buff, maxitem, form, p); + break; + } + case 'q': { + if (form[2] != '\0') /* modifiers? */ + return luaL_error(L, "specifier '%%q' cannot have modifiers"); + addliteral(L, &b, arg); + break; + } + case 's': { + size_t l; + const char *s = luaL_tolstring(L, arg, &l); + if (form[2] == '\0') /* no modifiers? */ + luaL_addvalue(&b); /* keep entire string */ + else { + luaL_argcheck(L, l == strlen(s), arg, "string contains zeros"); + checkformat(L, form, L_FMTFLAGSC, 1); + if (strchr(form, '.') == NULL && l >= 100) { + /* no precision and string is too long to be formatted */ + luaL_addvalue(&b); /* keep entire string */ + } + else { /* format the string into 'buff' */ + nb = l_sprintf(buff, maxitem, form, s); + lua_pop(L, 1); /* remove result from 'luaL_tolstring' */ + } + } + break; + } + default: { /* also treat cases 'pnLlh' */ + return luaL_error(L, "invalid conversion '%s' to 'format'", form); + } + } + lua_assert(cast_uint(nb) < maxitem); + luaL_addsize(&b, cast_uint(nb)); + } + } + luaL_pushresult(&b); + return 1; +} + +/* }====================================================== */ + + +/* +** {====================================================== +** PACK/UNPACK +** ======================================================= +*/ + + +/* value used for padding */ +#if !defined(LUAL_PACKPADBYTE) +#define LUAL_PACKPADBYTE 0x00 +#endif + +/* maximum size for the binary representation of an integer */ +#define MAXINTSIZE 16 + +/* number of bits in a character */ +#define NB CHAR_BIT + +/* mask for one character (NB 1's) */ +#define MC ((1 << NB) - 1) + +/* size of a lua_Integer */ +#define SZINT ((int)sizeof(lua_Integer)) + + +/* dummy union to get native endianness */ +static const union { + int dummy; + char little; /* true iff machine is little endian */ +} nativeendian = {1}; + + +/* +** information to pack/unpack stuff +*/ +typedef struct Header { + lua_State *L; + int islittle; + unsigned maxalign; +} Header; + + +/* +** options for pack/unpack +*/ +typedef enum KOption { + Kint, /* signed integers */ + Kuint, /* unsigned integers */ + Kfloat, /* single-precision floating-point numbers */ + Knumber, /* Lua "native" floating-point numbers */ + Kdouble, /* double-precision floating-point numbers */ + Kchar, /* fixed-length strings */ + Kstring, /* strings with prefixed length */ + Kzstr, /* zero-terminated strings */ + Kpadding, /* padding */ + Kpaddalign, /* padding for alignment */ + Knop /* no-op (configuration or spaces) */ +} KOption; + + +/* +** Read an integer numeral from string 'fmt' or return 'df' if +** there is no numeral +*/ +static int digit (int c) { return '0' <= c && c <= '9'; } + +static size_t getnum (const char **fmt, size_t df) { + if (!digit(**fmt)) /* no number? */ + return df; /* return default value */ + else { + size_t a = 0; + do { + a = a*10 + cast_uint(*((*fmt)++) - '0'); + } while (digit(**fmt) && a <= (MAX_SIZE - 9)/10); + return a; + } +} + + +/* +** Read an integer numeral and raises an error if it is larger +** than the maximum size of integers. +*/ +static unsigned getnumlimit (Header *h, const char **fmt, size_t df) { + size_t sz = getnum(fmt, df); + if (l_unlikely((sz - 1u) >= MAXINTSIZE)) + return cast_uint(luaL_error(h->L, + "integral size (%d) out of limits [1,%d]", sz, MAXINTSIZE)); + return cast_uint(sz); +} + + +/* +** Initialize Header +*/ +static void initheader (lua_State *L, Header *h) { + h->L = L; + h->islittle = nativeendian.little; + h->maxalign = 1; +} + + +/* +** Read and classify next option. 'size' is filled with option's size. +*/ +static KOption getoption (Header *h, const char **fmt, size_t *size) { + /* dummy structure to get native alignment requirements */ + struct cD { char c; union { LUAI_MAXALIGN; } u; }; + int opt = *((*fmt)++); + *size = 0; /* default */ + switch (opt) { + case 'b': *size = sizeof(char); return Kint; + case 'B': *size = sizeof(char); return Kuint; + case 'h': *size = sizeof(short); return Kint; + case 'H': *size = sizeof(short); return Kuint; + case 'l': *size = sizeof(long); return Kint; + case 'L': *size = sizeof(long); return Kuint; + case 'j': *size = sizeof(lua_Integer); return Kint; + case 'J': *size = sizeof(lua_Integer); return Kuint; + case 'T': *size = sizeof(size_t); return Kuint; + case 'f': *size = sizeof(float); return Kfloat; + case 'n': *size = sizeof(lua_Number); return Knumber; + case 'd': *size = sizeof(double); return Kdouble; + case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint; + case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint; + case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; + case 'c': + *size = getnum(fmt, cast_sizet(-1)); + if (l_unlikely(*size == cast_sizet(-1))) + luaL_error(h->L, "missing size for format option 'c'"); + return Kchar; + case 'z': return Kzstr; + case 'x': *size = 1; return Kpadding; + case 'X': return Kpaddalign; + case ' ': break; + case '<': h->islittle = 1; break; + case '>': h->islittle = 0; break; + case '=': h->islittle = nativeendian.little; break; + case '!': { + const size_t maxalign = offsetof(struct cD, u); + h->maxalign = getnumlimit(h, fmt, maxalign); + break; + } + default: luaL_error(h->L, "invalid format option '%c'", opt); + } + return Knop; +} + + +/* +** Read, classify, and fill other details about the next option. +** 'psize' is filled with option's size, 'notoalign' with its +** alignment requirements. +** Local variable 'size' gets the size to be aligned. (Kpadal option +** always gets its full alignment, other options are limited by +** the maximum alignment ('maxalign'). Kchar option needs no alignment +** despite its size. +*/ +static KOption getdetails (Header *h, size_t totalsize, const char **fmt, + size_t *psize, unsigned *ntoalign) { + KOption opt = getoption(h, fmt, psize); + size_t align = *psize; /* usually, alignment follows size */ + if (opt == Kpaddalign) { /* 'X' gets alignment from following option */ + if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) + luaL_argerror(h->L, 1, "invalid next option for option 'X'"); + } + if (align <= 1 || opt == Kchar) /* need no alignment? */ + *ntoalign = 0; + else { + if (align > h->maxalign) /* enforce maximum alignment */ + align = h->maxalign; + if (l_unlikely(!ispow2(align))) { /* not a power of 2? */ + *ntoalign = 0; /* to avoid warnings */ + luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); + } + else { + /* 'szmoda' = totalsize % align */ + unsigned szmoda = cast_uint(totalsize & (align - 1)); + *ntoalign = cast_uint((align - szmoda) & (align - 1)); + } + } + return opt; +} + + +/* +** Pack integer 'n' with 'size' bytes and 'islittle' endianness. +** The final 'if' handles the case when 'size' is larger than +** the size of a Lua integer, correcting the extra sign-extension +** bytes if necessary (by default they would be zeros). +*/ +static void packint (luaL_Buffer *b, lua_Unsigned n, + int islittle, unsigned size, int neg) { + char *buff = luaL_prepbuffsize(b, size); + unsigned i; + buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ + for (i = 1; i < size; i++) { + n >>= NB; + buff[islittle ? i : size - 1 - i] = (char)(n & MC); + } + if (neg && size > SZINT) { /* negative number need sign extension? */ + for (i = SZINT; i < size; i++) /* correct extra bytes */ + buff[islittle ? i : size - 1 - i] = (char)MC; + } + luaL_addsize(b, size); /* add result to buffer */ +} + + +/* +** Copy 'size' bytes from 'src' to 'dest', correcting endianness if +** given 'islittle' is different from native endianness. +*/ +static void copywithendian (char *dest, const char *src, + unsigned size, int islittle) { + if (islittle == nativeendian.little) + memcpy(dest, src, size); + else { + dest += size - 1; + while (size-- != 0) + *(dest--) = *(src++); + } +} + + +static int str_pack (lua_State *L) { + luaL_Buffer b; + Header h; + const char *fmt = luaL_checkstring(L, 1); /* format string */ + int arg = 1; /* current argument to pack */ + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + lua_pushnil(L); /* mark to separate arguments from string buffer */ + luaL_buffinit(L, &b); + while (*fmt != '\0') { + unsigned ntoalign; + size_t size; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + luaL_argcheck(L, size + ntoalign <= MAX_SIZE - totalsize, arg, + "result too long"); + totalsize += ntoalign + size; + while (ntoalign-- > 0) + luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */ + arg++; + switch (opt) { + case Kint: { /* signed integers */ + lua_Integer n = luaL_checkinteger(L, arg); + if (size < SZINT) { /* need overflow check? */ + lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1); + luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); + } + packint(&b, (lua_Unsigned)n, h.islittle, cast_uint(size), (n < 0)); + break; + } + case Kuint: { /* unsigned integers */ + lua_Integer n = luaL_checkinteger(L, arg); + if (size < SZINT) /* need overflow check? */ + luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)), + arg, "unsigned overflow"); + packint(&b, (lua_Unsigned)n, h.islittle, cast_uint(size), 0); + break; + } + case Kfloat: { /* C float */ + float f = (float)luaL_checknumber(L, arg); /* get argument */ + char *buff = luaL_prepbuffsize(&b, sizeof(f)); + /* move 'f' to final result, correcting endianness if needed */ + copywithendian(buff, (char *)&f, sizeof(f), h.islittle); + luaL_addsize(&b, size); + break; + } + case Knumber: { /* Lua float */ + lua_Number f = luaL_checknumber(L, arg); /* get argument */ + char *buff = luaL_prepbuffsize(&b, sizeof(f)); + /* move 'f' to final result, correcting endianness if needed */ + copywithendian(buff, (char *)&f, sizeof(f), h.islittle); + luaL_addsize(&b, size); + break; + } + case Kdouble: { /* C double */ + double f = (double)luaL_checknumber(L, arg); /* get argument */ + char *buff = luaL_prepbuffsize(&b, sizeof(f)); + /* move 'f' to final result, correcting endianness if needed */ + copywithendian(buff, (char *)&f, sizeof(f), h.islittle); + luaL_addsize(&b, size); + break; + } + case Kchar: { /* fixed-size string */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, len <= size, arg, "string longer than given size"); + luaL_addlstring(&b, s, len); /* add string */ + if (len < size) { /* does it need padding? */ + size_t psize = size - len; /* pad size */ + char *buff = luaL_prepbuffsize(&b, psize); + memset(buff, LUAL_PACKPADBYTE, psize); + luaL_addsize(&b, psize); + } + break; + } + case Kstring: { /* strings with length count */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, size >= sizeof(lua_Unsigned) || + len < ((lua_Unsigned)1 << (size * NB)), + arg, "string length does not fit in given size"); + /* pack length */ + packint(&b, (lua_Unsigned)len, h.islittle, cast_uint(size), 0); + luaL_addlstring(&b, s, len); + totalsize += len; + break; + } + case Kzstr: { /* zero-terminated string */ + size_t len; + const char *s = luaL_checklstring(L, arg, &len); + luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); + luaL_addlstring(&b, s, len); + luaL_addchar(&b, '\0'); /* add zero at the end */ + totalsize += len + 1; + break; + } + case Kpadding: luaL_addchar(&b, LUAL_PACKPADBYTE); /* FALLTHROUGH */ + case Kpaddalign: case Knop: + arg--; /* undo increment */ + break; + } + } + luaL_pushresult(&b); + return 1; +} + + +static int str_packsize (lua_State *L) { + Header h; + const char *fmt = luaL_checkstring(L, 1); /* format string */ + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + while (*fmt != '\0') { + unsigned ntoalign; + size_t size; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); + luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1, + "variable-length format"); + size += ntoalign; /* total space used by option */ + luaL_argcheck(L, totalsize <= LUA_MAXINTEGER - size, + 1, "format result too large"); + totalsize += size; + } + lua_pushinteger(L, cast_st2S(totalsize)); + return 1; +} + + +/* +** Unpack an integer with 'size' bytes and 'islittle' endianness. +** If size is smaller than the size of a Lua integer and integer +** is signed, must do sign extension (propagating the sign to the +** higher bits); if size is larger than the size of a Lua integer, +** it must check the unread bytes to see whether they do not cause an +** overflow. +*/ +static lua_Integer unpackint (lua_State *L, const char *str, + int islittle, int size, int issigned) { + lua_Unsigned res = 0; + int i; + int limit = (size <= SZINT) ? size : SZINT; + for (i = limit - 1; i >= 0; i--) { + res <<= NB; + res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i]; + } + if (size < SZINT) { /* real size smaller than lua_Integer? */ + if (issigned) { /* needs sign extension? */ + lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1); + res = ((res ^ mask) - mask); /* do sign extension */ + } + } + else if (size > SZINT) { /* must check unread bytes */ + int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; + for (i = limit; i < size; i++) { + if (l_unlikely((unsigned char)str[islittle ? i : size - 1 - i] != mask)) + luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); + } + } + return (lua_Integer)res; +} + + +static int str_unpack (lua_State *L) { + Header h; + const char *fmt = luaL_checkstring(L, 1); + size_t ld; + const char *data = luaL_checklstring(L, 2, &ld); + size_t pos = posrelatI(luaL_optinteger(L, 3, 1), ld) - 1; + int n = 0; /* number of results */ + luaL_argcheck(L, pos <= ld, 3, "initial position out of string"); + initheader(L, &h); + while (*fmt != '\0') { + unsigned ntoalign; + size_t size; + KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); + luaL_argcheck(L, ntoalign + size <= ld - pos, 2, + "data string too short"); + pos += ntoalign; /* skip alignment */ + /* stack space for item + next position */ + luaL_checkstack(L, 2, "too many results"); + n++; + switch (opt) { + case Kint: + case Kuint: { + lua_Integer res = unpackint(L, data + pos, h.islittle, + cast_int(size), (opt == Kint)); + lua_pushinteger(L, res); + break; + } + case Kfloat: { + float f; + copywithendian((char *)&f, data + pos, sizeof(f), h.islittle); + lua_pushnumber(L, (lua_Number)f); + break; + } + case Knumber: { + lua_Number f; + copywithendian((char *)&f, data + pos, sizeof(f), h.islittle); + lua_pushnumber(L, f); + break; + } + case Kdouble: { + double f; + copywithendian((char *)&f, data + pos, sizeof(f), h.islittle); + lua_pushnumber(L, (lua_Number)f); + break; + } + case Kchar: { + lua_pushlstring(L, data + pos, size); + break; + } + case Kstring: { + lua_Unsigned len = (lua_Unsigned)unpackint(L, data + pos, + h.islittle, cast_int(size), 0); + luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short"); + lua_pushlstring(L, data + pos + size, cast_sizet(len)); + pos += cast_sizet(len); /* skip string */ + break; + } + case Kzstr: { + size_t len = strlen(data + pos); + luaL_argcheck(L, pos + len < ld, 2, + "unfinished string for format 'z'"); + lua_pushlstring(L, data + pos, len); + pos += len + 1; /* skip string plus final '\0' */ + break; + } + case Kpaddalign: case Kpadding: case Knop: + n--; /* undo increment */ + break; + } + pos += size; + } + lua_pushinteger(L, cast_st2S(pos) + 1); /* next position */ + return n + 1; +} + +/* }====================================================== */ + + +static const luaL_Reg strlib[] = { + {"byte", str_byte}, + {"char", str_char}, + {"dump", str_dump}, + {"find", str_find}, + {"format", str_format}, + {"gmatch", gmatch}, + {"gsub", str_gsub}, + {"len", str_len}, + {"lower", str_lower}, + {"match", str_match}, + {"rep", str_rep}, + {"reverse", str_reverse}, + {"sub", str_sub}, + {"upper", str_upper}, + {"pack", str_pack}, + {"packsize", str_packsize}, + {"unpack", str_unpack}, + {NULL, NULL} +}; + + +static void createmetatable (lua_State *L) { + /* table to be metatable for strings */ + luaL_newlibtable(L, stringmetamethods); + luaL_setfuncs(L, stringmetamethods, 0); + lua_pushliteral(L, ""); /* dummy string */ + lua_pushvalue(L, -2); /* copy table */ + lua_setmetatable(L, -2); /* set table as metatable for strings */ + lua_pop(L, 1); /* pop dummy string */ + lua_pushvalue(L, -2); /* get string library */ + lua_setfield(L, -2, "__index"); /* metatable.__index = string */ + lua_pop(L, 1); /* pop metatable */ +} + + +/* +** Open string library +*/ +LUAMOD_API int luaopen_string (lua_State *L) { + luaL_newlib(L, strlib); + createmetatable(L); + return 1; +} + + +``` + +`Lua/ltable.c`: + +```c +/* +** $Id: ltable.c $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + +#define ltable_c +#define LUA_CORE + +#include "lprefix.h" + + +/* +** Implementation of tables (aka arrays, objects, or hash tables). +** Tables keep its elements in two parts: an array part and a hash part. +** Non-negative integer keys are all candidates to be kept in the array +** part. The actual size of the array is the largest 'n' such that +** more than half the slots between 1 and n are in use. +** Hash uses a mix of chained scatter table with Brent's variation. +** A main invariant of these tables is that, if an element is not +** in its main position (i.e. the 'original' position that its hash gives +** to it), then the colliding element is in its own main position. +** Hence even when the load factor reaches 100%, performance remains good. +*/ + +#include <math.h> +#include <limits.h> +#include <string.h> + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "lvm.h" + + +/* +** Only hash parts with at least 2^LIMFORLAST have a 'lastfree' field +** that optimizes finding a free slot. That field is stored just before +** the array of nodes, in the same block. Smaller tables do a complete +** search when looking for a free slot. +*/ +#define LIMFORLAST 3 /* log2 of real limit (8) */ + +/* +** The union 'Limbox' stores 'lastfree' and ensures that what follows it +** is properly aligned to store a Node. +*/ +typedef struct { Node *dummy; Node follows_pNode; } Limbox_aux; + +typedef union { + Node *lastfree; + char padding[offsetof(Limbox_aux, follows_pNode)]; +} Limbox; + +#define haslastfree(t) ((t)->lsizenode >= LIMFORLAST) +#define getlastfree(t) ((cast(Limbox *, (t)->node) - 1)->lastfree) + + +/* +** MAXABITS is the largest integer such that 2^MAXABITS fits in an +** unsigned int. +*/ +#define MAXABITS (l_numbits(int) - 1) + + +/* +** MAXASIZEB is the maximum number of elements in the array part such +** that the size of the array fits in 'size_t'. +*/ +#define MAXASIZEB (MAX_SIZET/(sizeof(Value) + 1)) + + +/* +** MAXASIZE is the maximum size of the array part. It is the minimum +** between 2^MAXABITS and MAXASIZEB. +*/ +#define MAXASIZE \ + (((1u << MAXABITS) < MAXASIZEB) ? (1u << MAXABITS) : cast_uint(MAXASIZEB)) + +/* +** MAXHBITS is the largest integer such that 2^MAXHBITS fits in a +** signed int. +*/ +#define MAXHBITS (MAXABITS - 1) + + +/* +** MAXHSIZE is the maximum size of the hash part. It is the minimum +** between 2^MAXHBITS and the maximum size such that, measured in bytes, +** it fits in a 'size_t'. +*/ +#define MAXHSIZE luaM_limitN(1 << MAXHBITS, Node) + + +/* +** When the original hash value is good, hashing by a power of 2 +** avoids the cost of '%'. +*/ +#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) + +/* +** for other types, it is better to avoid modulo by power of 2, as +** they can have many 2 factors. +*/ +#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1u)|1u)))) + + +#define hashstr(t,str) hashpow2(t, (str)->hash) +#define hashboolean(t,p) hashpow2(t, p) + + +#define hashpointer(t,p) hashmod(t, point2uint(p)) + + +#define dummynode (&dummynode_) + +/* +** Common hash part for tables with empty hash parts. That allows all +** tables to have a hash part, avoiding an extra check ("is there a hash +** part?") when indexing. Its sole node has an empty value and a key +** (DEADKEY, NULL) that is different from any valid TValue. +*/ +static const Node dummynode_ = { + {{NULL}, LUA_VEMPTY, /* value's value and type */ + LUA_TDEADKEY, 0, {NULL}} /* key type, next, and key value */ +}; + + +static const TValue absentkey = {ABSTKEYCONSTANT}; + + +/* +** Hash for integers. To allow a good hash, use the remainder operator +** ('%'). If integer fits as a non-negative int, compute an int +** remainder, which is faster. Otherwise, use an unsigned-integer +** remainder, which uses all bits and ensures a non-negative result. +*/ +static Node *hashint (const Table *t, lua_Integer i) { + lua_Unsigned ui = l_castS2U(i); + if (ui <= cast_uint(INT_MAX)) + return gnode(t, cast_int(ui) % cast_int((sizenode(t)-1) | 1)); + else + return hashmod(t, ui); +} + + +/* +** Hash for floating-point numbers. +** The main computation should be just +** n = frexp(n, &i); return (n * INT_MAX) + i +** but there are some numerical subtleties. +** In a two-complement representation, INT_MAX may not have an exact +** representation as a float, but INT_MIN does; because the absolute +** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the +** absolute value of the product 'frexp * -INT_MIN' is smaller or equal +** to INT_MAX. Next, the use of 'unsigned int' avoids overflows when +** adding 'i'; the use of '~u' (instead of '-u') avoids problems with +** INT_MIN. +*/ +#if !defined(l_hashfloat) +static unsigned l_hashfloat (lua_Number n) { + int i; + lua_Integer ni; + n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN); + if (!lua_numbertointeger(n, &ni)) { /* is 'n' inf/-inf/NaN? */ + lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == cast_num(HUGE_VAL)); + return 0; + } + else { /* normal case */ + unsigned int u = cast_uint(i) + cast_uint(ni); + return (u <= cast_uint(INT_MAX) ? u : ~u); + } +} +#endif + + +/* +** returns the 'main' position of an element in a table (that is, +** the index of its hash value). +*/ +static Node *mainpositionTV (const Table *t, const TValue *key) { + switch (ttypetag(key)) { + case LUA_VNUMINT: { + lua_Integer i = ivalue(key); + return hashint(t, i); + } + case LUA_VNUMFLT: { + lua_Number n = fltvalue(key); + return hashmod(t, l_hashfloat(n)); + } + case LUA_VSHRSTR: { + TString *ts = tsvalue(key); + return hashstr(t, ts); + } + case LUA_VLNGSTR: { + TString *ts = tsvalue(key); + return hashpow2(t, luaS_hashlongstr(ts)); + } + case LUA_VFALSE: + return hashboolean(t, 0); + case LUA_VTRUE: + return hashboolean(t, 1); + case LUA_VLIGHTUSERDATA: { + void *p = pvalue(key); + return hashpointer(t, p); + } + case LUA_VLCF: { + lua_CFunction f = fvalue(key); + return hashpointer(t, f); + } + default: { + GCObject *o = gcvalue(key); + return hashpointer(t, o); + } + } +} + + +l_sinline Node *mainpositionfromnode (const Table *t, Node *nd) { + TValue key; + getnodekey(cast(lua_State *, NULL), &key, nd); + return mainpositionTV(t, &key); +} + + +/* +** Check whether key 'k1' is equal to the key in node 'n2'. This +** equality is raw, so there are no metamethods. Floats with integer +** values have been normalized, so integers cannot be equal to +** floats. It is assumed that 'eqshrstr' is simply pointer equality, +** so that short strings are handled in the default case. The flag +** 'deadok' means to accept dead keys as equal to their original values. +** (Only collectable objects can produce dead keys.) Note that dead +** long strings are also compared by identity. Once a key is dead, +** its corresponding value may be collected, and then another value +** can be created with the same address. If this other value is given +** to 'next', 'equalkey' will signal a false positive. In a regular +** traversal, this situation should never happen, as all keys given to +** 'next' came from the table itself, and therefore could not have been +** collected. Outside a regular traversal, we have garbage in, garbage +** out. What is relevant is that this false positive does not break +** anything. (In particular, 'next' will return some other valid item +** on the table or nil.) +*/ +static int equalkey (const TValue *k1, const Node *n2, int deadok) { + if (rawtt(k1) != keytt(n2)) { /* not the same variants? */ + if (keyisshrstr(n2) && ttislngstring(k1)) { + /* an external string can be equal to a short-string key */ + return luaS_eqstr(tsvalue(k1), keystrval(n2)); + } + else if (deadok && keyisdead(n2) && iscollectable(k1)) { + /* a collectable value can be equal to a dead key */ + return gcvalue(k1) == gcvalueraw(keyval(n2)); + } + else + return 0; /* otherwise, different variants cannot be equal */ + } + else { /* equal variants */ + switch (keytt(n2)) { + case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: + return 1; + case LUA_VNUMINT: + return (ivalue(k1) == keyival(n2)); + case LUA_VNUMFLT: + return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2))); + case LUA_VLIGHTUSERDATA: + return pvalue(k1) == pvalueraw(keyval(n2)); + case LUA_VLCF: + return fvalue(k1) == fvalueraw(keyval(n2)); + case ctb(LUA_VLNGSTR): + return luaS_eqstr(tsvalue(k1), keystrval(n2)); + default: + return gcvalue(k1) == gcvalueraw(keyval(n2)); + } + } +} + + +/* +** "Generic" get version. (Not that generic: not valid for integers, +** which may be in array part, nor for floats with integral values.) +** See explanation about 'deadok' in function 'equalkey'. +*/ +static const TValue *getgeneric (Table *t, const TValue *key, int deadok) { + Node *n = mainpositionTV(t, key); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (equalkey(key, n, deadok)) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) + return &absentkey; /* not found */ + n += nx; + } + } +} + + +/* +** Return the index 'k' (converted to an unsigned) if it is inside +** the range [1, limit]. +*/ +static unsigned checkrange (lua_Integer k, unsigned limit) { + return (l_castS2U(k) - 1u < limit) ? cast_uint(k) : 0; +} + + +/* +** Return the index 'k' if 'k' is an appropriate key to live in the +** array part of a table, 0 otherwise. +*/ +#define arrayindex(k) checkrange(k, MAXASIZE) + + +/* +** Check whether an integer key is in the array part of a table and +** return its index there, or zero. +*/ +#define ikeyinarray(t,k) checkrange(k, t->asize) + + +/* +** Check whether a key is in the array part of a table and return its +** index there, or zero. +*/ +static unsigned keyinarray (Table *t, const TValue *key) { + return (ttisinteger(key)) ? ikeyinarray(t, ivalue(key)) : 0; +} + + +/* +** returns the index of a 'key' for table traversals. First goes all +** elements in the array part, then elements in the hash part. The +** beginning of a traversal is signaled by 0. +*/ +static unsigned findindex (lua_State *L, Table *t, TValue *key, + unsigned asize) { + unsigned int i; + if (ttisnil(key)) return 0; /* first iteration */ + i = keyinarray(t, key); + if (i != 0) /* is 'key' inside array part? */ + return i; /* yes; that's the index */ + else { + const TValue *n = getgeneric(t, key, 1); + if (l_unlikely(isabstkey(n))) + luaG_runerror(L, "invalid key to 'next'"); /* key not found */ + i = cast_uint(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ + /* hash elements are numbered after array ones */ + return (i + 1) + asize; + } +} + + +int luaH_next (lua_State *L, Table *t, StkId key) { + unsigned int asize = t->asize; + unsigned int i = findindex(L, t, s2v(key), asize); /* find original key */ + for (; i < asize; i++) { /* try first array part */ + lu_byte tag = *getArrTag(t, i); + if (!tagisempty(tag)) { /* a non-empty entry? */ + setivalue(s2v(key), cast_int(i) + 1); + farr2val(t, i, tag, s2v(key + 1)); + return 1; + } + } + for (i -= asize; i < sizenode(t); i++) { /* hash part */ + if (!isempty(gval(gnode(t, i)))) { /* a non-empty entry? */ + Node *n = gnode(t, i); + getnodekey(L, s2v(key), n); + setobj2s(L, key + 1, gval(n)); + return 1; + } + } + return 0; /* no more elements */ +} + + +/* Extra space in Node array if it has a lastfree entry */ +#define extraLastfree(t) (haslastfree(t) ? sizeof(Limbox) : 0) + +/* 'node' size in bytes */ +static size_t sizehash (Table *t) { + return cast_sizet(sizenode(t)) * sizeof(Node) + extraLastfree(t); +} + + +static void freehash (lua_State *L, Table *t) { + if (!isdummy(t)) { + /* get pointer to the beginning of Node array */ + char *arr = cast_charp(t->node) - extraLastfree(t); + luaM_freearray(L, arr, sizehash(t)); + } +} + + +/* +** {============================================================= +** Rehash +** ============================================================== +*/ + +static int insertkey (Table *t, const TValue *key, TValue *value); +static void newcheckedkey (Table *t, const TValue *key, TValue *value); + + +/* +** Structure to count the keys in a table. +** 'total' is the total number of keys in the table. +** 'na' is the number of *array indices* in the table (see 'arrayindex'). +** 'deleted' is true if there are deleted nodes in the hash part. +** 'nums' is a "count array" where 'nums[i]' is the number of integer +** keys between 2^(i - 1) + 1 and 2^i. Note that 'na' is the summation +** of 'nums'. +*/ +typedef struct { + unsigned total; + unsigned na; + int deleted; + unsigned nums[MAXABITS + 1]; +} Counters; + + +/* +** Check whether it is worth to use 'na' array entries instead of 'nh' +** hash nodes. (A hash node uses ~3 times more memory than an array +** entry: Two values plus 'next' versus one value.) Evaluate with size_t +** to avoid overflows. +*/ +#define arrayXhash(na,nh) (cast_sizet(na) <= cast_sizet(nh) * 3) + +/* +** Compute the optimal size for the array part of table 't'. +** This size maximizes the number of elements going to the array part +** while satisfying the condition 'arrayXhash' with the use of memory if +** all those elements went to the hash part. +** 'ct->na' enters with the total number of array indices in the table +** and leaves with the number of keys that will go to the array part; +** return the optimal size for the array part. +*/ +static unsigned computesizes (Counters *ct) { + int i; + unsigned int twotoi; /* 2^i (candidate for optimal size) */ + unsigned int a = 0; /* number of elements smaller than 2^i */ + unsigned int na = 0; /* number of elements to go to array part */ + unsigned int optimal = 0; /* optimal size for array part */ + /* traverse slices while 'twotoi' does not overflow and total of array + indices still can satisfy 'arrayXhash' against the array size */ + for (i = 0, twotoi = 1; + twotoi > 0 && arrayXhash(twotoi, ct->na); + i++, twotoi *= 2) { + unsigned nums = ct->nums[i]; + a += nums; + if (nums > 0 && /* grows array only if it gets more elements... */ + arrayXhash(twotoi, a)) { /* ...while using "less memory" */ + optimal = twotoi; /* optimal size (till now) */ + na = a; /* all elements up to 'optimal' will go to array part */ + } + } + ct->na = na; + return optimal; +} + + +static void countint (lua_Integer key, Counters *ct) { + unsigned int k = arrayindex(key); + if (k != 0) { /* is 'key' an array index? */ + ct->nums[luaO_ceillog2(k)]++; /* count as such */ + ct->na++; + } +} + + +l_sinline int arraykeyisempty (const Table *t, unsigned key) { + int tag = *getArrTag(t, key - 1); + return tagisempty(tag); +} + + +/* +** Count keys in array part of table 't'. +*/ +static void numusearray (const Table *t, Counters *ct) { + int lg; + unsigned int ttlg; /* 2^lg */ + unsigned int ause = 0; /* summation of 'nums' */ + unsigned int i = 1; /* index to traverse all array keys */ + unsigned int asize = t->asize; + /* traverse each slice */ + for (lg = 0, ttlg = 1; lg <= MAXABITS; lg++, ttlg *= 2) { + unsigned int lc = 0; /* counter */ + unsigned int lim = ttlg; + if (lim > asize) { + lim = asize; /* adjust upper limit */ + if (i > lim) + break; /* no more elements to count */ + } + /* count elements in range (2^(lg - 1), 2^lg] */ + for (; i <= lim; i++) { + if (!arraykeyisempty(t, i)) + lc++; + } + ct->nums[lg] += lc; + ause += lc; + } + ct->total += ause; + ct->na += ause; +} + + +/* +** Count keys in hash part of table 't'. As this only happens during +** a rehash, all nodes have been used. A node can have a nil value only +** if it was deleted after being created. +*/ +static void numusehash (const Table *t, Counters *ct) { + unsigned i = sizenode(t); + unsigned total = 0; + while (i--) { + Node *n = &t->node[i]; + if (isempty(gval(n))) { + lua_assert(!keyisnil(n)); /* entry was deleted; key cannot be nil */ + ct->deleted = 1; + } + else { + total++; + if (keyisinteger(n)) + countint(keyival(n), ct); + } + } + ct->total += total; +} + + +/* +** Convert an "abstract size" (number of slots in an array) to +** "concrete size" (number of bytes in the array). +*/ +static size_t concretesize (unsigned int size) { + if (size == 0) + return 0; + else /* space for the two arrays plus an unsigned in between */ + return size * (sizeof(Value) + 1) + sizeof(unsigned); +} + + +/* +** Resize the array part of a table. If new size is equal to the old, +** do nothing. Else, if new size is zero, free the old array. (It must +** be present, as the sizes are different.) Otherwise, allocate a new +** array, move the common elements to new proper position, and then +** frees the old array. +** We could reallocate the array, but we still would need to move the +** elements to their new position, so the copy implicit in realloc is a +** waste. Moreover, most allocators will move the array anyway when the +** new size is double the old one (the most common case). +*/ +static Value *resizearray (lua_State *L , Table *t, + unsigned oldasize, + unsigned newasize) { + if (oldasize == newasize) + return t->array; /* nothing to be done */ + else if (newasize == 0) { /* erasing array? */ + Value *op = t->array - oldasize; /* original array's real address */ + luaM_freemem(L, op, concretesize(oldasize)); /* free it */ + return NULL; + } + else { + size_t newasizeb = concretesize(newasize); + Value *np = cast(Value *, + luaM_reallocvector(L, NULL, 0, newasizeb, lu_byte)); + if (np == NULL) /* allocation error? */ + return NULL; + np += newasize; /* shift pointer to the end of value segment */ + if (oldasize > 0) { + /* move common elements to new position */ + size_t oldasizeb = concretesize(oldasize); + Value *op = t->array; /* original array */ + unsigned tomove = (oldasize < newasize) ? oldasize : newasize; + size_t tomoveb = (oldasize < newasize) ? oldasizeb : newasizeb; + lua_assert(tomoveb > 0); + memcpy(np - tomove, op - tomove, tomoveb); + luaM_freemem(L, op - oldasize, oldasizeb); /* free old block */ + } + return np; + } +} + + +/* +** Creates an array for the hash part of a table with the given +** size, or reuses the dummy node if size is zero. +** The computation for size overflow is in two steps: the first +** comparison ensures that the shift in the second one does not +** overflow. +*/ +static void setnodevector (lua_State *L, Table *t, unsigned size) { + if (size == 0) { /* no elements to hash part? */ + t->node = cast(Node *, dummynode); /* use common 'dummynode' */ + t->lsizenode = 0; + setdummy(t); /* signal that it is using dummy node */ + } + else { + int i; + int lsize = luaO_ceillog2(size); + if (lsize > MAXHBITS || (1 << lsize) > MAXHSIZE) + luaG_runerror(L, "table overflow"); + size = twoto(lsize); + if (lsize < LIMFORLAST) /* no 'lastfree' field? */ + t->node = luaM_newvector(L, size, Node); + else { + size_t bsize = size * sizeof(Node) + sizeof(Limbox); + char *node = luaM_newblock(L, bsize); + t->node = cast(Node *, node + sizeof(Limbox)); + getlastfree(t) = gnode(t, size); /* all positions are free */ + } + t->lsizenode = cast_byte(lsize); + setnodummy(t); + for (i = 0; i < cast_int(size); i++) { + Node *n = gnode(t, i); + gnext(n) = 0; + setnilkey(n); + setempty(gval(n)); + } + } +} + + +/* +** (Re)insert all elements from the hash part of 'ot' into table 't'. +*/ +static void reinserthash (lua_State *L, Table *ot, Table *t) { + unsigned j; + unsigned size = sizenode(ot); + for (j = 0; j < size; j++) { + Node *old = gnode(ot, j); + if (!isempty(gval(old))) { + /* doesn't need barrier/invalidate cache, as entry was + already present in the table */ + TValue k; + getnodekey(L, &k, old); + newcheckedkey(t, &k, gval(old)); + } + } +} + + +/* +** Exchange the hash part of 't1' and 't2'. (In 'flags', only the +** dummy bit must be exchanged: The 'isrealasize' is not related +** to the hash part, and the metamethod bits do not change during +** a resize, so the "real" table can keep their values.) +*/ +static void exchangehashpart (Table *t1, Table *t2) { + lu_byte lsizenode = t1->lsizenode; + Node *node = t1->node; + int bitdummy1 = t1->flags & BITDUMMY; + t1->lsizenode = t2->lsizenode; + t1->node = t2->node; + t1->flags = cast_byte((t1->flags & NOTBITDUMMY) | (t2->flags & BITDUMMY)); + t2->lsizenode = lsizenode; + t2->node = node; + t2->flags = cast_byte((t2->flags & NOTBITDUMMY) | bitdummy1); +} + + +/* +** Re-insert into the new hash part of a table the elements from the +** vanishing slice of the array part. +*/ +static void reinsertOldSlice (Table *t, unsigned oldasize, + unsigned newasize) { + unsigned i; + for (i = newasize; i < oldasize; i++) { /* traverse vanishing slice */ + lu_byte tag = *getArrTag(t, i); + if (!tagisempty(tag)) { /* a non-empty entry? */ + TValue key, aux; + setivalue(&key, l_castU2S(i) + 1); /* make the key */ + farr2val(t, i, tag, &aux); /* copy value into 'aux' */ + insertkey(t, &key, &aux); /* insert entry into the hash part */ + } + } +} + + +/* +** Clear new slice of the array. +*/ +static void clearNewSlice (Table *t, unsigned oldasize, unsigned newasize) { + for (; oldasize < newasize; oldasize++) + *getArrTag(t, oldasize) = LUA_VEMPTY; +} + + +/* +** Resize table 't' for the new given sizes. Both allocations (for +** the hash part and for the array part) can fail, which creates some +** subtleties. If the first allocation, for the hash part, fails, an +** error is raised and that is it. Otherwise, it copies the elements from +** the shrinking part of the array (if it is shrinking) into the new +** hash. Then it reallocates the array part. If that fails, the table +** is in its original state; the function frees the new hash part and then +** raises the allocation error. Otherwise, it sets the new hash part +** into the table, initializes the new part of the array (if any) with +** nils and reinserts the elements of the old hash back into the new +** parts of the table. +** Note that if the new size for the array part ('newasize') is equal to +** the old one ('oldasize'), this function will do nothing with that +** part. +*/ +void luaH_resize (lua_State *L, Table *t, unsigned newasize, + unsigned nhsize) { + Table newt; /* to keep the new hash part */ + unsigned oldasize = t->asize; + Value *newarray; + if (newasize > MAXASIZE) + luaG_runerror(L, "table overflow"); + /* create new hash part with appropriate size into 'newt' */ + newt.flags = 0; + setnodevector(L, &newt, nhsize); + if (newasize < oldasize) { /* will array shrink? */ + /* re-insert into the new hash the elements from vanishing slice */ + exchangehashpart(t, &newt); /* pretend table has new hash */ + reinsertOldSlice(t, oldasize, newasize); + exchangehashpart(t, &newt); /* restore old hash (in case of errors) */ + } + /* allocate new array */ + newarray = resizearray(L, t, oldasize, newasize); + if (l_unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */ + freehash(L, &newt); /* release new hash part */ + luaM_error(L); /* raise error (with array unchanged) */ + } + /* allocation ok; initialize new part of the array */ + exchangehashpart(t, &newt); /* 't' has the new hash ('newt' has the old) */ + t->array = newarray; /* set new array part */ + t->asize = newasize; + if (newarray != NULL) + *lenhint(t) = newasize / 2u; /* set an initial hint */ + clearNewSlice(t, oldasize, newasize); + /* re-insert elements from old hash part into new parts */ + reinserthash(L, &newt, t); /* 'newt' now has the old hash */ + freehash(L, &newt); /* free old hash part */ +} + + +void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize) { + unsigned nsize = allocsizenode(t); + luaH_resize(L, t, nasize, nsize); +} + + +/* +** Rehash a table. First, count its keys. If there are array indices +** outside the array part, compute the new best size for that part. +** Then, resize the table. +*/ +static void rehash (lua_State *L, Table *t, const TValue *ek) { + unsigned asize; /* optimal size for array part */ + Counters ct; + unsigned i; + unsigned nsize; /* size for the hash part */ + /* reset counts */ + for (i = 0; i <= MAXABITS; i++) ct.nums[i] = 0; + ct.na = 0; + ct.deleted = 0; + ct.total = 1; /* count extra key */ + if (ttisinteger(ek)) + countint(ivalue(ek), &ct); /* extra key may go to array */ + numusehash(t, &ct); /* count keys in hash part */ + if (ct.na == 0) { + /* no new keys to enter array part; keep it with the same size */ + asize = t->asize; + } + else { /* compute best size for array part */ + numusearray(t, &ct); /* count keys in array part */ + asize = computesizes(&ct); /* compute new size for array part */ + } + /* all keys not in the array part go to the hash part */ + nsize = ct.total - ct.na; + if (ct.deleted) { /* table has deleted entries? */ + /* insertion-deletion-insertion: give hash some extra size to + avoid repeated resizings */ + nsize += nsize >> 2; + } + /* resize the table to new computed sizes */ + luaH_resize(L, t, asize, nsize); +} + +/* +** }============================================================= +*/ + + +Table *luaH_new (lua_State *L) { + GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table)); + Table *t = gco2t(o); + t->metatable = NULL; + t->flags = maskflags; /* table has no metamethod fields */ + t->array = NULL; + t->asize = 0; + setnodevector(L, t, 0); + return t; +} + + +lu_mem luaH_size (Table *t) { + lu_mem sz = cast(lu_mem, sizeof(Table)) + concretesize(t->asize); + if (!isdummy(t)) + sz += sizehash(t); + return sz; +} + + +/* +** Frees a table. +*/ +void luaH_free (lua_State *L, Table *t) { + freehash(L, t); + resizearray(L, t, t->asize, 0); + luaM_free(L, t); +} + + +static Node *getfreepos (Table *t) { + if (haslastfree(t)) { /* does it have 'lastfree' information? */ + /* look for a spot before 'lastfree', updating 'lastfree' */ + while (getlastfree(t) > t->node) { + Node *free = --getlastfree(t); + if (keyisnil(free)) + return free; + } + } + else { /* no 'lastfree' information */ + unsigned i = sizenode(t); + while (i--) { /* do a linear search */ + Node *free = gnode(t, i); + if (keyisnil(free)) + return free; + } + } + return NULL; /* could not find a free place */ +} + + + +/* +** Inserts a new key into a hash table; first, check whether key's main +** position is free. If not, check whether colliding node is in its main +** position or not: if it is not, move colliding node to an empty place +** and put new key in its main position; otherwise (colliding node is in +** its main position), new key goes to an empty position. Return 0 if +** could not insert key (could not find a free space). +*/ +static int insertkey (Table *t, const TValue *key, TValue *value) { + Node *mp = mainpositionTV(t, key); + /* table cannot already contain the key */ + lua_assert(isabstkey(getgeneric(t, key, 0))); + if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */ + Node *othern; + Node *f = getfreepos(t); /* get a free place */ + if (f == NULL) /* cannot find a free place? */ + return 0; + lua_assert(!isdummy(t)); + othern = mainpositionfromnode(t, mp); + if (othern != mp) { /* is colliding node out of its main position? */ + /* yes; move colliding node into free position */ + while (othern + gnext(othern) != mp) /* find previous */ + othern += gnext(othern); + gnext(othern) = cast_int(f - othern); /* rechain to point to 'f' */ + *f = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + if (gnext(mp) != 0) { + gnext(f) += cast_int(mp - f); /* correct 'next' */ + gnext(mp) = 0; /* now 'mp' is free */ + } + setempty(gval(mp)); + } + else { /* colliding node is in its own main position */ + /* new node will go into free position */ + if (gnext(mp) != 0) + gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */ + else lua_assert(gnext(f) == 0); + gnext(mp) = cast_int(f - mp); + mp = f; + } + } + setnodekey(mp, key); + lua_assert(isempty(gval(mp))); + setobj2t(cast(lua_State *, 0), gval(mp), value); + return 1; +} + + +/* +** Insert a key in a table where there is space for that key, the +** key is valid, and the value is not nil. +*/ +static void newcheckedkey (Table *t, const TValue *key, TValue *value) { + unsigned i = keyinarray(t, key); + if (i > 0) /* is key in the array part? */ + obj2arr(t, i - 1, value); /* set value in the array */ + else { + int done = insertkey(t, key, value); /* insert key in the hash part */ + lua_assert(done); /* it cannot fail */ + cast(void, done); /* to avoid warnings */ + } +} + + +static void luaH_newkey (lua_State *L, Table *t, const TValue *key, + TValue *value) { + if (!ttisnil(value)) { /* do not insert nil values */ + int done = insertkey(t, key, value); + if (!done) { /* could not find a free place? */ + rehash(L, t, key); /* grow table */ + newcheckedkey(t, key, value); /* insert key in grown table */ + } + luaC_barrierback(L, obj2gco(t), key); + /* for debugging only: any new key may force an emergency collection */ + condchangemem(L, (void)0, (void)0, 1); + } +} + + +static const TValue *getintfromhash (Table *t, lua_Integer key) { + Node *n = hashint(t, key); + lua_assert(!ikeyinarray(t, key)); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (keyisinteger(n) && keyival(n) == key) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) break; + n += nx; + } + } + return &absentkey; +} + + +static int hashkeyisempty (Table *t, lua_Unsigned key) { + const TValue *val = getintfromhash(t, l_castU2S(key)); + return isempty(val); +} + + +static lu_byte finishnodeget (const TValue *val, TValue *res) { + if (!ttisnil(val)) { + setobj(((lua_State*)NULL), res, val); + } + return ttypetag(val); +} + + +lu_byte luaH_getint (Table *t, lua_Integer key, TValue *res) { + unsigned k = ikeyinarray(t, key); + if (k > 0) { + lu_byte tag = *getArrTag(t, k - 1); + if (!tagisempty(tag)) + farr2val(t, k - 1, tag, res); + return tag; + } + else + return finishnodeget(getintfromhash(t, key), res); +} + + +/* +** search function for short strings +*/ +const TValue *luaH_Hgetshortstr (Table *t, TString *key) { + Node *n = hashstr(t, key); + lua_assert(strisshr(key)); + for (;;) { /* check whether 'key' is somewhere in the chain */ + if (keyisshrstr(n) && eqshrstr(keystrval(n), key)) + return gval(n); /* that's it */ + else { + int nx = gnext(n); + if (nx == 0) + return &absentkey; /* not found */ + n += nx; + } + } +} + + +lu_byte luaH_getshortstr (Table *t, TString *key, TValue *res) { + return finishnodeget(luaH_Hgetshortstr(t, key), res); +} + + +static const TValue *Hgetlongstr (Table *t, TString *key) { + TValue ko; + lua_assert(!strisshr(key)); + setsvalue(cast(lua_State *, NULL), &ko, key); + return getgeneric(t, &ko, 0); /* for long strings, use generic case */ +} + + +static const TValue *Hgetstr (Table *t, TString *key) { + if (strisshr(key)) + return luaH_Hgetshortstr(t, key); + else + return Hgetlongstr(t, key); +} + + +lu_byte luaH_getstr (Table *t, TString *key, TValue *res) { + return finishnodeget(Hgetstr(t, key), res); +} + + +/* +** main search function +*/ +lu_byte luaH_get (Table *t, const TValue *key, TValue *res) { + const TValue *slot; + switch (ttypetag(key)) { + case LUA_VSHRSTR: + slot = luaH_Hgetshortstr(t, tsvalue(key)); + break; + case LUA_VNUMINT: + return luaH_getint(t, ivalue(key), res); + case LUA_VNIL: + slot = &absentkey; + break; + case LUA_VNUMFLT: { + lua_Integer k; + if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */ + return luaH_getint(t, k, res); /* use specialized version */ + /* else... */ + } /* FALLTHROUGH */ + default: + slot = getgeneric(t, key, 0); + break; + } + return finishnodeget(slot, res); +} + + +/* +** When a 'pset' cannot be completed, this function returns an encoding +** of its result, to be used by 'luaH_finishset'. +*/ +static int retpsetcode (Table *t, const TValue *slot) { + if (isabstkey(slot)) + return HNOTFOUND; /* no slot with that key */ + else /* return node encoded */ + return cast_int((cast(Node*, slot) - t->node)) + HFIRSTNODE; +} + + +static int finishnodeset (Table *t, const TValue *slot, TValue *val) { + if (!ttisnil(slot)) { + setobj(((lua_State*)NULL), cast(TValue*, slot), val); + return HOK; /* success */ + } + else + return retpsetcode(t, slot); +} + + +static int rawfinishnodeset (const TValue *slot, TValue *val) { + if (isabstkey(slot)) + return 0; /* no slot with that key */ + else { + setobj(((lua_State*)NULL), cast(TValue*, slot), val); + return 1; /* success */ + } +} + + +int luaH_psetint (Table *t, lua_Integer key, TValue *val) { + lua_assert(!ikeyinarray(t, key)); + return finishnodeset(t, getintfromhash(t, key), val); +} + + +static int psetint (Table *t, lua_Integer key, TValue *val) { + int hres; + luaH_fastseti(t, key, val, hres); + return hres; +} + + +/* +** This function could be just this: +** return finishnodeset(t, luaH_Hgetshortstr(t, key), val); +** However, it optimizes the common case created by constructors (e.g., +** {x=1, y=2}), which creates a key in a table that has no metatable, +** it is not old/black, and it already has space for the key. +*/ + +int luaH_psetshortstr (Table *t, TString *key, TValue *val) { + const TValue *slot = luaH_Hgetshortstr(t, key); + if (!ttisnil(slot)) { /* key already has a value? (all too common) */ + setobj(((lua_State*)NULL), cast(TValue*, slot), val); /* update it */ + return HOK; /* done */ + } + else if (checknoTM(t->metatable, TM_NEWINDEX)) { /* no metamethod? */ + if (ttisnil(val)) /* new value is nil? */ + return HOK; /* done (value is already nil/absent) */ + if (isabstkey(slot) && /* key is absent? */ + !(isblack(t) && iswhite(key))) { /* and don't need barrier? */ + TValue tk; /* key as a TValue */ + setsvalue(cast(lua_State *, NULL), &tk, key); + if (insertkey(t, &tk, val)) { /* insert key, if there is space */ + invalidateTMcache(t); + return HOK; + } + } + } + /* Else, either table has new-index metamethod, or it needs barrier, + or it needs to rehash for the new key. In any of these cases, the + operation cannot be completed here. Return a code for the caller. */ + return retpsetcode(t, slot); +} + + +int luaH_psetstr (Table *t, TString *key, TValue *val) { + if (strisshr(key)) + return luaH_psetshortstr(t, key, val); + else + return finishnodeset(t, Hgetlongstr(t, key), val); +} + + +int luaH_pset (Table *t, const TValue *key, TValue *val) { + switch (ttypetag(key)) { + case LUA_VSHRSTR: return luaH_psetshortstr(t, tsvalue(key), val); + case LUA_VNUMINT: return psetint(t, ivalue(key), val); + case LUA_VNIL: return HNOTFOUND; + case LUA_VNUMFLT: { + lua_Integer k; + if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */ + return psetint(t, k, val); /* use specialized version */ + /* else... */ + } /* FALLTHROUGH */ + default: + return finishnodeset(t, getgeneric(t, key, 0), val); + } +} + +/* +** Finish a raw "set table" operation, where 'hres' encodes where the +** value should have been (the result of a previous 'pset' operation). +** Beware: when using this function the caller probably need to check a +** GC barrier and invalidate the TM cache. +*/ +void luaH_finishset (lua_State *L, Table *t, const TValue *key, + TValue *value, int hres) { + lua_assert(hres != HOK); + if (hres == HNOTFOUND) { + TValue aux; + if (l_unlikely(ttisnil(key))) + luaG_runerror(L, "table index is nil"); + else if (ttisfloat(key)) { + lua_Number f = fltvalue(key); + lua_Integer k; + if (luaV_flttointeger(f, &k, F2Ieq)) { + setivalue(&aux, k); /* key is equal to an integer */ + key = &aux; /* insert it as an integer */ + } + else if (l_unlikely(luai_numisnan(f))) + luaG_runerror(L, "table index is NaN"); + } + else if (isextstr(key)) { /* external string? */ + /* If string is short, must internalize it to be used as table key */ + TString *ts = luaS_normstr(L, tsvalue(key)); + setsvalue2s(L, L->top.p++, ts); /* anchor 'ts' (EXTRA_STACK) */ + luaH_newkey(L, t, s2v(L->top.p - 1), value); + L->top.p--; + return; + } + luaH_newkey(L, t, key, value); + } + else if (hres > 0) { /* regular Node? */ + setobj2t(L, gval(gnode(t, hres - HFIRSTNODE)), value); + } + else { /* array entry */ + hres = ~hres; /* real index */ + obj2arr(t, cast_uint(hres), value); + } +} + + +/* +** beware: when using this function you probably need to check a GC +** barrier and invalidate the TM cache. +*/ +void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value) { + int hres = luaH_pset(t, key, value); + if (hres != HOK) + luaH_finishset(L, t, key, value, hres); +} + + +/* +** Ditto for a GC barrier. (No need to invalidate the TM cache, as +** integers cannot be keys to metamethods.) +*/ +void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) { + unsigned ik = ikeyinarray(t, key); + if (ik > 0) + obj2arr(t, ik - 1, value); + else { + int ok = rawfinishnodeset(getintfromhash(t, key), value); + if (!ok) { + TValue k; + setivalue(&k, key); + luaH_newkey(L, t, &k, value); + } + } +} + + +/* +** Try to find a boundary in the hash part of table 't'. From the +** caller, we know that 'asize + 1' is present. We want to find a larger +** key that is absent from the table, so that we can do a binary search +** between the two keys to find a boundary. We keep doubling 'j' until +** we get an absent index. If the doubling would overflow, we try +** LUA_MAXINTEGER. If it is absent, we are ready for the binary search. +** ('j', being max integer, is larger or equal to 'i', but it cannot be +** equal because it is absent while 'i' is present.) Otherwise, 'j' is a +** boundary. ('j + 1' cannot be a present integer key because it is not +** a valid integer in Lua.) +** About 'rnd': If we used a fixed algorithm, a bad actor could fill +** a table with only the keys that would be probed, in such a way that +** a small table could result in a huge length. To avoid that, we use +** the state's seed as a source of randomness. For the first probe, +** we "randomly double" 'i' by adding to it a random number roughly its +** width. +*/ +static lua_Unsigned hash_search (lua_State *L, Table *t, unsigned asize) { + lua_Unsigned i = asize + 1; /* caller ensures t[i] is present */ + unsigned rnd = G(L)->seed; + int n = (asize > 0) ? luaO_ceillog2(asize) : 0; /* width of 'asize' */ + unsigned mask = (1u << n) - 1; /* 11...111 with the width of 'asize' */ + unsigned incr = (rnd & mask) + 1; /* first increment (at least 1) */ + lua_Unsigned j = (incr <= l_castS2U(LUA_MAXINTEGER) - i) ? i + incr : i + 1; + rnd >>= n; /* used 'n' bits from 'rnd' */ + while (!hashkeyisempty(t, j)) { /* repeat until an absent t[j] */ + i = j; /* 'i' is a present index */ + if (j <= l_castS2U(LUA_MAXINTEGER)/2 - 1) { + j = j*2 + (rnd & 1); /* try again with 2j or 2j+1 */ + rnd >>= 1; + } + else { + j = LUA_MAXINTEGER; + if (hashkeyisempty(t, j)) /* t[j] not present? */ + break; /* 'j' now is an absent index */ + else /* weird case */ + return j; /* well, max integer is a boundary... */ + } + } + /* i < j && t[i] present && t[j] absent */ + while (j - i > 1u) { /* do a binary search between them */ + lua_Unsigned m = (i + j) / 2; + if (hashkeyisempty(t, m)) j = m; + else i = m; + } + return i; +} + + +static unsigned int binsearch (Table *array, unsigned int i, unsigned int j) { + lua_assert(i <= j); + while (j - i > 1u) { /* binary search */ + unsigned int m = (i + j) / 2; + if (arraykeyisempty(array, m)) j = m; + else i = m; + } + return i; +} + + +/* return a border, saving it as a hint for next call */ +static lua_Unsigned newhint (Table *t, unsigned hint) { + lua_assert(hint <= t->asize); + *lenhint(t) = hint; + return hint; +} + + +/* +** Try to find a border in table 't'. (A 'border' is an integer index +** such that t[i] is present and t[i+1] is absent, or 0 if t[1] is absent, +** or 'maxinteger' if t[maxinteger] is present.) +** If there is an array part, try to find a border there. First try +** to find it in the vicinity of the previous result (hint), to handle +** cases like 't[#t + 1] = val' or 't[#t] = nil', that move the border +** by one entry. Otherwise, do a binary search to find the border. +** If there is no array part, or its last element is non empty, the +** border may be in the hash part. +*/ +lua_Unsigned luaH_getn (lua_State *L, Table *t) { + unsigned asize = t->asize; + if (asize > 0) { /* is there an array part? */ + const unsigned maxvicinity = 4; + unsigned limit = *lenhint(t); /* start with the hint */ + if (limit == 0) + limit = 1; /* make limit a valid index in the array */ + if (arraykeyisempty(t, limit)) { /* t[limit] empty? */ + /* there must be a border before 'limit' */ + unsigned i; + /* look for a border in the vicinity of the hint */ + for (i = 0; i < maxvicinity && limit > 1; i++) { + limit--; + if (!arraykeyisempty(t, limit)) + return newhint(t, limit); /* 'limit' is a border */ + } + /* t[limit] still empty; search for a border in [0, limit) */ + return newhint(t, binsearch(t, 0, limit)); + } + else { /* 'limit' is present in table; look for a border after it */ + unsigned i; + /* look for a border in the vicinity of the hint */ + for (i = 0; i < maxvicinity && limit < asize; i++) { + limit++; + if (arraykeyisempty(t, limit)) + return newhint(t, limit - 1); /* 'limit - 1' is a border */ + } + if (arraykeyisempty(t, asize)) { /* last element empty? */ + /* t[limit] not empty; search for a border in [limit, asize) */ + return newhint(t, binsearch(t, limit, asize)); + } + } + /* last element non empty; set a hint to speed up finding that again */ + /* (keys in the hash part cannot be hints) */ + *lenhint(t) = asize; + } + /* no array part or t[asize] is not empty; check the hash part */ + lua_assert(asize == 0 || !arraykeyisempty(t, asize)); + if (isdummy(t) || hashkeyisempty(t, asize + 1)) + return asize; /* 'asize + 1' is empty */ + else /* 'asize + 1' is also non empty */ + return hash_search(L, t, asize); +} + + + +#if defined(LUA_DEBUG) + +/* export this function for the test library */ + +Node *luaH_mainposition (const Table *t, const TValue *key) { + return mainpositionTV(t, key); +} + +#endif + +``` + +`Lua/ltable.h`: + +```h +/* +** $Id: ltable.h $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + +#ifndef ltable_h +#define ltable_h + +#include "lobject.h" + + +#define gnode(t,i) (&(t)->node[i]) +#define gval(n) (&(n)->i_val) +#define gnext(n) ((n)->u.next) + + +/* +** Clear all bits of fast-access metamethods, which means that the table +** may have any of these metamethods. (First access that fails after the +** clearing will set the bit again.) +*/ +#define invalidateTMcache(t) ((t)->flags &= cast_byte(~maskflags)) + + +/* +** Bit BITDUMMY set in 'flags' means the table is using the dummy node +** for its hash part. +*/ + +#define BITDUMMY (1 << 6) +#define NOTBITDUMMY cast_byte(~BITDUMMY) +#define isdummy(t) ((t)->flags & BITDUMMY) + +#define setnodummy(t) ((t)->flags &= NOTBITDUMMY) +#define setdummy(t) ((t)->flags |= BITDUMMY) + + + +/* allocated size for hash nodes */ +#define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t)) + + +/* returns the Node, given the value of a table entry */ +#define nodefromval(v) cast(Node *, (v)) + + + +#define luaH_fastgeti(t,k,res,tag) \ + { Table *h = t; lua_Unsigned u = l_castS2U(k) - 1u; \ + if ((u < h->asize)) { \ + tag = *getArrTag(h, u); \ + if (!tagisempty(tag)) { farr2val(h, u, tag, res); }} \ + else { tag = luaH_getint(h, (k), res); }} + + +#define luaH_fastseti(t,k,val,hres) \ + { Table *h = t; lua_Unsigned u = l_castS2U(k) - 1u; \ + if ((u < h->asize)) { \ + lu_byte *tag = getArrTag(h, u); \ + if (checknoTM(h->metatable, TM_NEWINDEX) || !tagisempty(*tag)) \ + { fval2arr(h, u, tag, val); hres = HOK; } \ + else hres = ~cast_int(u); } \ + else { hres = luaH_psetint(h, k, val); }} + + +/* results from pset */ +#define HOK 0 +#define HNOTFOUND 1 +#define HNOTATABLE 2 +#define HFIRSTNODE 3 + +/* +** 'luaH_get*' operations set 'res', unless the value is absent, and +** return the tag of the result. +** The 'luaH_pset*' (pre-set) operations set the given value and return +** HOK, unless the original value is absent. In that case, if the key +** is really absent, they return HNOTFOUND. Otherwise, if there is a +** slot with that key but with no value, 'luaH_pset*' return an encoding +** of where the key is (usually called 'hres'). (pset cannot set that +** value because there might be a metamethod.) If the slot is in the +** hash part, the encoding is (HFIRSTNODE + hash index); if the slot is +** in the array part, the encoding is (~array index), a negative value. +** The value HNOTATABLE is used by the fast macros to signal that the +** value being indexed is not a table. +** (The size for the array part is limited by the maximum power of two +** that fits in an unsigned integer; that is INT_MAX+1. So, the C-index +** ranges from 0, which encodes to -1, to INT_MAX, which encodes to +** INT_MIN. The size of the hash part is limited by the maximum power of +** two that fits in a signed integer; that is (INT_MAX+1)/2. So, it is +** safe to add HFIRSTNODE to any index there.) +*/ + + +/* +** The array part of a table is represented by an inverted array of +** values followed by an array of tags, to avoid wasting space with +** padding. In between them there is an unsigned int, explained later. +** The 'array' pointer points between the two arrays, so that values are +** indexed with negative indices and tags with non-negative indices. + + Values Tags + -------------------------------------------------------- + ... | Value 1 | Value 0 |unsigned|0|1|... + -------------------------------------------------------- + ^ t->array + +** All accesses to 't->array' should be through the macros 'getArrTag' +** and 'getArrVal'. +*/ + +/* Computes the address of the tag for the abstract C-index 'k' */ +#define getArrTag(t,k) (cast(lu_byte*, (t)->array) + sizeof(unsigned) + (k)) + +/* Computes the address of the value for the abstract C-index 'k' */ +#define getArrVal(t,k) ((t)->array - 1 - (k)) + + +/* +** The unsigned between the two arrays is used as a hint for #t; +** see luaH_getn. It is stored there to avoid wasting space in +** the structure Table for tables with no array part. +*/ +#define lenhint(t) cast(unsigned*, (t)->array) + + +/* +** Move TValues to/from arrays, using C indices +*/ +#define arr2obj(h,k,val) \ + ((val)->tt_ = *getArrTag(h,(k)), (val)->value_ = *getArrVal(h,(k))) + +#define obj2arr(h,k,val) \ + (*getArrTag(h,(k)) = (val)->tt_, *getArrVal(h,(k)) = (val)->value_) + + +/* +** Often, we need to check the tag of a value before moving it. The +** following macros also move TValues to/from arrays, but receive the +** precomputed tag value or address as an extra argument. +*/ +#define farr2val(h,k,tag,res) \ + ((res)->tt_ = tag, (res)->value_ = *getArrVal(h,(k))) + +#define fval2arr(h,k,tag,val) \ + (*tag = (val)->tt_, *getArrVal(h,(k)) = (val)->value_) + + +LUAI_FUNC lu_byte luaH_get (Table *t, const TValue *key, TValue *res); +LUAI_FUNC lu_byte luaH_getshortstr (Table *t, TString *key, TValue *res); +LUAI_FUNC lu_byte luaH_getstr (Table *t, TString *key, TValue *res); +LUAI_FUNC lu_byte luaH_getint (Table *t, lua_Integer key, TValue *res); + +/* Special get for metamethods */ +LUAI_FUNC const TValue *luaH_Hgetshortstr (Table *t, TString *key); + +LUAI_FUNC int luaH_psetint (Table *t, lua_Integer key, TValue *val); +LUAI_FUNC int luaH_psetshortstr (Table *t, TString *key, TValue *val); +LUAI_FUNC int luaH_psetstr (Table *t, TString *key, TValue *val); +LUAI_FUNC int luaH_pset (Table *t, const TValue *key, TValue *val); + +LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key, + TValue *value); +LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key, + TValue *value); + +LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key, + TValue *value, int hres); +LUAI_FUNC Table *luaH_new (lua_State *L); +LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned nasize, + unsigned nhsize); +LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned nasize); +LUAI_FUNC lu_mem luaH_size (Table *t); +LUAI_FUNC void luaH_free (lua_State *L, Table *t); +LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); +LUAI_FUNC lua_Unsigned luaH_getn (lua_State *L, Table *t); + + +#if defined(LUA_DEBUG) +LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); +#endif + + +#endif + +``` + +`Lua/ltablib.c`: + +```c +/* +** $Id: ltablib.c $ +** Library for Table Manipulation +** See Copyright Notice in lua.h +*/ + +#define ltablib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include <limits.h> +#include <stddef.h> +#include <string.h> + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "llimits.h" + + +/* +** Operations that an object must define to mimic a table +** (some functions only need some of them) +*/ +#define TAB_R 1 /* read */ +#define TAB_W 2 /* write */ +#define TAB_L 4 /* length */ +#define TAB_RW (TAB_R | TAB_W) /* read/write */ + + +#define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n)) + + +static int checkfield (lua_State *L, const char *key, int n) { + lua_pushstring(L, key); + return (lua_rawget(L, -n) != LUA_TNIL); +} + + +/* +** Check that 'arg' either is a table or can behave like one (that is, +** has a metatable with the required metamethods) +*/ +static void checktab (lua_State *L, int arg, int what) { + if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */ + int n = 1; /* number of elements to pop */ + if (lua_getmetatable(L, arg) && /* must have metatable */ + (!(what & TAB_R) || checkfield(L, "__index", ++n)) && + (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) && + (!(what & TAB_L) || checkfield(L, "__len", ++n))) { + lua_pop(L, n); /* pop metatable and tested metamethods */ + } + else + luaL_checktype(L, arg, LUA_TTABLE); /* force an error */ + } +} + + +static int tcreate (lua_State *L) { + lua_Unsigned sizeseq = (lua_Unsigned)luaL_checkinteger(L, 1); + lua_Unsigned sizerest = (lua_Unsigned)luaL_optinteger(L, 2, 0); + luaL_argcheck(L, sizeseq <= cast_uint(INT_MAX), 1, "out of range"); + luaL_argcheck(L, sizerest <= cast_uint(INT_MAX), 2, "out of range"); + lua_createtable(L, cast_int(sizeseq), cast_int(sizerest)); + return 1; +} + + +static int tinsert (lua_State *L) { + lua_Integer pos; /* where to insert new element */ + lua_Integer e = aux_getn(L, 1, TAB_RW); + e = luaL_intop(+, e, 1); /* first empty element */ + switch (lua_gettop(L)) { + case 2: { /* called with only 2 arguments */ + pos = e; /* insert new element at the end */ + break; + } + case 3: { + lua_Integer i; + pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */ + /* check whether 'pos' is in [1, e] */ + luaL_argcheck(L, (lua_Unsigned)pos - 1u < (lua_Unsigned)e, 2, + "position out of bounds"); + for (i = e; i > pos; i--) { /* move up elements */ + lua_geti(L, 1, i - 1); + lua_seti(L, 1, i); /* t[i] = t[i - 1] */ + } + break; + } + default: { + return luaL_error(L, "wrong number of arguments to 'insert'"); + } + } + lua_seti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int tremove (lua_State *L) { + lua_Integer size = aux_getn(L, 1, TAB_RW); + lua_Integer pos = luaL_optinteger(L, 2, size); + if (pos != size) /* validate 'pos' if given */ + /* check whether 'pos' is in [1, size + 1] */ + luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2, + "position out of bounds"); + lua_geti(L, 1, pos); /* result = t[pos] */ + for ( ; pos < size; pos++) { + lua_geti(L, 1, pos + 1); + lua_seti(L, 1, pos); /* t[pos] = t[pos + 1] */ + } + lua_pushnil(L); + lua_seti(L, 1, pos); /* remove entry t[pos] */ + return 1; +} + + +/* +** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever +** possible, copy in increasing order, which is better for rehashing. +** "possible" means destination after original range, or smaller +** than origin, or copying to another table. +*/ +static int tmove (lua_State *L) { + lua_Integer f = luaL_checkinteger(L, 2); + lua_Integer e = luaL_checkinteger(L, 3); + lua_Integer t = luaL_checkinteger(L, 4); + int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ + checktab(L, 1, TAB_R); + checktab(L, tt, TAB_W); + if (e >= f) { /* otherwise, nothing to move */ + lua_Integer n, i; + luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3, + "too many elements to move"); + n = e - f + 1; /* number of elements to move */ + luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4, + "destination wrap around"); + if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) { + for (i = 0; i < n; i++) { + lua_geti(L, 1, f + i); + lua_seti(L, tt, t + i); + } + } + else { + for (i = n - 1; i >= 0; i--) { + lua_geti(L, 1, f + i); + lua_seti(L, tt, t + i); + } + } + } + lua_pushvalue(L, tt); /* return destination table */ + return 1; +} + + +static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) { + lua_geti(L, 1, i); + if (l_unlikely(!lua_isstring(L, -1))) + luaL_error(L, "invalid value (%s) at index %I in table for 'concat'", + luaL_typename(L, -1), (LUAI_UACINT)i); + luaL_addvalue(b); +} + + +static int tconcat (lua_State *L) { + luaL_Buffer b; + lua_Integer last = aux_getn(L, 1, TAB_R); + size_t lsep; + const char *sep = luaL_optlstring(L, 2, "", &lsep); + lua_Integer i = luaL_optinteger(L, 3, 1); + last = luaL_optinteger(L, 4, last); + luaL_buffinit(L, &b); + for (; i < last; i++) { + addfield(L, &b, i); + luaL_addlstring(&b, sep, lsep); + } + if (i == last) /* add last value (if interval was not empty) */ + addfield(L, &b, i); + luaL_pushresult(&b); + return 1; +} + + +/* +** {====================================================== +** Pack/unpack +** ======================================================= +*/ + +static int tpack (lua_State *L) { + int i; + int n = lua_gettop(L); /* number of elements to pack */ + lua_createtable(L, n, 1); /* create result table */ + lua_insert(L, 1); /* put it at index 1 */ + for (i = n; i >= 1; i--) /* assign elements */ + lua_seti(L, 1, i); + lua_pushinteger(L, n); + lua_setfield(L, 1, "n"); /* t.n = number of elements */ + return 1; /* return table */ +} + + +static int tunpack (lua_State *L) { + lua_Unsigned n; + lua_Integer i = luaL_optinteger(L, 2, 1); + lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); + if (i > e) return 0; /* empty range */ + n = l_castS2U(e) - l_castS2U(i); /* number of elements minus 1 */ + if (l_unlikely(n >= (unsigned int)INT_MAX || + !lua_checkstack(L, (int)(++n)))) + return luaL_error(L, "too many results to unpack"); + for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */ + lua_geti(L, 1, i); + } + lua_geti(L, 1, e); /* push last element */ + return (int)n; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Quicksort +** (based on 'Algorithms in MODULA-3', Robert Sedgewick; +** Addison-Wesley, 1993.) +** ======================================================= +*/ + + +/* +** Type for array indices. These indices are always limited by INT_MAX, +** so it is safe to cast them to lua_Integer even for Lua 32 bits. +*/ +typedef unsigned int IdxT; + + +/* Versions of lua_seti/lua_geti specialized for IdxT */ +#define geti(L,idt,idx) lua_geti(L, idt, l_castU2S(idx)) +#define seti(L,idt,idx) lua_seti(L, idt, l_castU2S(idx)) + + +/* +** Produce a "random" 'unsigned int' to randomize pivot choice. This +** macro is used only when 'sort' detects a big imbalance in the result +** of a partition. (If you don't want/need this "randomness", ~0 is a +** good choice.) +*/ +#if !defined(l_randomizePivot) +#define l_randomizePivot(L) luaL_makeseed(L) +#endif /* } */ + + +/* arrays larger than 'RANLIMIT' may use randomized pivots */ +#define RANLIMIT 100u + + +static void set2 (lua_State *L, IdxT i, IdxT j) { + seti(L, 1, i); + seti(L, 1, j); +} + + +/* +** Return true iff value at stack index 'a' is less than the value at +** index 'b' (according to the order of the sort). +*/ +static int sort_comp (lua_State *L, int a, int b) { + if (lua_isnil(L, 2)) /* no function? */ + return lua_compare(L, a, b, LUA_OPLT); /* a < b */ + else { /* function */ + int res; + lua_pushvalue(L, 2); /* push function */ + lua_pushvalue(L, a-1); /* -1 to compensate function */ + lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */ + lua_call(L, 2, 1); /* call function */ + res = lua_toboolean(L, -1); /* get result */ + lua_pop(L, 1); /* pop result */ + return res; + } +} + + +/* +** Does the partition: Pivot P is at the top of the stack. +** precondition: a[lo] <= P == a[up-1] <= a[up], +** so it only needs to do the partition from lo + 1 to up - 2. +** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up] +** returns 'i'. +*/ +static IdxT partition (lua_State *L, IdxT lo, IdxT up) { + IdxT i = lo; /* will be incremented before first use */ + IdxT j = up - 1; /* will be decremented before first use */ + /* loop invariant: a[lo .. i] <= P <= a[j .. up] */ + for (;;) { + /* next loop: repeat ++i while a[i] < P */ + while ((void)geti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (l_unlikely(i == up - 1)) /* a[up - 1] < P == a[up - 1] */ + luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* after the loop, a[i] >= P and a[lo .. i - 1] < P (a) */ + /* next loop: repeat --j while P < a[j] */ + while ((void)geti(L, 1, --j), sort_comp(L, -3, -1)) { + if (l_unlikely(j < i)) /* j <= i - 1 and a[j] > P, contradicts (a) */ + luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[j] */ + } + /* after the loop, a[j] <= P and a[j + 1 .. up] >= P */ + if (j < i) { /* no elements out of place? */ + /* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */ + lua_pop(L, 1); /* pop a[j] */ + /* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */ + set2(L, up - 1, i); + return i; + } + /* otherwise, swap a[i] - a[j] to restore invariant and repeat */ + set2(L, i, j); + } +} + + +/* +** Choose an element in the middle (2nd-3th quarters) of [lo,up] +** "randomized" by 'rnd' +*/ +static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) { + IdxT r4 = (up - lo) / 4; /* range/4 */ + IdxT p = (rnd ^ lo ^ up) % (r4 * 2) + (lo + r4); + lua_assert(lo + r4 <= p && p <= up - r4); + return p; +} + + +/* +** Quicksort algorithm (recursive function) +*/ +static void auxsort (lua_State *L, IdxT lo, IdxT up, unsigned rnd) { + while (lo < up) { /* loop for tail recursion */ + IdxT p; /* Pivot index */ + IdxT n; /* to be used later */ + /* sort elements 'lo', 'p', and 'up' */ + geti(L, 1, lo); + geti(L, 1, up); + if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */ + set2(L, lo, up); /* swap a[lo] - a[up] */ + else + lua_pop(L, 2); /* remove both values */ + if (up - lo == 1) /* only 2 elements? */ + return; /* already sorted */ + if (up - lo < RANLIMIT || rnd == 0) /* small interval or no randomize? */ + p = (lo + up)/2; /* middle element is a good pivot */ + else /* for larger intervals, it is worth a random pivot */ + p = choosePivot(lo, up, rnd); + geti(L, 1, p); + geti(L, 1, lo); + if (sort_comp(L, -2, -1)) /* a[p] < a[lo]? */ + set2(L, p, lo); /* swap a[p] - a[lo] */ + else { + lua_pop(L, 1); /* remove a[lo] */ + geti(L, 1, up); + if (sort_comp(L, -1, -2)) /* a[up] < a[p]? */ + set2(L, p, up); /* swap a[up] - a[p] */ + else + lua_pop(L, 2); + } + if (up - lo == 2) /* only 3 elements? */ + return; /* already sorted */ + geti(L, 1, p); /* get middle element (Pivot) */ + lua_pushvalue(L, -1); /* push Pivot */ + geti(L, 1, up - 1); /* push a[up - 1] */ + set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */ + p = partition(L, lo, up); + /* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */ + if (p - lo < up - p) { /* lower interval is smaller? */ + auxsort(L, lo, p - 1, rnd); /* call recursively for lower interval */ + n = p - lo; /* size of smaller interval */ + lo = p + 1; /* tail call for [p + 1 .. up] (upper interval) */ + } + else { + auxsort(L, p + 1, up, rnd); /* call recursively for upper interval */ + n = up - p; /* size of smaller interval */ + up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */ + } + if ((up - lo) / 128 > n) /* partition too imbalanced? */ + rnd = l_randomizePivot(L); /* try a new randomization */ + } /* tail call auxsort(L, lo, up, rnd) */ +} + + +static int sort (lua_State *L) { + lua_Integer n = aux_getn(L, 1, TAB_RW); + if (n > 1) { /* non-trivial interval? */ + luaL_argcheck(L, n < INT_MAX, 1, "array too big"); + if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ + luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */ + lua_settop(L, 2); /* make sure there are two arguments */ + auxsort(L, 1, (IdxT)n, 0); + } + return 0; +} + +/* }====================================================== */ + + +static const luaL_Reg tab_funcs[] = { + {"concat", tconcat}, + {"create", tcreate}, + {"insert", tinsert}, + {"pack", tpack}, + {"unpack", tunpack}, + {"remove", tremove}, + {"move", tmove}, + {"sort", sort}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_table (lua_State *L) { + luaL_newlib(L, tab_funcs); + return 1; +} + + +``` + +`Lua/ltm.c`: + +```c +/* +** $Id: ltm.c $ +** Tag methods +** See Copyright Notice in lua.h +*/ + +#define ltm_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <string.h> + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + +static const char udatatypename[] = "userdata"; + +LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTYPES] = { + "no value", + "nil", "boolean", udatatypename, "number", + "string", "table", "function", udatatypename, "thread", + "upvalue", "proto" /* these last cases are used for tests only */ +}; + + +void luaT_init (lua_State *L) { + static const char *const luaT_eventname[] = { /* ORDER TM */ + "__index", "__newindex", + "__gc", "__mode", "__len", "__eq", + "__add", "__sub", "__mul", "__mod", "__pow", + "__div", "__idiv", + "__band", "__bor", "__bxor", "__shl", "__shr", + "__unm", "__bnot", "__lt", "__le", + "__concat", "__call", "__close" + }; + int i; + for (i=0; i<TM_N; i++) { + G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]); + luaC_fix(L, obj2gco(G(L)->tmname[i])); /* never collect these names */ + } +} + + +/* +** function to be used with macro "fasttm": optimized for absence of +** tag methods +*/ +const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { + const TValue *tm = luaH_Hgetshortstr(events, ename); + lua_assert(event <= TM_EQ); + if (notm(tm)) { /* no tag method? */ + events->flags |= cast_byte(1u<<event); /* cache this fact */ + return NULL; + } + else return tm; +} + + +const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) { + Table *mt; + switch (ttype(o)) { + case LUA_TTABLE: + mt = hvalue(o)->metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(o)->metatable; + break; + default: + mt = G(L)->mt[ttype(o)]; + } + return (mt ? luaH_Hgetshortstr(mt, G(L)->tmname[event]) : &G(L)->nilvalue); +} + + +/* +** Return the name of the type of an object. For tables and userdata +** with metatable, use their '__name' metafield, if present. +*/ +const char *luaT_objtypename (lua_State *L, const TValue *o) { + Table *mt; + if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) || + (ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) { + const TValue *name = luaH_Hgetshortstr(mt, luaS_new(L, "__name")); + if (ttisstring(name)) /* is '__name' a string? */ + return getstr(tsvalue(name)); /* use it as type name */ + } + return ttypename(ttype(o)); /* else use standard type name */ +} + + +void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, const TValue *p3) { + StkId func = L->top.p; + setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ + setobj2s(L, func + 1, p1); /* 1st argument */ + setobj2s(L, func + 2, p2); /* 2nd argument */ + setobj2s(L, func + 3, p3); /* 3rd argument */ + L->top.p = func + 4; + /* metamethod may yield only when called from Lua code */ + if (isLuacode(L->ci)) + luaD_call(L, func, 0); + else + luaD_callnoyield(L, func, 0); +} + + +lu_byte luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, StkId res) { + ptrdiff_t result = savestack(L, res); + StkId func = L->top.p; + setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */ + setobj2s(L, func + 1, p1); /* 1st argument */ + setobj2s(L, func + 2, p2); /* 2nd argument */ + L->top.p += 3; + /* metamethod may yield only when called from Lua code */ + if (isLuacode(L->ci)) + luaD_call(L, func, 1); + else + luaD_callnoyield(L, func, 1); + res = restorestack(L, result); + setobjs2s(L, res, --L->top.p); /* move result to its place */ + return ttypetag(s2v(res)); /* return tag of the result */ +} + + +static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ + if (notm(tm)) + tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ + if (notm(tm)) + return -1; /* tag method not found */ + else /* call tag method and return the tag of the result */ + return luaT_callTMres(L, tm, p1, p2, res); +} + + +void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + if (l_unlikely(callbinTM(L, p1, p2, res, event) < 0)) { + switch (event) { + case TM_BAND: case TM_BOR: case TM_BXOR: + case TM_SHL: case TM_SHR: case TM_BNOT: { + if (ttisnumber(p1) && ttisnumber(p2)) + luaG_tointerror(L, p1, p2); + else + luaG_opinterror(L, p1, p2, "perform bitwise operation on"); + } + /* calls never return, but to avoid warnings: *//* FALLTHROUGH */ + default: + luaG_opinterror(L, p1, p2, "perform arithmetic on"); + } + } +} + + +/* +** The use of 'p1' after 'callbinTM' is safe because, when a tag +** method is not found, 'callbinTM' cannot change the stack. +*/ +void luaT_tryconcatTM (lua_State *L) { + StkId p1 = L->top.p - 2; /* first argument */ + if (l_unlikely(callbinTM(L, s2v(p1), s2v(p1 + 1), p1, TM_CONCAT) < 0)) + luaG_concaterror(L, s2v(p1), s2v(p1 + 1)); +} + + +void luaT_trybinassocTM (lua_State *L, const TValue *p1, const TValue *p2, + int flip, StkId res, TMS event) { + if (flip) + luaT_trybinTM(L, p2, p1, res, event); + else + luaT_trybinTM(L, p1, p2, res, event); +} + + +void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, + int flip, StkId res, TMS event) { + TValue aux; + setivalue(&aux, i2); + luaT_trybinassocTM(L, p1, &aux, flip, res, event); +} + + +/* +** Calls an order tag method. +*/ +int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2, + TMS event) { + int tag = callbinTM(L, p1, p2, L->top.p, event); /* try original event */ + if (tag >= 0) /* found tag method? */ + return !tagisfalse(tag); + luaG_ordererror(L, p1, p2); /* no metamethod found */ + return 0; /* to avoid warnings */ +} + + +int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, + int flip, int isfloat, TMS event) { + TValue aux; const TValue *p2; + if (isfloat) { + setfltvalue(&aux, cast_num(v2)); + } + else + setivalue(&aux, v2); + if (flip) { /* arguments were exchanged? */ + p2 = p1; p1 = &aux; /* correct them */ + } + else + p2 = &aux; + return luaT_callorderTM(L, p1, p2, event); +} + + +/* +** Create a vararg table at the top of the stack, with 'n' elements +** starting at 'f'. +*/ +static void createvarargtab (lua_State *L, StkId f, int n) { + int i; + TValue key, value; + Table *t = luaH_new(L); + sethvalue(L, s2v(L->top.p), t); + L->top.p++; + luaH_resize(L, t, cast_uint(n), 1); + setsvalue(L, &key, luaS_new(L, "n")); /* key is "n" */ + setivalue(&value, n); /* value is n */ + /* No need to anchor the key: Due to the resize, the next operation + cannot trigger a garbage collection */ + luaH_set(L, t, &key, &value); /* t.n = n */ + for (i = 0; i < n; i++) + luaH_setint(L, t, i + 1, s2v(f + i)); + luaC_checkGC(L); +} + + +/* +** initial stack: func arg1 ... argn extra1 ... +** ^ ci->func ^ L->top +** final stack: func nil ... nil extra1 ... func arg1 ... argn +** ^ ci->func +*/ +static void buildhiddenargs (lua_State *L, CallInfo *ci, const Proto *p, + int totalargs, int nfixparams, int nextra) { + int i; + ci->u.l.nextraargs = nextra; + luaD_checkstack(L, p->maxstacksize + 1); + /* copy function to the top of the stack, after extra arguments */ + setobjs2s(L, L->top.p++, ci->func.p); + /* move fixed parameters to after the copied function */ + for (i = 1; i <= nfixparams; i++) { + setobjs2s(L, L->top.p++, ci->func.p + i); + setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */ + } + ci->func.p += totalargs + 1; /* 'func' now lives after hidden arguments */ + ci->top.p += totalargs + 1; +} + + +void luaT_adjustvarargs (lua_State *L, CallInfo *ci, const Proto *p) { + int totalargs = cast_int(L->top.p - ci->func.p) - 1; + int nfixparams = p->numparams; + int nextra = totalargs - nfixparams; /* number of extra arguments */ + if (p->flag & PF_VATAB) { /* does it need a vararg table? */ + lua_assert(!(p->flag & PF_VAHID)); + createvarargtab(L, ci->func.p + nfixparams + 1, nextra); + /* move table to proper place (last parameter) */ + setobjs2s(L, ci->func.p + nfixparams + 1, L->top.p - 1); + } + else { /* no table */ + lua_assert(p->flag & PF_VAHID); + buildhiddenargs(L, ci, p, totalargs, nfixparams, nextra); + /* set vararg parameter to nil */ + setnilvalue(s2v(ci->func.p + nfixparams + 1)); + lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p); + } +} + + +void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc) { + int nextra = ci->u.l.nextraargs; + lua_Integer n; + if (tointegerns(rc, &n)) { /* integral value? */ + if (l_castS2U(n) - 1 < cast_uint(nextra)) { + StkId slot = ci->func.p - nextra + cast_int(n) - 1; + setobjs2s(((lua_State*)NULL), ra, slot); + return; + } + } + else if (ttisstring(rc)) { /* string value? */ + size_t len; + const char *s = getlstr(tsvalue(rc), len); + if (len == 1 && s[0] == 'n') { /* key is "n"? */ + setivalue(s2v(ra), nextra); + return; + } + } + setnilvalue(s2v(ra)); /* else produce nil */ +} + + +/* +** Get the number of extra arguments in a vararg function. If vararg +** table has been optimized away, that number is in the call info. +** Otherwise, get the field 'n' from the vararg table and check that it +** has a proper value (non-negative integer not larger than the stack +** limit). +*/ +static int getnumargs (lua_State *L, CallInfo *ci, Table *h) { + if (h == NULL) /* no vararg table? */ + return ci->u.l.nextraargs; + else { + TValue res; + if (luaH_getshortstr(h, luaS_new(L, "n"), &res) != LUA_VNUMINT || + l_castS2U(ivalue(&res)) > cast_uint(INT_MAX/2)) + luaG_runerror(L, "vararg table has no proper 'n'"); + return cast_int(ivalue(&res)); + } +} + + +/* +** Get 'wanted' vararg arguments and put them in 'where'. 'vatab' is +** the register of the vararg table or -1 if there is no vararg table. +*/ +void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted, + int vatab) { + Table *h = (vatab < 0) ? NULL : hvalue(s2v(ci->func.p + vatab + 1)); + int nargs = getnumargs(L, ci, h); /* number of available vararg args. */ + int i, touse; /* 'touse' is minimum between 'wanted' and 'nargs' */ + if (wanted < 0) { + touse = wanted = nargs; /* get all extra arguments available */ + checkstackp(L, nargs, where); /* ensure stack space */ + L->top.p = where + nargs; /* next instruction will need top */ + } + else + touse = (nargs > wanted) ? wanted : nargs; + if (h == NULL) { /* no vararg table? */ + for (i = 0; i < touse; i++) /* get vararg values from the stack */ + setobjs2s(L, where + i, ci->func.p - nargs + i); + } + else { /* get vararg values from vararg table */ + for (i = 0; i < touse; i++) { + lu_byte tag = luaH_getint(h, i + 1, s2v(where + i)); + if (tagisempty(tag)) + setnilvalue(s2v(where + i)); + } + } + for (; i < wanted; i++) /* complete required results with nil */ + setnilvalue(s2v(where + i)); +} + + +``` + +`Lua/ltm.h`: + +```h +/* +** $Id: ltm.h $ +** Tag methods +** See Copyright Notice in lua.h +*/ + +#ifndef ltm_h +#define ltm_h + + +#include "lobject.h" + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER TM" and "ORDER OP" +*/ +typedef enum { + TM_INDEX, + TM_NEWINDEX, + TM_GC, + TM_MODE, + TM_LEN, + TM_EQ, /* last tag method with fast access */ + TM_ADD, + TM_SUB, + TM_MUL, + TM_MOD, + TM_POW, + TM_DIV, + TM_IDIV, + TM_BAND, + TM_BOR, + TM_BXOR, + TM_SHL, + TM_SHR, + TM_UNM, + TM_BNOT, + TM_LT, + TM_LE, + TM_CONCAT, + TM_CALL, + TM_CLOSE, + TM_N /* number of elements in the enum */ +} TMS; + + +/* +** Mask with 1 in all fast-access methods. A 1 in any of these bits +** in the flag of a (meta)table means the metatable does not have the +** corresponding metamethod field. (Bit 6 of the flag indicates that +** the table is using the dummy node; bit 7 is used for 'isrealasize'.) +*/ +#define maskflags cast_byte(~(~0u << (TM_EQ + 1))) + + +/* +** Test whether there is no tagmethod. +** (Because tagmethods use raw accesses, the result may be an "empty" nil.) +*/ +#define notm(tm) ttisnil(tm) + +#define checknoTM(mt,e) ((mt) == NULL || (mt)->flags & (1u<<(e))) + +#define gfasttm(g,mt,e) \ + (checknoTM(mt, e) ? NULL : luaT_gettm(mt, e, (g)->tmname[e])) + +#define fasttm(l,mt,e) gfasttm(G(l), mt, e) + +#define ttypename(x) luaT_typenames_[(x) + 1] + +LUAI_DDEC(const char *const luaT_typenames_[LUA_TOTALTYPES];) + + +LUAI_FUNC const char *luaT_objtypename (lua_State *L, const TValue *o); + +LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); +LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, + TMS event); +LUAI_FUNC void luaT_init (lua_State *L); + +LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, const TValue *p3); +LUAI_FUNC lu_byte luaT_callTMres (lua_State *L, const TValue *f, + const TValue *p1, const TValue *p2, StkId p3); +LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event); +LUAI_FUNC void luaT_tryconcatTM (lua_State *L); +LUAI_FUNC void luaT_trybinassocTM (lua_State *L, const TValue *p1, + const TValue *p2, int inv, StkId res, TMS event); +LUAI_FUNC void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2, + int inv, StkId res, TMS event); +LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1, + const TValue *p2, TMS event); +LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2, + int inv, int isfloat, TMS event); + +LUAI_FUNC void luaT_adjustvarargs (lua_State *L, struct CallInfo *ci, + const Proto *p); +LUAI_FUNC void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc); +LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, StkId where, + int wanted, int vatab); + + +#endif + +``` + +`Lua/lua.h`: + +```h +/* +** $Id: lua.h $ +** Lua - A Scripting Language +** Lua.org, PUC-Rio, Brazil (www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include <stdarg.h> +#include <stddef.h> + + +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2025 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" + + +#define LUA_VERSION_MAJOR_N 5 +#define LUA_VERSION_MINOR_N 5 +#define LUA_VERSION_RELEASE_N 0 + +#define LUA_VERSION_NUM (LUA_VERSION_MAJOR_N * 100 + LUA_VERSION_MINOR_N) +#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + LUA_VERSION_RELEASE_N) + + +#include "luaconf.h" + + +/* mark for precompiled code ('<esc>Lua') */ +#define LUA_SIGNATURE "\x1bLua" + +/* option for multiple returns in 'lua_pcall' and 'lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** Pseudo-indices +** (The stack size is limited to INT_MAX/2; we keep some free empty +** space after that to help overflow detection.) +*/ +#define LUA_REGISTRYINDEX (-(INT_MAX/2 + 1000)) +#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) + + +/* thread status */ +#define LUA_OK 0 +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 + + +typedef struct lua_State lua_State; + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + +#define LUA_NUMTYPES 9 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* predefined values in the registry */ +/* index 1 is reserved for the reference mechanism */ +#define LUA_RIDX_GLOBALS 2 +#define LUA_RIDX_MAINTHREAD 3 +#define LUA_RIDX_LAST 3 + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + +/* unsigned integer type */ +typedef LUA_UNSIGNED lua_Unsigned; + +/* type for continuation-function contexts */ +typedef LUA_KCONTEXT lua_KContext; + + +/* +** Type for C functions registered with Lua +*/ +typedef int (*lua_CFunction) (lua_State *L); + +/* +** Type for continuation functions +*/ +typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx); + + +/* +** Type for functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud); + + +/* +** Type for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** Type for warning functions +*/ +typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont); + + +/* +** Type used by the debug API to collect debug information +*/ +typedef struct lua_Debug lua_Debug; + + +/* +** Functions to be called by the debugger in specific events +*/ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* +** RCS ident string +*/ +extern const char lua_ident[]; + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud, unsigned seed); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); +LUA_API int (lua_closethread) (lua_State *L, lua_State *from); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +LUA_API lua_Number (lua_version) (lua_State *L); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_absindex) (lua_State *L, int idx); +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_rotate) (lua_State *L, int idx, int n); +LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx); +LUA_API int (lua_checkstack) (lua_State *L, int n); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isinteger) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum); +LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API lua_Unsigned (lua_rawlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** Comparison and arithmetic functions +*/ + +#define LUA_OPADD 0 /* ORDER TM, ORDER OP */ +#define LUA_OPSUB 1 +#define LUA_OPMUL 2 +#define LUA_OPMOD 3 +#define LUA_OPPOW 4 +#define LUA_OPDIV 5 +#define LUA_OPIDIV 6 +#define LUA_OPBAND 7 +#define LUA_OPBOR 8 +#define LUA_OPBXOR 9 +#define LUA_OPSHL 10 +#define LUA_OPSHR 11 +#define LUA_OPUNM 12 +#define LUA_OPBNOT 13 + +LUA_API void (lua_arith) (lua_State *L, int op); + +#define LUA_OPEQ 0 +#define LUA_OPLT 1 +#define LUA_OPLE 2 + +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len); +LUA_API const char *(lua_pushexternalstring) (lua_State *L, + const char *s, size_t len, lua_Alloc falloc, void *ud); +LUA_API const char *(lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API int (lua_getglobal) (lua_State *L, const char *name); +LUA_API int (lua_gettable) (lua_State *L, int idx); +LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawget) (lua_State *L, int idx); +LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p); + +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API int (lua_getiuservalue) (lua_State *L, int idx, int n); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_setglobal) (lua_State *L, const char *name); +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n); +LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API int (lua_setiuservalue) (lua_State *L, int idx, int n); + + +/* +** 'load' and 'call' functions (load and run Lua code) +*/ +LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults, + lua_KContext ctx, lua_KFunction k); +#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) + +LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, + lua_KContext ctx, lua_KFunction k); +#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) + +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname, const char *mode); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx, + lua_KFunction k); +LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg, + int *nres); +LUA_API int (lua_status) (lua_State *L); +LUA_API int (lua_isyieldable) (lua_State *L); + +#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) + + +/* +** Warning-related functions +*/ +LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud); +LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont); + + +/* +** garbage-collection options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCISRUNNING 6 +#define LUA_GCGEN 7 +#define LUA_GCINC 8 +#define LUA_GCPARAM 9 + + +/* +** garbage-collection parameters +*/ +/* parameters for generational mode */ +#define LUA_GCPMINORMUL 0 /* control minor collections */ +#define LUA_GCPMAJORMINOR 1 /* control shift major->minor */ +#define LUA_GCPMINORMAJOR 2 /* control shift minor->major */ + +/* parameters for incremental mode */ +#define LUA_GCPPAUSE 3 /* size of pause between successive GCs */ +#define LUA_GCPSTEPMUL 4 /* GC "speed" */ +#define LUA_GCPSTEPSIZE 5 /* GC granularity */ + +/* number of parameters */ +#define LUA_GCPN 6 + + +LUA_API int (lua_gc) (lua_State *L, int what, ...); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); +LUA_API void (lua_len) (lua_State *L, int idx); + +#define LUA_N2SBUFFSZ 64 +LUA_API unsigned (lua_numbertocstring) (lua_State *L, int idx, char *buff); +LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); + +LUA_API void (lua_toclose) (lua_State *L, int idx); +LUA_API void (lua_closeslot) (lua_State *L, int idx); + + +/* +** {============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE)) + +#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL) +#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL) + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) lua_pushstring(L, "" s) + +#define lua_pushglobaltable(L) \ + ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + +#define lua_insert(L,idx) lua_rotate(L, (idx), 1) + +#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1)) + +#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1)) + +/* }============================================================== */ + + +/* +** {============================================================== +** compatibility macros +** =============================================================== +*/ + +#define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1) +#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1) +#define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1) + +#define lua_resetthread(L) lua_closethread(L,NULL) + +/* }============================================================== */ + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILCALL 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + + +LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar); +LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n); +LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n); + +LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n); +LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1, + int fidx2, int n2); + +LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook (lua_gethook) (lua_State *L); +LUA_API int (lua_gethookmask) (lua_State *L); +LUA_API int (lua_gethookcount) (lua_State *L); + + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */ + const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */ + const char *source; /* (S) */ + size_t srclen; /* (S) */ + int currentline; /* (l) */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + unsigned char nups; /* (u) number of upvalues */ + unsigned char nparams;/* (u) number of parameters */ + char isvararg; /* (u) */ + unsigned char extraargs; /* (t) number of extra arguments */ + char istailcall; /* (t) */ + int ftransfer; /* (r) index of first value transferred */ + int ntransfer; /* (r) number of transferred values */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + struct CallInfo *i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +#define LUAI_TOSTRAUX(x) #x +#define LUAI_TOSTR(x) LUAI_TOSTRAUX(x) + +#define LUA_VERSION_MAJOR LUAI_TOSTR(LUA_VERSION_MAJOR_N) +#define LUA_VERSION_MINOR LUAI_TOSTR(LUA_VERSION_MINOR_N) +#define LUA_VERSION_RELEASE LUAI_TOSTR(LUA_VERSION_RELEASE_N) + +#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE + + +/****************************************************************************** +* Copyright (C) 1994-2025 Lua.org, PUC-Rio. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + + +#endif + +``` + +`Lua/lua.hpp`: + +```hpp +// lua.hpp +// Lua header files for C++ +// 'extern "C" not supplied automatically in lua.h and other headers +// because Lua also compiles as C++ + +extern "C" { +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} + +``` + +`Lua/luaconf.h`: + +```h +/* +** $Id: luaconf.h $ +** Configuration file for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef luaconf_h +#define luaconf_h + +#include <limits.h> +#include <stddef.h> + + +/* +** =================================================================== +** General Configuration File for Lua +** +** Some definitions here can be changed externally, through the compiler +** (e.g., with '-D' options): They are commented out or protected +** by '#if !defined' guards. However, several other definitions +** should be changed directly here, either because they affect the +** Lua ABI (by making the changes here, you ensure that all software +** connected to Lua, such as C libraries, will be compiled with the same +** configuration); or because they are seldom changed. +** +** Search for "@@" to find all configurable definitions. +** =================================================================== +*/ + + +/* +** {==================================================================== +** System Configuration: macros to adapt (if needed) Lua to some +** particular platform, for instance restricting it to C89. +** ===================================================================== +*/ + +/* +@@ LUA_USE_C89 controls the use of non-ISO-C89 features. +** Define it if you want Lua to avoid the use of a few C99 features +** or Windows-specific features on Windows. +*/ +/* #define LUA_USE_C89 */ + + +/* +** By default, Lua on Windows use (some) specific Windows features +*/ +#if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE) +#define LUA_USE_WINDOWS /* enable goodies for regular Windows */ +#endif + + +#if defined(LUA_USE_WINDOWS) +#define LUA_DL_DLL /* enable support for DLL */ +#define LUA_USE_C89 /* broadly, Windows is C89 */ +#endif + + +/* +** When POSIX DLL ('LUA_USE_DLOPEN') is enabled, the Lua stand-alone +** application will try to dynamically link a 'readline' facility +** for its REPL. In that case, LUA_READLINELIB is the name of the +** library it will look for those facilities. If lua.c cannot open +** the specified library, it will generate a warning and then run +** without 'readline'. If that macro is not defined, lua.c will not +** use 'readline'. +*/ +#if defined(LUA_USE_LINUX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#define LUA_READLINELIB "libreadline.so" +#endif + + +#if defined(LUA_USE_MACOSX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* macOS does not need -ldl */ +#define LUA_READLINELIB "libedit.dylib" +#endif + + +#if defined(LUA_USE_IOS) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN +#endif + + +#if defined(LUA_USE_C89) && defined(LUA_USE_POSIX) +#error "POSIX is not compatible with C89" +#endif + + +/* +@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits. +*/ +#define LUAI_IS32INT ((UINT_MAX >> 30) >= 3) + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Number types. These options should not be +** set externally, because any other code connected to Lua must +** use the same configuration. +** =================================================================== +*/ + +/* +@@ LUA_INT_TYPE defines the type for Lua integers. +@@ LUA_FLOAT_TYPE defines the type for Lua floats. +** Lua should work fine with any mix of these options supported +** by your C compiler. The usual configurations are 64-bit integers +** and 'double' (the default), 32-bit integers and 'float' (for +** restricted platforms), and 'long'/'double' (for C compilers not +** compliant with C99, which may not have support for 'long long'). +*/ + +/* predefined options for LUA_INT_TYPE */ +#define LUA_INT_INT 1 +#define LUA_INT_LONG 2 +#define LUA_INT_LONGLONG 3 + +/* predefined options for LUA_FLOAT_TYPE */ +#define LUA_FLOAT_FLOAT 1 +#define LUA_FLOAT_DOUBLE 2 +#define LUA_FLOAT_LONGDOUBLE 3 + + +/* Default configuration ('long long' and 'double', for 64-bit Lua) */ +#define LUA_INT_DEFAULT LUA_INT_LONGLONG +#define LUA_FLOAT_DEFAULT LUA_FLOAT_DOUBLE + + +/* +@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. +*/ +/* #define LUA_32BITS */ + + +/* +@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for +** C89 ('long' and 'double'); Windows always has '__int64', so it does +** not need to use this case. +*/ +#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS) +#define LUA_C89_NUMBERS 1 +#else +#define LUA_C89_NUMBERS 0 +#endif + + +#if defined(LUA_32BITS) /* { */ +/* +** 32-bit integers and 'float' +*/ +#if LUAI_IS32INT /* use 'int' if big enough */ +#define LUA_INT_TYPE LUA_INT_INT +#else /* otherwise use 'long' */ +#define LUA_INT_TYPE LUA_INT_LONG +#endif +#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT + +#elif LUA_C89_NUMBERS /* }{ */ +/* +** largest types available for C89 ('long' and 'double') +*/ +#define LUA_INT_TYPE LUA_INT_LONG +#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE + +#else /* }{ */ +/* use defaults */ + +#define LUA_INT_TYPE LUA_INT_DEFAULT +#define LUA_FLOAT_TYPE LUA_FLOAT_DEFAULT + +#endif /* } */ + + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Paths. +** =================================================================== +*/ + +/* +** LUA_PATH_SEP is the character that separates templates in a path. +** LUA_PATH_MARK is the string that marks the substitution points in a +** template. +** LUA_EXEC_DIR in a Windows path is replaced by the executable's +** directory. +*/ +#define LUA_PATH_SEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXEC_DIR "!" + + +/* +@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for +** Lua libraries. +@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for +** C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ + +#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#if defined(_WIN32) /* { */ +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\" + +#if !defined(LUA_PATH_DEFAULT) +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ + LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ + ".\\?.lua;" ".\\?\\init.lua" +#endif + +#if !defined(LUA_CPATH_DEFAULT) +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.dll;" \ + LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ + LUA_CDIR"loadall.dll;" ".\\?.dll" +#endif + +#else /* }{ */ + +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/" +#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/" + +#if !defined(LUA_PATH_DEFAULT) +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ + "./?.lua;" "./?/init.lua" +#endif + +#if !defined(LUA_CPATH_DEFAULT) +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so" +#endif + +#endif /* } */ + + +/* +@@ LUA_DIRSEP is the directory separator (for submodules). +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Lua automatically uses "\".) +*/ +#if !defined(LUA_DIRSEP) + +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif + +#endif + + +/* +** LUA_IGMARK is a mark to ignore all after it when building the +** module name (e.g., used to build the luaopen_ function name). +** Typically, the suffix after the mark is the module version, +** as in "mod-v1.2.so". +*/ +#define LUA_IGMARK "-" + +/* }================================================================== */ + + +/* +** {================================================================== +** Marks for exported symbols in the C code +** =================================================================== +*/ + +/* +@@ LUA_API is a mark for all core API functions. +@@ LUALIB_API is a mark for all auxiliary library functions. +@@ LUAMOD_API is a mark for all standard library opening functions. +** CHANGE them if you need to define those functions in some special way. +** For instance, if you want to create one Windows DLL with the core and +** the libraries, you may want to use the following definition (define +** LUA_BUILD_AS_DLL to get it). +*/ +#if defined(LUA_BUILD_AS_DLL) /* { */ + +#if defined(LUA_CORE) || defined(LUA_LIB) /* { */ +#define LUA_API __declspec(dllexport) +#else /* }{ */ +#define LUA_API __declspec(dllimport) +#endif /* } */ + +#else /* }{ */ + +#define LUA_API extern + +#endif /* } */ + + +/* +** More often than not the libs go together with the core. +*/ +#define LUALIB_API LUA_API + +#if defined(__cplusplus) +/* Lua uses the "C name" when calling open functions */ +#define LUAMOD_API extern "C" +#else +#define LUAMOD_API LUA_API +#endif + +/* }================================================================== */ + + +/* +** {================================================================== +** Compatibility with previous versions +** =================================================================== +*/ + +/* +@@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word +*/ +#define LUA_COMPAT_GLOBAL + + +/* +@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated +** functions in the mathematical library. +** (These functions were already officially removed in 5.3; +** nevertheless they are still available here.) +*/ +/* #define LUA_COMPAT_MATHLIB */ + + +/* +@@ The following macros supply trivial compatibility for some +** changes in the API. The macros themselves document how to +** change your code to avoid using them. +** (Once more, these macros were officially removed in 5.3, but they are +** still available here.) +*/ +#define lua_strlen(L,i) lua_rawlen(L, (i)) + +#define lua_objlen(L,i) lua_rawlen(L, (i)) + +#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) +#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Numbers (low-level part). +** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_* +** satisfy your needs. +** =================================================================== +*/ + +/* +@@ LUAI_UACNUMBER is the result of a 'default argument promotion' +@@ over a floating number. +@@ l_floatatt(x) corrects float attribute 'x' to the proper float type +** by prefixing it with one of FLT/DBL/LDBL. +@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats. +@@ LUA_NUMBER_FMT is the format for writing floats with the maximum +** number of digits that respects tostring(tonumber(numeral)) == numeral. +** (That would be floor(log10(2^n)), where n is the number of bits in +** the float mantissa.) +@@ LUA_NUMBER_FMT_N is the format for writing floats with the minimum +** number of digits that ensures tonumber(tostring(number)) == number. +** (That would be LUA_NUMBER_FMT+2.) +@@ l_mathop allows the addition of an 'l' or 'f' to all math operations. +@@ l_floor takes the floor of a float. +@@ lua_str2number converts a decimal numeral to a number. +*/ + + +/* The following definition is good for most cases here */ + +#define l_floor(x) (l_mathop(floor)(x)) + + +/* now the variable definitions */ + +#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ + +#define LUA_NUMBER float + +#define l_floatatt(n) (FLT_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.7g" +#define LUA_NUMBER_FMT_N "%.9g" + +#define l_mathop(op) op##f + +#define lua_str2number(s,p) strtof((s), (p)) + + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */ + +#define LUA_NUMBER long double + +#define l_floatatt(n) (LDBL_##n) + +#define LUAI_UACNUMBER long double + +#define LUA_NUMBER_FRMLEN "L" +#define LUA_NUMBER_FMT "%.19Lg" +#define LUA_NUMBER_FMT_N "%.21Lg" + +#define l_mathop(op) op##l + +#define lua_str2number(s,p) strtold((s), (p)) + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */ + +#define LUA_NUMBER double + +#define l_floatatt(n) (DBL_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.15g" +#define LUA_NUMBER_FMT_N "%.17g" + +#define l_mathop(op) op + +#define lua_str2number(s,p) strtod((s), (p)) + +#else /* }{ */ + +#error "numeric float type not defined" + +#endif /* } */ + + + +/* +@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER. +@@ LUAI_UACINT is the result of a 'default argument promotion' +@@ over a LUA_INTEGER. +@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers. +@@ LUA_INTEGER_FMT is the format for writing integers. +@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER. +@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER. +@@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED. +@@ lua_integer2str converts an integer to a string. +*/ + + +/* The following definitions are good for most cases here */ + +#define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d" + +#define LUAI_UACINT LUA_INTEGER + +#define lua_integer2str(s,sz,n) \ + l_sprintf((s), sz, LUA_INTEGER_FMT, (LUAI_UACINT)(n)) + +/* +** use LUAI_UACINT here to avoid problems with promotions (which +** can turn a comparison between unsigneds into a signed comparison) +*/ +#define LUA_UNSIGNED unsigned LUAI_UACINT + + +/* now the variable definitions */ + +#if LUA_INT_TYPE == LUA_INT_INT /* { int */ + +#define LUA_INTEGER int +#define LUA_INTEGER_FRMLEN "" + +#define LUA_MAXINTEGER INT_MAX +#define LUA_MININTEGER INT_MIN + +#define LUA_MAXUNSIGNED UINT_MAX + +#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */ + +#define LUA_INTEGER long +#define LUA_INTEGER_FRMLEN "l" + +#define LUA_MAXINTEGER LONG_MAX +#define LUA_MININTEGER LONG_MIN + +#define LUA_MAXUNSIGNED ULONG_MAX + +#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */ + +/* use presence of macro LLONG_MAX as proxy for C99 compliance */ +#if defined(LLONG_MAX) /* { */ +/* use ISO C99 stuff */ + +#define LUA_INTEGER long long +#define LUA_INTEGER_FRMLEN "ll" + +#define LUA_MAXINTEGER LLONG_MAX +#define LUA_MININTEGER LLONG_MIN + +#define LUA_MAXUNSIGNED ULLONG_MAX + +#elif defined(LUA_USE_WINDOWS) /* }{ */ +/* in Windows, can use specific Windows types */ + +#define LUA_INTEGER __int64 +#define LUA_INTEGER_FRMLEN "I64" + +#define LUA_MAXINTEGER _I64_MAX +#define LUA_MININTEGER _I64_MIN + +#define LUA_MAXUNSIGNED _UI64_MAX + +#else /* }{ */ + +#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \ + or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)" + +#endif /* } */ + +#else /* }{ */ + +#error "numeric integer type not defined" + +#endif /* } */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Dependencies with C99 and other C details +** =================================================================== +*/ + +/* +@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89. +** (All uses in Lua have only one format item.) +*/ +#if !defined(LUA_USE_C89) +#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i) +#else +#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i)) +#endif + + +/* +@@ lua_strx2number converts a hexadecimal numeral to a number. +** In C99, 'strtod' does that conversion. Otherwise, you can +** leave 'lua_strx2number' undefined and Lua will provide its own +** implementation. +*/ +#if !defined(LUA_USE_C89) +#define lua_strx2number(s,p) lua_str2number(s,p) +#endif + + +/* +@@ lua_pointer2str converts a pointer to a readable string in a +** non-specified way. +*/ +#define lua_pointer2str(buff,sz,p) l_sprintf(buff,sz,"%p",p) + + +/* +@@ lua_number2strx converts a float to a hexadecimal numeral. +** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that. +** Otherwise, you can leave 'lua_number2strx' undefined and Lua will +** provide its own implementation. +*/ +#if !defined(LUA_USE_C89) +#define lua_number2strx(L,b,sz,f,n) \ + ((void)L, l_sprintf(b,sz,f,(LUAI_UACNUMBER)(n))) +#endif + + +/* +** 'strtof' and 'opf' variants for math functions are not valid in +** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the +** availability of these variants. ('math.h' is already included in +** all files that use these macros.) +*/ +#if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF)) +#undef l_mathop /* variants not available */ +#undef lua_str2number +#define l_mathop(op) (lua_Number)op /* no variant */ +#define lua_str2number(s,p) ((lua_Number)strtod((s), (p))) +#endif + + +/* +@@ LUA_KCONTEXT is the type of the context ('ctx') for continuation +** functions. It must be a numerical type; Lua will use 'intptr_t' if +** available, otherwise it will use 'ptrdiff_t' (the nearest thing to +** 'intptr_t' in C89) +*/ +#define LUA_KCONTEXT ptrdiff_t + +#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 199901L +#include <stdint.h> +#if defined(INTPTR_MAX) /* even in C99 this type is optional */ +#undef LUA_KCONTEXT +#define LUA_KCONTEXT intptr_t +#endif +#endif + + +/* +@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point). +** Change that if you do not want to use C locales. (Code using this +** macro must include the header 'locale.h'.) +*/ +#if !defined(lua_getlocaledecpoint) +#define lua_getlocaledecpoint() (localeconv()->decimal_point[0]) +#endif + + +/* +** macros to improve jump prediction, used mostly for error handling +** and debug facilities. (Some macros in the Lua API use these macros. +** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your +** code.) +*/ +#if !defined(luai_likely) + +#if defined(__GNUC__) && !defined(LUA_NOBUILTIN) +#define luai_likely(x) (__builtin_expect(((x) != 0), 1)) +#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0)) +#else +#define luai_likely(x) (x) +#define luai_unlikely(x) (x) +#endif + +#endif + + + +/* }================================================================== */ + + +/* +** {================================================================== +** Language Variations +** ===================================================================== +*/ + +/* +@@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some +** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from +** numbers to strings. Define LUA_NOCVTS2N to turn off automatic +** coercion from strings to numbers. +*/ +/* #define LUA_NOCVTN2S */ +/* #define LUA_NOCVTS2N */ + + +/* +@@ LUA_USE_APICHECK turns on several consistency checks on the C API. +** Define it as a help when debugging C code. +*/ +/* #define LUA_USE_APICHECK */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Macros that affect the API and must be stable (that is, must be the +** same when you compile Lua and when you compile code that links to +** Lua). +** ===================================================================== +*/ + +/* +@@ LUA_EXTRASPACE defines the size of a raw memory area associated with +** a Lua state with very fast access. +** CHANGE it if you need a different size. +*/ +#define LUA_EXTRASPACE (sizeof(void *)) + + +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +** of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 + + +/* +@@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib +** buffer system. +*/ +#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number))) + + +/* +@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure +** maximum alignment for the other items in that union. +*/ +#define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l + +/* }================================================================== */ + + + + + +/* =================================================================== */ + +/* +** Local configuration. You can use this space to add your redefinitions +** without modifying the main part of the file. +*/ + + + +#endif + + +``` + +`Lua/lualib.h`: + +```h +/* +** $Id: lualib.h $ +** Lua standard libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lualib_h +#define lualib_h + +#include "lua.h" + + +/* version suffix for environment variable names */ +#define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR + +#define LUA_GLIBK 1 +LUAMOD_API int (luaopen_base) (lua_State *L); + +#define LUA_LOADLIBNAME "package" +#define LUA_LOADLIBK (LUA_GLIBK << 1) +LUAMOD_API int (luaopen_package) (lua_State *L); + + +#define LUA_COLIBNAME "coroutine" +#define LUA_COLIBK (LUA_LOADLIBK << 1) +LUAMOD_API int (luaopen_coroutine) (lua_State *L); + +#define LUA_DBLIBNAME "debug" +#define LUA_DBLIBK (LUA_COLIBK << 1) +LUAMOD_API int (luaopen_debug) (lua_State *L); + +#define LUA_IOLIBNAME "io" +#define LUA_IOLIBK (LUA_DBLIBK << 1) +LUAMOD_API int (luaopen_io) (lua_State *L); + +#define LUA_MATHLIBNAME "math" +#define LUA_MATHLIBK (LUA_IOLIBK << 1) +LUAMOD_API int (luaopen_math) (lua_State *L); + +#define LUA_OSLIBNAME "os" +#define LUA_OSLIBK (LUA_MATHLIBK << 1) +LUAMOD_API int (luaopen_os) (lua_State *L); + +#define LUA_STRLIBNAME "string" +#define LUA_STRLIBK (LUA_OSLIBK << 1) +LUAMOD_API int (luaopen_string) (lua_State *L); + +#define LUA_TABLIBNAME "table" +#define LUA_TABLIBK (LUA_STRLIBK << 1) +LUAMOD_API int (luaopen_table) (lua_State *L); + +#define LUA_UTF8LIBNAME "utf8" +#define LUA_UTF8LIBK (LUA_TABLIBK << 1) +LUAMOD_API int (luaopen_utf8) (lua_State *L); + + +/* open selected libraries */ +LUALIB_API void (luaL_openselectedlibs) (lua_State *L, int load, int preload); + +/* open all libraries */ +#define luaL_openlibs(L) luaL_openselectedlibs(L, ~0, 0) + + +#endif + +``` + +`Lua/lundump.c`: + +```c +/* +** $Id: lundump.c $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#define lundump_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <limits.h> +#include <string.h> + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstring.h" +#include "ltable.h" +#include "lundump.h" +#include "lzio.h" + + +#if !defined(luai_verifycode) +#define luai_verifycode(L,f) /* empty */ +#endif + + +typedef struct { + lua_State *L; + ZIO *Z; + const char *name; + Table *h; /* list for string reuse */ + size_t offset; /* current position relative to beginning of dump */ + lua_Unsigned nstr; /* number of strings in the list */ + lu_byte fixed; /* dump is fixed in memory */ +} LoadState; + + +static l_noret error (LoadState *S, const char *why) { + luaO_pushfstring(S->L, "%s: bad binary format (%s)", S->name, why); + luaD_throw(S->L, LUA_ERRSYNTAX); +} + + +/* +** All high-level loads go through loadVector; you can change it to +** adapt to the endianness of the input +*/ +#define loadVector(S,b,n) loadBlock(S,b,cast_sizet(n)*sizeof((b)[0])) + +static void loadBlock (LoadState *S, void *b, size_t size) { + if (luaZ_read(S->Z, b, size) != 0) + error(S, "truncated chunk"); + S->offset += size; +} + + +static void loadAlign (LoadState *S, unsigned align) { + unsigned padding = align - cast_uint(S->offset % align); + if (padding < align) { /* (padding == align) means no padding */ + lua_Integer paddingContent; + loadBlock(S, &paddingContent, padding); + lua_assert(S->offset % align == 0); + } +} + + +#define getaddr(S,n,t) cast(t *, getaddr_(S,cast_sizet(n) * sizeof(t))) + +static const void *getaddr_ (LoadState *S, size_t size) { + const void *block = luaZ_getaddr(S->Z, size); + S->offset += size; + if (block == NULL) + error(S, "truncated fixed buffer"); + return block; +} + + +#define loadVar(S,x) loadVector(S,&x,1) + + +static lu_byte loadByte (LoadState *S) { + int b = zgetc(S->Z); + if (b == EOZ) + error(S, "truncated chunk"); + S->offset++; + return cast_byte(b); +} + + +static lua_Unsigned loadVarint (LoadState *S, lua_Unsigned limit) { + lua_Unsigned x = 0; + int b; + limit >>= 7; + do { + b = loadByte(S); + if (x > limit) + error(S, "integer overflow"); + x = (x << 7) | (b & 0x7f); + } while ((b & 0x80) != 0); + return x; +} + + +static size_t loadSize (LoadState *S) { + return cast_sizet(loadVarint(S, MAX_SIZE)); +} + + +static int loadInt (LoadState *S) { + return cast_int(loadVarint(S, cast_sizet(INT_MAX))); +} + + + +static lua_Number loadNumber (LoadState *S) { + lua_Number x; + loadVar(S, x); + return x; +} + + +static lua_Integer loadInteger (LoadState *S) { + lua_Unsigned cx = loadVarint(S, LUA_MAXUNSIGNED); + /* decode unsigned to signed */ + if ((cx & 1) != 0) + return l_castU2S(~(cx >> 1)); + else + return l_castU2S(cx >> 1); +} + + +/* +** Load a nullable string into slot 'sl' from prototype 'p'. The +** assignment to the slot and the barrier must be performed before any +** possible GC activity, to anchor the string. (Both 'loadVector' and +** 'luaH_setint' can call the GC.) +*/ +static void loadString (LoadState *S, Proto *p, TString **sl) { + lua_State *L = S->L; + TString *ts; + TValue sv; + size_t size = loadSize(S); + if (size == 0) { /* previously saved string? */ + lua_Unsigned idx = loadVarint(S, LUA_MAXUNSIGNED); /* get its index */ + TValue stv; + if (idx == 0) { /* no string? */ + lua_assert(*sl == NULL); /* must be prefilled */ + return; + } + if (novariant(luaH_getint(S->h, l_castU2S(idx), &stv)) != LUA_TSTRING) + error(S, "invalid string index"); + *sl = ts = tsvalue(&stv); /* get its value */ + luaC_objbarrier(L, p, ts); + return; /* do not save it again */ + } + else if ((size -= 1) <= LUAI_MAXSHORTLEN) { /* short string? */ + char buff[LUAI_MAXSHORTLEN + 1]; /* extra space for '\0' */ + loadVector(S, buff, size + 1); /* load string into buffer */ + *sl = ts = luaS_newlstr(L, buff, size); /* create string */ + luaC_objbarrier(L, p, ts); + } + else if (S->fixed) { /* for a fixed buffer, use a fixed string */ + const char *s = getaddr(S, size + 1, char); /* get content address */ + *sl = ts = luaS_newextlstr(L, s, size, NULL, NULL); + luaC_objbarrier(L, p, ts); + } + else { /* create internal copy */ + *sl = ts = luaS_createlngstrobj(L, size); /* create string */ + luaC_objbarrier(L, p, ts); + loadVector(S, getlngstr(ts), size + 1); /* load directly in final place */ + } + /* add string to list of saved strings */ + S->nstr++; + setsvalue(L, &sv, ts); + luaH_setint(L, S->h, l_castU2S(S->nstr), &sv); + luaC_objbarrierback(L, obj2gco(S->h), ts); +} + + +static void loadCode (LoadState *S, Proto *f) { + int n = loadInt(S); + loadAlign(S, sizeof(f->code[0])); + if (S->fixed) { + f->code = getaddr(S, n, Instruction); + f->sizecode = n; + } + else { + f->code = luaM_newvectorchecked(S->L, n, Instruction); + f->sizecode = n; + loadVector(S, f->code, n); + } +} + + +static void loadFunction(LoadState *S, Proto *f); + + +static void loadConstants (LoadState *S, Proto *f) { + int i; + int n = loadInt(S); + f->k = luaM_newvectorchecked(S->L, n, TValue); + f->sizek = n; + for (i = 0; i < n; i++) + setnilvalue(&f->k[i]); + for (i = 0; i < n; i++) { + TValue *o = &f->k[i]; + int t = loadByte(S); + switch (t) { + case LUA_VNIL: + setnilvalue(o); + break; + case LUA_VFALSE: + setbfvalue(o); + break; + case LUA_VTRUE: + setbtvalue(o); + break; + case LUA_VNUMFLT: + setfltvalue(o, loadNumber(S)); + break; + case LUA_VNUMINT: + setivalue(o, loadInteger(S)); + break; + case LUA_VSHRSTR: + case LUA_VLNGSTR: { + lua_assert(f->source == NULL); + loadString(S, f, &f->source); /* use 'source' to anchor string */ + if (f->source == NULL) + error(S, "bad format for constant string"); + setsvalue2n(S->L, o, f->source); /* save it in the right place */ + f->source = NULL; + break; + } + default: error(S, "invalid constant"); + } + } +} + + +static void loadProtos (LoadState *S, Proto *f) { + int i; + int n = loadInt(S); + f->p = luaM_newvectorchecked(S->L, n, Proto *); + f->sizep = n; + for (i = 0; i < n; i++) + f->p[i] = NULL; + for (i = 0; i < n; i++) { + f->p[i] = luaF_newproto(S->L); + luaC_objbarrier(S->L, f, f->p[i]); + loadFunction(S, f->p[i]); + } +} + + +/* +** Load the upvalues for a function. The names must be filled first, +** because the filling of the other fields can raise read errors and +** the creation of the error message can call an emergency collection; +** in that case all prototypes must be consistent for the GC. +*/ +static void loadUpvalues (LoadState *S, Proto *f) { + int i; + int n = loadInt(S); + f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc); + f->sizeupvalues = n; + for (i = 0; i < n; i++) /* make array valid for GC */ + f->upvalues[i].name = NULL; + for (i = 0; i < n; i++) { /* following calls can raise errors */ + f->upvalues[i].instack = loadByte(S); + f->upvalues[i].idx = loadByte(S); + f->upvalues[i].kind = loadByte(S); + } +} + + +static void loadDebug (LoadState *S, Proto *f) { + int i; + int n = loadInt(S); + if (S->fixed) { + f->lineinfo = getaddr(S, n, ls_byte); + f->sizelineinfo = n; + } + else { + f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte); + f->sizelineinfo = n; + loadVector(S, f->lineinfo, n); + } + n = loadInt(S); + if (n > 0) { + loadAlign(S, sizeof(int)); + if (S->fixed) { + f->abslineinfo = getaddr(S, n, AbsLineInfo); + f->sizeabslineinfo = n; + } + else { + f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo); + f->sizeabslineinfo = n; + loadVector(S, f->abslineinfo, n); + } + } + n = loadInt(S); + f->locvars = luaM_newvectorchecked(S->L, n, LocVar); + f->sizelocvars = n; + for (i = 0; i < n; i++) + f->locvars[i].varname = NULL; + for (i = 0; i < n; i++) { + loadString(S, f, &f->locvars[i].varname); + f->locvars[i].startpc = loadInt(S); + f->locvars[i].endpc = loadInt(S); + } + n = loadInt(S); + if (n != 0) /* does it have debug information? */ + n = f->sizeupvalues; /* must be this many */ + for (i = 0; i < n; i++) + loadString(S, f, &f->upvalues[i].name); +} + + +static void loadFunction (LoadState *S, Proto *f) { + f->linedefined = loadInt(S); + f->lastlinedefined = loadInt(S); + f->numparams = loadByte(S); + /* get only the meaningful flags */ + f->flag = cast_byte(loadByte(S) & ~PF_FIXED); + if (S->fixed) + f->flag |= PF_FIXED; /* signal that code is fixed */ + f->maxstacksize = loadByte(S); + loadCode(S, f); + loadConstants(S, f); + loadUpvalues(S, f); + loadProtos(S, f); + loadString(S, f, &f->source); + loadDebug(S, f); +} + + +static void checkliteral (LoadState *S, const char *s, const char *msg) { + char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */ + size_t len = strlen(s); + loadVector(S, buff, len); + if (memcmp(s, buff, len) != 0) + error(S, msg); +} + + +static l_noret numerror (LoadState *S, const char *what, const char *tname) { + const char *msg = luaO_pushfstring(S->L, "%s %s mismatch", tname, what); + error(S, msg); +} + + +static void checknumsize (LoadState *S, int size, const char *tname) { + if (size != loadByte(S)) + numerror(S, "size", tname); +} + + +static void checknumformat (LoadState *S, int eq, const char *tname) { + if (!eq) + numerror(S, "format", tname); +} + + +#define checknum(S,tvar,value,tname) \ + { tvar i; checknumsize(S, sizeof(i), tname); \ + loadVar(S, i); \ + checknumformat(S, i == value, tname); } + + +static void checkHeader (LoadState *S) { + /* skip 1st char (already read and checked) */ + checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk"); + if (loadByte(S) != LUAC_VERSION) + error(S, "version mismatch"); + if (loadByte(S) != LUAC_FORMAT) + error(S, "format mismatch"); + checkliteral(S, LUAC_DATA, "corrupted chunk"); + checknum(S, int, LUAC_INT, "int"); + checknum(S, Instruction, LUAC_INST, "instruction"); + checknum(S, lua_Integer, LUAC_INT, "Lua integer"); + checknum(S, lua_Number, LUAC_NUM, "Lua number"); +} + + +/* +** Load precompiled chunk. +*/ +LClosure *luaU_undump (lua_State *L, ZIO *Z, const char *name, int fixed) { + LoadState S; + LClosure *cl; + if (*name == '@' || *name == '=') + name = name + 1; + else if (*name == LUA_SIGNATURE[0]) + name = "binary string"; + S.name = name; + S.L = L; + S.Z = Z; + S.fixed = cast_byte(fixed); + S.offset = 1; /* fist byte was already read */ + checkHeader(&S); + cl = luaF_newLclosure(L, loadByte(&S)); + setclLvalue2s(L, L->top.p, cl); + luaD_inctop(L); + S.h = luaH_new(L); /* create list of saved strings */ + S.nstr = 0; + sethvalue2s(L, L->top.p, S.h); /* anchor it */ + luaD_inctop(L); + cl->p = luaF_newproto(L); + luaC_objbarrier(L, cl, cl->p); + loadFunction(&S, cl->p); + if (cl->nupvalues != cl->p->sizeupvalues) + error(&S, "corrupted chunk"); + luai_verifycode(L, cl->p); + L->top.p--; /* pop table */ + return cl; +} + + +``` + +`Lua/lundump.h`: + +```h +/* +** $Id: lundump.h $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#ifndef lundump_h +#define lundump_h + +#include <limits.h> + +#include "llimits.h" +#include "lobject.h" +#include "lzio.h" + + +/* data to catch conversion errors */ +#define LUAC_DATA "\x19\x93\r\n\x1a\n" + +#define LUAC_INT -0x5678 +#define LUAC_INST 0x12345678 +#define LUAC_NUM cast_num(-370.5) + +/* +** Encode major-minor version in one byte, one nibble for each +*/ +#define LUAC_VERSION (LUA_VERSION_MAJOR_N*16+LUA_VERSION_MINOR_N) + +#define LUAC_FORMAT 0 /* this is the official format */ + + +/* load one chunk; from lundump.c */ +LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name, + int fixed); + +/* dump one chunk; from ldump.c */ +LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, + void* data, int strip); + +#endif + +``` + +`Lua/lutf8lib.c`: + +```c +/* +** $Id: lutf8lib.c $ +** Standard library for UTF-8 manipulation +** See Copyright Notice in lua.h +*/ + +#define lutf8lib_c +#define LUA_LIB + +#include "lprefix.h" + + +#include <limits.h> +#include <stdlib.h> +#include <string.h> + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" +#include "llimits.h" + + +#define MAXUNICODE 0x10FFFFu + +#define MAXUTF 0x7FFFFFFFu + + +#define MSGInvalid "invalid UTF-8 code" + + +#define iscont(c) (((c) & 0xC0) == 0x80) +#define iscontp(p) iscont(*(p)) + + +/* from strlib */ +/* translate a relative string position: negative means back from end */ +static lua_Integer u_posrelat (lua_Integer pos, size_t len) { + if (pos >= 0) return pos; + else if (0u - (size_t)pos > len) return 0; + else return (lua_Integer)len + pos + 1; +} + + +/* +** Decode one UTF-8 sequence, returning NULL if byte sequence is +** invalid. The array 'limits' stores the minimum value for each +** sequence length, to check for overlong representations. Its first +** entry forces an error for non-ASCII bytes with no continuation +** bytes (count == 0). +*/ +static const char *utf8_decode (const char *s, l_uint32 *val, int strict) { + static const l_uint32 limits[] = + {~(l_uint32)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u}; + unsigned int c = (unsigned char)s[0]; + l_uint32 res = 0; /* final result */ + if (c < 0x80) /* ASCII? */ + res = c; + else { + int count = 0; /* to count number of continuation bytes */ + for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */ + unsigned int cc = (unsigned char)s[++count]; /* read next byte */ + if (!iscont(cc)) /* not a continuation byte? */ + return NULL; /* invalid byte sequence */ + res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ + } + res |= ((l_uint32)(c & 0x7F) << (count * 5)); /* add first byte */ + if (count > 5 || res > MAXUTF || res < limits[count]) + return NULL; /* invalid byte sequence */ + s += count; /* skip continuation bytes read */ + } + if (strict) { + /* check for invalid code points; too large or surrogates */ + if (res > MAXUNICODE || (0xD800u <= res && res <= 0xDFFFu)) + return NULL; + } + if (val) *val = res; + return s + 1; /* +1 to include first byte */ +} + + +/* +** utf8len(s [, i [, j [, lax]]]) --> number of characters that +** start in the range [i,j], or nil + current position if 's' is not +** well formed in that interval +*/ +static int utflen (lua_State *L) { + lua_Integer n = 0; /* counter for the number of characters */ + size_t len; /* string length in bytes */ + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); + lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len); + int lax = lua_toboolean(L, 4); + luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2, + "initial position out of bounds"); + luaL_argcheck(L, --posj < (lua_Integer)len, 3, + "final position out of bounds"); + while (posi <= posj) { + const char *s1 = utf8_decode(s + posi, NULL, !lax); + if (s1 == NULL) { /* conversion error? */ + luaL_pushfail(L); /* return fail ... */ + lua_pushinteger(L, posi + 1); /* ... and current position */ + return 2; + } + posi = ct_diff2S(s1 - s); + n++; + } + lua_pushinteger(L, n); + return 1; +} + + +/* +** codepoint(s, [i, [j [, lax]]]) -> returns codepoints for all +** characters that start in the range [i,j] +*/ +static int codepoint (lua_State *L) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); + lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len); + int lax = lua_toboolean(L, 4); + int n; + const char *se; + luaL_argcheck(L, posi >= 1, 2, "out of bounds"); + luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of bounds"); + if (posi > pose) return 0; /* empty interval; return no values */ + if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */ + return luaL_error(L, "string slice too long"); + n = (int)(pose - posi) + 1; /* upper bound for number of returns */ + luaL_checkstack(L, n, "string slice too long"); + n = 0; /* count the number of returns */ + se = s + pose; /* string end */ + for (s += posi - 1; s < se;) { + l_uint32 code; + s = utf8_decode(s, &code, !lax); + if (s == NULL) + return luaL_error(L, MSGInvalid); + lua_pushinteger(L, l_castU2S(code)); + n++; + } + return n; +} + + +static void pushutfchar (lua_State *L, int arg) { + lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg); + luaL_argcheck(L, code <= MAXUTF, arg, "value out of range"); + lua_pushfstring(L, "%U", (long)code); +} + + +/* +** utfchar(n1, n2, ...) -> char(n1)..char(n2)... +*/ +static int utfchar (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + if (n == 1) /* optimize common case of single char */ + pushutfchar(L, 1); + else { + int i; + luaL_Buffer b; + luaL_buffinit(L, &b); + for (i = 1; i <= n; i++) { + pushutfchar(L, i); + luaL_addvalue(&b); + } + luaL_pushresult(&b); + } + return 1; +} + + +/* +** offset(s, n, [i]) -> indices where n-th character counting from +** position 'i' starts and ends; 0 means character at 'i'. +*/ +static int byteoffset (lua_State *L) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Integer n = luaL_checkinteger(L, 2); + lua_Integer posi = (n >= 0) ? 1 : cast_st2S(len) + 1; + posi = u_posrelat(luaL_optinteger(L, 3, posi), len); + luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3, + "position out of bounds"); + if (n == 0) { + /* find beginning of current byte sequence */ + while (posi > 0 && iscontp(s + posi)) posi--; + } + else { + if (iscontp(s + posi)) + return luaL_error(L, "initial position is a continuation byte"); + if (n < 0) { + while (n < 0 && posi > 0) { /* move back */ + do { /* find beginning of previous character */ + posi--; + } while (posi > 0 && iscontp(s + posi)); + n++; + } + } + else { + n--; /* do not move for 1st character */ + while (n > 0 && posi < (lua_Integer)len) { + do { /* find beginning of next character */ + posi++; + } while (iscontp(s + posi)); /* (cannot pass final '\0') */ + n--; + } + } + } + if (n != 0) { /* did not find given character? */ + luaL_pushfail(L); + return 1; + } + lua_pushinteger(L, posi + 1); /* initial position */ + if ((s[posi] & 0x80) != 0) { /* multi-byte character? */ + if (iscont(s[posi])) + return luaL_error(L, "initial position is a continuation byte"); + while (iscontp(s + posi + 1)) + posi++; /* skip to last continuation byte */ + } + /* else one-byte character: final position is the initial one */ + lua_pushinteger(L, posi + 1); /* 'posi' now is the final position */ + return 2; +} + + +static int iter_aux (lua_State *L, int strict) { + size_t len; + const char *s = luaL_checklstring(L, 1, &len); + lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2); + if (n < len) { + while (iscontp(s + n)) n++; /* go to next character */ + } + if (n >= len) /* (also handles original 'n' being negative) */ + return 0; /* no more codepoints */ + else { + l_uint32 code; + const char *next = utf8_decode(s + n, &code, strict); + if (next == NULL || iscontp(next)) + return luaL_error(L, MSGInvalid); + lua_pushinteger(L, l_castU2S(n + 1)); + lua_pushinteger(L, l_castU2S(code)); + return 2; + } +} + + +static int iter_auxstrict (lua_State *L) { + return iter_aux(L, 1); +} + +static int iter_auxlax (lua_State *L) { + return iter_aux(L, 0); +} + + +static int iter_codes (lua_State *L) { + int lax = lua_toboolean(L, 2); + const char *s = luaL_checkstring(L, 1); + luaL_argcheck(L, !iscontp(s), 1, MSGInvalid); + lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict); + lua_pushvalue(L, 1); + lua_pushinteger(L, 0); + return 3; +} + + +/* pattern to match a single UTF-8 character */ +#define UTF8PATT "[\0-\x7F\xC2-\xFD][\x80-\xBF]*" + + +static const luaL_Reg funcs[] = { + {"offset", byteoffset}, + {"codepoint", codepoint}, + {"char", utfchar}, + {"len", utflen}, + {"codes", iter_codes}, + /* placeholders */ + {"charpattern", NULL}, + {NULL, NULL} +}; + + +LUAMOD_API int luaopen_utf8 (lua_State *L) { + luaL_newlib(L, funcs); + lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)/sizeof(char) - 1); + lua_setfield(L, -2, "charpattern"); + return 1; +} + + +``` + +`Lua/lvm.c`: + +```c +/* +** $Id: lvm.c $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#define lvm_c +#define LUA_CORE + +#include "lprefix.h" + +#include <float.h> +#include <limits.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + +/* +** By default, use jump tables in the main interpreter loop on gcc +** and compatible compilers. +*/ +#if !defined(LUA_USE_JUMPTABLE) +#if defined(__GNUC__) +#define LUA_USE_JUMPTABLE 1 +#else +#define LUA_USE_JUMPTABLE 0 +#endif +#endif + + + +/* limit for table tag-method chains (to avoid infinite loops) */ +#define MAXTAGLOOP 2000 + + +/* +** 'l_intfitsf' checks whether a given integer is in the range that +** can be converted to a float without rounding. Used in comparisons. +*/ + +/* number of bits in the mantissa of a float */ +#define NBM (l_floatatt(MANT_DIG)) + +/* +** Check whether some integers may not fit in a float, testing whether +** (maxinteger >> NBM) > 0. (That implies (1 << NBM) <= maxinteger.) +** (The shifts are done in parts, to avoid shifting by more than the size +** of an integer. In a worst case, NBM == 113 for long double and +** sizeof(long) == 32.) +*/ +#if ((((LUA_MAXINTEGER >> (NBM / 4)) >> (NBM / 4)) >> (NBM / 4)) \ + >> (NBM - (3 * (NBM / 4)))) > 0 + +/* limit for integers that fit in a float */ +#define MAXINTFITSF ((lua_Unsigned)1 << NBM) + +/* check whether 'i' is in the interval [-MAXINTFITSF, MAXINTFITSF] */ +#define l_intfitsf(i) ((MAXINTFITSF + l_castS2U(i)) <= (2 * MAXINTFITSF)) + +#else /* all integers fit in a float precisely */ + +#define l_intfitsf(i) 1 + +#endif + + +/* +** Try to convert a value from string to a number value. +** If the value is not a string or is a string not representing +** a valid numeral (or if coercions from strings to numbers +** are disabled via macro 'cvt2num'), do not modify 'result' +** and return 0. +*/ +static int l_strton (const TValue *obj, TValue *result) { + lua_assert(obj != result); + if (!cvt2num(obj)) /* is object not a string? */ + return 0; + else { + TString *st = tsvalue(obj); + size_t stlen; + const char *s = getlstr(st, stlen); + return (luaO_str2num(s, result) == stlen + 1); + } +} + + +/* +** Try to convert a value to a float. The float case is already handled +** by the macro 'tonumber'. +*/ +int luaV_tonumber_ (const TValue *obj, lua_Number *n) { + TValue v; + if (ttisinteger(obj)) { + *n = cast_num(ivalue(obj)); + return 1; + } + else if (l_strton(obj, &v)) { /* string coercible to number? */ + *n = nvalue(&v); /* convert result of 'luaO_str2num' to a float */ + return 1; + } + else + return 0; /* conversion failed */ +} + + +/* +** try to convert a float to an integer, rounding according to 'mode'. +*/ +int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode) { + lua_Number f = l_floor(n); + if (n != f) { /* not an integral value? */ + if (mode == F2Ieq) return 0; /* fails if mode demands integral value */ + else if (mode == F2Iceil) /* needs ceiling? */ + f += 1; /* convert floor to ceiling (remember: n != f) */ + } + return lua_numbertointeger(f, p); +} + + +/* +** try to convert a value to an integer, rounding according to 'mode', +** without string coercion. +** ("Fast track" handled by macro 'tointegerns'.) +*/ +int luaV_tointegerns (const TValue *obj, lua_Integer *p, F2Imod mode) { + if (ttisfloat(obj)) + return luaV_flttointeger(fltvalue(obj), p, mode); + else if (ttisinteger(obj)) { + *p = ivalue(obj); + return 1; + } + else + return 0; +} + + +/* +** try to convert a value to an integer. +*/ +int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode) { + TValue v; + if (l_strton(obj, &v)) /* does 'obj' point to a numerical string? */ + obj = &v; /* change it to point to its corresponding number */ + return luaV_tointegerns(obj, p, mode); +} + + +/* +** Try to convert a 'for' limit to an integer, preserving the semantics +** of the loop. Return true if the loop must not run; otherwise, '*p' +** gets the integer limit. +** (The following explanation assumes a positive step; it is valid for +** negative steps mutatis mutandis.) +** If the limit is an integer or can be converted to an integer, +** rounding down, that is the limit. +** Otherwise, check whether the limit can be converted to a float. If +** the float is too large, clip it to LUA_MAXINTEGER. If the float +** is too negative, the loop should not run, because any initial +** integer value is greater than such limit; so, the function returns +** true to signal that. (For this latter case, no integer limit would be +** correct; even a limit of LUA_MININTEGER would run the loop once for +** an initial value equal to LUA_MININTEGER.) +*/ +static int forlimit (lua_State *L, lua_Integer init, const TValue *lim, + lua_Integer *p, lua_Integer step) { + if (!luaV_tointeger(lim, p, (step < 0 ? F2Iceil : F2Ifloor))) { + /* not coercible to in integer */ + lua_Number flim; /* try to convert to float */ + if (!tonumber(lim, &flim)) /* cannot convert to float? */ + luaG_forerror(L, lim, "limit"); + /* else 'flim' is a float out of integer bounds */ + if (luai_numlt(0, flim)) { /* if it is positive, it is too large */ + if (step < 0) return 1; /* initial value must be less than it */ + *p = LUA_MAXINTEGER; /* truncate */ + } + else { /* it is less than min integer */ + if (step > 0) return 1; /* initial value must be greater than it */ + *p = LUA_MININTEGER; /* truncate */ + } + } + return (step > 0 ? init > *p : init < *p); /* not to run? */ +} + + +/* +** Prepare a numerical for loop (opcode OP_FORPREP). +** Before execution, stack is as follows: +** ra : initial value +** ra + 1 : limit +** ra + 2 : step +** Return true to skip the loop. Otherwise, +** after preparation, stack will be as follows: +** ra : loop counter (integer loops) or limit (float loops) +** ra + 1 : step +** ra + 2 : control variable +*/ +static int forprep (lua_State *L, StkId ra) { + TValue *pinit = s2v(ra); + TValue *plimit = s2v(ra + 1); + TValue *pstep = s2v(ra + 2); + if (ttisinteger(pinit) && ttisinteger(pstep)) { /* integer loop? */ + lua_Integer init = ivalue(pinit); + lua_Integer step = ivalue(pstep); + lua_Integer limit; + if (step == 0) + luaG_runerror(L, "'for' step is zero"); + if (forlimit(L, init, plimit, &limit, step)) + return 1; /* skip the loop */ + else { /* prepare loop counter */ + lua_Unsigned count; + if (step > 0) { /* ascending loop? */ + count = l_castS2U(limit) - l_castS2U(init); + if (step != 1) /* avoid division in the too common case */ + count /= l_castS2U(step); + } + else { /* step < 0; descending loop */ + count = l_castS2U(init) - l_castS2U(limit); + /* 'step+1' avoids negating 'mininteger' */ + count /= l_castS2U(-(step + 1)) + 1u; + } + /* use 'chgivalue' for places that for sure had integers */ + chgivalue(s2v(ra), l_castU2S(count)); /* change init to count */ + setivalue(s2v(ra + 1), step); /* change limit to step */ + chgivalue(s2v(ra + 2), init); /* change step to init */ + } + } + else { /* try making all values floats */ + lua_Number init; lua_Number limit; lua_Number step; + if (l_unlikely(!tonumber(plimit, &limit))) + luaG_forerror(L, plimit, "limit"); + if (l_unlikely(!tonumber(pstep, &step))) + luaG_forerror(L, pstep, "step"); + if (l_unlikely(!tonumber(pinit, &init))) + luaG_forerror(L, pinit, "initial value"); + if (step == 0) + luaG_runerror(L, "'for' step is zero"); + if (luai_numlt(0, step) ? luai_numlt(limit, init) + : luai_numlt(init, limit)) + return 1; /* skip the loop */ + else { + /* make sure all values are floats */ + setfltvalue(s2v(ra), limit); + setfltvalue(s2v(ra + 1), step); + setfltvalue(s2v(ra + 2), init); /* control variable */ + } + } + return 0; +} + + +/* +** Execute a step of a float numerical for loop, returning +** true iff the loop must continue. (The integer case is +** written online with opcode OP_FORLOOP, for performance.) +*/ +static int floatforloop (StkId ra) { + lua_Number step = fltvalue(s2v(ra + 1)); + lua_Number limit = fltvalue(s2v(ra)); + lua_Number idx = fltvalue(s2v(ra + 2)); /* control variable */ + idx = luai_numadd(L, idx, step); /* increment index */ + if (luai_numlt(0, step) ? luai_numle(idx, limit) + : luai_numle(limit, idx)) { + chgfltvalue(s2v(ra + 2), idx); /* update control variable */ + return 1; /* jump back */ + } + else + return 0; /* finish the loop */ +} + + +/* +** Finish the table access 'val = t[key]' and return the tag of the result. +*/ +lu_byte luaV_finishget (lua_State *L, const TValue *t, TValue *key, + StkId val, lu_byte tag) { + int loop; /* counter to avoid infinite loops */ + const TValue *tm; /* metamethod */ + for (loop = 0; loop < MAXTAGLOOP; loop++) { + if (tag == LUA_VNOTABLE) { /* 't' is not a table? */ + lua_assert(!ttistable(t)); + tm = luaT_gettmbyobj(L, t, TM_INDEX); + if (l_unlikely(notm(tm))) + luaG_typeerror(L, t, "index"); /* no metamethod */ + /* else will try the metamethod */ + } + else { /* 't' is a table */ + tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ + if (tm == NULL) { /* no metamethod? */ + setnilvalue(s2v(val)); /* result is nil */ + return LUA_VNIL; + } + /* else will try the metamethod */ + } + if (ttisfunction(tm)) { /* is metamethod a function? */ + tag = luaT_callTMres(L, tm, t, key, val); /* call it */ + return tag; /* return tag of the result */ + } + t = tm; /* else try to access 'tm[key]' */ + luaV_fastget(t, key, s2v(val), luaH_get, tag); + if (!tagisempty(tag)) + return tag; /* done */ + /* else repeat (tail call 'luaV_finishget') */ + } + luaG_runerror(L, "'__index' chain too long; possible loop"); + return 0; /* to avoid warnings */ +} + + +/* +** Finish a table assignment 't[key] = val'. +** About anchoring the table before the call to 'luaH_finishset': +** This call may trigger an emergency collection. When loop>0, +** the table being accessed is a field in some metatable. If this +** metatable is weak and the table is not anchored, this collection +** could collect that table while it is being updated. +*/ +void luaV_finishset (lua_State *L, const TValue *t, TValue *key, + TValue *val, int hres) { + int loop; /* counter to avoid infinite loops */ + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; /* '__newindex' metamethod */ + if (hres != HNOTATABLE) { /* is 't' a table? */ + Table *h = hvalue(t); /* save 't' table */ + tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */ + if (tm == NULL) { /* no metamethod? */ + sethvalue2s(L, L->top.p, h); /* anchor 't' */ + L->top.p++; /* assume EXTRA_STACK */ + luaH_finishset(L, h, key, val, hres); /* set new value */ + L->top.p--; + invalidateTMcache(h); + luaC_barrierback(L, obj2gco(h), val); + return; + } + /* else will try the metamethod */ + } + else { /* not a table; check metamethod */ + tm = luaT_gettmbyobj(L, t, TM_NEWINDEX); + if (l_unlikely(notm(tm))) + luaG_typeerror(L, t, "index"); + } + /* try the metamethod */ + if (ttisfunction(tm)) { + luaT_callTM(L, tm, t, key, val); + return; + } + t = tm; /* else repeat assignment over 'tm' */ + luaV_fastset(t, key, val, hres, luaH_pset); + if (hres == HOK) { + luaV_finishfastset(L, t, val); + return; /* done */ + } + /* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */ + } + luaG_runerror(L, "'__newindex' chain too long; possible loop"); +} + + +/* +** Function to be used for 0-terminated string order comparison +*/ +#if !defined(l_strcoll) +#define l_strcoll strcoll +#endif + + +/* +** Compare two strings 'ts1' x 'ts2', returning an integer less-equal- +** -greater than zero if 'ts1' is less-equal-greater than 'ts2'. +** The code is a little tricky because it allows '\0' in the strings +** and it uses 'strcoll' (to respect locales) for each segment +** of the strings. Note that segments can compare equal but still +** have different lengths. +*/ +static int l_strcmp (const TString *ts1, const TString *ts2) { + size_t rl1; /* real length */ + const char *s1 = getlstr(ts1, rl1); + size_t rl2; + const char *s2 = getlstr(ts2, rl2); + for (;;) { /* for each segment */ + int temp = l_strcoll(s1, s2); + if (temp != 0) /* not equal? */ + return temp; /* done */ + else { /* strings are equal up to a '\0' */ + size_t zl1 = strlen(s1); /* index of first '\0' in 's1' */ + size_t zl2 = strlen(s2); /* index of first '\0' in 's2' */ + if (zl2 == rl2) /* 's2' is finished? */ + return (zl1 == rl1) ? 0 : 1; /* check 's1' */ + else if (zl1 == rl1) /* 's1' is finished? */ + return -1; /* 's1' is less than 's2' ('s2' is not finished) */ + /* both strings longer than 'zl'; go on comparing after the '\0' */ + zl1++; zl2++; + s1 += zl1; rl1 -= zl1; s2 += zl2; rl2 -= zl2; + } + } +} + + +/* +** Check whether integer 'i' is less than float 'f'. If 'i' has an +** exact representation as a float ('l_intfitsf'), compare numbers as +** floats. Otherwise, use the equivalence 'i < f <=> i < ceil(f)'. +** If 'ceil(f)' is out of integer range, either 'f' is greater than +** all integers or less than all integers. +** (The test with 'l_intfitsf' is only for performance; the else +** case is correct for all values, but it is slow due to the conversion +** from float to int.) +** When 'f' is NaN, comparisons must result in false. +*/ +l_sinline int LTintfloat (lua_Integer i, lua_Number f) { + if (l_intfitsf(i)) + return luai_numlt(cast_num(i), f); /* compare them as floats */ + else { /* i < f <=> i < ceil(f) */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */ + return i < fi; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f > 0; /* greater? */ + } +} + + +/* +** Check whether integer 'i' is less than or equal to float 'f'. +** See comments on previous function. +*/ +l_sinline int LEintfloat (lua_Integer i, lua_Number f) { + if (l_intfitsf(i)) + return luai_numle(cast_num(i), f); /* compare them as floats */ + else { /* i <= f <=> i <= floor(f) */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */ + return i <= fi; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f > 0; /* greater? */ + } +} + + +/* +** Check whether float 'f' is less than integer 'i'. +** See comments on previous function. +*/ +l_sinline int LTfloatint (lua_Number f, lua_Integer i) { + if (l_intfitsf(i)) + return luai_numlt(f, cast_num(i)); /* compare them as floats */ + else { /* f < i <=> floor(f) < i */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */ + return fi < i; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f < 0; /* less? */ + } +} + + +/* +** Check whether float 'f' is less than or equal to integer 'i'. +** See comments on previous function. +*/ +l_sinline int LEfloatint (lua_Number f, lua_Integer i) { + if (l_intfitsf(i)) + return luai_numle(f, cast_num(i)); /* compare them as floats */ + else { /* f <= i <=> ceil(f) <= i */ + lua_Integer fi; + if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */ + return fi <= i; /* compare them as integers */ + else /* 'f' is either greater or less than all integers */ + return f < 0; /* less? */ + } +} + + +/* +** Return 'l < r', for numbers. +*/ +l_sinline int LTnum (const TValue *l, const TValue *r) { + lua_assert(ttisnumber(l) && ttisnumber(r)); + if (ttisinteger(l)) { + lua_Integer li = ivalue(l); + if (ttisinteger(r)) + return li < ivalue(r); /* both are integers */ + else /* 'l' is int and 'r' is float */ + return LTintfloat(li, fltvalue(r)); /* l < r ? */ + } + else { + lua_Number lf = fltvalue(l); /* 'l' must be float */ + if (ttisfloat(r)) + return luai_numlt(lf, fltvalue(r)); /* both are float */ + else /* 'l' is float and 'r' is int */ + return LTfloatint(lf, ivalue(r)); + } +} + + +/* +** Return 'l <= r', for numbers. +*/ +l_sinline int LEnum (const TValue *l, const TValue *r) { + lua_assert(ttisnumber(l) && ttisnumber(r)); + if (ttisinteger(l)) { + lua_Integer li = ivalue(l); + if (ttisinteger(r)) + return li <= ivalue(r); /* both are integers */ + else /* 'l' is int and 'r' is float */ + return LEintfloat(li, fltvalue(r)); /* l <= r ? */ + } + else { + lua_Number lf = fltvalue(l); /* 'l' must be float */ + if (ttisfloat(r)) + return luai_numle(lf, fltvalue(r)); /* both are float */ + else /* 'l' is float and 'r' is int */ + return LEfloatint(lf, ivalue(r)); + } +} + + +/* +** return 'l < r' for non-numbers. +*/ +static int lessthanothers (lua_State *L, const TValue *l, const TValue *r) { + lua_assert(!ttisnumber(l) || !ttisnumber(r)); + if (ttisstring(l) && ttisstring(r)) /* both are strings? */ + return l_strcmp(tsvalue(l), tsvalue(r)) < 0; + else + return luaT_callorderTM(L, l, r, TM_LT); +} + + +/* +** Main operation less than; return 'l < r'. +*/ +int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { + if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ + return LTnum(l, r); + else return lessthanothers(L, l, r); +} + + +/* +** return 'l <= r' for non-numbers. +*/ +static int lessequalothers (lua_State *L, const TValue *l, const TValue *r) { + lua_assert(!ttisnumber(l) || !ttisnumber(r)); + if (ttisstring(l) && ttisstring(r)) /* both are strings? */ + return l_strcmp(tsvalue(l), tsvalue(r)) <= 0; + else + return luaT_callorderTM(L, l, r, TM_LE); +} + + +/* +** Main operation less than or equal to; return 'l <= r'. +*/ +int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) { + if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */ + return LEnum(l, r); + else return lessequalothers(L, l, r); +} + + +/* +** Main operation for equality of Lua values; return 't1 == t2'. +** L == NULL means raw equality (no metamethods) +*/ +int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { + const TValue *tm; + if (ttype(t1) != ttype(t2)) /* not the same type? */ + return 0; + else if (ttypetag(t1) != ttypetag(t2)) { + switch (ttypetag(t1)) { + case LUA_VNUMINT: { /* integer == float? */ + /* integer and float can only be equal if float has an integer + value equal to the integer */ + lua_Integer i2; + return (luaV_flttointeger(fltvalue(t2), &i2, F2Ieq) && + ivalue(t1) == i2); + } + case LUA_VNUMFLT: { /* float == integer? */ + lua_Integer i1; /* see comment in previous case */ + return (luaV_flttointeger(fltvalue(t1), &i1, F2Ieq) && + i1 == ivalue(t2)); + } + case LUA_VSHRSTR: case LUA_VLNGSTR: { + /* compare two strings with different variants: they can be + equal when one string is a short string and the other is + an external string */ + return luaS_eqstr(tsvalue(t1), tsvalue(t2)); + } + default: + /* only numbers (integer/float) and strings (long/short) can have + equal values with different variants */ + return 0; + } + } + else { /* equal variants */ + switch (ttypetag(t1)) { + case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: + return 1; + case LUA_VNUMINT: + return (ivalue(t1) == ivalue(t2)); + case LUA_VNUMFLT: + return (fltvalue(t1) == fltvalue(t2)); + case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); + case LUA_VSHRSTR: + return eqshrstr(tsvalue(t1), tsvalue(t2)); + case LUA_VLNGSTR: + return luaS_eqstr(tsvalue(t1), tsvalue(t2)); + case LUA_VUSERDATA: { + if (uvalue(t1) == uvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + case LUA_VTABLE: { + if (hvalue(t1) == hvalue(t2)) return 1; + else if (L == NULL) return 0; + tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); + if (tm == NULL) + tm = fasttm(L, hvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + case LUA_VLCF: + return (fvalue(t1) == fvalue(t2)); + default: /* functions and threads */ + return (gcvalue(t1) == gcvalue(t2)); + } + if (tm == NULL) /* no TM? */ + return 0; /* objects are different */ + else { + int tag = luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */ + return !tagisfalse(tag); + } + } +} + + +/* macro used by 'luaV_concat' to ensure that element at 'o' is a string */ +#define tostring(L,o) \ + (ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1))) + +/* +** Check whether object is a short empty string to optimize concatenation. +** (External strings can be empty too; they will be concatenated like +** non-empty ones.) +*/ +#define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0) + +/* copy strings in stack from top - n up to top - 1 to buffer */ +static void copy2buff (StkId top, int n, char *buff) { + size_t tl = 0; /* size already copied */ + do { + TString *st = tsvalue(s2v(top - n)); + size_t l; /* length of string being copied */ + const char *s = getlstr(st, l); + memcpy(buff + tl, s, l * sizeof(char)); + tl += l; + } while (--n > 0); +} + + +/* +** Main operation for concatenation: concat 'total' values in the stack, +** from 'L->top.p - total' up to 'L->top.p - 1'. +*/ +void luaV_concat (lua_State *L, int total) { + if (total == 1) + return; /* "all" values already concatenated */ + do { + StkId top = L->top.p; + int n = 2; /* number of elements handled in this pass (at least 2) */ + if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) || + !tostring(L, s2v(top - 1))) + luaT_tryconcatTM(L); /* may invalidate 'top' */ + else if (isemptystr(s2v(top - 1))) /* second operand is empty? */ + cast_void(tostring(L, s2v(top - 2))); /* result is first operand */ + else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */ + setobjs2s(L, top - 2, top - 1); /* result is second op. */ + } + else { + /* at least two string values; get as many as possible */ + size_t tl = tsslen(tsvalue(s2v(top - 1))); /* total length */ + TString *ts; + /* collect total length and number of strings */ + for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { + size_t l = tsslen(tsvalue(s2v(top - n - 1))); + if (l_unlikely(l >= MAX_SIZE - sizeof(TString) - tl)) { + L->top.p = top - total; /* pop strings to avoid wasting stack */ + luaG_runerror(L, "string length overflow"); + } + tl += l; + } + if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */ + char buff[LUAI_MAXSHORTLEN]; + copy2buff(top, n, buff); /* copy strings to buffer */ + ts = luaS_newlstr(L, buff, tl); + } + else { /* long string; copy strings directly to final result */ + ts = luaS_createlngstrobj(L, tl); + copy2buff(top, n, getlngstr(ts)); + } + setsvalue2s(L, top - n, ts); /* create result */ + } + total -= n - 1; /* got 'n' strings to create one new */ + L->top.p -= n - 1; /* popped 'n' strings and pushed one */ + } while (total > 1); /* repeat until only 1 result left */ +} + + +/* +** Main operation 'ra = #rb'. +*/ +void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { + const TValue *tm; + switch (ttypetag(rb)) { + case LUA_VTABLE: { + Table *h = hvalue(rb); + tm = fasttm(L, h->metatable, TM_LEN); + if (tm) break; /* metamethod? break switch to call it */ + setivalue(s2v(ra), l_castU2S(luaH_getn(L, h))); /* else primitive len */ + return; + } + case LUA_VSHRSTR: { + setivalue(s2v(ra), tsvalue(rb)->shrlen); + return; + } + case LUA_VLNGSTR: { + setivalue(s2v(ra), cast_st2S(tsvalue(rb)->u.lnglen)); + return; + } + default: { /* try metamethod */ + tm = luaT_gettmbyobj(L, rb, TM_LEN); + if (l_unlikely(notm(tm))) /* no metamethod? */ + luaG_typeerror(L, rb, "get length of"); + break; + } + } + luaT_callTMres(L, tm, rb, rb, ra); +} + + +/* +** Integer division; return 'm // n', that is, floor(m/n). +** C division truncates its result (rounds towards zero). +** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer, +** otherwise 'floor(q) == trunc(q) - 1'. +*/ +lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) { + if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ + if (n == 0) + luaG_runerror(L, "attempt to divide by zero"); + return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */ + } + else { + lua_Integer q = m / n; /* perform C division */ + if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */ + q -= 1; /* correct result for different rounding */ + return q; + } +} + + +/* +** Integer modulus; return 'm % n'. (Assume that C '%' with +** negative operands follows C99 behavior. See previous comment +** about luaV_idiv.) +*/ +lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) { + if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ + if (n == 0) + luaG_runerror(L, "attempt to perform 'n%%0'"); + return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */ + } + else { + lua_Integer r = m % n; + if (r != 0 && (r ^ n) < 0) /* 'm/n' would be non-integer negative? */ + r += n; /* correct result for different rounding */ + return r; + } +} + + +/* +** Float modulus +*/ +lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) { + lua_Number r; + luai_nummod(L, m, n, r); + return r; +} + + +/* number of bits in an integer */ +#define NBITS l_numbits(lua_Integer) + + +/* +** Shift left operation. (Shift right just negates 'y'.) +*/ +lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) { + if (y < 0) { /* shift right? */ + if (y <= -NBITS) return 0; + else return intop(>>, x, -y); + } + else { /* shift left */ + if (y >= NBITS) return 0; + else return intop(<<, x, y); + } +} + + +/* +** create a new Lua closure, push it in the stack, and initialize +** its upvalues. +*/ +static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, + StkId ra) { + int nup = p->sizeupvalues; + Upvaldesc *uv = p->upvalues; + int i; + LClosure *ncl = luaF_newLclosure(L, nup); + ncl->p = p; + setclLvalue2s(L, ra, ncl); /* anchor new closure in stack */ + for (i = 0; i < nup; i++) { /* fill in its upvalues */ + if (uv[i].instack) /* upvalue refers to local variable? */ + ncl->upvals[i] = luaF_findupval(L, base + uv[i].idx); + else /* get upvalue from enclosing function */ + ncl->upvals[i] = encup[uv[i].idx]; + luaC_objbarrier(L, ncl, ncl->upvals[i]); + } +} + + +/* +** finish execution of an opcode interrupted by a yield +*/ +void luaV_finishOp (lua_State *L) { + CallInfo *ci = L->ci; + StkId base = ci->func.p + 1; + Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */ + OpCode op = GET_OPCODE(inst); + switch (op) { /* finish its execution */ + case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: { + setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top.p); + break; + } + case OP_UNM: case OP_BNOT: case OP_LEN: + case OP_GETTABUP: case OP_GETTABLE: case OP_GETI: + case OP_GETFIELD: case OP_SELF: { + setobjs2s(L, base + GETARG_A(inst), --L->top.p); + break; + } + case OP_LT: case OP_LE: + case OP_LTI: case OP_LEI: + case OP_GTI: case OP_GEI: + case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ + int res = !l_isfalse(s2v(L->top.p - 1)); + L->top.p--; + lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); + if (res != GETARG_k(inst)) /* condition failed? */ + ci->u.l.savedpc++; /* skip jump instruction */ + break; + } + case OP_CONCAT: { + StkId top = L->top.p - 1; /* top when 'luaT_tryconcatTM' was called */ + int a = GETARG_A(inst); /* first element to concatenate */ + int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */ + setobjs2s(L, top - 2, top); /* put TM result in proper position */ + L->top.p = top - 1; /* top is one after last element (at top-2) */ + luaV_concat(L, total); /* concat them (may yield again) */ + break; + } + case OP_CLOSE: { /* yielded closing variables */ + ci->u.l.savedpc--; /* repeat instruction to close other vars. */ + break; + } + case OP_RETURN: { /* yielded closing variables */ + StkId ra = base + GETARG_A(inst); + /* adjust top to signal correct number of returns, in case the + return is "up to top" ('isIT') */ + L->top.p = ra + ci->u2.nres; + /* repeat instruction to close other vars. and complete the return */ + ci->u.l.savedpc--; + break; + } + default: { + /* only these other opcodes can yield */ + lua_assert(op == OP_TFORCALL || op == OP_CALL || + op == OP_TAILCALL || op == OP_SETTABUP || op == OP_SETTABLE || + op == OP_SETI || op == OP_SETFIELD); + break; + } + } +} + + + + +/* +** {================================================================== +** Macros for arithmetic/bitwise/comparison opcodes in 'luaV_execute' +** +** All these macros are to be used exclusively inside the main +** iterpreter loop (function luaV_execute) and may access directly +** the local variables of that function (L, i, pc, ci, etc.). +** =================================================================== +*/ + +#define l_addi(L,a,b) intop(+, a, b) +#define l_subi(L,a,b) intop(-, a, b) +#define l_muli(L,a,b) intop(*, a, b) +#define l_band(a,b) intop(&, a, b) +#define l_bor(a,b) intop(|, a, b) +#define l_bxor(a,b) intop(^, a, b) + +#define l_lti(a,b) (a < b) +#define l_lei(a,b) (a <= b) +#define l_gti(a,b) (a > b) +#define l_gei(a,b) (a >= b) + + +/* +** Arithmetic operations with immediate operands. 'iop' is the integer +** operation, 'fop' is the float operation. +*/ +#define op_arithI(L,iop,fop) { \ + TValue *ra = vRA(i); \ + TValue *v1 = vRB(i); \ + int imm = GETARG_sC(i); \ + if (ttisinteger(v1)) { \ + lua_Integer iv1 = ivalue(v1); \ + pc++; setivalue(ra, iop(L, iv1, imm)); \ + } \ + else if (ttisfloat(v1)) { \ + lua_Number nb = fltvalue(v1); \ + lua_Number fimm = cast_num(imm); \ + pc++; setfltvalue(ra, fop(L, nb, fimm)); \ + }} + + +/* +** Auxiliary function for arithmetic operations over floats and others +** with two operands. +*/ +#define op_arithf_aux(L,v1,v2,fop) { \ + lua_Number n1; lua_Number n2; \ + if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \ + StkId ra = RA(i); \ + pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \ + }} + + +/* +** Arithmetic operations over floats and others with register operands. +*/ +#define op_arithf(L,fop) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = vRC(i); \ + op_arithf_aux(L, v1, v2, fop); } + + +/* +** Arithmetic operations with K operands for floats. +*/ +#define op_arithfK(L,fop) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ + op_arithf_aux(L, v1, v2, fop); } + + +/* +** Arithmetic operations over integers and floats. +*/ +#define op_arith_aux(L,v1,v2,iop,fop) { \ + if (ttisinteger(v1) && ttisinteger(v2)) { \ + StkId ra = RA(i); \ + lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \ + pc++; setivalue(s2v(ra), iop(L, i1, i2)); \ + } \ + else op_arithf_aux(L, v1, v2, fop); } + + +/* +** Arithmetic operations with register operands. +*/ +#define op_arith(L,iop,fop) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = vRC(i); \ + op_arith_aux(L, v1, v2, iop, fop); } + + +/* +** Arithmetic operations with K operands. +*/ +#define op_arithK(L,iop,fop) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ + op_arith_aux(L, v1, v2, iop, fop); } + + +/* +** Bitwise operations with constant operand. +*/ +#define op_bitwiseK(L,op) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = KC(i); \ + lua_Integer i1; \ + lua_Integer i2 = ivalue(v2); \ + if (tointegerns(v1, &i1)) { \ + StkId ra = RA(i); \ + pc++; setivalue(s2v(ra), op(i1, i2)); \ + }} + + +/* +** Bitwise operations with register operands. +*/ +#define op_bitwise(L,op) { \ + TValue *v1 = vRB(i); \ + TValue *v2 = vRC(i); \ + lua_Integer i1; lua_Integer i2; \ + if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \ + StkId ra = RA(i); \ + pc++; setivalue(s2v(ra), op(i1, i2)); \ + }} + + +/* +** Order operations with register operands. 'opn' actually works +** for all numbers, but the fast track improves performance for +** integers. +*/ +#define op_order(L,opi,opn,other) { \ + TValue *ra = vRA(i); \ + int cond; \ + TValue *rb = vRB(i); \ + if (ttisinteger(ra) && ttisinteger(rb)) { \ + lua_Integer ia = ivalue(ra); \ + lua_Integer ib = ivalue(rb); \ + cond = opi(ia, ib); \ + } \ + else if (ttisnumber(ra) && ttisnumber(rb)) \ + cond = opn(ra, rb); \ + else \ + Protect(cond = other(L, ra, rb)); \ + docondjump(); } + + +/* +** Order operations with immediate operand. (Immediate operand is +** always small enough to have an exact representation as a float.) +*/ +#define op_orderI(L,opi,opf,inv,tm) { \ + TValue *ra = vRA(i); \ + int cond; \ + int im = GETARG_sB(i); \ + if (ttisinteger(ra)) \ + cond = opi(ivalue(ra), im); \ + else if (ttisfloat(ra)) { \ + lua_Number fa = fltvalue(ra); \ + lua_Number fim = cast_num(im); \ + cond = opf(fa, fim); \ + } \ + else { \ + int isf = GETARG_C(i); \ + Protect(cond = luaT_callorderiTM(L, ra, im, inv, isf, tm)); \ + } \ + docondjump(); } + +/* }================================================================== */ + + +/* +** {================================================================== +** Function 'luaV_execute': main interpreter loop +** =================================================================== +*/ + +/* +** some macros for common tasks in 'luaV_execute' +*/ + + +#define RA(i) (base+GETARG_A(i)) +#define vRA(i) s2v(RA(i)) +#define RB(i) (base+GETARG_B(i)) +#define vRB(i) s2v(RB(i)) +#define KB(i) (k+GETARG_B(i)) +#define RC(i) (base+GETARG_C(i)) +#define vRC(i) s2v(RC(i)) +#define KC(i) (k+GETARG_C(i)) +#define RKC(i) ((TESTARG_k(i)) ? k + GETARG_C(i) : s2v(base + GETARG_C(i))) + + + +#define updatetrap(ci) (trap = ci->u.l.trap) + +#define updatebase(ci) (base = ci->func.p + 1) + + +#define updatestack(ci) \ + { if (l_unlikely(trap)) { updatebase(ci); ra = RA(i); } } + + +/* +** Execute a jump instruction. The 'updatetrap' allows signals to stop +** tight loops. (Without it, the local copy of 'trap' could never change.) +*/ +#define dojump(ci,i,e) { pc += GETARG_sJ(i) + e; updatetrap(ci); } + + +/* for test instructions, execute the jump instruction that follows it */ +#define donextjump(ci) { Instruction ni = *pc; dojump(ci, ni, 1); } + +/* +** do a conditional jump: skip next instruction if 'cond' is not what +** was expected (parameter 'k'), else do next instruction, which must +** be a jump. +*/ +#define docondjump() if (cond != GETARG_k(i)) pc++; else donextjump(ci); + + +/* +** Correct global 'pc'. +*/ +#define savepc(ci) (ci->u.l.savedpc = pc) + + +/* +** Whenever code can raise errors, the global 'pc' and the global +** 'top' must be correct to report occasional errors. +*/ +#define savestate(L,ci) (savepc(ci), L->top.p = ci->top.p) + + +/* +** Protect code that, in general, can raise errors, reallocate the +** stack, and change the hooks. +*/ +#define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci)) + +/* special version that does not change the top */ +#define ProtectNT(exp) (savepc(ci), (exp), updatetrap(ci)) + +/* +** Protect code that can only raise errors. (That is, it cannot change +** the stack or hooks.) +*/ +#define halfProtect(exp) (savestate(L,ci), (exp)) + +/* +** macro executed during Lua functions at points where the +** function can yield. +*/ +#if !defined(luai_threadyield) +#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} +#endif + +/* 'c' is the limit of live values in the stack */ +#define checkGC(L,c) \ + { luaC_condGC(L, (savepc(ci), L->top.p = (c)), \ + updatetrap(ci)); \ + luai_threadyield(L); } + + +/* fetch an instruction and prepare its execution */ +#define vmfetch() { \ + if (l_unlikely(trap)) { /* stack reallocation or hooks? */ \ + trap = luaG_traceexec(L, pc); /* handle hooks */ \ + updatebase(ci); /* correct stack */ \ + } \ + i = *(pc++); \ +} + +#define vmdispatch(o) switch(o) +#define vmcase(l) case l: +#define vmbreak break + + +void luaV_execute (lua_State *L, CallInfo *ci) { + LClosure *cl; + TValue *k; + StkId base; + const Instruction *pc; + int trap; +#if LUA_USE_JUMPTABLE +#include "ljumptab.h" +#endif + startfunc: + trap = L->hookmask; + returning: /* trap already set */ + cl = ci_func(ci); + k = cl->p->k; + pc = ci->u.l.savedpc; + if (l_unlikely(trap)) + trap = luaG_tracecall(L); + base = ci->func.p + 1; + /* main loop of interpreter */ + for (;;) { + Instruction i; /* instruction being executed */ + vmfetch(); + #if 0 + { /* low-level line tracing for debugging Lua */ + #include "lopnames.h" + int pcrel = pcRel(pc, cl->p); + printf("line: %d; %s (%d)\n", luaG_getfuncline(cl->p, pcrel), + opnames[GET_OPCODE(i)], pcrel); + } + #endif + lua_assert(base == ci->func.p + 1); + lua_assert(base <= L->top.p && L->top.p <= L->stack_last.p); + /* for tests, invalidate top for instructions not expecting it */ + lua_assert(luaP_isIT(i) || (cast_void(L->top.p = base), 1)); + vmdispatch (GET_OPCODE(i)) { + vmcase(OP_MOVE) { + StkId ra = RA(i); + setobjs2s(L, ra, RB(i)); + vmbreak; + } + vmcase(OP_LOADI) { + StkId ra = RA(i); + lua_Integer b = GETARG_sBx(i); + setivalue(s2v(ra), b); + vmbreak; + } + vmcase(OP_LOADF) { + StkId ra = RA(i); + int b = GETARG_sBx(i); + setfltvalue(s2v(ra), cast_num(b)); + vmbreak; + } + vmcase(OP_LOADK) { + StkId ra = RA(i); + TValue *rb = k + GETARG_Bx(i); + setobj2s(L, ra, rb); + vmbreak; + } + vmcase(OP_LOADKX) { + StkId ra = RA(i); + TValue *rb; + rb = k + GETARG_Ax(*pc); pc++; + setobj2s(L, ra, rb); + vmbreak; + } + vmcase(OP_LOADFALSE) { + StkId ra = RA(i); + setbfvalue(s2v(ra)); + vmbreak; + } + vmcase(OP_LFALSESKIP) { + StkId ra = RA(i); + setbfvalue(s2v(ra)); + pc++; /* skip next instruction */ + vmbreak; + } + vmcase(OP_LOADTRUE) { + StkId ra = RA(i); + setbtvalue(s2v(ra)); + vmbreak; + } + vmcase(OP_LOADNIL) { + StkId ra = RA(i); + int b = GETARG_B(i); + do { + setnilvalue(s2v(ra++)); + } while (b--); + vmbreak; + } + vmcase(OP_GETUPVAL) { + StkId ra = RA(i); + int b = GETARG_B(i); + setobj2s(L, ra, cl->upvals[b]->v.p); + vmbreak; + } + vmcase(OP_SETUPVAL) { + StkId ra = RA(i); + UpVal *uv = cl->upvals[GETARG_B(i)]; + setobj(L, uv->v.p, s2v(ra)); + luaC_barrier(L, uv, s2v(ra)); + vmbreak; + } + vmcase(OP_GETTABUP) { + StkId ra = RA(i); + TValue *upval = cl->upvals[GETARG_B(i)]->v.p; + TValue *rc = KC(i); + TString *key = tsvalue(rc); /* key must be a short string */ + lu_byte tag; + luaV_fastget(upval, key, s2v(ra), luaH_getshortstr, tag); + if (tagisempty(tag)) + Protect(luaV_finishget(L, upval, rc, ra, tag)); + vmbreak; + } + vmcase(OP_GETTABLE) { + StkId ra = RA(i); + TValue *rb = vRB(i); + TValue *rc = vRC(i); + lu_byte tag; + if (ttisinteger(rc)) { /* fast track for integers? */ + luaV_fastgeti(rb, ivalue(rc), s2v(ra), tag); + } + else + luaV_fastget(rb, rc, s2v(ra), luaH_get, tag); + if (tagisempty(tag)) + Protect(luaV_finishget(L, rb, rc, ra, tag)); + vmbreak; + } + vmcase(OP_GETI) { + StkId ra = RA(i); + TValue *rb = vRB(i); + int c = GETARG_C(i); + lu_byte tag; + luaV_fastgeti(rb, c, s2v(ra), tag); + if (tagisempty(tag)) { + TValue key; + setivalue(&key, c); + Protect(luaV_finishget(L, rb, &key, ra, tag)); + } + vmbreak; + } + vmcase(OP_GETFIELD) { + StkId ra = RA(i); + TValue *rb = vRB(i); + TValue *rc = KC(i); + TString *key = tsvalue(rc); /* key must be a short string */ + lu_byte tag; + luaV_fastget(rb, key, s2v(ra), luaH_getshortstr, tag); + if (tagisempty(tag)) + Protect(luaV_finishget(L, rb, rc, ra, tag)); + vmbreak; + } + vmcase(OP_SETTABUP) { + int hres; + TValue *upval = cl->upvals[GETARG_A(i)]->v.p; + TValue *rb = KB(i); + TValue *rc = RKC(i); + TString *key = tsvalue(rb); /* key must be a short string */ + luaV_fastset(upval, key, rc, hres, luaH_psetshortstr); + if (hres == HOK) + luaV_finishfastset(L, upval, rc); + else + Protect(luaV_finishset(L, upval, rb, rc, hres)); + vmbreak; + } + vmcase(OP_SETTABLE) { + StkId ra = RA(i); + int hres; + TValue *rb = vRB(i); /* key (table is in 'ra') */ + TValue *rc = RKC(i); /* value */ + if (ttisinteger(rb)) { /* fast track for integers? */ + luaV_fastseti(s2v(ra), ivalue(rb), rc, hres); + } + else { + luaV_fastset(s2v(ra), rb, rc, hres, luaH_pset); + } + if (hres == HOK) + luaV_finishfastset(L, s2v(ra), rc); + else + Protect(luaV_finishset(L, s2v(ra), rb, rc, hres)); + vmbreak; + } + vmcase(OP_SETI) { + StkId ra = RA(i); + int hres; + int b = GETARG_B(i); + TValue *rc = RKC(i); + luaV_fastseti(s2v(ra), b, rc, hres); + if (hres == HOK) + luaV_finishfastset(L, s2v(ra), rc); + else { + TValue key; + setivalue(&key, b); + Protect(luaV_finishset(L, s2v(ra), &key, rc, hres)); + } + vmbreak; + } + vmcase(OP_SETFIELD) { + StkId ra = RA(i); + int hres; + TValue *rb = KB(i); + TValue *rc = RKC(i); + TString *key = tsvalue(rb); /* key must be a short string */ + luaV_fastset(s2v(ra), key, rc, hres, luaH_psetshortstr); + if (hres == HOK) + luaV_finishfastset(L, s2v(ra), rc); + else + Protect(luaV_finishset(L, s2v(ra), rb, rc, hres)); + vmbreak; + } + vmcase(OP_NEWTABLE) { + StkId ra = RA(i); + unsigned b = cast_uint(GETARG_vB(i)); /* log2(hash size) + 1 */ + unsigned c = cast_uint(GETARG_vC(i)); /* array size */ + Table *t; + if (b > 0) + b = 1u << (b - 1); /* hash size is 2^(b - 1) */ + if (TESTARG_k(i)) { /* non-zero extra argument? */ + lua_assert(GETARG_Ax(*pc) != 0); + /* add it to array size */ + c += cast_uint(GETARG_Ax(*pc)) * (MAXARG_vC + 1); + } + pc++; /* skip extra argument */ + L->top.p = ra + 1; /* correct top in case of emergency GC */ + t = luaH_new(L); /* memory allocation */ + sethvalue2s(L, ra, t); + if (b != 0 || c != 0) + luaH_resize(L, t, c, b); /* idem */ + checkGC(L, ra + 1); + vmbreak; + } + vmcase(OP_SELF) { + StkId ra = RA(i); + lu_byte tag; + TValue *rb = vRB(i); + TValue *rc = KC(i); + TString *key = tsvalue(rc); /* key must be a short string */ + setobj2s(L, ra + 1, rb); + luaV_fastget(rb, key, s2v(ra), luaH_getshortstr, tag); + if (tagisempty(tag)) + Protect(luaV_finishget(L, rb, rc, ra, tag)); + vmbreak; + } + vmcase(OP_ADDI) { + op_arithI(L, l_addi, luai_numadd); + vmbreak; + } + vmcase(OP_ADDK) { + op_arithK(L, l_addi, luai_numadd); + vmbreak; + } + vmcase(OP_SUBK) { + op_arithK(L, l_subi, luai_numsub); + vmbreak; + } + vmcase(OP_MULK) { + op_arithK(L, l_muli, luai_nummul); + vmbreak; + } + vmcase(OP_MODK) { + savestate(L, ci); /* in case of division by 0 */ + op_arithK(L, luaV_mod, luaV_modf); + vmbreak; + } + vmcase(OP_POWK) { + op_arithfK(L, luai_numpow); + vmbreak; + } + vmcase(OP_DIVK) { + op_arithfK(L, luai_numdiv); + vmbreak; + } + vmcase(OP_IDIVK) { + savestate(L, ci); /* in case of division by 0 */ + op_arithK(L, luaV_idiv, luai_numidiv); + vmbreak; + } + vmcase(OP_BANDK) { + op_bitwiseK(L, l_band); + vmbreak; + } + vmcase(OP_BORK) { + op_bitwiseK(L, l_bor); + vmbreak; + } + vmcase(OP_BXORK) { + op_bitwiseK(L, l_bxor); + vmbreak; + } + vmcase(OP_SHLI) { + StkId ra = RA(i); + TValue *rb = vRB(i); + int ic = GETARG_sC(i); + lua_Integer ib; + if (tointegerns(rb, &ib)) { + pc++; setivalue(s2v(ra), luaV_shiftl(ic, ib)); + } + vmbreak; + } + vmcase(OP_SHRI) { + StkId ra = RA(i); + TValue *rb = vRB(i); + int ic = GETARG_sC(i); + lua_Integer ib; + if (tointegerns(rb, &ib)) { + pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic)); + } + vmbreak; + } + vmcase(OP_ADD) { + op_arith(L, l_addi, luai_numadd); + vmbreak; + } + vmcase(OP_SUB) { + op_arith(L, l_subi, luai_numsub); + vmbreak; + } + vmcase(OP_MUL) { + op_arith(L, l_muli, luai_nummul); + vmbreak; + } + vmcase(OP_MOD) { + savestate(L, ci); /* in case of division by 0 */ + op_arith(L, luaV_mod, luaV_modf); + vmbreak; + } + vmcase(OP_POW) { + op_arithf(L, luai_numpow); + vmbreak; + } + vmcase(OP_DIV) { /* float division (always with floats) */ + op_arithf(L, luai_numdiv); + vmbreak; + } + vmcase(OP_IDIV) { /* floor division */ + savestate(L, ci); /* in case of division by 0 */ + op_arith(L, luaV_idiv, luai_numidiv); + vmbreak; + } + vmcase(OP_BAND) { + op_bitwise(L, l_band); + vmbreak; + } + vmcase(OP_BOR) { + op_bitwise(L, l_bor); + vmbreak; + } + vmcase(OP_BXOR) { + op_bitwise(L, l_bxor); + vmbreak; + } + vmcase(OP_SHL) { + op_bitwise(L, luaV_shiftl); + vmbreak; + } + vmcase(OP_SHR) { + op_bitwise(L, luaV_shiftr); + vmbreak; + } + vmcase(OP_MMBIN) { + StkId ra = RA(i); + Instruction pi = *(pc - 2); /* original arith. expression */ + TValue *rb = vRB(i); + TMS tm = (TMS)GETARG_C(i); + StkId result = RA(pi); + lua_assert(OP_ADD <= GET_OPCODE(pi) && GET_OPCODE(pi) <= OP_SHR); + Protect(luaT_trybinTM(L, s2v(ra), rb, result, tm)); + vmbreak; + } + vmcase(OP_MMBINI) { + StkId ra = RA(i); + Instruction pi = *(pc - 2); /* original arith. expression */ + int imm = GETARG_sB(i); + TMS tm = (TMS)GETARG_C(i); + int flip = GETARG_k(i); + StkId result = RA(pi); + Protect(luaT_trybiniTM(L, s2v(ra), imm, flip, result, tm)); + vmbreak; + } + vmcase(OP_MMBINK) { + StkId ra = RA(i); + Instruction pi = *(pc - 2); /* original arith. expression */ + TValue *imm = KB(i); + TMS tm = (TMS)GETARG_C(i); + int flip = GETARG_k(i); + StkId result = RA(pi); + Protect(luaT_trybinassocTM(L, s2v(ra), imm, flip, result, tm)); + vmbreak; + } + vmcase(OP_UNM) { + StkId ra = RA(i); + TValue *rb = vRB(i); + lua_Number nb; + if (ttisinteger(rb)) { + lua_Integer ib = ivalue(rb); + setivalue(s2v(ra), intop(-, 0, ib)); + } + else if (tonumberns(rb, nb)) { + setfltvalue(s2v(ra), luai_numunm(L, nb)); + } + else + Protect(luaT_trybinTM(L, rb, rb, ra, TM_UNM)); + vmbreak; + } + vmcase(OP_BNOT) { + StkId ra = RA(i); + TValue *rb = vRB(i); + lua_Integer ib; + if (tointegerns(rb, &ib)) { + setivalue(s2v(ra), intop(^, ~l_castS2U(0), ib)); + } + else + Protect(luaT_trybinTM(L, rb, rb, ra, TM_BNOT)); + vmbreak; + } + vmcase(OP_NOT) { + StkId ra = RA(i); + TValue *rb = vRB(i); + if (l_isfalse(rb)) + setbtvalue(s2v(ra)); + else + setbfvalue(s2v(ra)); + vmbreak; + } + vmcase(OP_LEN) { + StkId ra = RA(i); + Protect(luaV_objlen(L, ra, vRB(i))); + vmbreak; + } + vmcase(OP_CONCAT) { + StkId ra = RA(i); + int n = GETARG_B(i); /* number of elements to concatenate */ + L->top.p = ra + n; /* mark the end of concat operands */ + ProtectNT(luaV_concat(L, n)); + checkGC(L, L->top.p); /* 'luaV_concat' ensures correct top */ + vmbreak; + } + vmcase(OP_CLOSE) { + StkId ra = RA(i); + lua_assert(!GETARG_B(i)); /* 'close must be alive */ + Protect(luaF_close(L, ra, LUA_OK, 1)); + vmbreak; + } + vmcase(OP_TBC) { + StkId ra = RA(i); + /* create new to-be-closed upvalue */ + halfProtect(luaF_newtbcupval(L, ra)); + vmbreak; + } + vmcase(OP_JMP) { + dojump(ci, i, 0); + vmbreak; + } + vmcase(OP_EQ) { + StkId ra = RA(i); + int cond; + TValue *rb = vRB(i); + Protect(cond = luaV_equalobj(L, s2v(ra), rb)); + docondjump(); + vmbreak; + } + vmcase(OP_LT) { + op_order(L, l_lti, LTnum, lessthanothers); + vmbreak; + } + vmcase(OP_LE) { + op_order(L, l_lei, LEnum, lessequalothers); + vmbreak; + } + vmcase(OP_EQK) { + StkId ra = RA(i); + TValue *rb = KB(i); + /* basic types do not use '__eq'; we can use raw equality */ + int cond = luaV_rawequalobj(s2v(ra), rb); + docondjump(); + vmbreak; + } + vmcase(OP_EQI) { + StkId ra = RA(i); + int cond; + int im = GETARG_sB(i); + if (ttisinteger(s2v(ra))) + cond = (ivalue(s2v(ra)) == im); + else if (ttisfloat(s2v(ra))) + cond = luai_numeq(fltvalue(s2v(ra)), cast_num(im)); + else + cond = 0; /* other types cannot be equal to a number */ + docondjump(); + vmbreak; + } + vmcase(OP_LTI) { + op_orderI(L, l_lti, luai_numlt, 0, TM_LT); + vmbreak; + } + vmcase(OP_LEI) { + op_orderI(L, l_lei, luai_numle, 0, TM_LE); + vmbreak; + } + vmcase(OP_GTI) { + op_orderI(L, l_gti, luai_numgt, 1, TM_LT); + vmbreak; + } + vmcase(OP_GEI) { + op_orderI(L, l_gei, luai_numge, 1, TM_LE); + vmbreak; + } + vmcase(OP_TEST) { + StkId ra = RA(i); + int cond = !l_isfalse(s2v(ra)); + docondjump(); + vmbreak; + } + vmcase(OP_TESTSET) { + StkId ra = RA(i); + TValue *rb = vRB(i); + if (l_isfalse(rb) == GETARG_k(i)) + pc++; + else { + setobj2s(L, ra, rb); + donextjump(ci); + } + vmbreak; + } + vmcase(OP_CALL) { + StkId ra = RA(i); + CallInfo *newci; + int b = GETARG_B(i); + int nresults = GETARG_C(i) - 1; + if (b != 0) /* fixed number of arguments? */ + L->top.p = ra + b; /* top signals number of arguments */ + /* else previous instruction set top */ + savepc(ci); /* in case of errors */ + if ((newci = luaD_precall(L, ra, nresults)) == NULL) + updatetrap(ci); /* C call; nothing else to be done */ + else { /* Lua call: run function in this same C frame */ + ci = newci; + goto startfunc; + } + vmbreak; + } + vmcase(OP_TAILCALL) { + StkId ra = RA(i); + int b = GETARG_B(i); /* number of arguments + 1 (function) */ + int n; /* number of results when calling a C function */ + int nparams1 = GETARG_C(i); + /* delta is virtual 'func' - real 'func' (vararg functions) */ + int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; + if (b != 0) + L->top.p = ra + b; + else /* previous instruction set top */ + b = cast_int(L->top.p - ra); + savepc(ci); /* several calls here can raise errors */ + if (TESTARG_k(i)) { + luaF_closeupval(L, base); /* close upvalues from current call */ + lua_assert(L->tbclist.p < base); /* no pending tbc variables */ + lua_assert(base == ci->func.p + 1); + } + if ((n = luaD_pretailcall(L, ci, ra, b, delta)) < 0) /* Lua function? */ + goto startfunc; /* execute the callee */ + else { /* C function? */ + ci->func.p -= delta; /* restore 'func' (if vararg) */ + luaD_poscall(L, ci, n); /* finish caller */ + updatetrap(ci); /* 'luaD_poscall' can change hooks */ + goto ret; /* caller returns after the tail call */ + } + } + vmcase(OP_RETURN) { + StkId ra = RA(i); + int n = GETARG_B(i) - 1; /* number of results */ + int nparams1 = GETARG_C(i); + if (n < 0) /* not fixed? */ + n = cast_int(L->top.p - ra); /* get what is available */ + savepc(ci); + if (TESTARG_k(i)) { /* may there be open upvalues? */ + ci->u2.nres = n; /* save number of returns */ + if (L->top.p < ci->top.p) + L->top.p = ci->top.p; + luaF_close(L, base, CLOSEKTOP, 1); + updatetrap(ci); + updatestack(ci); + } + if (nparams1) /* vararg function? */ + ci->func.p -= ci->u.l.nextraargs + nparams1; + L->top.p = ra + n; /* set call for 'luaD_poscall' */ + luaD_poscall(L, ci, n); + updatetrap(ci); /* 'luaD_poscall' can change hooks */ + goto ret; + } + vmcase(OP_RETURN0) { + if (l_unlikely(L->hookmask)) { + StkId ra = RA(i); + L->top.p = ra; + savepc(ci); + luaD_poscall(L, ci, 0); /* no hurry... */ + trap = 1; + } + else { /* do the 'poscall' here */ + int nres = get_nresults(ci->callstatus); + L->ci = ci->previous; /* back to caller */ + L->top.p = base - 1; + for (; l_unlikely(nres > 0); nres--) + setnilvalue(s2v(L->top.p++)); /* all results are nil */ + } + goto ret; + } + vmcase(OP_RETURN1) { + if (l_unlikely(L->hookmask)) { + StkId ra = RA(i); + L->top.p = ra + 1; + savepc(ci); + luaD_poscall(L, ci, 1); /* no hurry... */ + trap = 1; + } + else { /* do the 'poscall' here */ + int nres = get_nresults(ci->callstatus); + L->ci = ci->previous; /* back to caller */ + if (nres == 0) + L->top.p = base - 1; /* asked for no results */ + else { + StkId ra = RA(i); + setobjs2s(L, base - 1, ra); /* at least this result */ + L->top.p = base; + for (; l_unlikely(nres > 1); nres--) + setnilvalue(s2v(L->top.p++)); /* complete missing results */ + } + } + ret: /* return from a Lua function */ + if (ci->callstatus & CIST_FRESH) + return; /* end this frame */ + else { + ci = ci->previous; + goto returning; /* continue running caller in this frame */ + } + } + vmcase(OP_FORLOOP) { + StkId ra = RA(i); + if (ttisinteger(s2v(ra + 1))) { /* integer loop? */ + lua_Unsigned count = l_castS2U(ivalue(s2v(ra))); + if (count > 0) { /* still more iterations? */ + lua_Integer step = ivalue(s2v(ra + 1)); + lua_Integer idx = ivalue(s2v(ra + 2)); /* control variable */ + chgivalue(s2v(ra), l_castU2S(count - 1)); /* update counter */ + idx = intop(+, idx, step); /* add step to index */ + chgivalue(s2v(ra + 2), idx); /* update control variable */ + pc -= GETARG_Bx(i); /* jump back */ + } + } + else if (floatforloop(ra)) /* float loop */ + pc -= GETARG_Bx(i); /* jump back */ + updatetrap(ci); /* allows a signal to break the loop */ + vmbreak; + } + vmcase(OP_FORPREP) { + StkId ra = RA(i); + savestate(L, ci); /* in case of errors */ + if (forprep(L, ra)) + pc += GETARG_Bx(i) + 1; /* skip the loop */ + vmbreak; + } + vmcase(OP_TFORPREP) { + /* before: 'ra' has the iterator function, 'ra + 1' has the state, + 'ra + 2' has the initial value for the control variable, and + 'ra + 3' has the closing variable. This opcode then swaps the + control and the closing variables and marks the closing variable + as to-be-closed. + */ + StkId ra = RA(i); + TValue temp; /* to swap control and closing variables */ + setobj(L, &temp, s2v(ra + 3)); + setobjs2s(L, ra + 3, ra + 2); + setobj2s(L, ra + 2, &temp); + /* create to-be-closed upvalue (if closing var. is not nil) */ + halfProtect(luaF_newtbcupval(L, ra + 2)); + pc += GETARG_Bx(i); /* go to end of the loop */ + i = *(pc++); /* fetch next instruction */ + lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i)); + goto l_tforcall; + } + vmcase(OP_TFORCALL) { + l_tforcall: { + /* 'ra' has the iterator function, 'ra + 1' has the state, + 'ra + 2' has the closing variable, and 'ra + 3' has the control + variable. The call will use the stack starting at 'ra + 3', + so that it preserves the first three values, and the first + return will be the new value for the control variable. + */ + StkId ra = RA(i); + setobjs2s(L, ra + 5, ra + 3); /* copy the control variable */ + setobjs2s(L, ra + 4, ra + 1); /* copy state */ + setobjs2s(L, ra + 3, ra); /* copy function */ + L->top.p = ra + 3 + 3; + ProtectNT(luaD_call(L, ra + 3, GETARG_C(i))); /* do the call */ + updatestack(ci); /* stack may have changed */ + i = *(pc++); /* go to next instruction */ + lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i)); + goto l_tforloop; + }} + vmcase(OP_TFORLOOP) { + l_tforloop: { + StkId ra = RA(i); + if (!ttisnil(s2v(ra + 3))) /* continue loop? */ + pc -= GETARG_Bx(i); /* jump back */ + vmbreak; + }} + vmcase(OP_SETLIST) { + StkId ra = RA(i); + unsigned n = cast_uint(GETARG_vB(i)); + unsigned last = cast_uint(GETARG_vC(i)); + Table *h = hvalue(s2v(ra)); + if (n == 0) + n = cast_uint(L->top.p - ra) - 1; /* get up to the top */ + else + L->top.p = ci->top.p; /* correct top in case of emergency GC */ + last += n; + if (TESTARG_k(i)) { + last += cast_uint(GETARG_Ax(*pc)) * (MAXARG_vC + 1); + pc++; + } + /* when 'n' is known, table should have proper size */ + if (last > h->asize) { /* needs more space? */ + /* fixed-size sets should have space preallocated */ + lua_assert(GETARG_vB(i) == 0); + luaH_resizearray(L, h, last); /* preallocate it at once */ + } + for (; n > 0; n--) { + TValue *val = s2v(ra + n); + obj2arr(h, last - 1, val); + last--; + luaC_barrierback(L, obj2gco(h), val); + } + vmbreak; + } + vmcase(OP_CLOSURE) { + StkId ra = RA(i); + Proto *p = cl->p->p[GETARG_Bx(i)]; + halfProtect(pushclosure(L, p, cl->upvals, base, ra)); + checkGC(L, ra + 1); + vmbreak; + } + vmcase(OP_VARARG) { + StkId ra = RA(i); + int n = GETARG_C(i) - 1; /* required results (-1 means all) */ + int vatab = GETARG_k(i) ? GETARG_B(i) : -1; + Protect(luaT_getvarargs(L, ci, ra, n, vatab)); + vmbreak; + } + vmcase(OP_GETVARG) { + StkId ra = RA(i); + TValue *rc = vRC(i); + luaT_getvararg(ci, ra, rc); + vmbreak; + } + vmcase(OP_ERRNNIL) { + TValue *ra = vRA(i); + if (!ttisnil(ra)) + halfProtect(luaG_errnnil(L, cl, GETARG_Bx(i))); + vmbreak; + } + vmcase(OP_VARARGPREP) { + ProtectNT(luaT_adjustvarargs(L, ci, cl->p)); + if (l_unlikely(trap)) { /* previous "Protect" updated trap */ + luaD_hookcall(L, ci); + L->oldpc = 1; /* next opcode will be seen as a "new" line */ + } + updatebase(ci); /* function has new base after adjustment */ + vmbreak; + } + vmcase(OP_EXTRAARG) { + lua_assert(0); + vmbreak; + } + } + } +} + +/* }================================================================== */ + +``` + +`Lua/lvm.h`: + +```h +/* +** $Id: lvm.h $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lvm_h +#define lvm_h + + +#include "ldo.h" +#include "lobject.h" +#include "ltm.h" + + +#if !defined(LUA_NOCVTN2S) +#define cvt2str(o) ttisnumber(o) +#else +#define cvt2str(o) 0 /* no conversion from numbers to strings */ +#endif + + +#if !defined(LUA_NOCVTS2N) +#define cvt2num(o) ttisstring(o) +#else +#define cvt2num(o) 0 /* no conversion from strings to numbers */ +#endif + + +/* +** You can define LUA_FLOORN2I if you want to convert floats to integers +** by flooring them (instead of raising an error if they are not +** integral values) +*/ +#if !defined(LUA_FLOORN2I) +#define LUA_FLOORN2I F2Ieq +#endif + + +/* +** Rounding modes for float->integer coercion + */ +typedef enum { + F2Ieq, /* no rounding; accepts only integral values */ + F2Ifloor, /* takes the floor of the number */ + F2Iceil /* takes the ceiling of the number */ +} F2Imod; + + +/* convert an object to a float (including string coercion) */ +#define tonumber(o,n) \ + (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n)) + + +/* convert an object to a float (without string coercion) */ +#define tonumberns(o,n) \ + (ttisfloat(o) ? ((n) = fltvalue(o), 1) : \ + (ttisinteger(o) ? ((n) = cast_num(ivalue(o)), 1) : 0)) + + +/* convert an object to an integer (including string coercion) */ +#define tointeger(o,i) \ + (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \ + : luaV_tointeger(o,i,LUA_FLOORN2I)) + + +/* convert an object to an integer (without string coercion) */ +#define tointegerns(o,i) \ + (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \ + : luaV_tointegerns(o,i,LUA_FLOORN2I)) + + +#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2)) + +#define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2) + + +/* +** fast track for 'gettable' +*/ +#define luaV_fastget(t,k,res,f, tag) \ + (tag = (!ttistable(t) ? LUA_VNOTABLE : f(hvalue(t), k, res))) + + +/* +** Special case of 'luaV_fastget' for integers, inlining the fast case +** of 'luaH_getint'. +*/ +#define luaV_fastgeti(t,k,res,tag) \ + if (!ttistable(t)) tag = LUA_VNOTABLE; \ + else { luaH_fastgeti(hvalue(t), k, res, tag); } + + +#define luaV_fastset(t,k,val,hres,f) \ + (hres = (!ttistable(t) ? HNOTATABLE : f(hvalue(t), k, val))) + +#define luaV_fastseti(t,k,val,hres) \ + if (!ttistable(t)) hres = HNOTATABLE; \ + else { luaH_fastseti(hvalue(t), k, val, hres); } + + +/* +** Finish a fast set operation (when fast set succeeds). +*/ +#define luaV_finishfastset(L,t,v) luaC_barrierback(L, gcvalue(t), v) + + +/* +** Shift right is the same as shift left with a negative 'y' +*/ +#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y)) + + + +LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); +LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n); +LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode); +LUAI_FUNC int luaV_tointegerns (const TValue *obj, lua_Integer *p, + F2Imod mode); +LUAI_FUNC int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode); +LUAI_FUNC lu_byte luaV_finishget (lua_State *L, const TValue *t, TValue *key, + StkId val, lu_byte tag); +LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key, + TValue *val, int aux); +LUAI_FUNC void luaV_finishOp (lua_State *L); +LUAI_FUNC void luaV_execute (lua_State *L, CallInfo *ci); +LUAI_FUNC void luaV_concat (lua_State *L, int total); +LUAI_FUNC lua_Integer luaV_idiv (lua_State *L, lua_Integer x, lua_Integer y); +LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y); +LUAI_FUNC lua_Number luaV_modf (lua_State *L, lua_Number x, lua_Number y); +LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y); +LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb); + +#endif + +``` + +`Lua/lzio.c`: + +```c +/* +** $Id: lzio.c $ +** Buffered streams +** See Copyright Notice in lua.h +*/ + +#define lzio_c +#define LUA_CORE + +#include "lprefix.h" + + +#include <string.h> + +#include "lua.h" + +#include "lapi.h" +#include "llimits.h" +#include "lmem.h" +#include "lstate.h" +#include "lzio.h" + + +int luaZ_fill (ZIO *z) { + size_t size; + lua_State *L = z->L; + const char *buff; + lua_unlock(L); + buff = z->reader(L, z->data, &size); + lua_lock(L); + if (buff == NULL || size == 0) + return EOZ; + z->n = size - 1; /* discount char being returned */ + z->p = buff; + return cast_uchar(*(z->p++)); +} + + +void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { + z->L = L; + z->reader = reader; + z->data = data; + z->n = 0; + z->p = NULL; +} + + +/* --------------------------------------------------------------- read --- */ + +static int checkbuffer (ZIO *z) { + if (z->n == 0) { /* no bytes in buffer? */ + if (luaZ_fill(z) == EOZ) /* try to read more */ + return 0; /* no more input */ + else { + z->n++; /* luaZ_fill consumed first byte; put it back */ + z->p--; + } + } + return 1; /* now buffer has something */ +} + + +size_t luaZ_read (ZIO *z, void *b, size_t n) { + while (n) { + size_t m; + if (!checkbuffer(z)) + return n; /* no more input; return number of missing bytes */ + m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ + memcpy(b, z->p, m); + z->n -= m; + z->p += m; + b = (char *)b + m; + n -= m; + } + return 0; +} + + +const void *luaZ_getaddr (ZIO* z, size_t n) { + const void *res; + if (!checkbuffer(z)) + return NULL; /* no more input */ + if (z->n < n) /* not enough bytes? */ + return NULL; /* block not whole; cannot give an address */ + res = z->p; /* get block address */ + z->n -= n; /* consume these bytes */ + z->p += n; + return res; +} + +``` + +`Lua/lzio.h`: + +```h +/* +** $Id: lzio.h $ +** Buffered streams +** See Copyright Notice in lua.h +*/ + + +#ifndef lzio_h +#define lzio_h + +#include "lua.h" + +#include "lmem.h" + + +#define EOZ (-1) /* end of stream */ + +typedef struct Zio ZIO; + +#define zgetc(z) (((z)->n--)>0 ? cast_uchar(*(z)->p++) : luaZ_fill(z)) + + +typedef struct Mbuffer { + char *buffer; + size_t n; + size_t buffsize; +} Mbuffer; + +#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) + +#define luaZ_buffer(buff) ((buff)->buffer) +#define luaZ_sizebuffer(buff) ((buff)->buffsize) +#define luaZ_bufflen(buff) ((buff)->n) + +#define luaZ_buffremove(buff,i) ((buff)->n -= cast_sizet(i)) +#define luaZ_resetbuffer(buff) ((buff)->n = 0) + + +#define luaZ_resizebuffer(L, buff, size) \ + ((buff)->buffer = luaM_reallocvchar(L, (buff)->buffer, \ + (buff)->buffsize, size), \ + (buff)->buffsize = size) + +#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) + + +LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, + void *data); +LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */ + +LUAI_FUNC const void *luaZ_getaddr (ZIO* z, size_t n); + + +/* --------- Private Part ------------------ */ + +struct Zio { + size_t n; /* bytes still unread */ + const char *p; /* current position in buffer */ + lua_Reader reader; /* reader function */ + void *data; /* additional data */ + lua_State *L; /* Lua state (for reader) */ +}; + + +LUAI_FUNC int luaZ_fill (ZIO *z); + +#endif + +``` + +`LudaSocket/ludasocket.cpp`: + +```cpp +#include "ludasocket.h" + +#define WIN32_LEAN_AND_MEAN +#include <Windows.h> +#include <WinSock2.h> +#include <WS2tcpip.h> + +#pragma comment(lib, "ws2_32.lib") + +#include <thread> +#include <atomic> +#include <mutex> +#include <vector> +#include <sstream> + +namespace luda { + + // Simple JSON helpers + namespace json { + std::string Escape(const std::string& str) { + std::string result; + result.reserve(str.size() + 16); + for (char c : str) { + switch (c) { + case '"': result += "\\\""; break; + case '\\': result += "\\\\"; break; + case '\b': result += "\\b"; break; + case '\f': result += "\\f"; break; + case '\n': result += "\\n"; break; + case '\r': result += "\\r"; break; + case '\t': result += "\\t"; break; + default: result += c; break; + } + } + return result; + } + + std::string CreateMessage(const std::string& type, const std::string& data) { + return "{\"type\":\"" + type + "\",\"data\":\"" + Escape(data) + "\"}"; + } + + bool ParseMessage(const std::string& json, std::string& type, std::string& data) { + auto findValue = [&json](const std::string& key) -> std::string { + std::string searchKey = "\"" + key + "\":\""; + size_t pos = json.find(searchKey); + if (pos == std::string::npos) return ""; + + pos += searchKey.length(); + std::string result; + bool escaped = false; + + for (size_t i = pos; i < json.length(); ++i) { + char c = json[i]; + if (escaped) { + switch (c) { + case 'n': result += '\n'; break; + case 'r': result += '\r'; break; + case 't': result += '\t'; break; + case 'b': result += '\b'; break; + case 'f': result += '\f'; break; + default: result += c; break; + } + escaped = false; + } + else if (c == '\\') { + escaped = true; + } + else if (c == '"') { + break; + } + else { + result += c; + } + } + return result; + }; + + type = findValue("type"); + data = findValue("data"); + if (data.empty()) { + data = findValue("script"); + } + return !type.empty(); + } + } + + // WebSocket frame opcodes + enum class WsOpcode : uint8_t { + Continuation = 0x0, + Text = 0x1, + Binary = 0x2, + Close = 0x8, + Ping = 0x9, + Pong = 0xA + }; + + class LudaSocket::Impl { + public: + Impl() : m_listenSocket(INVALID_SOCKET), m_clientSocket(INVALID_SOCKET), + m_running(false), m_clientConnected(false) { + WSADATA wsaData; + WSAStartup(MAKEWORD(2, 2), &wsaData); + } + + ~Impl() { + Stop(); + WSACleanup(); + } + + bool Start(uint16_t port) { + if (m_running) { + Stop(); + } + + m_listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_listenSocket == INVALID_SOCKET) { + return false; + } + + // Allow address reuse + int opt = 1; + setsockopt(m_listenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); + + sockaddr_in addr = {}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(port); + + if (bind(m_listenSocket, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) { + closesocket(m_listenSocket); + m_listenSocket = INVALID_SOCKET; + return false; + } + + if (listen(m_listenSocket, 1) == SOCKET_ERROR) { + closesocket(m_listenSocket); + m_listenSocket = INVALID_SOCKET; + return false; + } + + m_running = true; + m_acceptThread = std::thread(&Impl::AcceptLoop, this); + + return true; + } + + void Stop() { + m_running = false; + + if (m_clientSocket != INVALID_SOCKET) { + shutdown(m_clientSocket, SD_BOTH); + closesocket(m_clientSocket); + m_clientSocket = INVALID_SOCKET; + } + + if (m_listenSocket != INVALID_SOCKET) { + closesocket(m_listenSocket); + m_listenSocket = INVALID_SOCKET; + } + + if (m_acceptThread.joinable()) { + m_acceptThread.join(); + } + + if (m_clientThread.joinable()) { + m_clientThread.join(); + } + + m_clientConnected = false; + } + + bool IsRunning() const { + return m_running; + } + + bool IsClientConnected() const { + return m_clientConnected; + } + + void SetScriptCallback(ScriptCallback callback) { + std::lock_guard<std::mutex> lock(m_callbackMutex); + m_scriptCallback = callback; + } + + void SetConnectionCallback(ConnectionCallback callback) { + std::lock_guard<std::mutex> lock(m_callbackMutex); + m_connectionCallback = callback; + } + + void SendMessage(const std::string& type, const std::string& data) { + if (!m_clientConnected) return; + + std::string message = json::CreateMessage(type, data); + std::vector<uint8_t> payload(message.begin(), message.end()); + + std::lock_guard<std::mutex> lock(m_sendMutex); + SendFrame(WsOpcode::Text, payload.data(), payload.size()); + } + + private: + void AcceptLoop() { + while (m_running) { + // Set socket to non-blocking for accept with timeout + fd_set readSet; + FD_ZERO(&readSet); + FD_SET(m_listenSocket, &readSet); + + timeval timeout = { 0, 100000 }; // 100ms + int result = select(0, &readSet, nullptr, nullptr, &timeout); + + if (result > 0 && FD_ISSET(m_listenSocket, &readSet)) { + SOCKET clientSocket = accept(m_listenSocket, nullptr, nullptr); + if (clientSocket != INVALID_SOCKET) { + // Close existing client if any + if (m_clientSocket != INVALID_SOCKET) { + shutdown(m_clientSocket, SD_BOTH); + closesocket(m_clientSocket); + if (m_clientThread.joinable()) { + m_clientThread.join(); + } + } + + m_clientSocket = clientSocket; + + // Perform WebSocket handshake + if (PerformHandshake()) { + m_clientConnected = true; + + // Notify callback + ConnectionCallback cb; + { + std::lock_guard<std::mutex> lock(m_callbackMutex); + cb = m_connectionCallback; + } + if (cb) cb(true); + + // Start client receive thread + m_clientThread = std::thread(&Impl::ClientReceiveLoop, this); + } + else { + closesocket(m_clientSocket); + m_clientSocket = INVALID_SOCKET; + } + } + } + } + } + + bool PerformHandshake() { + char buffer[4096]; + int received = recv(m_clientSocket, buffer, sizeof(buffer) - 1, 0); + if (received <= 0) return false; + buffer[received] = '\0'; + + std::string request(buffer); + + // Find Sec-WebSocket-Key + std::string keyHeader = "Sec-WebSocket-Key: "; + size_t keyPos = request.find(keyHeader); + if (keyPos == std::string::npos) return false; + + keyPos += keyHeader.length(); + size_t keyEnd = request.find("\r\n", keyPos); + std::string clientKey = request.substr(keyPos, keyEnd - keyPos); + + // Generate accept key (simplified - proper implementation would use SHA1) + // For a real implementation, you'd compute: base64(SHA1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")) + // Using a simplified version that works with most clients + std::string acceptKey = ComputeAcceptKey(clientKey); + + // Send handshake response + std::ostringstream response; + response << "HTTP/1.1 101 Switching Protocols\r\n"; + response << "Upgrade: websocket\r\n"; + response << "Connection: Upgrade\r\n"; + response << "Sec-WebSocket-Accept: " << acceptKey << "\r\n"; + response << "\r\n"; + + std::string responseStr = response.str(); + return send(m_clientSocket, responseStr.c_str(), (int)responseStr.length(), 0) != SOCKET_ERROR; + } + + std::string ComputeAcceptKey(const std::string& clientKey) { + // WebSocket GUID + const std::string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + std::string combined = clientKey + guid; + + // SHA-1 hash + uint32_t h0 = 0x67452301; + uint32_t h1 = 0xEFCDAB89; + uint32_t h2 = 0x98BADCFE; + uint32_t h3 = 0x10325476; + uint32_t h4 = 0xC3D2E1F0; + + // Padding + std::vector<uint8_t> msg(combined.begin(), combined.end()); + uint64_t originalLen = msg.size() * 8; + msg.push_back(0x80); + while ((msg.size() % 64) != 56) { + msg.push_back(0x00); + } + for (int i = 7; i >= 0; --i) { + msg.push_back((originalLen >> (i * 8)) & 0xFF); + } + + // Process blocks + for (size_t i = 0; i < msg.size(); i += 64) { + uint32_t w[80]; + for (int j = 0; j < 16; ++j) { + w[j] = (msg[i + j * 4] << 24) | (msg[i + j * 4 + 1] << 16) | + (msg[i + j * 4 + 2] << 8) | msg[i + j * 4 + 3]; + } + for (int j = 16; j < 80; ++j) { + uint32_t temp = w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16]; + w[j] = (temp << 1) | (temp >> 31); + } + + uint32_t a = h0, b = h1, c = h2, d = h3, e = h4; + + for (int j = 0; j < 80; ++j) { + uint32_t f, k; + if (j < 20) { + f = (b & c) | ((~b) & d); + k = 0x5A827999; + } + else if (j < 40) { + f = b ^ c ^ d; + k = 0x6ED9EBA1; + } + else if (j < 60) { + f = (b & c) | (b & d) | (c & d); + k = 0x8F1BBCDC; + } + else { + f = b ^ c ^ d; + k = 0xCA62C1D6; + } + + uint32_t temp = ((a << 5) | (a >> 27)) + f + e + k + w[j]; + e = d; + d = c; + c = (b << 30) | (b >> 2); + b = a; + a = temp; + } + + h0 += a; h1 += b; h2 += c; h3 += d; h4 += e; + } + + // Create hash bytes + uint8_t hash[20]; + for (int i = 0; i < 4; ++i) { + hash[i] = (h0 >> (24 - i * 8)) & 0xFF; + hash[i + 4] = (h1 >> (24 - i * 8)) & 0xFF; + hash[i + 8] = (h2 >> (24 - i * 8)) & 0xFF; + hash[i + 12] = (h3 >> (24 - i * 8)) & 0xFF; + hash[i + 16] = (h4 >> (24 - i * 8)) & 0xFF; + } + + // Base64 encode + return Base64Encode(hash, 20); + } + + void ClientReceiveLoop() { + std::vector<uint8_t> buffer(4096); + std::vector<uint8_t> messageBuffer; + + while (m_running && m_clientConnected) { + fd_set readSet; + FD_ZERO(&readSet); + FD_SET(m_clientSocket, &readSet); + + timeval timeout = { 0, 100000 }; // 100ms + int result = select(0, &readSet, nullptr, nullptr, &timeout); + + if (result <= 0) continue; + + int received = recv(m_clientSocket, (char*)buffer.data(), (int)buffer.size(), 0); + + if (received <= 0) { + // Client disconnected + m_clientConnected = false; + ConnectionCallback cb; + { + std::lock_guard<std::mutex> lock(m_callbackMutex); + cb = m_connectionCallback; + } + if (cb) cb(false); + break; + } + + // Parse WebSocket frames + size_t offset = 0; + while (offset < static_cast<size_t>(received)) { + if (offset + 2 > static_cast<size_t>(received)) break; + + uint8_t byte0 = buffer[offset]; + uint8_t byte1 = buffer[offset + 1]; + + bool fin = (byte0 & 0x80) != 0; + WsOpcode opcode = static_cast<WsOpcode>(byte0 & 0x0F); + bool masked = (byte1 & 0x80) != 0; + uint64_t payloadLen = byte1 & 0x7F; + + offset += 2; + + if (payloadLen == 126) { + if (offset + 2 > static_cast<size_t>(received)) break; + payloadLen = (buffer[offset] << 8) | buffer[offset + 1]; + offset += 2; + } + else if (payloadLen == 127) { + if (offset + 8 > static_cast<size_t>(received)) break; + payloadLen = 0; + for (int i = 0; i < 8; ++i) { + payloadLen = (payloadLen << 8) | buffer[offset + i]; + } + offset += 8; + } + + uint8_t mask[4] = { 0 }; + if (masked) { + if (offset + 4 > static_cast<size_t>(received)) break; + memcpy(mask, &buffer[offset], 4); + offset += 4; + } + + if (offset + payloadLen > static_cast<size_t>(received)) break; + + // Extract payload + std::vector<uint8_t> payload(payloadLen); + for (uint64_t i = 0; i < payloadLen; ++i) { + payload[i] = buffer[offset + i]; + if (masked) { + payload[i] ^= mask[i % 4]; + } + } + offset += payloadLen; + + // Handle frame + switch (opcode) { + case WsOpcode::Text: + case WsOpcode::Binary: + messageBuffer.insert(messageBuffer.end(), payload.begin(), payload.end()); + if (fin) { + HandleMessage(std::string(messageBuffer.begin(), messageBuffer.end())); + messageBuffer.clear(); + } + break; + case WsOpcode::Continuation: + messageBuffer.insert(messageBuffer.end(), payload.begin(), payload.end()); + if (fin) { + HandleMessage(std::string(messageBuffer.begin(), messageBuffer.end())); + messageBuffer.clear(); + } + break; + case WsOpcode::Ping: + SendFrame(WsOpcode::Pong, payload.data(), payload.size()); + break; + case WsOpcode::Close: + m_clientConnected = false; + { + ConnectionCallback cb; + { + std::lock_guard<std::mutex> lock(m_callbackMutex); + cb = m_connectionCallback; + } + if (cb) cb(false); + } + return; + default: + break; + } + } + } + } + + void HandleMessage(const std::string& message) { + std::string type, data; + if (json::ParseMessage(message, type, data)) { + if (type == "execute") { + ScriptCallback cb; + { + std::lock_guard<std::mutex> lock(m_callbackMutex); + cb = m_scriptCallback; + } + if (cb) { + cb(data); + } + } + } + } + + bool SendFrame(WsOpcode opcode, const uint8_t* payload, size_t payloadLen) { + if (m_clientSocket == INVALID_SOCKET) return false; + + std::vector<uint8_t> frame; + + // First byte: FIN + opcode + frame.push_back(0x80 | static_cast<uint8_t>(opcode)); + + // Server doesn't mask frames + if (payloadLen < 126) { + frame.push_back(static_cast<uint8_t>(payloadLen)); + } + else if (payloadLen <= 65535) { + frame.push_back(126); + frame.push_back(static_cast<uint8_t>((payloadLen >> 8) & 0xFF)); + frame.push_back(static_cast<uint8_t>(payloadLen & 0xFF)); + } + else { + frame.push_back(127); + for (int i = 7; i >= 0; --i) { + frame.push_back(static_cast<uint8_t>((payloadLen >> (i * 8)) & 0xFF)); + } + } + + // Unmasked payload (server to client) + frame.insert(frame.end(), payload, payload + payloadLen); + + int sent = send(m_clientSocket, (const char*)frame.data(), (int)frame.size(), 0); + return sent != SOCKET_ERROR; + } + + std::string Base64Encode(const uint8_t* data, size_t len) { + static const char* chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::string result; + result.reserve(((len + 2) / 3) * 4); + + for (size_t i = 0; i < len; i += 3) { + uint32_t n = static_cast<uint32_t>(data[i]) << 16; + if (i + 1 < len) n |= static_cast<uint32_t>(data[i + 1]) << 8; + if (i + 2 < len) n |= static_cast<uint32_t>(data[i + 2]); + + result += chars[(n >> 18) & 0x3F]; + result += chars[(n >> 12) & 0x3F]; + result += (i + 1 < len) ? chars[(n >> 6) & 0x3F] : '='; + result += (i + 2 < len) ? chars[n & 0x3F] : '='; + } + + return result; + } + + private: + SOCKET m_listenSocket; + SOCKET m_clientSocket; + std::atomic<bool> m_running; + std::atomic<bool> m_clientConnected; + + std::thread m_acceptThread; + std::thread m_clientThread; + + std::mutex m_sendMutex; + std::mutex m_callbackMutex; + + ScriptCallback m_scriptCallback; + ConnectionCallback m_connectionCallback; + }; + + // LudaSocket implementation + LudaSocket::LudaSocket() : m_impl(std::make_unique<Impl>()) {} + LudaSocket::~LudaSocket() = default; + + bool LudaSocket::Start(uint16_t port) { + return m_impl->Start(port); + } + + void LudaSocket::Stop() { + m_impl->Stop(); + } + + bool LudaSocket::IsRunning() const { + return m_impl->IsRunning(); + } + + bool LudaSocket::IsClientConnected() const { + return m_impl->IsClientConnected(); + } + + void LudaSocket::SetScriptCallback(ScriptCallback callback) { + m_impl->SetScriptCallback(callback); + } + + void LudaSocket::SetConnectionCallback(ConnectionCallback callback) { + m_impl->SetConnectionCallback(callback); + } + + void LudaSocket::SendOutput(const std::string& message) { + m_impl->SendMessage("output", message); + } + + void LudaSocket::SendError(const std::string& message) { + m_impl->SendMessage("error", message); + } + + void LudaSocket::SendSuccess(const std::string& message) { + m_impl->SendMessage("success", message); + } + + void LudaSocket::SendPrint(const std::string& message) { + m_impl->SendMessage("print", message); + } + + // Global instance + static LudaSocket* g_instance = nullptr; + static std::mutex g_instanceMutex; + + LudaSocket& GetInstance() { + std::lock_guard<std::mutex> lock(g_instanceMutex); + if (!g_instance) { + g_instance = new LudaSocket(); + } + return *g_instance; + } + + // Convenience functions + bool Start(uint16_t port) { + return GetInstance().Start(port); + } + + void Stop() { + GetInstance().Stop(); + } + + bool IsRunning() { + return GetInstance().IsRunning(); + } + + bool IsClientConnected() { + return GetInstance().IsClientConnected(); + } + + void SetScriptCallback(ScriptCallback callback) { + GetInstance().SetScriptCallback(callback); + } + + void SetConnectionCallback(ConnectionCallback callback) { + GetInstance().SetConnectionCallback(callback); + } + + void SendOutput(const std::string& message) { + GetInstance().SendOutput(message); + } + + void SendError(const std::string& message) { + GetInstance().SendError(message); + } + + void SendSuccess(const std::string& message) { + GetInstance().SendSuccess(message); + } + + void SendPrint(const std::string& message) { + GetInstance().SendPrint(message); + } + +} // namespace luda + +``` + +`LudaSocket/ludasocket.h`: + +```h +#pragma once + +#include <string> +#include <functional> +#include <memory> + +/*#ifdef LUDASOCKET_EXPORTS +#define LUDA_API __declspec(dllexport) +#else +#define LUDA_API __declspec(dllimport) +#endif +*/ +namespace luda { + + // Callback type for when a script is received for execution + using ScriptCallback = std::function<void(const std::string& script)>; + + // Callback for connection state changes + using ConnectionCallback = std::function<void(bool connected)>; + + class LudaSocket { + public: + LudaSocket(); + ~LudaSocket(); + + // Prevent copying + LudaSocket(const LudaSocket&) = delete; + LudaSocket& operator=(const LudaSocket&) = delete; + + // Start the WebSocket server on the specified port + bool Start(uint16_t port = 8080); + + // Stop the server + void Stop(); + + // Check if server is running + bool IsRunning() const; + + // Check if a client (UI) is connected + bool IsClientConnected() const; + + // Set callback for incoming script execution requests + void SetScriptCallback(ScriptCallback callback); + + // Set callback for connection state changes + void SetConnectionCallback(ConnectionCallback callback); + + // Send responses back to the UI + void SendOutput(const std::string& message); + void SendError(const std::string& message); + void SendSuccess(const std::string& message = "Script executed successfully."); + void SendPrint(const std::string& message); + + private: + class Impl; + std::unique_ptr<Impl> m_impl; + }; + + LudaSocket& GetInstance(); + + bool Start(uint16_t port = 8080); + void Stop(); + bool IsRunning(); + bool IsClientConnected(); + void SetScriptCallback(ScriptCallback callback); + void SetConnectionCallback(ConnectionCallback callback); + void SendOutput(const std::string& message); + void SendError(const std::string& message); + void SendSuccess(const std::string& message = "Script executed successfully."); + void SendPrint(const std::string& message); + +} // namespace luda + +``` + +`Plugin.cpp`: + +```cpp +#include <windows.h> +#include <iostream> +#include <thread> + +#include <LudaSocket/ludasocket.h> + +#define __EA64__ +#include <IdaSDK/ida.hpp> +#include <IdaSDK/idp.hpp> +#include <IdaSDK/loader.hpp> +#include <IdaSDK/hexrays.hpp> + +#include <Executor/Executor.h> + +Executor* executor; +bool has_initiated = false; +plugmod_t* idaapi init() { + if (!has_initiated) + { + executor = new Executor(); + + luda::SetScriptCallback([](const std::string& script) { + executor->run_script(script); + }); + + luda::SetConnectionCallback([](bool connected) { + if (connected) { + msg("[LUDA] UI connected\n"); + } + else { + msg("[LUDA] UI disconnected!\n"); + } + }); + + if (luda::Start(8080)) { + msg("[LUDA] Server started on port 8080\n"); + executor->initialize(); + has_initiated = true; + } + else { + msg("[LUDA] Failed to start server\n"); + } + } + + return PLUGIN_OK; +} + +void idaapi term() { + /* todo */ +} + +__declspec(dllexport) plugin_t PLUGIN = { + IDP_INTERFACE_VERSION, + PLUGIN_HIDE, + init, + term, + nullptr, + "Launch the LUDA WS server", + ":shrug:", + "LUDA", + NULL +}; +``` + +`README.md`: + +```md +# LUDA + +![LUDA Banner](LUDA.png) + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +![IDA Version](https://img.shields.io/badge/IDA-9.X-blue) +![Lua Version](https://img.shields.io/badge/Lua-5.5-2C2D72?logo=lua) +![Status](https://img.shields.io/badge/Status-In_Development-orange) + +High-performance Lua scripting for IDA Pro's SDK. Direct SDK bindings, minimal overhead, built for x86_64 binary analysis. + +> **Note:** LUDA is under active development and not yet production ready. + +--- + +## Overview + +LUDA provides direct IDA SDK access from Lua, enabling rapid prototyping and automation of reverse engineering workflows. Lua's minimal overhead and clean syntax make it ideal for performance-critical analysis pipelines. + +**Target Platform:** x86_64 only. Other architectures are not supported. + +--- + +## Features + +- **Direct SDK Bindings** — Call native IDA SDK functions without wrappers or proxies +- **Low Overhead** — Lua's lightweight runtime keeps complex analysis responsive +- **Clean API** — Intuitive bindings designed for reverse engineering workflows +- **Assembler Integration** — Convert assembly to bytes via Keystone engine +- **Extensible** — Minimal dependencies, easy to integrate into custom tooling + +--- + +## Installation + +1. Download the latest release or build from source +2. Place `LUDA.dll` in your IDA plugins folder: `<IDA_DIR>/plugins/` +3. Restart IDA Pro + +### Building from Source +```bash +git clone https://github.com/stolevchristian/LUDA.git +cd LUDA +``` + +#### Dependencies + +**Keystone Engine** — Required for assembly support. + +Download a prebuilt library from [keystone-engine.org](https://www.keystone-engine.org/download/) or build from source: +```bash +git clone https://github.com/keystone-engine/keystone.git +cd keystone && mkdir build && cd build +cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded .. +cmake --build . --config Release +``` + +Place `keystone.lib` in the `keystone/` folder. + +#### Build + +Open in CLion or build via CMake: +```bash +mkdir build && cd build +cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release .. +cmake --build . --config Release +``` + +--- + +## Usage + +### Read Memory +```lua +local address = 0xDEADBEEF +local bytes = memory.read(address, 5) + +for i, v in ipairs(bytes) do + print(string.format("0x%02X", v)) +end +``` + +### Write Memory +```lua +local address = 0xDEADBEEF +memory.write(address, {0xCC, 0xCC}) +``` + +### Disassemble +```lua +local func_addr = 0xDEADBEEF +local disasm = hexrays.disassemble(func_addr) + +print(string.format("Function at 0x%X has %d instructions", func_addr, #disasm)) +``` + +### Assemble +```lua +local bytes = assemble("mov rax, 0xF") +-- bytes = { 0x48, 0xC7, 0xC0, 0x0F, 0x00, 0x00, 0x00 } +``` + +### Decompile +```lua +local function_address = 0xDEADBEEF +local disassembly = hexrays.decompile(function_address) +print(disassembly) +--[[ +_QWORD *__fastcall sub_deadbeef(_QWORD *a1) +{ + *a1 = HIDWORD(*a1) | (*a1 << 32); + return a1; +} +]]-- +``` + +### Xrefs +```lua +local function_address = 0xDEADBEEF + +for i,v in next, xrefs.get(function_address) do + print(i, "0x" .. hex(v)) +end +--[[ +1 0x180003F03 +2 0x180004288 +3 0x1800043E3 +4 0x180004768 +5 0x1800048C3 +6 0x180004C48 +7 0x1800FCB77 +8 0x1800FCD1C +9 0x1800FDCC5 +]]-- +``` + +### Functions +```lua +local function_address = get_function("_IntegrityCheck__text") -- or get_function(0xDEADCODE) + +print("Integrity check:", "0x" .. hex(function_address)) +``` +--- + +## Changelog + +### v0.2.0 — *January 2025* +- Ported build system from Visual Studio to CLion/CMake +- Added Keystone assembler integration (`assemble()` function) +- Upgraded from Lua 5.1 to Lua 5.5 for proper 64-bit support +- Improved memory read/write API stability + +### v0.1.0 — *Initial Release* +- Core IDA SDK bindings +- Memory read/write operations +- Hex-Rays disassembly support +- Socket-based communication protocol for external UI + +--- + +## Roadmap + +- [ ] Pattern scanning API +- [ ] Function signature matching +- [ ] Struct/type creation bindings +- [ ] Documentation site + +--- + +## License + +MIT License. See [LICENSE](LICENSE) for details. + +--- + +<div align="center"> + +[GitHub](https://github.com/stolevchristian/LUDA) + +</div> + +``` + +`keystone/arm.h`: + +```h +/* Keystone Assembler Engine */ +/* By Nguyen Anh Quynh, 2016 */ + +#ifndef KEYSTONE_ARM_H +#define KEYSTONE_ARM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "keystone.h" + +typedef enum ks_err_asm_arm { + KS_ERR_ASM_ARM_INVALIDOPERAND = KS_ERR_ASM_ARCH, + KS_ERR_ASM_ARM_MISSINGFEATURE, + KS_ERR_ASM_ARM_MNEMONICFAIL, +} ks_err_asm_arm; + +#ifdef __cplusplus +} +#endif + +#endif + +``` + +`keystone/arm64.h`: + +```h +/* Keystone Assembler Engine */ +/* By Nguyen Anh Quynh, 2016 */ + +#ifndef KEYSTONE_ARM64_H +#define KEYSTONE_ARM64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "keystone.h" + +typedef enum ks_err_asm_arm64 { + KS_ERR_ASM_ARM64_INVALIDOPERAND = KS_ERR_ASM_ARCH, + KS_ERR_ASM_ARM64_MISSINGFEATURE, + KS_ERR_ASM_ARM64_MNEMONICFAIL, +} ks_err_asm_arm64; + +#ifdef __cplusplus +} +#endif + +#endif + +``` + +`keystone/evm.h`: + +```h +/* Keystone Assembler Engine */ +/* By Nguyen Anh Quynh, 2016-2018 */ + +#ifndef KEYSTONE_EVM_H +#define KEYSTONE_EVM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "keystone.h" + +typedef enum ks_err_asm_evm { + KS_ERR_ASM_EVM_INVALIDOPERAND = KS_ERR_ASM_ARCH, + KS_ERR_ASM_EVM_MISSINGFEATURE, + KS_ERR_ASM_EVM_MNEMONICFAIL, +} ks_err_asm_evm; + +#ifdef __cplusplus +} +#endif + +#endif + +``` + +`keystone/hexagon.h`: + +```h +/* Keystone Assembler Engine */ +/* By Nguyen Anh Quynh, 2016 */ + +#ifndef KEYSTONE_HEXAGON_H +#define KEYSTONE_HEXAGON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "keystone.h" + +typedef enum ks_err_asm_hexagon { + KS_ERR_ASM_HEXAGON_INVALIDOPERAND = KS_ERR_ASM_ARCH, + KS_ERR_ASM_HEXAGON_MISSINGFEATURE, + KS_ERR_ASM_HEXAGON_MNEMONICFAIL, +} ks_err_asm_hexagon; + + +#ifdef __cplusplus +} +#endif + +#endif + +``` + +`keystone/keystone.h`: + +```h +/* Keystone Assembler Engine (www.keystone-engine.org) */ +/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */ + +#ifndef KEYSTONE_ENGINE_H +#define KEYSTONE_ENGINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdbool.h> + +#ifdef _MSC_VER // MSVC compiler +#pragma warning(disable:4201) +#pragma warning(disable:4100) +#define KEYSTONE_EXPORT __declspec(dllexport) +#else +#ifdef __GNUC__ +#include <stdbool.h> +#define KEYSTONE_EXPORT __attribute__((visibility("default"))) +#else +#define KEYSTONE_EXPORT +#endif +#endif + + +struct ks_struct; +typedef struct ks_struct ks_engine; + +// Keystone API version +#define KS_API_MAJOR 0 +#define KS_API_MINOR 9 + +// Package version +#define KS_VERSION_MAJOR KS_API_MAJOR +#define KS_VERSION_MINOR KS_API_MINOR +#define KS_VERSION_EXTRA 2 + +/* + Macro to create combined version which can be compared to + result of ks_version() API. +*/ +#define KS_MAKE_VERSION(major, minor) ((major << 8) + minor) + +// Architecture type +typedef enum ks_arch { + KS_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2) + KS_ARCH_ARM64, // ARM-64, also called AArch64 + KS_ARCH_MIPS, // Mips architecture + KS_ARCH_X86, // X86 architecture (including x86 & x86-64) + KS_ARCH_PPC, // PowerPC architecture (currently unsupported) + KS_ARCH_SPARC, // Sparc architecture + KS_ARCH_SYSTEMZ, // SystemZ architecture (S390X) + KS_ARCH_HEXAGON, // Hexagon architecture + KS_ARCH_EVM, // Ethereum Virtual Machine architecture + KS_ARCH_MAX, +} ks_arch; + +// Mode type +typedef enum ks_mode { + KS_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode) + KS_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode + // arm / arm64 + KS_MODE_ARM = 1 << 0, // ARM mode + KS_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2) + KS_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM + // mips + KS_MODE_MICRO = 1 << 4, // MicroMips mode + KS_MODE_MIPS3 = 1 << 5, // Mips III ISA + KS_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA + KS_MODE_MIPS32 = 1 << 2, // Mips32 ISA + KS_MODE_MIPS64 = 1 << 3, // Mips64 ISA + // x86 / x64 + KS_MODE_16 = 1 << 1, // 16-bit mode + KS_MODE_32 = 1 << 2, // 32-bit mode + KS_MODE_64 = 1 << 3, // 64-bit mode + // ppc + KS_MODE_PPC32 = 1 << 2, // 32-bit mode + KS_MODE_PPC64 = 1 << 3, // 64-bit mode + KS_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode + // sparc + KS_MODE_SPARC32 = 1 << 2, // 32-bit mode + KS_MODE_SPARC64 = 1 << 3, // 64-bit mode + KS_MODE_V9 = 1 << 4, // SparcV9 mode +} ks_mode; + +// All generic errors related to input assembly >= KS_ERR_ASM +#define KS_ERR_ASM 128 + +// All architecture-specific errors related to input assembly >= KS_ERR_ASM_ARCH +#define KS_ERR_ASM_ARCH 512 + +// All type of errors encountered by Keystone API. +typedef enum ks_err { + KS_ERR_OK = 0, // No error: everything was fine + KS_ERR_NOMEM, // Out-Of-Memory error: ks_open(), ks_emulate() + KS_ERR_ARCH, // Unsupported architecture: ks_open() + KS_ERR_HANDLE, // Invalid handle + KS_ERR_MODE, // Invalid/unsupported mode: ks_open() + KS_ERR_VERSION, // Unsupported version (bindings) + KS_ERR_OPT_INVALID, // Unsupported option + + // generic input assembly errors - parser specific + KS_ERR_ASM_EXPR_TOKEN = KS_ERR_ASM, // unknown token in expression + KS_ERR_ASM_DIRECTIVE_VALUE_RANGE, // literal value out of range for directive + KS_ERR_ASM_DIRECTIVE_ID, // expected identifier in directive + KS_ERR_ASM_DIRECTIVE_TOKEN, // unexpected token in directive + KS_ERR_ASM_DIRECTIVE_STR, // expected string in directive + KS_ERR_ASM_DIRECTIVE_COMMA, // expected comma in directive + KS_ERR_ASM_DIRECTIVE_RELOC_NAME, // expected relocation name in directive + KS_ERR_ASM_DIRECTIVE_RELOC_TOKEN, // unexpected token in .reloc directive + KS_ERR_ASM_DIRECTIVE_FPOINT, // invalid floating point in directive + KS_ERR_ASM_DIRECTIVE_UNKNOWN, // unknown directive + KS_ERR_ASM_DIRECTIVE_EQU, // invalid equal directive + KS_ERR_ASM_DIRECTIVE_INVALID, // (generic) invalid directive + KS_ERR_ASM_VARIANT_INVALID, // invalid variant + KS_ERR_ASM_EXPR_BRACKET, // brackets expression not supported on this target + KS_ERR_ASM_SYMBOL_MODIFIER, // unexpected symbol modifier following '@' + KS_ERR_ASM_SYMBOL_REDEFINED, // invalid symbol redefinition + KS_ERR_ASM_SYMBOL_MISSING, // cannot find a symbol + KS_ERR_ASM_RPAREN, // expected ')' in parentheses expression + KS_ERR_ASM_STAT_TOKEN, // unexpected token at start of statement + KS_ERR_ASM_UNSUPPORTED, // unsupported token yet + KS_ERR_ASM_MACRO_TOKEN, // unexpected token in macro instantiation + KS_ERR_ASM_MACRO_PAREN, // unbalanced parentheses in macro argument + KS_ERR_ASM_MACRO_EQU, // expected '=' after formal parameter identifier + KS_ERR_ASM_MACRO_ARGS, // too many positional arguments + KS_ERR_ASM_MACRO_LEVELS_EXCEED, // macros cannot be nested more than 20 levels deep + KS_ERR_ASM_MACRO_STR, // invalid macro string + KS_ERR_ASM_MACRO_INVALID, // invalid macro (generic error) + KS_ERR_ASM_ESC_BACKSLASH, // unexpected backslash at end of escaped string + KS_ERR_ASM_ESC_OCTAL, // invalid octal escape sequence (out of range) + KS_ERR_ASM_ESC_SEQUENCE, // invalid escape sequence (unrecognized character) + KS_ERR_ASM_ESC_STR, // broken escape string + KS_ERR_ASM_TOKEN_INVALID, // invalid token + KS_ERR_ASM_INSN_UNSUPPORTED, // this instruction is unsupported in this mode + KS_ERR_ASM_FIXUP_INVALID, // invalid fixup + KS_ERR_ASM_LABEL_INVALID, // invalid label + KS_ERR_ASM_FRAGMENT_INVALID, // invalid fragment + + // generic input assembly errors - architecture specific + KS_ERR_ASM_INVALIDOPERAND = KS_ERR_ASM_ARCH, + KS_ERR_ASM_MISSINGFEATURE, + KS_ERR_ASM_MNEMONICFAIL, +} ks_err; + +// Resolver callback to provide value for a missing symbol in @symbol. +// To handle a symbol, the resolver must put value of the symbol in @value, +// then returns True. +// If we do not resolve a missing symbol, this function must return False. +// In that case, ks_asm() would eventually return with error KS_ERR_ASM_SYMBOL_MISSING. + +// To register the resolver, pass its function address to ks_option(), using +// option KS_OPT_SYM_RESOLVER. For example, see samples/sample.c. +typedef bool (*ks_sym_resolver)(const char *symbol, uint64_t *value); + +// Runtime option for the Keystone engine +typedef enum ks_opt_type { + KS_OPT_SYNTAX = 1, // Choose syntax for input assembly + KS_OPT_SYM_RESOLVER, // Set symbol resolver callback +} ks_opt_type; + + +// Runtime option value (associated with ks_opt_type above) +typedef enum ks_opt_value { + KS_OPT_SYNTAX_INTEL = 1 << 0, // X86 Intel syntax - default on X86 (KS_OPT_SYNTAX). + KS_OPT_SYNTAX_ATT = 1 << 1, // X86 ATT asm syntax (KS_OPT_SYNTAX). + KS_OPT_SYNTAX_NASM = 1 << 2, // X86 Nasm syntax (KS_OPT_SYNTAX). + KS_OPT_SYNTAX_MASM = 1 << 3, // X86 Masm syntax (KS_OPT_SYNTAX) - unsupported yet. + KS_OPT_SYNTAX_GAS = 1 << 4, // X86 GNU GAS syntax (KS_OPT_SYNTAX). + KS_OPT_SYNTAX_RADIX16 = 1 << 5, // All immediates are in hex format (i.e 12 is 0x12) +} ks_opt_value; + + +#include "arm64.h" +#include "arm.h" +#include "evm.h" +#include "hexagon.h" +#include "mips.h" +#include "ppc.h" +#include "sparc.h" +#include "systemz.h" +#include "x86.h" + +/* + Return combined API version & major and minor version numbers. + + @major: major number of API version + @minor: minor number of API version + + @return hexical number as (major << 8 | minor), which encodes both + major & minor versions. + NOTE: This returned value can be compared with version number made + with macro KS_MAKE_VERSION + + For example, second API version would return 1 in @major, and 1 in @minor + The return value would be 0x0101 + + NOTE: if you only care about returned value, but not major and minor values, + set both @major & @minor arguments to NULL. +*/ +KEYSTONE_EXPORT +unsigned int ks_version(unsigned int *major, unsigned int *minor); + + +/* + Determine if the given architecture is supported by this library. + + @arch: architecture type (KS_ARCH_*) + + @return True if this library supports the given arch. +*/ +KEYSTONE_EXPORT +bool ks_arch_supported(ks_arch arch); + + +/* + Create new instance of Keystone engine. + + @arch: architecture type (KS_ARCH_*) + @mode: hardware mode. This is combined of KS_MODE_* + @ks: pointer to ks_engine, which will be updated at return time + + @return KS_ERR_OK on success, or other value on failure (refer to ks_err enum + for detailed error). +*/ +KEYSTONE_EXPORT +ks_err ks_open(ks_arch arch, int mode, ks_engine **ks); + + +/* + Close KS instance: MUST do to release the handle when it is not used anymore. + NOTE: this must be called only when there is no longer usage of Keystone. + The reason is the this API releases some cached memory, thus access to any + Keystone API after ks_close() might crash your application. + After this, @ks is invalid, and nolonger usable. + + @ks: pointer to a handle returned by ks_open() + + @return KS_ERR_OK on success, or other value on failure (refer to ks_err enum + for detailed error). +*/ +KEYSTONE_EXPORT +ks_err ks_close(ks_engine *ks); + + +/* + Report the last error number when some API function fail. + Like glibc's errno, ks_errno might not retain its old error once accessed. + + @ks: handle returned by ks_open() + + @return: error code of ks_err enum type (KS_ERR_*, see above) +*/ +KEYSTONE_EXPORT +ks_err ks_errno(ks_engine *ks); + + +/* + Return a string describing given error code. + + @code: error code (see KS_ERR_* above) + + @return: returns a pointer to a string that describes the error code + passed in the argument @code + */ +KEYSTONE_EXPORT +const char *ks_strerror(ks_err code); + + +/* + Set option for Keystone engine at runtime + + @ks: handle returned by ks_open() + @type: type of option to be set. See ks_opt_type + @value: option value corresponding with @type + + @return: KS_ERR_OK on success, or other value on failure. + Refer to ks_err enum for detailed error. +*/ +KEYSTONE_EXPORT +ks_err ks_option(ks_engine *ks, ks_opt_type type, size_t value); + + +/* + Assemble a string given its the buffer, size, start address and number + of instructions to be decoded. + This API dynamically allocate memory to contain assembled instruction. + Resulted array of bytes containing the machine code is put into @*encoding + + NOTE 1: this API will automatically determine memory needed to contain + output bytes in *encoding. + + NOTE 2: caller must free the allocated memory itself to avoid memory leaking. + + @ks: handle returned by ks_open() + @str: NULL-terminated assembly string. Use ; or \n to separate statements. + @address: address of the first assembly instruction, or 0 to ignore. + @encoding: array of bytes containing encoding of input assembly string. + NOTE: *encoding will be allocated by this function, and should be freed + with ks_free() function. + @encoding_size: size of *encoding + @stat_count: number of statements successfully processed + + @return: 0 on success, or -1 on failure. + + On failure, call ks_errno() for error code. +*/ +KEYSTONE_EXPORT +int ks_asm(ks_engine *ks, + const char *string, + uint64_t address, + unsigned char **encoding, size_t *encoding_size, + size_t *stat_count); + + +/* + Free memory allocated by ks_asm() + + @p: memory allocated in @encoding argument of ks_asm() +*/ +KEYSTONE_EXPORT +void ks_free(unsigned char *p); + + +#ifdef __cplusplus +} +#endif + +#endif + +``` + +`keystone/mips.h`: + +```h +/* Keystone Assembler Engine */ +/* By Nguyen Anh Quynh, 2016 */ + +#ifndef KEYSTONE_MIPS_H +#define KEYSTONE_MIPS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "keystone.h" + +typedef enum ks_err_asm_mips { + KS_ERR_ASM_MIPS_INVALIDOPERAND = KS_ERR_ASM_ARCH, + KS_ERR_ASM_MIPS_MISSINGFEATURE, + KS_ERR_ASM_MIPS_MNEMONICFAIL, +} ks_err_asm_mips; + +#ifdef __cplusplus +} +#endif + +#endif + +``` + +`keystone/ppc.h`: + +```h +/* Keystone Assembler Engine */ +/* By Nguyen Anh Quynh, 2016 */ + +#ifndef KEYSTONE_PPC_H +#define KEYSTONE_PPC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "keystone.h" + +typedef enum ks_err_asm_ppc { + KS_ERR_ASM_PPC_INVALIDOPERAND = KS_ERR_ASM_ARCH, + KS_ERR_ASM_PPC_MISSINGFEATURE, + KS_ERR_ASM_PPC_MNEMONICFAIL, +} ks_err_asm_ppc; + + +#ifdef __cplusplus +} +#endif + +#endif + +``` + +`keystone/sparc.h`: + +```h +/* Keystone Assembler Engine */ +/* By Nguyen Anh Quynh, 2016 */ + +#ifndef KEYSTONE_SPARC_H +#define KEYSTONE_SPARC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "keystone.h" + +typedef enum ks_err_asm_sparc { + KS_ERR_ASM_SPARC_INVALIDOPERAND = KS_ERR_ASM_ARCH, + KS_ERR_ASM_SPARC_MISSINGFEATURE, + KS_ERR_ASM_SPARC_MNEMONICFAIL, +} ks_err_asm_sparc; + + +#ifdef __cplusplus +} +#endif + +#endif + +``` + +`keystone/systemz.h`: + +```h +/* Keystone Assembler Engine */ +/* By Nguyen Anh Quynh, 2016 */ + +#ifndef KEYSTONE_SYSTEMZ_H +#define KEYSTONE_SYSTEMZ_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "keystone.h" + +typedef enum ks_err_asm_systemz { + KS_ERR_ASM_SYSTEMZ_INVALIDOPERAND = KS_ERR_ASM_ARCH, + KS_ERR_ASM_SYSTEMZ_MISSINGFEATURE, + KS_ERR_ASM_SYSTEMZ_MNEMONICFAIL, +} ks_err_asm_systemz; + + +#ifdef __cplusplus +} +#endif + +#endif + +``` + +`keystone/x86.h`: + +```h +/* Keystone Assembler Engine */ +/* By Nguyen Anh Quynh, 2016 */ + +#ifndef KEYSTONE_X86_H +#define KEYSTONE_X86_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "keystone.h" + +typedef enum ks_err_asm_x86 { + KS_ERR_ASM_X86_INVALIDOPERAND = KS_ERR_ASM_ARCH, + KS_ERR_ASM_X86_MISSINGFEATURE, + KS_ERR_ASM_X86_MNEMONICFAIL, +} ks_err_asm_x86; + +#ifdef __cplusplus +} +#endif + +#endif + +``` \ No newline at end of file diff --git a/archive/thalium/symless.txt b/archive/thalium/symless.txt new file mode 100644 index 00000000..e14e40da --- /dev/null +++ b/archive/thalium/symless.txt @@ -0,0 +1,7657 @@ +Project Path: arc_thalium_symless_y23z6t5j + +Source Tree: + +```txt +arc_thalium_symless_y23z6t5j +├── LICENCE +├── README.md +├── img +│ ├── plugin_builder_form.png +│ ├── plugin_built_structure.png +│ └── plugin_context_menu.png +├── plugin +│ ├── install.py +│ └── symless_plugin.py +├── run_script.py +├── scripts +│ ├── ctors.py +│ ├── dump.py +│ ├── entries.py +│ └── vtables.py +├── symless +│ ├── __init__.py +│ ├── allocators.py +│ ├── config +│ │ ├── __init__.py +│ │ ├── config.json +│ │ └── imports.csv +│ ├── conflict.py +│ ├── cpustate +│ │ ├── __init__.py +│ │ ├── arch.py +│ │ └── cpustate.py +│ ├── existing.py +│ ├── generation +│ │ ├── __init__.py +│ │ ├── generate.py +│ │ └── structures.py +│ ├── main.py +│ ├── model +│ │ ├── __init__.py +│ │ ├── entrypoints.py +│ │ └── model.py +│ ├── plugins +│ │ ├── __init__.py +│ │ └── builder.py +│ ├── resources +│ │ ├── bigger_champi.png +│ │ ├── champi.png +│ │ ├── cross.png +│ │ └── propag.png +│ ├── symbols +│ │ ├── __init__.py +│ │ └── rename.py +│ └── utils +│ ├── ida_utils.py +│ ├── utils.py +│ └── vtables.py +└── symless.py + +``` + +`LICENCE`: + +``` + MIT License + + Copyright (c) 2023 Thalium Team + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +``` + +`README.md`: + +```md +# Symless + +An **IDA Pro plugin** that assists with **structure reconstruction**. Using static data-flow analysis to gather information, Symless automates most of the structure creation workflow. Its key features are: + +* Inferring and creating structure fields based on access patterns +* Identifying and creating C++ virtual function tables +* Placing cross-references to link each structure field with the code that uses it + +## Installation + +```bash +$ python3 plugin/install.py [-u] +``` + +Or install manually: copy the [symless](symless/) directory and [symless_plugin.py](plugin/symless_plugin.py) file into your IDA plugins folder. + +## Usage + +The **interactive plugin** helps reconstruct a chosen structure. In the Disassembly or Pseudocode view, right-click a line that uses the structure you want to rebuild and select **Propagate structure** from the context menu: + +<p align="center"> + <kbd> + <img src="img/plugin_context_menu.png"/> + </kbd> +</p> + +A form will appear prompting for: + +* The **name of the new structure** to create, or an existing structure to extend. +* An **entry point** for the data-flow analysis, which is performed on the microcode. This entry point is a microcode operand that holds a pointer to the structure. + +> [!NOTE] +> The microcode is IDA's intermediate representation (IR), generated from the CPU-specific assembly. Because of its similarity with the assembly, it is not difficult to read. + +<p align="center"> + <kbd> + <img src="img/plugin_builder_form.png" width="448"/> + </kbd> +</p> + +Additional options are: + +* **Shifted by**, the shift to apply to the structure pointer +* **Spread in callees**, whether the analysis should extend into called functions and discovered virtual methods + +Clicking **Propagate** starts the analysis. The structure pointer is tracked from the selected entry, and observed accesses are used to infer structure fields. + +> [!TIP] +> To get a more complete structure, run the analysis from the code that initializes the structure (for example, right after an allocation or inside a constructor). + +The new structure is added to the Local Types view. Cross-references are added on assembly operands for each field access: + +<p align="center"> + <kbd> + <img src="img/plugin_built_structure.png"/> + </kbd> +</p> + +You can then edit field types directly from the pseudocode. The plugin reduces the amount of back-and-forth navigation between disassembly, pseudocode and local types, required when creating structures and placing cross-references. + +## CLI mode + +An **automatic command-line** mode also exists, able to identify and automatically reconstruct most of the structures used in a binary. Symless uses two sources to discover structures: + +* Dynamic memory allocations +* C++ virtual function tables and constructors + +This automatic mode is intended as a pre-analysis step, to create structures and improve decompilation before manual work. + +First, add the memory allocators used in your executable in [imports.csv](symless/config/imports.csv). This allows Symless to rebuild structures from dynamic allocations. If you don't, only C++ classes with virtual tables will be reconstructed. + +The pre-analysis is ran using: + +```bash + $ python3 symless.py [-c config.csv] <target> +``` + +* ```config.csv``` - configuration file to use (defaults to [imports.csv](symless/config/imports.csv)) +* ```target``` - a binary or an IDA database + +if target is an executable, a new IDA database will be created. When the analysis finishes, the database is populated with the reconstructed structures. + +### Limitations + +The main challenge for the automatic analysis is resolving conflicts between structures. This can cause functions to be incorrectly typed, or duplicated structures to be created. In some cases it is better to use the interactive plugin, which is less prone to errors. + +## Support + +All architectures supported by your IDA decompiler are supported. + +Supported IDA versions are **IDA 8.4 and later**. + +## Credits + +Thalium Team, and Célian Debéthune for working on the architecture-agnostic version during his internship at Thalium. + +``` + +`plugin/install.py`: + +```py +#!/usr/bin/python3 + +import os +import shutil +import sys + +""" + Installs the symless plugin into your IDA plugins user directory : + On Windows: %APPDATA%/Hex-Rays/IDA Pro + On Linux and Mac: $HOME/.idapro + + + Install: $ python3 install.py + Uninstall: $ python3 install.py -u +""" + + +def usage(): + print(f"Usage: python {sys.argv[0]} [-u] [--dev]") + print("-u => uninstall plugin") + print("--dev => use symlinks to sync with git repo folder") + + +ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) +TO_COPY = [("symless_plugin.py", False), ("../symless", True)] + + +def install(where: str, symlink: bool) -> int: + # check no installation is present + for file, is_dir in TO_COPY: + filepath = os.path.join(where, os.path.basename(file)) + if os.path.exists(filepath): + print(f"Replacing existing {'directory' if is_dir else 'file'} \"{filepath}\"") + if is_dir: + shutil.rmtree(filepath) + else: + os.remove(filepath) + + # install + for file, is_dir in TO_COPY: + src = os.path.abspath(os.path.join(ROOT_DIR, file)) + dst = os.path.join(where, os.path.basename(file)) + + if symlink: + print(f'Linking "{dst}"') + os.symlink(src, dst) + else: + print(f'Creating "{dst}"') + if is_dir: + shutil.copytree(src, dst, dirs_exist_ok=True) + else: + shutil.copy(src, dst) + + return 0 + + +def uninstall(where: str) -> int: + for file, is_dir in TO_COPY: + path = os.path.join(where, os.path.basename(file)) + + print(f"Deleting {path}") + try: + if is_dir and not os.path.islink(path): + shutil.rmtree(path) + else: + os.unlink(path) + except FileNotFoundError: + pass + + return 0 + + +if __name__ == "__main__": + # find IDA installation + if os.name == "posix": + ida_plugins_dir = os.path.expandvars("/$HOME/.idapro/plugins") + elif os.name == "nt": + ida_plugins_dir = os.path.expandvars("%APPDATA%/Hex-Rays/IDA Pro/plugins") + else: + print(f"Could not retrieve IDA install folder on OS {os.name}") + exit(1) + + # make sure the "plugins" dir exists + os.makedirs(ida_plugins_dir, exist_ok=True) + + # args parsing + do_install = True + symlink = False + for arg in sys.argv[1:]: + if arg == "-u": + do_install = False + elif arg == "--dev": + symlink = True + else: + usage() + exit(1) + + ok = install(ida_plugins_dir, symlink) if do_install else uninstall(ida_plugins_dir) + if ok == 0: + print("Done") + +``` + +`plugin/symless_plugin.py`: + +```py +import base64 +import collections +import importlib +import os +import pkgutil +import sys +import traceback +from typing import Collection + +import idaapi + +import symless +import symless.plugins as plugins +import symless.utils.utils as utils + + +class fixedBtn(idaapi.Form.ButtonInput): + def __init__(self, plugin: "SymlessPlugin", form: "SymlessInfoForm"): + super().__init__(self.reload, "0") + self.plugin = plugin + self.form = form + + def reload(self, code): + idaapi.show_wait_box("Reloading Symless..") + + try: + # terminate all extensions + self.plugin.term() + + remove_old_modules() + + # rebind all extensions + self.plugin.find_extensions(reload=True) + + except Exception as e: + idaapi.hide_wait_box() + utils.g_logger.critical(repr(e) + "\n" + traceback.format_exc()) + else: + idaapi.hide_wait_box() + self.form.Close(1) + + def get_tag(self): + return "<Reload:%s%d:%s%s:%s:%s>" % ( + self.tp, + self.id, + "+" if self.is_relative_offset else "", + self.width, + self.swidth, + ":" if self.hlp is None else self.hlp, + ) + + +class SymlessInfoForm(idaapi.Form): + def __init__(self, plugin: "SymlessPlugin"): + icon_path = os.path.join(os.path.abspath(symless.__path__[0]), "resources", "bigger_champi.png") + with open(icon_path, "rb") as file: + icon_b64 = base64.b64encode(file.read()).decode() + + img_html = "<img src='data:image/png;base64,%s'>" % icon_b64 + info_html = """ + <div style='text-align: center; margin: 6px; font-size: 16px;'> + <pre style='font-size: 72px;'>Symless</pre> + <pre style='font-size: 16px; text-align: left;'>%s<br><br>Version: <b>%.1f</b></pre> + </div> + """ % ( + symless.PLUGIN_DESC, + symless.PLUGIN_VERSION, + ) + + super().__init__( + "BUTTON YES NONE\nBUTTON CANCEL NONE\nSymless plugin\n{img}<|>{info}\n{reload}", + { + "img": idaapi.Form.StringLabel(img_html, tp=idaapi.Form.FT_HTML_LABEL, size=None), + "info": idaapi.Form.StringLabel(info_html, tp=idaapi.Form.FT_HTML_LABEL, size=None), + "reload": fixedBtn(plugin, self), + }, + ) + + +# Symless plugin +class SymlessPlugin(idaapi.plugin_t): + flags = idaapi.PLUGIN_MOD | idaapi.PLUGIN_PROC # | idaapi.PLUGIN_HIDE + comment = "Symless interactive plugin" + wanted_name = "Symless" + help = "" # not used, IDA < 7.6 compatibility + wanted_hotkey = "" # not used, IDA < 7.6 compatibility + + # find & initialize all extensions + def init(self) -> idaapi.plugmod_t: + self.ext: Collection[plugins.plugin_t] = collections.deque() + self.find_extensions() + return idaapi.PLUGIN_KEEP + + # find and load extensions from symless plugins folder + def find_extensions(self, reload: bool = False): + for mod_info in pkgutil.walk_packages(plugins.__path__, prefix="symless.plugins."): + if mod_info.ispkg: + continue + + spec = mod_info.module_finder.find_spec(mod_info.name) + module = importlib.util.module_from_spec(spec) + + # module is already loaded + if module.__name__ in sys.modules: + module = sys.modules[module.__name__] + + # load the module + else: + sys.modules[module.__name__] = module + try: + spec.loader.exec_module(module) + except BaseException as e: + sys.modules.pop(module.__name__) + utils.g_logger.error(f"Error while loading extension {mod_info.name}:") + utils.g_logger.error(repr(e) + "\n" + traceback.format_exc()) + continue + + # module defines an extension + if not hasattr(module, "get_plugin"): + continue + ext: plugins.plugin_t = module.get_plugin() + + # notify the extension that it has been reloaded + if reload: + ext.reload() + + self.ext.append(ext) + + # display info panel + def run(self, args): + info = SymlessInfoForm(self) + info.Compile() + info.Execute() + info.Free() + + # term all extensions + def term(self): + while len(self.ext) > 0: + ext = self.ext.pop() + ext.term() + + +def PLUGIN_ENTRY() -> idaapi.plugin_t: + return SymlessPlugin() + + +# remove old symless modules from loaded modules +def remove_old_modules(): + to_remove = set() + for k in sys.modules.keys(): + if k.startswith("symless"): + to_remove.add(k) + + for r in to_remove: + print(f"Removing old {r} ..") + del sys.modules[r] + +``` + +`run_script.py`: + +```py +import os +import platform +import random +import string +import subprocess +import sys +import tempfile +from typing import List, Optional, Tuple + +# max & min supported majors +MIN_MAJOR = 8 +MAX_MAJOR = 9 + + +def stderr_print(line: str): + sys.stderr.write(line + "\n") + + +# remove quote from start & end of a path +# quotes can appear in env var when setting them from windows cmd +def unquote(path: str) -> str: + if path[0] == '"' and path[-1] == '"': + return path[1:-1] + return path + + +# find IDA install on Windows +def find_ida_win() -> Optional[str]: + # TODO : Run all over USER PATH in Windows, not only Program Files + for major in range(MAX_MAJOR, MIN_MAJOR - 1, -1): + for minor in range(9, 0, -1): + current = f"C:\\Program Files\\IDA Pro {major}.{minor}" + if os.path.exists(current): + return current + return None + + +# find IDA install on Linux +def find_ida_Linux() -> Optional[str]: + # find in PATH + if "PATH" in os.environ: + for path in os.environ["PATH"].split(":"): + if os.path.exists(os.path.join(path, "idat64")) or os.path.exists(os.path.join(path, "idat")): + return path + + # find in default location + for major in range(MAX_MAJOR, MIN_MAJOR - 1, -1): + for minor in range(9, -1, -1): + p1 = "%s/idapro-%d.%d" % (os.environ["HOME"], major, minor) + p2 = "%s/ida-pro-%d.%d" % (os.environ["HOME"], major, minor) + if os.path.exists(p1): + return p1 + if os.path.exists(p2): + return p2 + return None + + +# find idat executables +def find_idat() -> Tuple[Optional[str], str]: + ida_dir = None + + # user defined IDA path + if "IDA_DIR" in os.environ.keys(): + ida_dir = os.path.abspath(unquote(os.environ["IDA_DIR"])) + else: + if sys.platform == "win32": + ida_dir = find_ida_win() + else: + ida_dir = find_ida_Linux() + + if ida_dir is None: + stderr_print("Please specify an IDA installation location using IDA_DIR env or add IDA to PATH") + return None + + else: + print(f'Using IDA installation: "{ida_dir}"') + + suffix = ".exe" if sys.platform == "win32" else "" + idat = os.path.join(ida_dir, "idat" + suffix) + idat64 = os.path.join(ida_dir, "idat64" + suffix) + + if not (os.path.isfile(idat) or os.path.isfile(idat64)): + stderr_print('Missing idat%s in "%s"' % (suffix, ida_dir)) + return None + + # earliest IDA 9 version - only idat64 + if not os.path.isfile(idat): + return (None, idat64) + + # IDA 9 + - only idat + if not os.path.isfile(idat64): + return (None, idat) + + # IDA 8 or earlier + return (idat, idat64) + + +# craft IDA batch command +def craft_ida_command(idat: str, idb: str, script: str, script_args: List[str]) -> Tuple[str, str]: + exec_name = os.path.basename(idb).split(".")[0] + log_file = tempfile.mktemp(prefix=f"{exec_name}_", suffix=".log") + + if len(script_args) == 0: + quoted_args = "" + else: + quoted_args = ' \\"' + '\\" \\"'.join(script_args) + '\\"' + + cmd = f'"{idat}" -A -L"{log_file}" -S"\\"{script}\\"{quoted_args}" "{idb}"' + + return (cmd, log_file) + + +# run ida -B filepath +def run_ida_batchmode(idat: str, filepath: str) -> int: + args = f'"{idat}" -B "{filepath}"' + process = subprocess.Popen(args, shell=(platform.system() != "Windows")) + + code = process.wait() + if code != 0: + return code + + # remove assembler file generated by analysis.idc + os.remove(filepath + ".asm") + + return 0 + + +# Create .idb from 32 bits executable or .i64 from 64 bits exe +def make_idb(ida_install: tuple, filepath: str) -> Tuple[str, int]: + if ida_install[0] and run_ida_batchmode(ida_install[0], filepath) == 0: + return (f"{filepath}.idb", 0) + + # 32 bits analysis failed, try 64 bits mode + code = run_ida_batchmode(ida_install[1], filepath) + if code == 0: + return (f"{filepath}.i64", 0) + + return (None, code) + + +# random string prefix to retrieve script output in IDA logs +def get_random_string(size: int) -> str: + letters = string.ascii_lowercase + return "".join(random.choice(letters) for i in range(size)) + + +def is_idb(filename: str) -> bool: + return filename.split(".")[-1] in ("i64", "idb") + + +def run_ida(ida_install: tuple, input_file: str, script: str, script_args: List[str]) -> bool: + if not is_idb(input_file): + print("Creating IDA database from binary %s" % input_file) + (idb_file, ret_code) = make_idb(ida_install, input_file) + if ret_code != 0: + stderr_print(f"Could not create initial database, IDA batchmode returned {ret_code}") + return False + else: + idb_file = input_file + + # no script, just generate idb + if len(script) == 0: + return True + + prefix = get_random_string(6) + script_args += ["--prefix", prefix] + + idat = ida_install[1] if idb_file.endswith(".i64") else ida_install[0] + cmd, log_file = craft_ida_command(idat, idb_file, script, script_args) + + # TODO: stderr is a dirty hack, find a better solution + # used here to ignore these information when piping script output + stderr_print("Running IDA script..") + stderr_print("* IDAT : %s" % idat) + stderr_print("* Script: %s%s" % (script, "" if len(script_args) == 0 else ' ("%s")' % '", "'.join(script_args))) + stderr_print("* Base : %s" % idb_file) + stderr_print("* Logs : %s" % log_file) + + process = subprocess.Popen(cmd, shell=(platform.system() != "Windows")) + code = process.wait() + + try: + output = open(log_file, "r") + except FileNotFoundError: + stderr_print("[-] IDA script did not produce logs, return code: %d" % code) + return False + + if code == 0: + stderr_print("IDA script terminated successfully.") + + line = True + while line: + line = output.readline() + if not line.startswith(prefix): + continue + + print(line.strip()[len(prefix) :]) + else: + stderr_print("Trace:") + stderr_print(output.read()) + stderr_print(f"[-] Status code:\t{hex(code)}") + + output.close() + + return code == 0 + + +def run_script(script: str, input_file: str, args: List[str] = None) -> int: + ida_install = find_idat() + if ida_install is None: + return 1 + + if args is None: + args = [] # new args array, do not used the same default one between multiple calls + + return int(not run_ida(ida_install, input_file, script, args)) + + +def usage(): + print("Usage: run_script.py <script.py> <input.i64> [args]") + + +def main() -> int: + if len(sys.argv) < 3: + usage() + return 1 + + return run_script(sys.argv[1], sys.argv[2], sys.argv[3:]) + + +if __name__ == "__main__": + exit(main()) + +``` + +`scripts/ctors.py`: + +```py +import argparse +import inspect +import os +import re +import sys + +import idaapi +import idc + +# add symless dir to search path +symless_dir = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda: 0))), "..")) +sys.path.append(symless_dir) + +import symless.model.entrypoints as entrypoints +import symless.utils.ida_utils as ida_utils + +re_ctors = re.compile(r"\b((?:[\w_]+::)*)([\S ]+)::(~?)\2(?:\(|$)") + + +""" Debug script - Find ctors/dtors in binary """ + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--prefix", type=str, default="") + args = parser.parse_args(idc.ARGV[1:]) + + # wait for autoanalysis, we'll need its results + idaapi.auto_wait() + + families = entrypoints.get_ctors() + + i = 0 + for vtbl in families: + print("%sFamily 0x%x:" % (args.prefix, vtbl)) + + for ctor in families[vtbl]: + fea = ctor.func.start_ea + name = ida_utils.demangle_ea(fea) + + match = re_ctors.match(name) + if match is None: + if "vector deleting destructor" in name: + typ = "[V-DESTRUCTOR]" + elif "scalar deleting destructor" in name: + typ = "[S-DESTRUCTOR]" + else: + typ = "[UNKNOWN]" + elif len(match.group(3)): + typ = "[DESTRUCTOR]" + else: + typ = "[CONSTRUCTOR]" + + print("%s %s 0x%x -> %s" % (args.prefix, typ, fea, name)) + print(args.prefix) + + i += 1 + +idc.qexit(0) + +``` + +`scripts/dump.py`: + +```py +import argparse + +import idaapi +import idautils +import idc + +""" Dump all informations found in one database (types, functions, ..) """ + + +def report(output: str = ""): + if len(output) == 0: + print(g_prefix) + return + + for i in output.splitlines(): + print("%s%s" % (g_prefix, i)) + + +def dump_functions() -> dict: + out = {"total": 0} + + all_fcts = [fea for fea in idautils.Functions()] + all_fcts.sort() + + for fea in all_fcts: + # only print user defined function types + if not idaapi.is_userti(fea): + continue + + name = idaapi.print_type(fea, idaapi.PRTYPE_CPP) + if name and len(name): + report(name) + else: + report("VOID[0x%x]" % fea) + + out["total"] += 1 + + return out + + +def get_xref_type(xref: int): + func = idaapi.get_func(xref) + if func is not None: + name = "%s%s" % ( + idaapi.get_short_name(func.start_ea), + "" if xref == func.start_ea else (" + 0x%x" % (xref - func.start_ea)), + ) + return (name, 1) + + if idaapi.is_member_id(xref): + return (idaapi.get_member_fullname(xref), 2) + + if idaapi.get_struc(xref) is not None: + return (idaapi.get_struc_name(xref), 4) + + name = idaapi.get_short_name(xref) + + return (name, 8) + + +def dump_xrefs_to(ea: int, shift: str = "", mask: int = 0xFF) -> int: + count = 0 + for xref in idautils.XrefsTo(ea): + name, type = get_xref_type(xref.frm) + if (type & mask) != 0: + count += 1 + report("%sxref: 0x%x%s" % (shift, xref.frm, (" (%s)" % name) if name else "")) + + return count + + +def dump_structures() -> dict: + out = { + "total vtables": 0, + "total structures": 0, + "total members": 0, + "typed members": 0, + "xrefs on members": 0, + "xrefs on structs": 0, + "xrefs on vtables": 0, + } + + # sort structures by name + structures = list() + for idx, sid, name in idautils.Structs(): + structures.append((name, sid)) + structures.sort() + + for name, sid in structures: + struc = idaapi.get_struc(sid) + + # do not dump hidden structs + # if struc.props & idaapi.SF_HIDDEN: + # continue + + is_vtable = "_vtbl" in name + + if is_vtable: + out["total vtables"] += 1 + else: + out["total structures"] += 1 + + report("struc %s:" % name) + report("\tprops: 0x%x" % struc.props) + report("\tsize : 0x%x" % idaapi.get_struc_size(sid)) + xref_count = dump_xrefs_to(sid, "\t", 8) + report("\tmembers: %d" % struc.memqty) + + if is_vtable: + out["xrefs on vtables"] += xref_count + else: + out["xrefs on structs"] += xref_count + + for m in struc.members: + out["total members"] += 1 + + m_name = idaapi.get_member_name(m.id) + m_size = idaapi.get_member_size(m) + + m_type = idaapi.tinfo_t() + if idaapi.get_member_tinfo(m_type, m): + m_type_str = str(m_type) + else: + m_type_str = None + + report("\t0x%x: %s" % (m.soff, m_name)) + report("\t\tsize: 0x%x" % m_size) + if m_type_str: + report("\t\ttype: %s" % m_type_str) + out["typed members"] += 1 + + out["xrefs on members"] += dump_xrefs_to(m.id, "\t\t", 9) + + report() + + return out + + +def dump_local_types() -> dict: + out = {"total": 0} + + idati = idaapi.get_idati() + + count = idaapi.get_ordinal_count(idati) + if count == 0 or count == 0xFFFFFFFF: + return + + # get all struct types + types = list() + for i in range(count): + tinfo = idaapi.tinfo_t() + if tinfo.get_numbered_type(idati, i) and tinfo.is_struct(): + types.append((i, tinfo)) + + # sort by name + types.sort(key=lambda k: str(k[1])) + + for _, tinfo in types: + # do not print types imported as structures + name = str(tinfo) + if idaapi.get_struc_id(name) != idaapi.BADADDR: + continue + + out["total"] += 1 + + flags = idaapi.PRTYPE_MULTI | idaapi.PRTYPE_CPP | idaapi.PRTYPE_DEF | idaapi.PRTYPE_TYPE + report(idaapi.print_tinfo("", 0, 0, flags, tinfo, name, "")) + report() + + return out + + +if __name__ == "__main__": + global g_prefix + + parser = argparse.ArgumentParser() + parser.add_argument("--prefix", type=str, default="") + args = parser.parse_args(idc.ARGV[1:]) + + g_prefix = args.prefix + + seq = ( + ("Functions", dump_functions), + ("Local types", dump_local_types), + ("Structures", dump_structures), + ) + + report() + for name, fct in seq: + report("##### %s #####" % name) + ret = fct() + + report("\n##### Statistics for %s #####" % name) + for key, val in ret.items(): + report("%s: %d" % (key, val)) + report() + + idc.qexit(0) + +``` + +`scripts/entries.py`: + +```py +import argparse +import inspect +import os +import sys + +import idaapi +import idc + +# add symless dir to search path +symless_dir = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda: 0))), "..")) +sys.path.append(symless_dir) + +import symless.allocators as allocators +import symless.model.entrypoints as entrypoints + +# import symless.model.model as model + +""" Debug script - Get all entrypoints (structures creations) identified in one binary """ + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--prefix", type=str, default="") + args = parser.parse_args(idc.ARGV[1:]) + + idaapi.auto_wait() + + config_path = os.path.abspath(os.path.join(symless_dir, "symless", "config", "imports.csv")) + + imports = allocators.get_allocators(config_path) + if not len(imports): + print("%sNo allocators identified" % args.prefix) + idc.qexit(0) + + # get initial entrypoints + ctx = entrypoints.retrieve_entrypoints(imports) + + # build entries tree + # model.analyze_entrypoints(ctx) + + entries = ctx.get_entrypoints() + allocs = ctx.get_allocators() + + e_str = str(entries) + print("%sEntrypoints:" % args.prefix) + for line in e_str.splitlines(): + print("%s%s" % (args.prefix, line)) + + print(args.prefix) + + print("%sAllocators:" % args.prefix) + for i in allocs: + print("%s%s" % (args.prefix, i)) + +idc.qexit(0) + +``` + +`scripts/vtables.py`: + +```py +import argparse +import inspect +import os +import sys + +import idaapi +import idc + +# add symless dir to search path +symless_dir = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda: 0))), "..")) +sys.path.append(symless_dir) + +import symless.utils.ida_utils as ida_utils +import symless.utils.vtables as vtables + +""" Debug script - Scans binary for vtables """ + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--prefix", type=str, default="") + args = parser.parse_args(idc.ARGV[1:]) + + idaapi.auto_wait() + + stats = [0, 0] + + for vtbl in vtables.get_all_vtables(): + name = ida_utils.demangle_ea(vtbl.ea) + print("%s0x%x (size: 0x%x) -> %s" % (args.prefix, vtbl.ea, vtbl.size(), name)) + + for x in vtbl.get_loads(): + print("%s\tload @ 0x%x" % (args.prefix, x)) + + stats[1] += 1 + stats[0] += 1 if "vftable" in name else 0 + + print("%sTotal: %d, corrects (from symbols): %d" % (args.prefix, stats[1], stats[0])) + +idc.qexit(0) + +``` + +`symless.py`: + +```py +#!/usr/bin/python3 + +import argparse +import os +import sys + +import symless.config as config + +""" IDA main """ + + +def ida_main(): + # parse arguments + parser = argparse.ArgumentParser() + parser.add_argument("--config", type=str, help="config file") + parser.add_argument("--prefix", type=str, default="", help="log prefix") + args = parser.parse_args(idc.ARGV[1:]) + start_analysis(args.config) + + +""" Command line main """ + + +def cmd_usage(): + print(f"Usage: python {sys.argv[0]} [-c config.csv] <file(s)>") + + +def cmd_main(): + files = [] + root_dir = os.path.realpath(os.path.join(config.g_settings.root, "..")) + config_path = os.path.join(root_dir, "symless", "config", "imports.csv") + + # parse arguments + i, length = 1, len(sys.argv) + while i < length: + if sys.argv[i] == "-c": + i += 1 + if i == length: + cmd_usage() + return + config_path = sys.argv[i] + else: + files.append(sys.argv[i]) + i += 1 + + if len(files) == 0: + cmd_usage() + return + + args = ["--config", config_path] + + runner = os.path.join(root_dir, "symless.py") + for file in files: + run_script(runner, os.path.abspath(file), args) + + +""" Symless main """ + +if __name__ == "__main__": + try: + # flake8: noqa: F401 + import idc + + except ModuleNotFoundError: + from run_script import run_script + + cmd_main() # script run from command line + + else: + from symless.main import start_analysis + + ida_main() # script run from IDA + + idc.qexit(0) + +``` + +`symless/__init__.py`: + +```py +# plugin info +PLUGIN_VERSION = 1.1 +PLUGIN_DESC = "Structure reconstruction assistant" + +``` + +`symless/allocators.py`: + +```py +import enum +import re +from typing import Any, List, Tuple + +import idaapi + +import symless.cpustate.cpustate as cpustate +import symless.utils.ida_utils as ida_utils +import symless.utils.utils as utils + +# do not consider alloc bigger than this to be object allocs +g_max_alloc = 0x4000 + + +def valid_size(size: int): + return size > 0 and size < g_max_alloc + + +class alloc_action_t(enum.Enum): # allocator action + STATIC_ALLOCATION = 0 # malloc(n) + WRAPPED_ALLOCATOR = 1 # func(x) -> return malloc(x) + UNDEFINED = 2 + + +# a heap allocation function +class allocator_t: + index = dict() + + def __init__(self, ea: int, type: str): + self.ea = ea + self.type = type + self.index = self.next_index() + + def next_index(self) -> int: + try: + allocator_t.index[self.type] += 1 + return allocator_t.index[self.type] + except KeyError: + allocator_t.index[self.type] = 0 + return 0 + + def get_name(self) -> str: + return f"{self.type}_like_{self.index:x}" + + def get_child(self, ea: int, args: tuple) -> "allocator_t": + child = self.__class__.__new__(self.__class__) # is there a nicer way to do this ? + child.__init__(ea, *args) + return child + + # how to type the allocator once identified + def make_type(self, func_data: idaapi.func_type_data_t): + pass + + # what type of allocation for given state + allocation size for STATIC_ALLOCATION + def on_call(self, state: cpustate.state_t) -> Tuple[alloc_action_t, Any]: + return (alloc_action_t.UNDEFINED, 0) + + # for WRAPPED_ALLOCATOR action, does wrapper return value confirms it is a wrapper + def on_wrapper_ret(self, state: cpustate.state_t, call_ea: int) -> bool: + if isinstance(state.ret, cpustate.call_t) and state.ret.where == call_ea: + return True + + return False + + def __hash__(self): + return self.ea + + def __eq__(self, other): + return isinstance(other, allocator_t) and self.ea == other.ea + + def __repr__(self): + return f"[0x{self.ea:x}] - {ida_utils.demangle_ea(self.ea)} ({self.get_name()})" + + +# malloc like function, takes one size parameter and returns memory space +class malloc_like_t(allocator_t): + def __init__(self, ea: int, size_index: int = 0): + allocator_t.__init__(self, ea, "malloc") + self.size_index = size_index + + def on_call(self, state: cpustate.state_t) -> Tuple[alloc_action_t, Any]: + if len(state.call_args) <= self.size_index: + return (alloc_action_t.UNDEFINED, 0) + + arg = state.call_args[self.size_index] + if isinstance(arg, cpustate.arg_t): + index = arg.idx + return (alloc_action_t.WRAPPED_ALLOCATOR, (index,)) + elif isinstance(arg, cpustate.int_t) and valid_size(arg.get_uval()): + return (alloc_action_t.STATIC_ALLOCATION, arg.get_uval()) + + return (alloc_action_t.UNDEFINED, 0) + + def make_type(self, func_data: idaapi.func_type_data_t): + func_data.rettype = ida_utils.void_ptr() + + tinfo = ida_utils.get_local_type("size_t") + if tinfo.get_decltype() == idaapi.BT_UNK: + tinfo = ida_utils.get_basic_type(idaapi.BT_INT) + + ida_utils.set_function_argument(func_data, self.size_index, tinfo, name="size") + + def __repr__(self): + return f"malloc_like_t : [0x{self.ea:x}] - {ida_utils.demangle_ea(self.ea)} ({self.get_name()}) index : ({self.size_index})" + + +# calloc like allocator, takes two parameters: count & unit size +class calloc_like_t(allocator_t): + def __init__(self, ea: int, count_index: int = 0, size_index: int = 1): + allocator_t.__init__(self, ea, "calloc") + self.count_index = count_index + self.size_index = size_index + + def on_call(self, state: cpustate.state_t) -> Tuple[alloc_action_t, Any]: + if len(state.call_args) <= max(self.size_index, self.count_index): + return (alloc_action_t.UNDEFINED, 0) + + count_arg = state.call_args[self.count_index] + size_arg = state.call_args[self.size_index] + if isinstance(count_arg, cpustate.arg_t) and isinstance(size_arg, cpustate.arg_t): + count_index = count_arg.idx + size_index = size_arg.idx + return (alloc_action_t.WRAPPED_ALLOCATOR, (count_index, size_index)) + elif ( + isinstance(count_arg, cpustate.int_t) + and valid_size(count_arg.get_uval()) + and isinstance(size_arg, cpustate.int_t) + and valid_size(size_arg.get_uval()) + ): + size = count_arg.get_uval() * size_arg.get_uval() + return (alloc_action_t.STATIC_ALLOCATION, size) + + return (alloc_action_t.UNDEFINED, 0) + + def make_type(self, func_data: idaapi.func_type_data_t): + func_data.rettype = ida_utils.void_ptr() + + tinfo = ida_utils.get_local_type("size_t") + if tinfo.get_decltype() == idaapi.BT_UNK: + tinfo = ida_utils.get_basic_type(idaapi.BT_INT) + + ida_utils.set_function_argument(func_data, self.count_index, tinfo, name="nmemb") + ida_utils.set_function_argument(func_data, self.size_index, tinfo, name="size") + + def __repr__(self): + return f"calloc_like_t : [0x{self.ea:x}] - {ida_utils.demangle_ea(self.ea)} ({self.get_name()}) index : ({self.size_index})" + + +# realloc is just a malloc with the size parameter at index 1 +class realloc_t(malloc_like_t): + def __init__(self, ea: int, size_index: int = 1): + allocator_t.__init__(self, ea, "realloc") + self.size_index = size_index + + def __repr__(self): + return f"realloc_like_t : [0x{self.ea:x}] - {ida_utils.demangle_ea(self.ea)} ({self.get_name()}) index : ({self.size_index})" + + +available_allocators = {"malloc": malloc_like_t, "calloc": calloc_like_t, "realloc": realloc_t} + + +# parse calloc(0, 1) into (calloc_like_t, [0,1]) +def parse_allocator(declaration: str) -> Tuple[allocator_t, list]: + pattern = re.compile(r"^([a-zA-Z]+)(?:\((\s*[0-9]+\s*(?:\|\s*[0-9]+\s*)*)?\))?$") + match = pattern.match(declaration) + if match is None: + return (None, None) + + try: + allocator = available_allocators[match.group(1)] + except KeyError: + return (None, None) + + args = list() + if match.group(2) is not None: + for index in match.group(2).split("|"): + if len(index) == 0: + continue + + try: + args.append(int(index)) + except ValueError: + return (None, None) + + return (allocator, args) + + +# reads config.csv data to find memory allocators in the binary, used as entry points +def get_allocators(config_path: str) -> List[allocator_t]: + imports = [] + + try: + config = open(config_path) + except FileNotFoundError as e: + utils.g_logger.error("Can not retrieve config file (%s)" % str(e)) + return None + + i = 1 + current = config.readline() + while current: + current = current.strip().split("#")[0] + + if len(current) == 0 or current[0] == "#": + pass + else: + keys = current.split(",") + length = len(keys) + if length > 3 or length < 2: + utils.g_logger.error("%s bad syntax at line %d" % (config_path, i)) + return None + + import_type, args = parse_allocator(keys[-1].strip()) + if import_type is None: + utils.g_logger.error( + '%s bad syntax for allocator type "%s" at line %d' % (config_path, keys[-1].strip(), i) + ) + return None + + # entry point from lib import + if length == 3: + module_name = keys[0].strip() + import_name = keys[1].strip() + + module = ida_utils.get_import_module_index(module_name) + + if module is None: + utils.g_logger.debug( + "import %s from module %s absent from binary (module not imported)" % (import_name, module_name) + ) + pass + else: + ea = ida_utils.get_import_from_module(module, import_name) + if ea is None: + utils.g_logger.debug("import %s from module %s absent from binary" % (import_name, module_name)) + else: + utils.g_logger.info("retrieved entry point %s from module %s" % (import_name, module_name)) + + imports.append(import_type(ea, *args)) + + # entry point as function from binary + elif length == 2: + func_name = keys[0].strip() + + try: + func_ea = int(func_name, 16) + except ValueError: + func_ea = idaapi.get_name_ea(idaapi.BADADDR, func_name) + + func = idaapi.get_func(func_ea) + if func is None or func.start_ea != func_ea: + utils.g_logger.error('Unable to located entry point "%s"' % func_name) + else: + utils.g_logger.info('Retrieved entry point "%s"' % func_name) + + imports.append(import_type(func.start_ea, *args)) + + current = config.readline() + i += 1 + + config.close() + + return imports + +``` + +`symless/config/__init__.py`: + +```py +import inspect +import json +import os + +# different log levels +LOG_LEVEL_CRITICAL = 50 +LOG_LEVEL_ERROR = 40 +LOG_LEVEL_WARNING = 30 +LOG_LEVEL_INFO = 20 +LOG_LEVEL_DEBUG = 10 +LOG_LEVEL_VERBOSE_DEBUG = 5 + +LOG_LEVEL_DEFAULT = LOG_LEVEL_WARNING + + +# symless global settings +class Settings: + def __init__(self): + self.log_level = LOG_LEVEL_DEFAULT + self.debug = False + self.rebase_db = True # set imagebase to 0 before generating structures (& names) + + # symless folder path + self.root = os.path.realpath(os.path.join(os.path.dirname(inspect.getsourcefile(lambda: 0)), "..")) + + config_file = os.path.join(self.root, "config", "config.json") + self.initialize(config_file) + + # initialize global config with config file + def initialize(self, config_file: str): + with open(config_file, "rb") as config: + settings = json.load(config) + for key, value in settings.items(): + self.__setattr__(key, value) + self.debug = self.log_level <= LOG_LEVEL_DEBUG + + +# global settings variable +g_settings = Settings() + +``` + +`symless/config/config.json`: + +```json +{ + "log_level": 30, + "rebase_db" : true +} + +``` + +`symless/config/imports.csv`: + +```csv +CSV Schema (1 sample row): +Headers: # List of imports / functions used as entry points for the analysis +Sample: "# format #1: module_name", " impot_name", " type" +... [53 more rows omitted] + +``` + +`symless/conflict.py`: + +```py +import collections +from typing import Collection, Dict, List, Set, Tuple + +import symless.generation as generation +import symless.model as model +import symless.utils.ida_utils as ida_utils +import symless.utils.utils as utils + + +# when two possible fields are overlapping in a structure, select which to keep +def fields_conflicts_solver(field: generation.field_t, old_field: generation.field_t) -> bool: + if old_field.has_type(): + if not field.has_type(): + return False + + return old_field.replace(field) + + # default: keep smallest + return field.size <= old_field.size + + +# when converting from var field to field +# which field size to use, between all possibilities +def field_size_solver(field: model.field_t) -> int: + # default policy: minimum size + return min(field.get_size()) + + +# define less derived structure between multiple structs +# the less derived should be the nearest from their common base +def less_derived(candidates: List[Tuple[generation.structure_t, int]]) -> Tuple[generation.structure_t, int]: + # search for smallest struc with smallest shift applied + candidates.sort(key=lambda i: (i[1], i[0].get_size())) + chosen = candidates[0] + + __vftable = chosen[0].get_field(0) + if not isinstance(__vftable, generation.vtbl_ptr_field_t): + return chosen + + # if we still have multiple candidates left, and they are cpp classes + # find the one with the less derived associated vtable + for struc, shift in filter(lambda c: c[1] == chosen[1] and c[0].get_size() == chosen[0].get_size(), candidates[1:]): + __vftable_2 = struc.get_field(0) + if not isinstance(__vftable_2, generation.vtbl_ptr_field_t): + continue + + if __vftable.value.get_most_derived(__vftable_2.value) == __vftable.value: + chosen = (struc, shift) + + return chosen + + +# find which class a vtable belongs to +# among multiple structures referencing this vtable +def vtable_owner_solver(candidates: List[Tuple[generation.structure_t, int]]) -> Tuple[generation.structure_t, int]: + # find less derived candidate (smallest shift and smallest size) + candidates.sort(key=lambda i: (i[1], i[0].get_size())) + return candidates[0] + + +# get groups of structures that are conflicting +# i.e they flow through the same entries (with same shift) and can be duplicated +def get_conflicting_structures( + entries: model.entry_record_t, +) -> Collection[Set[generation.structure_t]]: + # list of every conflict by belligerent (structure & shift) + conflict_per_struc: Dict[Tuple[int, generation.structure_t], Set[Tuple[int, generation.structure_t]]] = dict() + + # get conflicts for each entry + for entry in entries.get_entries(): + # do not consider ep that may not represent a structure + lower, upper = entry.get_boundaries() + if lower == 0 and upper <= ida_utils.get_ptr_size(): + continue + + # get conflicting strucs & shift for entry + flow = entry.get_flow() + shifts = set([shift for shift, _ in flow]) + conflicts = [set([j for j in filter(lambda k: k[0] == i, flow)]) for i in shifts] + + # update conflicts record + for conflict in conflicts: + if len(conflict) <= 1: # no conflict + continue + + utils.g_logger.debug( + f"conflicting strucs for {entry.entry_id()}: ({', '.join(['[%s:0x%x]' % (struc.get_name(), shift) for shift, struc in conflict])})" + ) + + # update conflict_per_struc record + # regroup structures conflicting on same shift as potential duplicates + cqueue = collections.deque(conflict) + while len(cqueue) > 0: + current = cqueue.pop() + + if current in conflict_per_struc: + previous = conflict_per_struc[current] + cqueue.extend(previous.difference(conflict)) + conflict.update(previous) + + conflict_per_struc[current] = conflict + + # build conflict record + out: Collection[Set[generation.structure_t]] = collections.deque() + for conflict in conflict_per_struc.values(): + conflicts_strucs = set([struc for _, struc in conflict]) # conflicting strucs set + + # if conflict subset exists, update it + no_subset = True + for existing in out: + if conflicts_strucs.issubset(existing): # conflict already recorded + pass + elif existing.issubset(conflicts_strucs): # update conflict + existing.update(conflicts_strucs) + else: + continue + + no_subset = False + break + + # else add conflict group to record + if no_subset: + out.append(conflicts_strucs) + + return out + + +# can we consider two structures to be duplicates & merge them +def are_structures_identical(one: generation.structure_t, other: generation.structure_t) -> bool: + i, j = 0, 0 + while i < len(one.range) and j < len(other.range): + off_one, size_one = one.range[i][0], one.range[i][1] + off_other, size_other = other.range[j][0], other.range[j][1] + + if off_one < off_other: + # overlapping fields + if off_one + size_one < off_other: + return False + + i += 1 + + elif off_one > off_other: + # overlapping fields + if off_other + size_other < off_one: + return False + + j += 1 + + else: + f_field = one.get_field(off_one) + s_field = other.get_field(off_other) + + # find most basic field + if isinstance(f_field, s_field.__class__): + base, oth = s_field, f_field + + elif isinstance(s_field, f_field.__class__): + base, oth = f_field, s_field + + # the two fields have unrelated types + else: + return False + + # two fields types do not match + if not base.match(oth): + return False + + i += 1 + j += 1 + + # if not at the end of both structures, they are different + if i < len(one.range) or j < len(other.range): + return False + + return True + + +# merge src struc into dst +def merge_structures(src: generation.structure_t, dst: generation.structure_t, entries: model.entry_record_t): + utils.g_logger.info("Merging duplicated structures %s & %s" % (src.get_name(), dst.get_name())) + + # merge fields + for field in src.get_fields(): + dst.set_field(field, fields_conflicts_solver) + + # merge root entries + for shift, entry in src.associated_root(): + dst.associate_root(entry, shift) + + # replace associated structures in entries + for entry in entries.get_entries(): + shift, struc = entry.get_structure() + if struc == src: + entry.set_structure(shift, dst) + + +# try to find & merge duplicated structures +def remove_dupes(entries: model.entry_record_t, structures: generation.structure_record_t): + # find conflicting structures + dupe_conflicts = get_conflicting_structures(entries) + + # merge duplicates + for c in dupe_conflicts: + conflict = list(c) + + utils.g_logger.debug("Found conflict between structures (%s)" % ", ".join([struc.get_name() for struc in c])) + + i = 0 + while i < len(conflict) - 1: + dst = structures.get_structure(conflict[i]) + + j = i + 1 + while j < len(conflict): + src = structures.get_structure(conflict[j]) + + # different strucs, to be merged + if dst != src and are_structures_identical(src, dst): + merge_structures(src, dst, entries) + structures.replace_by(src, dst) + del conflict[j] + continue + + j += 1 + + i += 1 + +``` + +`symless/cpustate/__init__.py`: + +```py +import copy +import enum +from typing import Collection, Dict, Generator, List, Optional + +import ida_hexrays +import idaapi + +import symless.utils.ida_utils as ida_utils +import symless.utils.utils as utils + +######################## +# CPU state definition # +######################## + + +# abstract operand - define a micro-operand type & possible value +# use to track values propagation between variables +class absop_t: + # should this operand value be transfered from a caller to a callee as an arg + def should_dive(self) -> bool: + return True + + def __eq__(self, other) -> bool: + raise Exception('Class "%s" should implement __eq__()' % self.__class__.__name__) + + def __hash__(self) -> int: + raise Exception('Class "%s" should implement __hash__()' % self.__class__.__name__) + + +# return value of an unknown call +class call_t(absop_t): + def __init__(self, arg: int, where: int): + self.arg = arg + self.where = where + + def __eq__(self, other) -> bool: + return isinstance(other, call_t) and self.where == other.where and self.arg == other.arg + + def __hash__(self) -> int: + return self.where + + def __repr__(self): + return f"call:0x{self.arg:x}@0x{self.where:x}" + + +# (unknown) function argument operand +class arg_t(absop_t): + def __init__(self, idx: int): + self.idx = idx + + def should_dive(self) -> bool: + return False # one caller arg is not a callee arg + + def __eq__(self, other) -> bool: + return isinstance(other, arg_t) and self.idx == other.idx + + def __hash__(self) -> int: + return self.idx + + def __repr__(self): + return "arg%d" % self.idx + + +# shifted buffer +class buff_t(absop_t): + def __init__(self, shift: int = 0): + self.shift = shift + + def shift_by(self, add: int, size: int) -> "buff_t": + out = copy.copy(self) + out.shift = utils.to_c_integer(out.shift + add, size) + return out + + +# a pointer dereference (ex: access to an object's field) +class deref_t(absop_t): + def __init__(self, ptr: Optional[absop_t], size: int): + self.ptr = ptr # pointer beeing dereferenced + self.size = size + + def __eq__(self, other) -> bool: + return isinstance(other, deref_t) and self.ptr == other.ptr and self.size == other.size + + def __hash__(self) -> int: + return hash((self.ptr, self.size)) + + def __repr__(self): + return f"[{self.ptr}:{self.size:#x}]" + + +# structure pointer +class sid_t(buff_t): + def __init__(self, sid, shift=0): + super().__init__(shift) + self.sid = sid + + def should_dive(self) -> bool: + return False # sid represents an entrypoint -> local to a function + + def __eq__(self, other) -> bool: + return isinstance(other, sid_t) and self.sid == other.sid and self.shift == other.shift + + def __hash__(self) -> int: + return hash((self.sid, self.shift)) + + def __repr__(self): + return f"sid{self.sid:x}+0x{self.shift:x}" + + +# immediate operand, applies to the same operation than buff_t +class int_t(buff_t): + def __init__(self, val: int, sizeof: int): + super().__init__(val) + + self.size = sizeof + self.shift = utils.to_c_integer(self.shift, self.size) + + def get_val(self) -> int: + return self.shift + + def get_uval(self) -> int: # unsigned value + return utils.to_c_integer(self.shift, self.size, False) + + def __eq__(self, other) -> bool: + return isinstance(other, int_t) and self.shift == other.shift and self.size == other.size + + def __hash__(self) -> int: + return hash((self.shift, self.size)) + + def __repr__(self): + return f"int{self.size*8}:0x{self.get_uval():x}" + + +# a memory address or a value read @ addr +class mem_t(int_t): + def __init__(self, value: int, addr: int, sizeof: int): + super().__init__(value, sizeof) + self.addr = addr + + def __repr__(self): + return f"mem:0x{self.addr:x}:0x{self.get_uval():x}" + + +# memory write +class write_t: + def __init__(self, ea: int, target: Optional[absop_t], size: int, value: Optional[absop_t]): + self.ea = ea # ea of the write + self.target = target # write dst + self.size = size # write size + self.value = value # written value + + def __repr__(self): + return f"{self.ea:#x} u{self.size*8}[{self.target}]={self.value}" + + +# memory read +class read_t: + def __init__(self, ea: int, target: Optional[absop_t], size: int, dst: ida_hexrays.mop_t): + self.ea = ea # ea of the read + self.target = target # read src + self.size = size # read size + self.dst = dst # dst operand of the read, no copy + + def __repr__(self): + dstname = ( + ida_hexrays.get_mreg_name(self.dst.r, self.size) + if self.dst.t == ida_hexrays.mop_r + else f"stk:{self.dst.s.off:x}" + ) + return f"{self.ea:#x} {dstname}=u{self.size*8}[{self.target}]" + + +# memory access +class access_t: + def __init__(self, ea: int, target: Optional[absop_t], loc: idaapi.mop_t, size: int): + self.ea = ea # ea for the access + self.target = target # target beeing accessed + self.size = size # access size + self.loc = loc # target operand, no copy it should not get freed + + def __repr__(self): + return f"{self.ea:#x} u{self.size*8}[{self.target}]" + + +# a visited function +class function_t: + def __init__(self, mba: ida_hexrays.mba_t): + self.ea = mba.entry_ea + + # location of function's ret code + self.retloc: Optional[ida_hexrays.vdloc_t] = None + + # location of function's arguments + self.argloc: Collection[ida_hexrays.vdloc_t] = list() + + # tinfo for function, force decompile for accurate arguments count + finfo = ida_utils.get_fct_type(self.ea, True) + if not finfo: + return + + fdata = idaapi.func_type_data_t() + if not finfo.get_func_details(fdata): + utils.g_logger.warning(f"No func_details for fea {self.ea:#x}") + return + + # update retloc & arglocs + if fdata.retloc.atype() != idaapi.ALOC_NONE: + self.retloc = mba.idaloc2vd(fdata.retloc, ida_utils.get_ptr_size()) + + for arg in fdata: + self.argloc.append(mba.idaloc2vd(arg.argloc, ida_utils.get_ptr_size())) + + def get_args_count(self) -> int: + return len(self.argloc) + + def get_retloc(self) -> Optional[ida_hexrays.vdloc_t]: + return self.retloc + + def get_argloc(self, idx: int) -> Optional[ida_hexrays.vdloc_t]: + if idx < self.get_args_count(): + return self.argloc[idx] + return None + + def __repr__(self): + return f"fct {hex(self.ea)} ({self.get_args_count()} args)" + + +# types for state last processed instruction +class last_insn_type_t(enum.Enum): + LAST_INSN_ANY = 0 + LAST_INSN_RET = 1 + LAST_INSN_CALL = 2 + + +# a cpu state (stack, registers (variables), ..) +class state_t: + def __init__(self, mba: ida_hexrays.mba_t, fct: Optional[function_t]): + self.mba = mba # microcode where the propagation takes place + self.fct = fct # owning function's model + + # type of the last processed instruction + # we mostly care about function calls & ret + self.last_insn_type: last_insn_type_t = last_insn_type_t.LAST_INSN_ANY + + # record current micro registers values (mreg_t: value) + self.registers: Dict[int, absop_t] = {} + + # record current stack variables values (index: value) + self.locals: Dict[int, absop_t] = {} + + self.writes: List[write_t] = [] # writes performed by last insn + self.reads: List[read_t] = [] # reads performed by last insn + self.accesses: List[access_t] = [] # memory accesses performed by last insn + + self.call_to: Optional[idaapi.func_t] = None # current call target + self.call_args: List[Optional[absop_t]] = [] # arguments for current call insn + + self.ret: Optional[absop_t] = None # current ret value + + # start ea for function in which we propagate + def get_fea(self) -> int: + return self.fct.ea + + # get value for given mreg_t + def get_register(self, mreg: int) -> Optional[absop_t]: + return self.registers.get(mreg) + + # set value for mreg_t + def set_register(self, mreg: int, value: Optional[absop_t]): + if value is not None: + self.registers[mreg] = value + else: + self.drop_register(mreg) + + # drop recorded value for mreg_t + def drop_register(self, mreg: int): + self.registers.pop(mreg, None) + + # get value for given stack variable + def get_local(self, idx: int) -> Optional[absop_t]: + return self.locals.get(idx) + + # set value for stack variable + def set_local(self, idx: int, value: Optional[absop_t]): + if value is not None: + self.locals[idx] = value + else: + self.drop_local(idx) + + # drop recorded stack variable + def drop_local(self, idx: int): + self.locals.pop(idx, None) + + # get value for given micro operand + def get_var_from_mop(self, mop: ida_hexrays.mop_t) -> Optional[absop_t]: + if mop.t == ida_hexrays.mop_r: + return self.get_register(mop.r) + if mop.t == ida_hexrays.mop_S: + return self.get_local(mop.s.off) + utils.g_logger.warning(f"{ida_utils.g_mopt_name[mop.t]} operands not handled") + return None + + # set value for given micro operand + def set_var_from_mop(self, mop: ida_hexrays.mop_t, value: Optional[absop_t]): + if mop.t == ida_hexrays.mop_r: + self.set_register(mop.r, value) + elif mop.t == ida_hexrays.mop_S: + self.set_local(mop.s.off, value) + else: + utils.g_logger.error(f"{ida_utils.g_mopt_name[mop.t]} operands not handled") + + # drop var from given micro operand + def drop_var_from_mop(self, mop: ida_hexrays.mop_t): + if mop.t == ida_hexrays.mop_r: + self.drop_register(mop.r) + elif mop.t == ida_hexrays.mop_S: + self.drop_local(mop.s.off) + else: + utils.g_logger.info(f"{ida_utils.g_mopt_name[mop.t]} operands not handled") + + # get value at the specified vd location (stack or register) + def get_var_from_loc(self, loc: ida_hexrays.vdloc_t) -> Optional[absop_t]: + if loc.is_reg1(): + return self.get_register(loc.reg1()) + if loc.is_stkoff(): + return self.get_local(loc.stkoff()) + return None + + # set value at the specified vd location (stack or register) + def set_var_from_loc(self, loc: ida_hexrays.vdloc_t, value: Optional[absop_t]): + if loc.is_reg1(): + self.set_register(loc.reg1(), value) + elif loc.is_stkoff(): + self.set_local(loc.stkoff(), value) + + # drop recorded values for kregs used to pass results between inlined minsns + def drop_kregs(self): + for kreg in self.mba.tmp_result_kregs: + self.drop_register(kreg) + self.drop_register(self.mba.call_result_kreg) + + def get_vars(self) -> Generator[absop_t, None, None]: + for var in self.registers.values(): + yield var + for var in self.locals.values(): + yield var + + def get_nb_types(self, wanted_type) -> int: + ret = 0 + for var in self.get_vars(): + ret += int(isinstance(var, wanted_type)) + return ret + + # reset information about current insn + def reset(self): + self.last_insn_type = last_insn_type_t.LAST_INSN_ANY + self.writes.clear() + self.reads.clear() + self.accesses.clear() + self.call_to = None + self.call_args.clear() + self.ret = None + + # copy persistent content into another state + def copy(self) -> "state_t": + out = state_t(self.mba, self.fct) + out.registers = copy.copy(self.registers) + out.locals = copy.copy(self.locals) + return out + + # save write + def write_to(self, ea: int, target: Optional[absop_t], loc: idaapi.mop_t, size: int, value: Optional[absop_t]): + self.access_to(ea, target, loc, size) + self.writes.append(write_t(ea, target, size, value)) + + # save read + def read_from(self, ea: int, target: Optional[absop_t], loc: idaapi.mop_t, size: int, dst: ida_hexrays.mop_t): + self.access_to(ea, target, loc, size) + self.reads.append(read_t(ea, target, size, dst)) + + # save access + def access_to(self, ea: int, target: Optional[absop_t], loc: idaapi.mop_t, size: int): + self.accesses.append(access_t(ea, target, loc, size)) + + # state contains call info from last call instruction + def has_call_info(self) -> bool: + return self.last_insn_type == last_insn_type_t.LAST_INSN_CALL + + # state contains ret info from last function ret + def has_ret_info(self) -> bool: + return self.last_insn_type == last_insn_type_t.LAST_INSN_RET + + # cpu state representation + def __repr__(self) -> str: + regs = ", ".join([f"{idaapi.get_mreg_name(r, 8)}({v})" for r, v in self.registers.items()]) + lcls = ", ".join([f"{loc:#x}({val})" for loc, val in sorted(self.locals.items(), key=lambda k: k[0])]) + return f"[regs: {regs}], [stack: {lcls}]" + +``` + +`symless/cpustate/arch.py`: + +```py +import idaapi + + +def is_arch_supported() -> bool: + return is_proc_supported() + + +def is_proc_supported() -> bool: + # name = idaapi.inf_get_procname() + return True # every arch should be supported by microcode + + +def get_proc_name() -> str: + return idaapi.inf_get_procname() + +``` + +`symless/cpustate/cpustate.py`: + +```py +from collections import deque +from collections.abc import Callable +from typing import Collection, Dict, Generator, List, Set, Tuple + +import ida_hexrays +import idaapi + +import symless.config as config +import symless.utils.ida_utils as ida_utils +import symless.utils.utils as utils +from symless.cpustate import * + +# max functions depth to propagate a structure +MAX_PROPAGATION_RECURSION = 100 + + +# handles mov (mop_r | mop_S), (mop_r | mop_S) +def handle_mov_var_var(state: state_t, insn: ida_hexrays.minsn_t): + v = state.get_var_from_mop(insn.l) + state.set_var_from_mop(insn.d, v) + + +# handles mov mop_n, (mop_r | mop_S) +def handle_mov_imm_var(state: state_t, insn: ida_hexrays.minsn_t): + v = int_t(insn.l.nnn.value, insn.d.size) + state.set_var_from_mop(insn.d, v) + + +# handles mov mop_v, (mop_r | mop_S) +def handle_mov_gbl_var(state: state_t, insn: ida_hexrays.minsn_t): + gvalue = ida_utils.get_nb_bytes(insn.l.g, insn.d.size) + if gvalue is None: + return state.drop_var_from_mop(insn.d) + v = mem_t(gvalue, insn.l.g, insn.d.size) + state.set_var_from_mop(insn.d, v) + + +# handles mov mop_a, (mop_r | mop_S) +def handle_mov_addr_var(state: state_t, insn: ida_hexrays.minsn_t): + if insn.l.a.t != ida_hexrays.mop_v: # mop_l, mop_S or mop_r + return state.drop_var_from_mop(insn.d) + v = mem_t(insn.l.a.g, insn.l.a.g, insn.d.size) + state.set_var_from_mop(insn.d, v) + + +# handles stx (mop_r | mop_S), mop_r, (mop_r | mop_S) +# note: sel register is ignored +def handle_stx_var_var(state: state_t, insn: ida_hexrays.minsn_t): + dst = state.get_var_from_mop(insn.d) + # if not isinstance(dst, buff_t): # stx to unknown + # return + + v = state.get_var_from_mop(insn.l) + state.write_to(insn.ea, dst, insn.d, insn.l.size, v) + + +# handles stx mop_n, mop_r, (mop_r | mop_S) +# note: sel register is ignored +def handle_stx_imm_var(state: state_t, insn: ida_hexrays.minsn_t): + dst = state.get_var_from_mop(insn.d) + # if not isinstance(dst, buff_t): # stx to unknown + # return + + v = int_t(insn.l.nnn.value, insn.l.size) + state.write_to(insn.ea, dst, insn.d, insn.l.size, v) + + +# handles stx mop_v, mop_r, (mop_r | mop_S) +# note: sel register is ignored +def handle_stx_gbl_var(state: state_t, insn: ida_hexrays.minsn_t): + dst = state.get_var_from_mop(insn.d) + # if not isinstance(dst, buff_t): # stx to unknown + # return + + gvalue = ida_utils.get_nb_bytes(insn.l.g, insn.l.size) + if gvalue is not None: + v = mem_t(gvalue, insn.l.g, insn.l.size) + state.write_to(insn.ea, dst, insn.d, insn.l.size, v) + + +# handles stx mop_a, mop_r, (mop_r | mop_S) +# note: sel register is ignored +def handle_stx_addr_var(state: state_t, insn: ida_hexrays.minsn_t): + dst = state.get_var_from_mop(insn.d) + # if not isinstance(dst, buff_t): # stx to unknown + # return + + if insn.l.a.t != ida_hexrays.mop_v: # mop_l, mop_S or mop_r + return + v = mem_t(insn.l.a.g, insn.l.a.g, insn.l.size) + state.write_to(insn.ea, dst, insn.d, insn.l.size, v) + + +# handles ldx mop_r, (mop_r | mop_S), (mop_r | mop_S) +# note: sel register is ignored +def handle_ldx_var_var(state: state_t, insn: ida_hexrays.minsn_t): + src = state.get_var_from_mop(insn.r) + state.read_from(insn.ea, src, insn.r, insn.d.size, insn.d) # record read + + # set dst mop value + deref = deref_t(src, insn.d.size) # default : unknown access + if isinstance(src, mem_t): + v = ida_utils.get_nb_bytes(src.get_uval(), insn.d.size) # try getting read value from memory + deref = mem_t(v, src.get_uval(), insn.d.size) if v is not None else deref + state.set_var_from_mop(insn.d, deref) + + +# handles xdu (mop_r | mop_S), (mop_r | mop_S) +def handle_xdu_var_var(state: state_t, insn: ida_hexrays.minsn_t): + assert insn.l.size < insn.d.size + + src = state.get_var_from_mop(insn.l) + if not isinstance(src, int_t): # only makes sense to extend int + return state.drop_var_from_mop(insn.d) + + v = copy.copy(src) + v.size = insn.d.size + state.set_var_from_mop(insn.d, v) + + +# handles xdu mop_n, (mop_r | mop_S) +def handle_xdu_imm_var(state: state_t, insn: ida_hexrays.minsn_t): + handle_mov_imm_var(state, insn) + + +# handles xds (mop_r | mop_S), (mop_r | mop_S) +def handle_xds_var_var(state: state_t, insn: ida_hexrays.minsn_t): + handle_xdu_var_var(state, insn) # we do not differenciate signed / unsigned (should we ?) + + +# handles xds mop_n, (mop_r | mop_S) +def handle_xds_imm_var(state: state_t, insn: ida_hexrays.minsn_t): + handle_xdu_imm_var(state, insn) + + +# handles call mop_v, (mop_f | mop_z) +def handle_call(state: state_t, insn: ida_hexrays.minsn_t): + state.last_insn_type = last_insn_type_t.LAST_INSN_CALL + + # resolve call arguments + if insn.d.t == ida_hexrays.mop_f: + # assert(insn.l.g == insn.d.f.callee) # insn.d.f.callee is not always resolved + + # we should not have non-flattened mop_d in the args list + assert not any([i.t == ida_hexrays.mop_d for i in insn.d.f.args]) + + state.call_args.extend([state.get_var_from_mop(i) for i in insn.d.f.args]) + utils.g_logger.debug(f"call site {insn.ea:#x} : {len(state.call_args)} argument(s)") + + # try to resolve callee + callee = idaapi.get_func(insn.l.g) + if callee is None or callee.start_ea != insn.l.g: + return + + utils.g_logger.debug(f"call @ {insn.ea:#x} resolved to function {callee.start_ea:#x}") + state.call_to = callee + + +# handles icall mop_r, mop_r, (mop_f | mop_z) +# note: sel register is ignored +def handle_icall(state: state_t, insn: ida_hexrays.minsn_t): + state.last_insn_type = last_insn_type_t.LAST_INSN_CALL + + # resolve call arguments + if insn.d.t == ida_hexrays.mop_f: + assert not any([i.t == ida_hexrays.mop_d for i in insn.d.f.args]) + + state.call_args.extend([state.get_var_from_mop(i) for i in insn.d.f.args]) + utils.g_logger.debug(f"icall site {insn.ea:#x} : {len(state.call_args)} argument(s)") + + # try to resolve callee + off = state.get_var_from_mop(insn.r) + if not isinstance(off, mem_t): + return + + callee = idaapi.get_func(off.get_uval()) + if callee is None or callee.start_ea != off.get_uval(): + return + + utils.g_logger.debug(f"icall @ {insn.ea:#x} resolved to function {callee.start_ea:#x}") + state.call_to = callee + + +# special case for ret handling +# there are no micro-insn for a ret +def handle_ret(state: state_t) -> state_t: + state.reset() + + state.last_insn_type = last_insn_type_t.LAST_INSN_RET + + retloc = state.fct.get_retloc() + if retloc is None: + return state + + state.ret = state.get_var_from_loc(retloc) + + return state + + +# handles add (mop_r | mop_S), mop_n, (mop_r | mop_S) +def handle_add_var_imm(state: state_t, insn: ida_hexrays.minsn_t, sign: int = 1): + v = state.get_var_from_mop(insn.l) + if not isinstance(v, buff_t): + return state.drop_var_from_mop(insn.d) + + shifted_v = v.shift_by(sign * insn.r.nnn.value, insn.r.size) + state.set_var_from_mop(insn.d, shifted_v) + + # this add can be a lea we need to type + # register an access for a field of size 0 (size is unknown) + state.access_to(insn.ea, shifted_v, insn.l, 0) + + +# handles add (mop_r | mop_S), (mop_r | mop_S), (mop_r | mop_S) +def handle_add_var_var(state: state_t, insn: ida_hexrays.minsn_t, sign: int = 1): + v = state.get_var_from_mop(insn.l) + v2 = state.get_var_from_mop(insn.r) + if not isinstance(v, buff_t) or not isinstance(v2, int_t): + return state.drop_var_from_mop(insn.d) + + shifted_v = v.shift_by(sign * v2.get_val(), insn.r.size) + state.set_var_from_mop(insn.d, shifted_v) + + state.access_to(insn.ea, shifted_v, insn.l, 0) # dummy access + + +# handles sub (mop_r | mop_S), mop_n, (mop_r | mop_S) +def handle_sub_var_imm(state: state_t, insn: ida_hexrays.minsn_t): + handle_add_var_imm(state, insn, -1) + + +# handles sub (mop_r | mop_S), (mop_r | mop_S), (mop_r | mop_S) +def handle_sub_var_var(state: state_t, insn: ida_hexrays.minsn_t): + handle_add_var_var(state, insn, -1) + + +# handlers per instructions types +g_per_minsn_handlers = { + ida_hexrays.m_mov: ( + # mov rax, rcx + ( + (ida_hexrays.mop_r, ida_hexrays.mop_S), + (ida_hexrays.mop_z,), + (ida_hexrays.mop_r, ida_hexrays.mop_S), + handle_mov_var_var, + ), + # mov #0, rax + ((ida_hexrays.mop_n,), (ida_hexrays.mop_z,), (ida_hexrays.mop_r, ida_hexrays.mop_S), handle_mov_imm_var), + # mov dword_0, rax + ((ida_hexrays.mop_v,), (ida_hexrays.mop_z,), (ida_hexrays.mop_r, ida_hexrays.mop_S), handle_mov_gbl_var), + # mov &dword_0, rax + ((ida_hexrays.mop_a,), (ida_hexrays.mop_z,), (ida_hexrays.mop_r, ida_hexrays.mop_S), handle_mov_addr_var), + ), + ida_hexrays.m_stx: ( + # stx rax, ds, rcx + ( + (ida_hexrays.mop_r, ida_hexrays.mop_S), + (ida_hexrays.mop_r,), + (ida_hexrays.mop_r, ida_hexrays.mop_S), + handle_stx_var_var, + ), + # stx #0, ds, rcx + ((ida_hexrays.mop_n,), (ida_hexrays.mop_r,), (ida_hexrays.mop_r, ida_hexrays.mop_S), handle_stx_imm_var), + # stx dword_0, ds, rcx + ((ida_hexrays.mop_v,), (ida_hexrays.mop_r,), (ida_hexrays.mop_r, ida_hexrays.mop_S), handle_stx_gbl_var), + # stx &dword_0, ds, rcx + ((ida_hexrays.mop_a,), (ida_hexrays.mop_r,), (ida_hexrays.mop_r, ida_hexrays.mop_S), handle_stx_addr_var), + ), + ida_hexrays.m_ldx: ( + # ldx ds, rax, rcx + ( + (ida_hexrays.mop_r,), + (ida_hexrays.mop_r, ida_hexrays.mop_S), + (ida_hexrays.mop_r, ida_hexrays.mop_S), + handle_ldx_var_var, + ), + ), + ida_hexrays.m_xdu: ( + # xdu esi, rsi + ( + (ida_hexrays.mop_r, ida_hexrays.mop_S), + (ida_hexrays.mop_z,), + (ida_hexrays.mop_r, ida_hexrays.mop_S), + handle_xdu_var_var, + ), + # xdu #0, rsi + ((ida_hexrays.mop_n,), (ida_hexrays.mop_z,), (ida_hexrays.mop_r, ida_hexrays.mop_S), handle_xdu_imm_var), + ), + ida_hexrays.m_xds: ( + # xds esi, rsi + ( + (ida_hexrays.mop_r, ida_hexrays.mop_S), + (ida_hexrays.mop_z,), + (ida_hexrays.mop_r, ida_hexrays.mop_S), + handle_xds_var_var, + ), + # xds #0, rsi + ((ida_hexrays.mop_n,), (ida_hexrays.mop_z,), (ida_hexrays.mop_r, ida_hexrays.mop_S), handle_xds_imm_var), + ), + ida_hexrays.m_call: ( + # call sub_0, (arg1, arg2, ..) + ((ida_hexrays.mop_v,), (ida_hexrays.mop_z,), (ida_hexrays.mop_f, ida_hexrays.mop_z), handle_call), + ), + ida_hexrays.m_icall: ( + # icall cs, x16, (arg1, arg2, ..) + ((ida_hexrays.mop_r,), (ida_hexrays.mop_r,), (ida_hexrays.mop_f, ida_hexrays.mop_z), handle_icall), + ), + ida_hexrays.m_add: ( + # add rax, #0, rax + ( + (ida_hexrays.mop_r, ida_hexrays.mop_S), + (ida_hexrays.mop_n,), + (ida_hexrays.mop_r, ida_hexrays.mop_S), + handle_add_var_imm, + ), + # add rax, rcx, rax + ( + (ida_hexrays.mop_r, ida_hexrays.mop_S), + (ida_hexrays.mop_r, ida_hexrays.mop_S), + (ida_hexrays.mop_r, ida_hexrays.mop_S), + handle_add_var_var, + ), + ), + ida_hexrays.m_sub: ( + # sub rax, #0, rax + ( + (ida_hexrays.mop_r, ida_hexrays.mop_S), + (ida_hexrays.mop_n,), + (ida_hexrays.mop_r, ida_hexrays.mop_S), + handle_sub_var_imm, + ), + # sub rax, rcx, rax + ( + (ida_hexrays.mop_r, ida_hexrays.mop_S), + (ida_hexrays.mop_r, ida_hexrays.mop_S), + (ida_hexrays.mop_r, ida_hexrays.mop_S), + handle_sub_var_var, + ), + ), +} + + +def get_handler_for_insn(insn: ida_hexrays.minsn_t) -> Optional[Callable[[state_t, ida_hexrays.minsn_t], None]]: + family = g_per_minsn_handlers.get(insn.opcode, tuple()) + + for lft, rgt, dst, handler in family: + if insn.l.t in lft and insn.r.t in rgt and insn.d.t in dst: + return handler + + return None + + +# debug: pretty print current state and insn +def dbg_dump_state_insn(insn: ida_hexrays.minsn_t, state: state_t): + if utils.g_logger.level > config.LOG_LEVEL_VERBOSE_DEBUG: + return + + utils.g_logger.log(config.LOG_LEVEL_VERBOSE_DEBUG, f"<----- insn & state @ {insn.ea:#x} ----->") + utils.g_logger.log(config.LOG_LEVEL_VERBOSE_DEBUG, f"insn: {ida_utils.insn_str_full(insn)}") + utils.g_logger.log(config.LOG_LEVEL_VERBOSE_DEBUG, f"state: {state}") + + +# divide a microinstruction into the subinstructions composing it +# returned instructions are ordered from first to last executed +# + patch instructions to use special kreg to transfer results +# note: this does not handle mop_d in mop_f arguments list +def flatten_minsn(minsn: ida_hexrays.minsn_t, mba: ida_hexrays.mba_t) -> Collection[ida_hexrays.minsn_t]: + subs = deque() + used_kregs = deque() + + # always copy, minsn in IDA Python 8.4 have a tendency to get freed prematurely. TODO: why ? + # mk_copy &= any([(op.t == ida_hexrays.mop_d) for op in (minsn.l, minsn.r, minsn.d)]) + to_patch = ida_hexrays.minsn_t(minsn) + + # search operands for sub instructions + for num_op in ("l", "r", "d"): + op = getattr(to_patch, num_op) + if op.t != ida_hexrays.mop_d: # is op a subinsn + continue + + # sub ret value is used as insn operand + if op.d.d.t == ida_hexrays.mop_z: + sub = ida_hexrays.minsn_t(op.d) # copy to patch + + # kreg to use for transfering sub ret to insn + kreg = mba.tmp_result_kregs.pop() + used_kregs.append(kreg) + krop = ida_hexrays.mop_t(kreg, op.size) # make mop_r + + # sub ret to kreg + sub.d = krop + + # sub may also contain sub instructions + subs.extend(flatten_minsn(sub, mba)) + + # sub-insn does not return a value (m_call) + else: + # insn should read call ret from call_result_kreg + # call_result_kreg is set by flow_in_callee + krop = ida_hexrays.mop_t(mba.call_result_kreg, op.size) + + subs.extend(flatten_minsn(op.d, mba)) + + # replace minsn original operand with kreg + setattr(to_patch, num_op, krop) + + # release used kregs + mba.tmp_result_kregs.extend(used_kregs) + + subs.append(to_patch) + return subs + + +# process one instruction & update current state +def process_instruction(state: state_t, insn: ida_hexrays.minsn_t): + # reset previous instruction state + state.reset() + + # modify the current state according to the insn + handler = get_handler_for_insn(insn) + if handler is None: + utils.g_logger.log(config.LOG_LEVEL_VERBOSE_DEBUG, f"unsupported insn @ {insn.ea:#x}") + state.drop_var_from_mop(insn.d) + else: + handler(state, insn) + + # dump the new state + dbg_dump_state_insn(insn, state) + + +# select most interesting state (most sid_t, call_t) +def select_state(states: List[state_t]) -> state_t: + states.sort(key=lambda e: (e.get_nb_types(sid_t), e.get_nb_types(call_t)), reverse=True) + return states[0] + + +# get the starting state for a basic block +# if many states are possible, select the one with the most info in it +def get_previous_state(block: ida_hexrays.mblock_t, prev_states: Dict[int, state_t]) -> state_t: + npred = block.npred() + initial = prev_states[idaapi.BADADDR] + + # get all candidates for previous state + states = [] + for i in range(npred): + prev = block.pred(i) + if prev in prev_states: + states.append(prev_states[prev]) + + if len(states) == 0: + return state_t(initial.mba, initial.fct) + + return select_state(states).copy() + + +# next node to visit from given list +def pop_node(nodes: Collection[Tuple[int, Set[int]]], visited: Set[int]) -> int: + # default: next node in graph flow + idx = 0 + + # find a block with all its predecessor visited + sel = [i for i, (_, preds) in enumerate(nodes) if len(preds.difference(visited)) == 0] + + if len(sel): + idx = sel[0] + + # find first node in nodes with a visited pred + else: + sel = [i for i, (_, preds) in enumerate(nodes) if len(visited.intersection(preds)) > 0] + + if len(sel): + idx = sel[0] + + node = nodes[idx][0] + visited.add(node) # update visited + del nodes[idx] # remove node from list + + return node + + +def walk_topological(mba: ida_hexrays.mba_t) -> Generator[int, None, None]: + # generate a list of nodes with predecessors + nodes: Collection[Tuple[int, Set[int]]] = list() + + cur: ida_hexrays.mblock_t = mba.blocks + while cur: + # avoid empty blocks (head, tail & other purged blocks) + if not cur.empty(): + preds = set(cur.pred(i) for i in range(cur.npred()) if not mba.get_mblock(cur.pred(i)).empty()) + nodes.append((cur.serial, preds)) + cur = cur.nextb + + visited: Set[int] = set() + while len(nodes): + yield pop_node(nodes, visited) + + +# Injector into state_t +class injector_t: + def __init__(self, callback=None, when: int = 0): + self.callback = callback # callback(state: state_t, ea: int, sub_ea: int, before_update: bool) + self.when = when # when & 1 -> inject before, when & 2 -> inject after + + # inject value before processing current instruction + def inject_before(self, state: state_t, ea: int, sub_ea: int): + if self.when & 1: + self.callback(state, ea, sub_ea, True) + + # inject value after the current instruction has been processed + def inject_after(self, state: state_t, ea: int, sub_ea: int): + if self.when & 2: + self.callback(state, ea, sub_ea, False) + + +# should_propagate default callback +def always_propagate(fct: function_t, state: state_t) -> bool: + return True + + +# data flow control parameters +# used to control propagation & retrieve information +class dflow_ctrl_t: + def __init__( + self, + injector: injector_t = injector_t(), + dive_cb=always_propagate, + depth: int = MAX_PROPAGATION_RECURSION, + ): + self.injector = injector # state injector + self.visited: Dict[int, function_t] = dict() # visited functions + + self.depth = depth # current (reverse) depth + self.max_depth = depth # maximum depth to reach + + self.dive_cb = dive_cb # callback deciding whether or not to dive into callee + + # is there a potential new state we need to visit + def should_propagate(self, fct: function_t, state: state_t) -> bool: + if self.depth < 0: # max depth has been reached + return False + + return self.dive_cb(fct, state) + + # has propagation passed by function + def has_function(self, ea: int) -> bool: + return ea in self.visited + + # get or create function + def get_function_for_mba(self, mba: ida_hexrays.mba_t) -> function_t: + if not self.has_function(mba.entry_ea): + self.visited[mba.entry_ea] = function_t(mba) + return self.visited[mba.entry_ea] + + # get or create function model for ea + def get_function(self, fct: idaapi.func_t) -> Optional[function_t]: + mba = ida_utils.get_func_microcode(fct) + if mba is None: + return None + + return self.get_function_for_mba(mba) + + +# copy callee's arguments from caller's state and propagate in callee +def flow_in_callee( + call_ea: int, state: state_t, params: dflow_ctrl_t +) -> Generator[Tuple[int, int, state_t], None, None]: + ret_value = call_t(idaapi.BADADDR if state.call_to is None else state.call_to.start_ea, call_ea) + + if state.call_to is not None: # callee was resolved + # microcode for callee + mba = ida_utils.get_func_microcode(state.call_to) + if mba is None: + utils.g_logger.warning(f"No mba for callee {state.call_to.start_ea:#x}") + else: + # callee initial state + cistate = state_t(mba, params.get_function_for_mba(mba)) + populate_arguments(cistate, state) + + params.depth -= 1 + + # propagate in callee + # peep at intermediate states to catch return values + for ea, sea, cstate in function_data_flow(cistate, params): + if isinstance(cstate.ret, absop_t) and cstate.ret.should_dive() and cstate.fct == cistate.fct: + ret_value = cstate.ret + + yield ea, sea, cstate + + params.depth += 1 + + # set last call return value + utils.g_logger.debug(f"ret value for call @ {call_ea:#x} set to {ret_value}") + state.set_register(state.mba.call_result_kreg, ret_value) + + +# propagate in a function, using given initial state and parameters +def function_data_flow(initial_state: state_t, params: dflow_ctrl_t) -> Generator[Tuple[int, int, state_t], None, None]: + mba = initial_state.mba + + # apply entry injection before deciding if we should continue + # note: function's ea may differ from first insn.ea + params.injector.inject_before(initial_state, mba.entry_ea, -1) + + # check if we can get new info by propagating there + if not params.should_propagate(initial_state.fct, initial_state): + return + + # record initial states for every node + prev_states = dict() # bb index -> state + prev_states[idaapi.BADADDR] = initial_state + + # analyze calls & resolve callees arguments + # this takes decompilation, do it after we are sure to analyze the function + ida_utils.mba_analyze_calls(mba) + + # get nodes flooding order + nodes = walk_topological(mba) + + # get entry basic block + try: + block = mba.get_mblock(next(nodes)) + except StopIteration: # function has no block + utils.g_logger.error(f"No entry block for function 0x{mba.entry_ea}") + return + + insn = block.head # first instruction + state = initial_state # first state + + # two minsn may have the same ea, use sub_ea to distinguish them + sub_ea = 0 + + # for every basic block + while True: + # for every instruction + while insn: + # for every subinstruction forming the instruction + for subinsn in flatten_minsn(insn, mba): + params.injector.inject_before(state, subinsn.ea, sub_ea) + + process_instruction(state, subinsn) + + # yield state after processing the insn + yield subinsn.ea, sub_ea, state + + # we need to go deeper + if state.has_call_info(): + yield from flow_in_callee(subinsn.ea, state, params) + + params.injector.inject_after(state, subinsn.ea, sub_ea) + sub_ea += 1 + + # forget intermediate results + state.drop_kregs() + + sub_ea = sub_ea if (insn.next and insn.ea == insn.next.ea) else 0 + insn = insn.next + + # tail basic block (ending with a ret) + # there are no specific minsn for ret, a tail bb is only followed by the special BLT_STOP bb + # note: a call to a noreturn function creates a special bb without any successor + if block.nsucc() == 1 and mba.get_mblock(block.succ(0)).type == idaapi.BLT_STOP: + yield block.end, -1, handle_ret(state) + + # add updated state to previous states + prev_states[block.serial] = state + + # next block to process + try: + block = mba.get_mblock(next(nodes)) + except StopIteration: + break + + insn = block.head + state = get_previous_state(block, prev_states) + + +# copy arguments from caller state to callee state +def populate_arguments(callee_state: state_t, caller_state: Optional[state_t] = None): + # make sure the number of arguments of the call site VS function's prototype are the same + if caller_state and callee_state.fct.get_args_count() != len(caller_state.call_args): + utils.g_logger.warning( + f"fct {callee_state.get_fea():#x} mismatch between fct nargs ({callee_state.fct.get_args_count()}) and call site args {len(caller_state.call_args)}" + ) + + for i in range(callee_state.fct.get_args_count()): + val = ( + caller_state.call_args[i] if (caller_state and i < len(caller_state.call_args)) else None + ) # get caller value for the arg + val = val if isinstance(val, absop_t) and val.should_dive() else arg_t(i) # use default arg when required + + callee_state.set_var_from_loc(callee_state.fct.get_argloc(i), val) + + +# generate cpu state for given function +def generate_state( + func: idaapi.func_t, params: Optional[dflow_ctrl_t] = None +) -> Generator[Tuple[int, int, state_t], None, None]: + mba = ida_utils.get_func_microcode(func) + if mba is None: + utils.g_logger.error(f"no microcode for {func.start_ea}, no states generated") + return + + params = params or dflow_ctrl_t() + starting_state = state_t(mba, params.get_function_for_mba(mba)) + populate_arguments(starting_state) + + yield from function_data_flow(starting_state, params) + +``` + +`symless/existing.py`: + +```py +from collections import deque +from typing import Collection, Tuple + +import idaapi + +import symless.generation as generation +import symless.utils.ida_utils as ida_utils +import symless.utils.utils as utils + + +# add special gap field to structure +def make_gap(struc: idaapi.tinfo_t, off: int, size: int): + udm = idaapi.udm_t() + udm.offset = off * 8 + udm.size = size * 8 + udm.name = f"gap{off:X}" + udm.tafld_bits |= idaapi.TAFLD_GAP # is_gap + + # set type to _BYTE[size] + arr = idaapi.array_type_data_t(0, size) + arr.elem_type = ida_utils.get_basic_type(idaapi.BT_VOID | idaapi.BTMT_SIZE12) + udm.type = idaapi.tinfo_t() + udm.type.create_array(arr) + + tcode = struc.add_udm(udm, idaapi.ETF_MAY_DESTROY) + if tcode != idaapi.TERR_OK: + utils.g_logger.error( + f'Failed to gap {struc.get_type_name()} with {udm.name} (size {size:#x}, type "{udm.type}"): "{idaapi.tinfo_errstr(tcode)}" ({tcode:#x})' + ) + + +# remove padding fields from structure +def remove_padd_fields(struc: idaapi.tinfo_t): + details = idaapi.udt_type_data_t() + struc.get_udt_details(details) + + # search for gaps + gaps: Collection[Tuple[int, int]] = deque() + for udm in details: + if udm.name == f"gap{(udm.offset // 8):X}": # gap identified by name + # we do not want consecutive gaps latter, merge them + if len(gaps) and gaps[0][1] == udm.offset: + gaps[0] = (gaps[0][0], udm.offset + udm.size) + else: + gaps.appendleft((udm.offset, udm.offset + udm.size)) + + # merge all gaps into real gaps fields + for off, end in gaps: + make_gap(struc, off // 8, (end // 8) - (off // 8)) + + +# remove gap flag from padd fields +# + padd to reach at least min_size bytes +def add_padd_fields(struc: idaapi.tinfo_t, min_size: int): + csize = struc.get_size() + if csize < min_size: # padd to final size + make_gap(struc, csize, min_size - csize) + + # remove gap flags from gaps - collapse padding + details = idaapi.udt_type_data_t() + struc.get_udt_details(details) + for udm in details: + udm.tafld_bits &= ~idaapi.TAFLD_GAP + struc.create_udt(details) # removes tid & ordinal info + + +# get the structure path assigned to given operand +def get_op_stroff(ea: int, n: int): + delta, path = idaapi.sval_pointer(), idaapi.tid_array(idaapi.MAXSTRUCPATH) + if idaapi.get_stroff_path(path.cast(), delta.cast(), ea, n) == 0: + return idaapi.BADADDR + return path[0] + + +# find existing vtable structure from vtable ea +def find_existing_vtable(ea: int) -> int: + tinfo = idaapi.tinfo_t() + if not (idaapi.get_tinfo(tinfo, ea) and tinfo.is_udt()): + return idaapi.BADADDR + return tinfo.get_tid() + + +# find existing structure with given name +def find_existing_structure(name: str) -> int: + tinfo = ida_utils.get_local_type(name) + if tinfo is None: + return idaapi.BADADDR + + if tinfo.is_forward_struct(): + ida_utils.replace_forward_ref(tinfo) + elif not tinfo.is_udt(): + return idaapi.BADADDR + + return tinfo.get_tid() + + +# should we replace an existing type in the idb by our struc ptr +# types we think it's ok to replace are void, scalars and scalars pointers +def should_arg_type_be_replaced(tinfo: idaapi.tinfo_t) -> bool: + if tinfo.is_ptr(): + ptr_data = idaapi.ptr_type_data_t() + if not tinfo.get_ptr_details(ptr_data) or ptr_data.parent.get_realtype() != idaapi.BT_UNK: + return False + tinfo = ptr_data.obj_type # decide on pointee type + + # void, ints, floats, bools + return idaapi.get_base_type(tinfo.get_realtype()) < idaapi.BT_PTR + + +# should we replace an existing struc field type +# only replace integers and void pointer +def should_field_type_be_replaced(tinfo: idaapi.tinfo_t) -> bool: + if tinfo.is_ptr(): + ptr_data = idaapi.ptr_type_data_t() + if not tinfo.get_ptr_details(ptr_data) or ptr_data.parent.get_realtype() != idaapi.BT_UNK: + return False + + # void* + return idaapi.get_base_type(ptr_data.obj_type.get_realtype()) == idaapi.BT_VOID + + # integers + rt = idaapi.get_base_type(tinfo.get_realtype()) + return rt >= idaapi.BT_INT8 and rt <= idaapi.BT_INT + + +# should we rename a field, avoid renaming user-provided fields +def should_field_name_be_replaced(offset: int, old_name: str, new_name: str) -> bool: + default_names = { # record of preferred default names + "": -1, + generation.unk_data_field_t.get_default_name(offset): 0, + generation.field_t.get_default_name(offset): 1, + generation.ptr_field_t.get_default_name(offset): 2, + generation.fct_ptr_field_t.get_default_name(offset): 3, + generation.struc_ptr_field_t.get_default_name(offset): 3, + generation.vtbl_ptr_field_t.get_default_name(offset): 4, + } + + old_name_score = default_names.get(old_name, 5) + new_name_score = default_names.get(new_name, 5) + return new_name_score > old_name_score + +``` + +`symless/generation/__init__.py`: + +```py +import collections +from math import log2 +from typing import Any, Collection, Generator, List, Optional, Set, Tuple, Union + +import idaapi + +import symless.config as config +import symless.existing as existing +import symless.model as model +import symless.symbols as symbols +import symless.utils.ida_utils as ida_utils +import symless.utils.utils as utils + + +# a structure's field +class field_t: + def __init__( + self, + offset: int, + size: int, + flow: model.entry_t, + block: model.block_t, + ): + self.offset = offset + self.size = size + self.flow: model.entry_t = flow # root of the flow this field comes from + self.block: model.block_t = block # block where this field was set + self.name: Optional[str] = None # field's name + self.owner: Optional[structure_t] = None # structure this field belongs to + + # default name for the fields of this class, at given offset + def get_default_name(off: int) -> str: + return f"field_{off:08x}" + + # get field's name + def get_name(self) -> str: + if self.name is None: + return self.__class__.get_default_name(self.offset) + return self.name + + # compute wished field's name using symbols + # does not handle conflicts + def preferred_name(self) -> Optional[str]: + return None + + # compute field's name + def set_name(self, taken: Set[str]): + self.name = self.preferred_name() + if self.name is None: + return + + if self.name in taken: + self.name = f"{self.name}_0x{self.offset:x}" + + taken.add(self.name) + + # comment associated to the field + def get_comment(self) -> Optional[str]: + return None + + # get field's type + def get_type(self) -> idaapi.tinfo_t: + if self.size not in (1, 2, 4, 8, 16): + raise RuntimeError(f"Unexpected {self} size") + + # try the cool kids types + bt = ("uint8_t", "uint16_t", "uint32_t", "uint64_t", "uint128_t")[int(log2(self.size))] + t = ida_utils.get_local_type(bt) + if t is not None: + return t + + # resort to verbose types + bt = (idaapi.BT_INT8, idaapi.BT_INT16, idaapi.BT_INT32, idaapi.BT_INT64, idaapi.BT_INT128)[int(log2(self.size))] + return ida_utils.get_basic_type(bt | idaapi.BTMT_USIGNED) + + # do we have information on this field's type + def has_type(self) -> bool: + return False + + # set the structure this fields belongs to + def set_owner(self, struc: "structure_t"): + self.owner = struc + + # are self & other types compatible + def match(self, other: "field_t") -> bool: + return self.size == other.size + + # should we replace the field by given other field + def replace(self, other: "field_t") -> bool: + # other has a more derived type, use it + if isinstance(other, self.__class__) and not isinstance(self, other.__class__): + return True + + # replacement class is less derived, keep current value + if isinstance(self, other.__class__) and not isinstance(other, self.__class__): + return False + + # try to keep the type set in the constructor / init function + # i.e nearest function from the root of the data flow + distance_new = other.flow.distance_to(other.block.get_owner()) + distance_old = self.flow.distance_to(self.block.get_owner()) + if distance_new < distance_old: + return True + if distance_new > distance_old: + return False + + utils.g_logger.warning( + f"Can not decide between fields set in {self.block.get_owner().entry_id()} and {other.block.get_owner().entry_id()} for structure {self.owner.get_name()}" + ) + return False + + def __str__(self) -> str: + return f"{self.get_name()}[{self.offset:#x}:{self.size:x}]" + + +# class for fields of unknown size +# i.e we know the address of the field was used, no idea what's in there +class unk_data_field_t(field_t): + def __init__(self, offset, block: model.block_t): + super().__init__(offset, 1, None, block) + + def get_default_name(off: int) -> str: + return f"buff_{off:08x}" + + def replace(self, other: "field_t") -> bool: + return True + + # char[1] + def get_type(self) -> idaapi.tinfo_t: + t = idaapi.tinfo_t() + a = idaapi.array_type_data_t() + a.elem_type = ida_utils.get_basic_type(idaapi.BT_INT8 | idaapi.BTMT_CHAR) + a.nelems = 1 + t.create_array(a) + return t + + +# field typed with an unknown pointer +class ptr_field_t(field_t): + def __init__(self, value: Any, offset: int, flow: model.entry_t, block: model.block_t): + super().__init__(offset, ida_utils.get_ptr_size(), flow, block) + self.value = value # for base ptr_field_t, an integer value + + def get_default_name(off: int) -> str: + return f"ptr_{off:08x}" + + def has_type(self) -> bool: + return True + + # guess type from pointed data type + def get_type(self) -> idaapi.tinfo_t: + if not idaapi.is_mapped(self.value): + return ida_utils.void_ptr() + + tinfo = idaapi.tinfo_t() + if not idaapi.get_tinfo(tinfo, self.value): + return ida_utils.void_ptr() + + tinfo.create_ptr(tinfo) + return tinfo + + +# field typed with a function pointer +class fct_ptr_field_t(ptr_field_t): + def __init__( + self, + fct_ea: int, + offset: int, + flow: model.entry_t, + block: model.block_t, + ): + super().__init__(fct_ea, offset, flow, block) + + def get_default_name(off: int) -> str: + return f"method_{off:08x}" + + def preferred_name(self) -> Optional[str]: + signature = ida_utils.demangle_ea(self.value) + if len(signature) == 0: + return None + + simple = symbols.method_name_from_signature(signature) + + # as much as we would love to use '~' in dtor names, IDA does not really support it + # it can cause problems when applying stroff & xrefs + if simple[0] == "~": + simple = "%s_dtor%s" % (simple[1:], "" if self.offset == 0 else f"_{(self.offset//self.size):x}") + + return simple.strip("~") + + def get_comment(self) -> str: + return f"{self.value:#x}" + + def get_type(self) -> idaapi.tinfo_t: + func_tinfo, func_data = ida_utils.get_or_create_fct_type(self.value) + + # owner is a vtable, make sure to type method's 'this' argument + if isinstance(self.owner, vtable_struc_t): + this, shift = self.owner.get_class() + this_tinfo = this.find_ptr_tinfo() + ida_utils.set_function_argument(func_data, 0, this_tinfo, shift, this_tinfo, "this") + func_tinfo.create_func(func_data) + + if func_tinfo.create_ptr(func_tinfo): + return func_tinfo + return ida_utils.void_ptr() # default to void* + + def match(self, other: "fct_ptr_field_t") -> bool: + return self.value == other.value + + +# field typed with a structure pointer +class struc_ptr_field_t(ptr_field_t): + def __init__( + self, + ep: model.entry_t, + offset: int, + flow: model.entry_t, + block: model.block_t, + ): + super().__init__(ep, offset, flow, block) + + def get_default_name(off: int) -> str: + return f"struc_{off:08x}" + + # get structure this field points to + def get_structure(self) -> Tuple[int, "structure_t"]: + return self.value.get_structure() + + def get_type(self) -> idaapi.tinfo_t: + shift, struc = self.get_structure() + + tinfo = struc.find_ptr_tinfo() + if tinfo is None: + utils.g_logger.error('Could not retrieve local type with name "%s" for field typing' % struc.get_name()) + return None + + ida_utils.shift_ptr(tinfo, tinfo, shift) + return tinfo + + def match(self, other: "struc_ptr_field_t") -> bool: + self_shift, self_struc = self.get_structure() + other_shift, other_struc = other.get_structure() + + return self_shift == other_shift and self_struc == other_struc + + +# field typed with a vtable pointer +class vtbl_ptr_field_t(struc_ptr_field_t): + def __init__( + self, + types: Collection[model.ftype_t], + offset: int, + flow: model.entry_t, + block: model.block_t, + ): + super().__init__(None, offset, flow, block) + + # all vtables that went in this field + self.values: Collection[model.vtbl_entry_t] = collections.deque() + + # are we sure the list of vtables set are in the ctor order + self.in_order = not isinstance(flow, model.arg_entry_t) + + # fill types + for type in filter( + lambda t: isinstance(t, model.ftype_struc_t) and isinstance(t.entry, model.vtbl_entry_t), + types[::-1], + ): + self.add_vtable(type.entry, self.in_order) + + def get_default_name(off: int) -> str: + return "%s%s" % (idaapi.VTBL_MEMNAME, "" if off == 0 else f"_{off:08x}") + + def get_comment(self) -> str: + _, vtbl = self.get_structure() + return vtbl.get_name() + + # add given vtable to vtables values list + # if as_latest is set, consider it to be effective field's value + # else use less derived vtable between first & last added + def add_vtable(self, vtbl: model.vtbl_entry_t, as_latest: bool): + if vtbl in self.values: # already encountered + return + + self.values.append(vtbl) + self.value = vtbl if as_latest else vtbl.get_most_derived(self.values[0]) + + def replace(self, other: "field_t") -> bool: + if not isinstance(other, vtbl_ptr_field_t): + return False + + # from same data flow, take latest vtable into account + if other.flow == self.flow: + old_vtbl = self.value + new_vtbl = other.value + + # keep all info into new field + other.values = self.values + other.value = self.value + other.add_vtable(new_vtbl, other.in_order) + + utils.g_logger.debug( + f"__vftable_{self.offset:#x} selecting vtbl {other.value.ea:#x} between ({old_vtbl.ea:#x}, {new_vtbl.ea:#x}) (in order {other.in_order})" + ) + return True + + # else: different data flow not taken into account + # effective vtable should have been found in this flow + + return False + + +# model of a structure +class structure_t: + def __init__(self, sid: int): + self.sid = sid # model sid + self.size = -1 # structure size, if known + self.ea = idaapi.BADADDR + self.ida_tid = idaapi.BADADDR # associated IDA struc tid + + self.fields: dict[int, field_t] = dict() # structure's members + self.range: Collection[tuple[int, int]] = list() # structure's ranges occupied by fields (offset, size) + + self.name: Optional[str] = None # struc's name + + # set of structure's entries in the data flow + # records (shift, entry), shift is used when entry is a shift ptr on our strucs + self.root_eps: Set[Tuple[int, model.entry_t]] = set() + + # is the structure associated with some xrefs + # if not, no need to generate it + self.has_xrefs = False + + # force this struc generation into IDA database + self.force_generation = False + + def set_size(self, size: int): + self.size = size + + # structure size from its last field + def get_size_from_fields(self) -> int: + if len(self.range) == 0: + return 0 + last = self.range[-1] + return last[0] + last[1] + + # structure size, known (malloc) or from the fields we found + def get_size(self) -> int: + if self.size >= 0: + return self.size + return self.get_size_from_fields() + + # add a field to the structure + # solver_cb callback used to resolve overlapping fields + def set_field(self, field: field_t, solver_cb) -> bool: + offset = field.offset + field_end = offset + field.size + + # check boundaries + if offset < 0 or (self.size >= 0 and field_end > self.size): + utils.g_logger.warning( + f"Could not add field (0x{offset:x}:0x{field.size:x}) to {self.get_name()} of size {self.size:x}" + ) + return False + + # compute overlapping fields + i = 0 + replaced = collections.deque() + while i < len(self.range) and self.range[i][0] < field_end: + other_end = self.range[i][0] + self.range[i][1] + + # overlapping fields + if other_end > offset: + replace = solver_cb(field, self.fields[self.range[i][0]]) + if replace: + replaced.appendleft(i) + else: # do not insert new field + utils.g_logger.debug( + "Could not add %s (set in %s) to %s because of conflicts with existing (0x%x:0x%x)" + % ( + field.get_name(), + field.block.get_owner().entry_id(), + self.get_name(), + self.range[i][0], + self.range[i][1], + ) + ) + return False + + i += 1 + + # remove conflicting fields + for j in replaced: + old = self.fields[self.range[j][0]] + utils.g_logger.debug( + "Discarding %s (set in %s) from %s, replacing with %s (set in %s)" + % ( + old.get_name(), + old.block.get_owner().entry_id(), + self.get_name(), + field.get_name(), + field.block.get_owner().entry_id(), + ) + ) + del self.fields[self.range[j][0]] + del self.range[j] + + # add new field + idx = i - len(replaced) + self.range.insert(idx, (offset, field.size)) + self.fields[offset] = field + field.set_owner(self) + + utils.g_logger.debug(f"Adding {field} to {self.get_name()}") + + return True + + # get the field that occupies given offset + def has_field_at(self, offset: int) -> Optional[field_t]: + i = 0 + while i < len(self.range) and self.range[i][0] <= offset: + if self.range[i][0] + self.range[i][1] > offset: + return self.fields[self.range[i][0]] + i += 1 + return None + + # get field starting at given offset + def get_field(self, offset: int) -> Optional[field_t]: + try: + return self.fields[offset] + except KeyError: + return None + + def get_fields(self) -> Generator[field_t, None, None]: + for field in self.fields.values(): + yield field + + # add one root entrypoint + # defining a data flow this structure follows + def associate_root(self, entry: model.entry_t, shift: int): + # struc's default name is based on ea, on assign it once + if self.ea == idaapi.BADADDR: + self.ea = entry.ea + + self.root_eps.add((shift, entry)) + entry.set_structure(shift, self) + + # get structure's entries in the data flow + def associated_root(self) -> Generator[Tuple[int, model.entry_t], None, None]: + for shift, root in self.root_eps: + yield shift, root + + # get the flow of nodes traveled by the structure + # yields (root node, current shift, current block) + def node_flow(self, all_roots: bool = True) -> Generator[Tuple[model.entry_t, model.block_t, int], None, None]: + for initial_shift, initial_root in self.associated_root(): + for root, node, shift in model.flow_from_root(initial_root, all_roots): + yield (root, node, shift + initial_shift) + + # find existing IDA structure that this model represents + def find_existing(self) -> int: + self.ida_tid = existing.find_existing_structure(self.get_name()) + return self.ida_tid + + # set existing IDA struc represented by this model + def set_existing(self, tid: int): + self.ida_tid = tid + + # get IDA tinfo_t representing our structure + # the structure must have been created before + def find_tinfo(self) -> idaapi.tinfo_t: + if self.ida_tid == idaapi.BADADDR: + raise Exception(f"find_tinfo on {self.get_name()} failed, no IDA structure associated") + + tif = idaapi.tinfo_t() + tif.get_type_by_tid(self.ida_tid) # should return True + return tif + + # get a struc pointer tinfo + def find_ptr_tinfo(self) -> idaapi.tinfo_t: + t = self.find_tinfo() + t.create_ptr(t) + return t + + def get_name(self) -> str: + if self.name is not None: + return self.name + return self.default_name() + + def set_name(self, name: str): + self.name = name + + def default_name(self) -> str: + return f"struc_0x{self.ea:x}" + + # define fields names + def compute_names(self): + taken = set() # used names set, avoid conflicts + for field in self.fields.values(): + field.set_name(taken) + + # comment associated to structure + def get_comment(self) -> Optional[str]: + if not config.g_settings.debug: + return None + + eas = set([(shift, root.ea) for shift, root in self.associated_root()]) + return "Root nodes:\n%s" % "\n".join( + [("%s, shift: 0x%x" % (ida_utils.addr_friendly_name(ea), shift)) for shift, ea in eas] + ) + + # do we generate this structure in IDA + def relevant(self) -> bool: + return self.force_generation or ( + self.has_xrefs + and len(self.fields) > 0 + and ( # a struc without xref is useless + len(self.fields) > 1 + or self.get_size_from_fields() > ida_utils.get_ptr_size() # more than 1 field - not a buffer + or self.fields[ # unique field not at off 0 - not a buffer + self.range[0][0] + ].has_type() # unique field has a relevant type + ) + ) + + # do we need to apply the __cppobj flag + def is_cppobj(self) -> bool: + return isinstance(self.get_field(0), vtbl_ptr_field_t) + + # do we need to apply the VFT flag + def is_vtable(self) -> bool: + return False + + def __eq__(self, other) -> bool: + return isinstance(other, structure_t) and self.sid == other.sid + + def __hash__(self) -> int: + return self.sid + + +# model of a vtable +class vtable_struc_t(structure_t): + def __init__(self, sid: int): + super().__init__(sid) + self.owning_class: Optional[Tuple[structure_t, int]] = None # class owning this vtable, with associated offset + + def default_name(self) -> str: + return f"loc_{self.ea:x}{idaapi.VTBL_SUFFIX}" + + def get_comment(self) -> str: + out = ( + f"Vtable at 0x{self.ea:x}\nOwned by {self.owning_class[0].get_name()} at offset 0x{self.owning_class[1]:x}" + ) + return out + + # find existing vtable structure from typed vtable + def find_existing(self) -> int: + self.ida_tid = existing.find_existing_vtable(self.ea) + if self.ida_tid != idaapi.BADADDR: + return self.ida_tid + + return super().find_existing() + + def set_class(self, owner: structure_t, offset: int): + self.owning_class = (owner, offset) + + def get_class(self) -> Tuple[structure_t, int]: + return self.owning_class + + # no need to build unlinked vtables + def relevant(self) -> bool: + return self.owning_class is not None + + def is_vtable(self) -> bool: + return True + + +# empty structure model from associated entry +def empty_model_from_ep(entry: model.entry_t) -> structure_t: + if isinstance(entry, model.vtbl_entry_t): + return vtable_struc_t(entry.struc_id) + + struc = structure_t(entry.struc_id) + if isinstance(entry, model.alloc_entry_t): # known size structure + struc.set_size(entry.size) + + return struc + + +# defines a structure that has been merged into another +# identify the new structure +class merge_t: + def __init__(self, merge: structure_t): + self.merge_id = merge.sid + + +# record of all structures to be built +class structure_record_t: + def __init__(self, entries: model.entry_record_t): + self.structures: List[Union[structure_t, merge_t]] = [None for _ in range(entries.structures_count())] + + # fill structures array + for entry in entries.get_entries(): + if not entry.is_root(): + continue + + # make empty structure model from entry point + if self.structures[entry.struc_id] is None: + self.structures[entry.struc_id] = empty_model_from_ep(entry) + + # associate structure with its entries in the data flow + self.structures[entry.struc_id].associate_root(entry, entry.struc_shift) + + # define a structure to have been merged into another + def replace_by(self, struc: structure_t, merge: structure_t): + self.structures[struc.sid] = merge_t(merge) + + def _get_structure(self, sid: int) -> Union[structure_t, merge_t]: + return self.structures[sid] + + # get effective structure + # if given structure has been merged, return merge result + def get_structure(self, struc: structure_t) -> structure_t: + st = self._get_structure(struc.sid) + while isinstance(st, merge_t): + st = self._get_structure(st.merge_id) + return st + + def get_structures( + self, cls: type = structure_t, include_discarded: bool = True + ) -> Generator[structure_t, None, None]: + for struc in self.structures: + if isinstance(struc, cls) and (include_discarded or struc.relevant()): + yield struc + +``` + +`symless/generation/generate.py`: + +```py +from typing import Dict, Tuple + +import idaapi +import idautils +import idc + +import symless.allocators as allocators +import symless.existing as existing +import symless.model.entrypoints as entrypoints +import symless.symbols as symbols +import symless.utils.ida_utils as ida_utils +from symless.generation import * + +# folder in local types listing, to store symless generated types +STRUC_DIR = "Symless" + + +# make symless structures directory +def make_structures_dir(): + root = ida_utils.get_local_types_folder() + if root is None: + return + + err = root.mkdir(STRUC_DIR) + if err not in (idaapi.DTE_OK, idaapi.DTE_ALREADY_EXISTS): + utils.g_logger.error(f'Could not create {STRUC_DIR} local types folder: "{root.errstr(err)}"') + + +# create an empty IDA structure used to contain given struc +def make_IDA_structure(struc: structure_t): + if struc.ida_tid != idaapi.BADADDR: + return + + name = struc.get_name() + + # check for existing struc + ida_tid = struc.find_existing() + if ida_tid != idaapi.BADADDR: + utils.g_logger.info(f'Re-using existing structure (tid {ida_tid:#x}) for model "{name}"') + return + + # create new structure + struc.set_existing(idc.add_struc(idaapi.BADADDR, name, False)) + if struc.ida_tid == idaapi.BADADDR: + utils.g_logger.error(f'Could not create empty structure "{name}"') + return + + # move it to symless folder + root = ida_utils.get_local_types_folder() + if root is None: + return + + err = root.rename(name, f"{STRUC_DIR}/{name}") + if err != idaapi.DTE_OK: + utils.g_logger.warning(f'Could not move structure "{name}" into {STRUC_DIR} directory: "{root.errstr(err)}"') + + +# do we want to type IDA base with given entry data +def should_type_entry(entry: entrypoints.entry_t, ctx: entrypoints.context_t) -> bool: + # root is always right + if entry.is_root(): + return True + + shift, struc = entry.get_structure() + if not struc.relevant(): + return False # structure will not be generated + + struc_tif = struc.find_tinfo() + + # do not overwrite typing set by user on operands + # TODO we should only check the operand we will type + # for now check all operands on the instructions about to be typed + # assume only one operand per instruction can be typed with a struc path (not sure if always true) + for ea, _, _ in entry.get_operands(): + for n in range(idaapi.UA_MAXOP): + # operand was typed with a different structure than ours, stop + if existing.get_op_stroff(ea, n) not in (idaapi.BADADDR, struc_tif.get_tid()): + return False + + # always type with vtbl, no matter its size + if isinstance(struc, vtable_struc_t): + return True + + # arguments entries special case + if isinstance(entry, entrypoints.arg_entry_t): + # always type virtual functions + fct = ctx.get_function(entry.ea) + if fct.is_virtual(): + return True + + # avoid other shifted ptr arguments + if shift != 0: + return False + + # TODO: do not type when arg is already typed with different struc + + # do not type entries that do not represent a structure + lower, upper = entry.get_boundaries() + if lower == 0 and upper <= ida_utils.get_ptr_size(): + return False + + return True + + +# update given function's returned type with the given entry +def type_function_return(fct: entrypoints.prototype_t, entry: entrypoints.entry_t): + # entry is not returned, exit + if fct.get_ret() != entry: + return + + shift, struc = entry.get_structure() + + # avoid prone to error shifted pointers + if shift != 0: + return + + func_tinfo, func_data = ida_utils.get_or_create_fct_type(fct.ea) + if not existing.should_arg_type_be_replaced(func_data.rettype): + return + + tinfo = struc.find_ptr_tinfo() + func_data.rettype = tinfo + + if func_tinfo.create_func(func_data): + idaapi.apply_tinfo(fct.ea, func_tinfo, idaapi.TINFO_DEFINITE) + + utils.g_logger.info(f"Typing return of fct_0x{fct.ea:x} with {tinfo}") + + +# update function's type with given arg entrypoint +def type_function_argument(fct: entrypoints.prototype_t, arg: entrypoints.entry_t): + if not isinstance(arg, entrypoints.arg_entry_t): + return + + idx = arg.index + + func_tinfo, func_data = ida_utils.get_or_create_fct_type(fct.ea) + if idx >= func_data.size(): + return + + # do not replace existing (complex) type + if not existing.should_arg_type_be_replaced(func_data[idx].type): + return + + shift, struc = arg.get_structure() + ida_utils.set_function_argument( + func_data, + idx, + struc.find_ptr_tinfo(), + shift, + struc.find_tinfo(), + "this" if idx == 0 else None, + ) + + if not func_tinfo.create_func(func_data): + utils.g_logger.error(f"Could not type arg_{idx} of fct_0x{fct.ea:x} with {arg.entry_id()}") + return + + idaapi.apply_tinfo(fct.ea, func_tinfo, idaapi.TINFO_DEFINITE) + + utils.g_logger.info(f"Typing fct_0x{fct.ea:x} arg_{idx} with {struc.get_name()} shifted by 0x{shift:x}") + + +# which op of the given insn should we type with a structure path +# for given reg and given field offset +# returns op and shift to apply +def find_op_for_stroff(insn: idaapi.insn_t, regid: int, off: int) -> Tuple[Optional[idaapi.op_t], int]: + for op in ida_utils.get_insn_ops(insn): + # disp/phrase with regid for base register, this is what we want to type + # we assume they should not be more than one disp/phrase op per insn + if op.type in (idaapi.o_phrase, idaapi.o_displ) and op.phrase == regid: + # compute shift to apply to type with member at given offset + displ = op.addr if op.type == idaapi.o_displ else 0 # signed int32 + shift = utils.to_c_integer(off - displ, 4) + + return (op, shift) + + # immediate operand, preceded regid + # this must be an arithmetic operation on our struc ptr, we must type the immediate value + # this assumes the src reg is before the immediate, which is the case on IDA disass for arm and x64 + if ( + op.type == idaapi.o_imm + and op.n > 0 + and insn.ops[op.n - 1].type == idaapi.o_reg + and insn.ops[op.n - 1].reg == regid + ): + imm_size = idaapi.get_dtype_size(op.dtype) + + displ = op.value + shift = utils.to_c_integer(off - displ, imm_size) + + return (op, shift) + + return (None, 0) + + +# type operand with the given "struct offset" +def apply_stroff_to_op(ea: int, regid: int, struc: idaapi.tinfo_t, off: int): + insn = idaapi.insn_t() + if idaapi.decode_insn(insn, ea) == 0: + return + + udm = idaapi.udm_t() + udm.offset = off * 8 + mid = struc.get_udm_tid(ida_utils.find_udm_wrap(struc, udm)) + path = idaapi.tid_array(idaapi.MAXSTRUCPATH) + + op, shift = find_op_for_stroff(insn, regid, off) + if op is None: + utils.g_logger.warning(f"No op to apply stroff for {ea:#x} {idaapi.get_reg_name(regid,8)}({regid:#x})") + return + + # type operand with struc path + path[0] = struc.get_tid() + path[1] = mid + idaapi.op_stroff(ea, op.n, path.cast(), 2, shift) + + idaapi.auto_wait() # let IDA digest + + # IDA 8.4: in some cases op_stroff does not set the right struc path + # instead of '[#struc.field_0]' we end up with '[#struc]' + # thus missing an xref on field_0 for the instruction + # this "fix" should force the xref + if mid not in idautils.DataRefsFrom(ea): + path[0] = mid + idaapi.op_stroff(ea, op.n, path.cast(), 1, shift) # type op + idaapi.add_dref(ea, mid, idaapi.dr_I | idaapi.XREF_USER) # force xref + + utils.g_logger.debug(f"Typing op {ea:#x} {op.n} with stroff {path[0]:#x}:{shift:#x}") + + +# type IDA base with data from given entrypoint +def type_entry(entry: entrypoints.entry_t, ctx: entrypoints.context_t): + if not should_type_entry(entry, ctx): + utils.g_logger.debug(f"Not typing database with {entry.entry_id()} data") + return + + # make sure the associated structure exists in IDA + shift, struc = entry.get_structure() + if struc.ida_tid == idaapi.BADADDR: + utils.g_logger.warning( + f'Structure "{struc.get_name()}" was not generated, preventing from typing {entry.entry_id()}' + ) + return + + struc_tif = struc.find_tinfo() + + utils.g_logger.debug(f"Typing database with {entry.entry_id()} data") + + # type disassembly operands + for ea, regid, offs in entry.get_operands(): + apply_stroff_to_op(ea, regid, struc_tif, shift + offs[0]) + + # multiple fields referenced by one instruction, add xrefs on additional fields + for i in range(1, len(offs)): + udm = idaapi.udm_t() + udm.offset = (shift + offs[i]) * 8 + mid = struc_tif.get_udm_tid(ida_utils.find_udm_wrap(struc_tif, udm)) + idaapi.add_dref(ea, mid, idaapi.dr_I | idaapi.XREF_USER) + utils.g_logger.debug(f"Adding xref for field {struc_tif.get_tid():#x}:{(shift + offs[i]):#x} on {ea:#x}") + + # type containing function + fct_ea = entry.get_function() + if fct_ea != idaapi.BADADDR: + fct = ctx.get_function(fct_ea) + + # type function's arguments + type_function_argument(fct, entry) + + # type function's return + type_function_return(fct, entry) + + +# set type & rename memory allocators if needed +def type_allocator(alloc: allocators.allocator_t): + # give a default name + if not symbols.has_relevant_name(alloc.ea): + idaapi.set_name(alloc.ea, alloc.get_name()) + + # set function type + func_tinfo, func_data = ida_utils.get_or_create_fct_type(alloc.ea) + if func_tinfo.is_ptr(): # avoid function pointers + return + + alloc.make_type(func_data) + + if func_tinfo.create_func(func_data): + idaapi.apply_tinfo(alloc.ea, func_tinfo, idaapi.TINFO_DEFINITE) + + utils.g_logger.info(f"Typing allocator_{alloc.ea:x} ({alloc.get_name()})") + + +# apply __cppobj & VFT flags +def apply_udt_flags(struc: structure_t, tinfo: idaapi.tinfo_t): + taudt = idaapi.TAUDT_CPPOBJ if struc.is_cppobj() else 0 + taudt |= idaapi.TAUDT_VFTABLE if struc.is_vtable() else 0 + if taudt == 0: + return + + # apply flags to tinfo + details = idaapi.udt_type_data_t() + tinfo.get_udt_details(details) + details.taudt_bits |= taudt + tinfo.create_udt(details) + + +# add given field to given IDA structure +def add_field_to_IDA_struc(struc: idaapi.tinfo_t, field: field_t, updated: Dict[int, Tuple[idaapi.tinfo_t, int]]): + bits_offset = field.offset * 8 + bits_size = field.size * 8 + + t_ord = struc.get_ordinal() + if t_ord == 0: # ordinal number was lost + pass + + elif t_ord not in updated: + updated[t_ord] = (struc.copy(), struc.get_size()) # copy or pray + existing.remove_padd_fields(struc) # reset gapX fields as padding + else: + struc, _ = updated[t_ord] # use our updated tinfo, not IDA's + + # search for existing field + udm = idaapi.udm_t() + udm.offset = bits_offset + if ida_utils.find_udm_wrap(struc, udm) == idaapi.BADADDR: + pass + + elif udm.is_gap(): # no STRMEM_SKIP_GAPS on IDA 8 + # we want to add a field beyond the gap, without knowing what's after - abort + # a gap should not be followed by another gap + if bits_offset + bits_size > udm.offset + udm.size: + utils.g_logger.warning( + f"Abort making {field} into {struc.get_type_name()}: bigger than gap[{udm.offset//8:#x}:{udm.size//8:x}]" + ) + return + + udm = idaapi.udm_t() # ignore gap + + # field is within an embedded structure + elif udm.type.is_udt() and (udm.offset + udm.size) >= (bits_offset + bits_size): + field.offset = (bits_offset - udm.offset) // 8 # update field_t directly, it is not re-used after + return add_field_to_IDA_struc(udm.type, field, updated) + + # existing field with different boundaries + elif udm.offset != bits_offset or udm.size != bits_size: + utils.g_logger.warning( + f"Abort making {field} into {struc.get_type_name()}: conflict with {udm.name}[{udm.offset//8:#x}:{udm.size//8:x}]" + ) + return + + # replace field type if ok to do so + ftif = field.get_type() + if udm.type.get_realtype() == idaapi.BT_UNK or ( + existing.should_field_type_be_replaced(udm.type) and not existing.should_field_type_be_replaced(ftif) + ): + udm.type = ftif + + # set field comment if no existing + fcomm = field.get_comment() + if fcomm and len(udm.cmt) == 0: + udm.cmt = fcomm + + # replace field name if needed + fname = field.get_name() + if existing.should_field_name_be_replaced(field.offset, udm.name, fname): + udm.name = fname + + # add field to struc tinfo + udm.offset = bits_offset + udm.size = bits_size + tcode = struc.add_udm(udm, idaapi.ETF_MAY_DESTROY) + if tcode != idaapi.TERR_OK: + utils.g_logger.error( + f'Failed making {udm.name} (off {udm.offset//8:#x}, size {udm.size//8:#x}, type "{udm.type}") to {struc.get_type_name()}: "{idaapi.tinfo_errstr(tcode)}" ({tcode:#x})' + ) + + +# fill IDA structure with given model info +# does not overwrite fields of already existing IDA structure +def fill_IDA_structure(struc: structure_t): + if struc.ida_tid == idaapi.BADADDR: + utils.g_logger.error(f'Could not generate structure "{struc.get_name()}"') + return + + struc_tif = struc.find_tinfo() + + # record of structures to update (current struc and its embedded strucs) + updated: Dict[int, Tuple[idaapi.tinfo_t, int]] = dict() + updated[struc_tif.get_ordinal()] = (struc_tif, struc_tif.get_size()) + existing.remove_padd_fields(struc_tif) + + # set udt attr + apply_udt_flags(struc, struc_tif) + + # add fields + for field in struc.fields.values(): + add_field_to_IDA_struc(struc_tif, field, updated) + + # set structure's comment + scomm = struc.get_comment() + if scomm and struc_tif.get_type_cmt() is None: + tcode = struc_tif.set_type_cmt(scomm) + if tcode != idaapi.TERR_OK: + utils.g_logger.error( + f'Failed to set comment for {struc.get_name()}: "{idaapi.tinfo_errstr(tcode)}" ({tcode:#x})' + ) + + # reset gapX fields on all updated structures + save to IDA + while len(updated): + t_ord, (tinfo, min_size) = updated.popitem() + existing.add_padd_fields(tinfo, min_size) + tinfo.set_numbered_type(None, t_ord, idaapi.NTF_REPLACE) + + +# imports all structures defined into given record into IDA +def import_structures(record: structure_record_t): + # prepare symless structures directory + make_structures_dir() + + # create empty structures + for struc in record.get_structures(include_discarded=False): + make_IDA_structure(struc) + + # fill the structures + for struc in record.get_structures(include_discarded=False): + fill_IDA_structure(struc) + + # type vtables with vtables structures + for vtbl in record.get_structures(cls=vtable_struc_t, include_discarded=False): + tinfo = vtbl.find_tinfo() + if not idaapi.apply_tinfo(vtbl.ea, tinfo, idaapi.TINFO_DEFINITE): + utils.g_logger.warning(f"Could not apply type {tinfo} to vtable 0x{vtbl.ea:x}") + + +# apply structures types to IDA base +def import_context(context: entrypoints.context_t): + entries = context.get_entrypoints() + + # type entries + for entry in entries.get_entries(): + type_entry(entry, context) + + # type allocators + for allocator in context.get_allocators(): + type_allocator(allocator) + +``` + +`symless/generation/structures.py`: + +```py +from collections import defaultdict +from typing import Dict, List, Tuple + +import symless.conflict as conflict +import symless.model.entrypoints as entrypoints +import symless.symbols.rename as rename +import symless.utils.utils as utils +from symless.generation import * + + +# create a structure's field (fixed) from an entrypoint's field (ambiguous) +# size_solver_cb is used to choose prefered field's size +def make_field( + var_field: model.field_t, shift: int, flow: model.entry_t, block: model.block_t, size_solver_cb +) -> "field_t": + var_type = var_field.get_type() + offset = shift + var_field.offset + + # unknown pointer + if isinstance(var_type, model.ftype_ptr_t): + v = var_type.value.get_uval() # value size is assumed to be ptr_size + return ptr_field_t(v, offset, flow, block) + + # structure pointer + if isinstance(var_type, model.ftype_struc_t): + # vtable pointer + if isinstance(var_type.entry, model.vtbl_entry_t): + return vtbl_ptr_field_t(list(var_field.type), offset, flow, block) + + else: + return struc_ptr_field_t(var_type.entry, offset, flow, block) + + # function pointer + if isinstance(var_type, model.ftype_fct_t): + fea = var_type.value.get_uval() + return fct_ptr_field_t(fea, offset, flow, block) + + size = size_solver_cb(var_field) + return field_t(offset, size, flow, block) + + +# fill structures models +def define_structure(struc: structure_t): + visited: Set[Tuple[model.entry_t, model.block_t, int]] = set() + + # get structures fields from associated entries fields + for root, node, shift in struc.node_flow(): + # do not visit the same node twice, with the same shift + path_id = (node.get_owner(), node, shift) + if path_id in visited: + continue + visited.add(path_id) + + # add all fields from node + for vfield in node.get_fields(): + # structure field from entrypoint field + field = make_field(vfield, shift, root, node, conflict.field_size_solver) + struc.set_field(field, conflict.fields_conflicts_solver) + + # search for xrefs on paddings and create dummy fields for it + # this happens when the address of a field is used, without its content beeing accessed + for entry, node, shift in visited: + for _, _, offs in entry.get_operands(): + for off in offs: + eoff = shift + off + if struc.has_field_at(eoff) is None and eoff < struc.get_size(): + struc.set_field(unk_data_field_t(eoff, node), None) + + +# define which structure an entry is associated to +def associate_entry( + entry: entrypoints.entry_t, entries: entrypoints.entry_record_t +) -> Optional[Tuple[int, structure_t]]: + if not entry.has_structure(): + # read entries special case + if isinstance(entry, model.read_entry_t): + # get the read field + src_shift, src_struc = associate_entry(entry.src, entries) + field = src_struc.get_field(entry.src_off + src_shift) + if not isinstance(field, struc_ptr_field_t): + entries.remove_entry(entry) + return None + + eff_shift, eff_struc = associate_entry(field.value, entries) + utils.g_logger.debug( + f"{entry.entry_id()} associated with struc {eff_struc.get_name()} (shift {eff_shift:#x})" + ) + + entry.set_structure(eff_shift, eff_struc) + + else: + # select less derived structure that flew through this ep + candidates: List[Tuple[structure_t, int] | None] = list() + for shift, parent in entry.get_parents(): + pshift, pstruc = associate_entry(parent, entries) + candidates.append((pstruc, shift + pshift)) + + selected = conflict.less_derived(candidates) + entry.set_structure(selected[1], selected[0]) + + return entry.get_structure() + + +# find best structure to associate to each entrypoint +def associate_entries(entries: model.entry_record_t): + for entry in entries.get_entries(): + associate_entry(entry, entries) + + +# compute the owner of each defined vtable +def select_vtables_owners(record: structure_record_t): + owners: Dict[vtable_struc_t, List[Tuple[structure_t, int]]] = defaultdict(list) + + # find all conflicts on owners + for struc in record.get_structures(include_discarded=False): + for field in struc.fields.values(): + if not isinstance(field, vtbl_ptr_field_t): + continue + + _, vtbl = field.get_structure() + owners[vtbl].append((struc, field.offset)) + + # select owner among candidates for each vtable + for vtbl in owners: + owner, shift = conflict.vtable_owner_solver(owners[vtbl]) + vtbl.set_class(owner, shift) + + +# generate structures models from entrypoints +def define_structures(ctx: entrypoints.context_t) -> structure_record_t: + entries = ctx.get_entrypoints() + record = structure_record_t(entries) + + # make strucs models and generate empty structures + for struc in record.get_structures(): + define_structure(struc) + + # define which structure to be associated to each entry + associate_entries(entries) + + # find & merge duplicated structures + conflict.remove_dupes(entries, record) + + # associate vtables to their owners + select_vtables_owners(record) + + # rename structure models using symbols + rename.define_structures_names(record) + + return record + +``` + +`symless/main.py`: + +```py +import time + +import idaapi + +import symless.allocators as allocators +import symless.config as config +import symless.cpustate.arch as arch +import symless.generation.generate as generate +import symless.generation.structures as structures +import symless.model.entrypoints as entrypoints +import symless.model.model as model +import symless.utils.utils as utils + + +def start_analysis(config_path): + # check binary type + if not arch.is_arch_supported(): + utils.g_logger.error("Unsupported arch (%s) or filetype" % arch.get_proc_name()) + return + + # check that the decompiler exists + if not idaapi.init_hexrays_plugin(): + utils.g_logger.error("You do not have the decompiler for this architecture") + return + + # rebase if required + if config.g_settings.rebase_db: + err = idaapi.rebase_program(-idaapi.get_imagebase(), idaapi.MSF_FIXONCE) + if err != idaapi.MOVE_SEGM_OK: + utils.g_logger.error(f"Unable to rebase program: {err}") + + # initial ida autoanalysis + start = time.time() + idaapi.auto_wait() + utils.print_delay("Initial IDA autoanalysis", start, time.time()) + + # retrieve allocators + imports = allocators.get_allocators(config_path) + if imports is None: + utils.g_logger.error("No allocators identified") + imports = list() + + # retrieve first entrypoints + start = time.time() + ctx = entrypoints.retrieve_entrypoints(imports) + utils.print_delay("Initial entrypoints retrieved", start, time.time()) + + # build entrypoints graph + start = time.time() + model.analyze_entrypoints(ctx) + utils.print_delay("Entrypoints graph built", start, time.time()) + + # build structures + start = time.time() + strucs = structures.define_structures(ctx) + utils.print_delay("Structures defined", start, time.time()) + + # import structures in IDA + start = time.time() + generate.import_structures(strucs) + generate.import_context(ctx) + utils.print_delay("IDA database typed", start, time.time()) + + # finalize operations + start = time.time() + idaapi.auto_wait() + utils.print_delay("Final IDA autoanalysis", start, time.time()) + +``` + +`symless/model/__init__.py`: + +```py +from collections import defaultdict, deque +from typing import ( + Any, + Collection, + Dict, + Generator, + Iterator, + List, + Optional, + Set, + Tuple, +) + +import ida_hexrays +import idaapi + +import symless.allocators as allocators +import symless.cpustate.cpustate as cpustate +import symless.generation as generation +import symless.symbols as symbols +import symless.utils.ida_utils as ida_utils +import symless.utils.utils as utils +import symless.utils.vtables as vtables + + +# a field's type & potential value +class ftype_t: + def __init__(self, value: cpustate.absop_t): + self.value = value + + # should we propagate the field value when read + def should_propagate(self) -> bool: + return False + + # get value to use when propagating with cpustate + def get_propagated_value(self) -> cpustate.absop_t: + if self.should_propagate(): + return self.value + return None + + def __eq__(self, other) -> bool: + return isinstance(other, self.__class__) and self.value == other.value + + def __hash__(self) -> int: + return hash((self.__class__, self.value)) + + def __str__(self) -> str: + return f"{self.__class__.__name__}:({self.value})" + + +# structure pointer type +class ftype_struc_t(ftype_t): + def __init__(self, entry: "entry_t"): + super().__init__(cpustate.sid_t(entry.id)) + self.entry = entry # entry this field points to + + +# function pointer type +class ftype_fct_t(ftype_t): + def __init__(self, value: cpustate.mem_t): + super().__init__(value) + + def should_propagate(self) -> bool: + return True + + +# default pointer type +class ftype_ptr_t(ftype_t): + def __init__(self, value: cpustate.mem_t): + super().__init__(value) + + def should_propagate(self) -> bool: + return True + + +# entry point field +class field_t: + def __init__(self, offset: int): + self.offset = offset + self.size: int = 0 # bitfield of possible sizes + self.type: deque[ftype_t] = deque() # list of affected types, in propagation's order + + # add a type to the field's possible types list + def set_type(self, type: ftype_t): + self.type.appendleft(type) # record types in propagation order + + # get last affected type + def get_type(self) -> Optional[ftype_t]: + if len(self.type) == 0: + return None + return self.type[0] + + # add possible size + def set_size(self, size: int): + self.size |= size + + # get all possible field's sizes + def get_size(self) -> Collection[int]: + out = deque() + for i in range(8): + if self.size & (1 << i): + out.append(pow(2, i)) + return out + + def __str__(self) -> str: + return f"field_{self.offset:#x}:{self.size:#x}" + + +# records the data flow of a structure in a basic block +# since our data flow is flattened, loops & conditions are not taken into account +# then a basic block is an execution flow ended by a call or a ret +class block_t: + def __init__(self, owner: "entry_t", id: int = 0): + self.owner = owner + self.fields: Dict[int, field_t] = dict() # fields defined in the block & their types + + # block index in owner's blocks list + self.id = id + + # structure's boundaries, from accessed fields + self.max = 0 + self.min = 0 + + # called ep following this block in the data flow + self.callee: Optional[Tuple[int, "entry_t"]] = None + + # following & preceding block in the entrypoint flow + self.next: Optional[block_t] = None + self.previous: Optional[block_t] = None + + def has_callee(self) -> bool: + return self.callee is not None + + def get_callee(self) -> Optional[Tuple[int, "entry_t"]]: + return self.callee + + # set the called ep following this block & shift applied + def set_callee(self, callee: "entry_t", shift: int): + self.callee = (shift, callee) + + def get_owner(self) -> "entry_t": + return self.owner + + # returns following block in the entrypoint + def get_next(self) -> "block_t": + if self.next is None: + self.next = block_t(self.owner, self.id + 1) + self.next.previous = self + return self.next + + def has_field(self, offset: int) -> bool: + return offset in self.fields + + # get field defined by this block + def get_field(self, offset: int) -> field_t: + return self.fields[offset] + + # get all fields + def get_fields(self) -> Iterator[field_t]: + return self.fields.values() + + # add / get existing field + def add_field(self, offset: int, size: int) -> field_t: + # accept negative offset, a field can be retrieved with a CONTAINING_RECORD() + + if offset not in self.fields: + self.fields[offset] = field_t(offset) + self.fields[offset].set_size(size) + + # change size upper boundary + end = offset + size + if end > self.max: + self.max = end + + # change size lower boundary + if offset < self.min: + self.min = offset + + return self.fields[offset] + + # get the latest type for a field + # scope: current block + following (called) entry + def get_field_type(self, offset: int) -> Optional[ftype_t]: + ftype = None + if self.has_callee(): + shift, callee = self.get_callee() + ftype = callee.get_field_type(offset - shift) + + return self.get_field(offset).get_type() if (ftype is None and self.has_field(offset)) else ftype + + def __eq__(self, other) -> bool: + return isinstance(other, block_t) and other.id == self.id + + def __hash__(self) -> int: + return self.id + + +# data flow entrypoints +# defines a structure's entry into the data flow +# records information defining a structure propagated from the given entrypoint +class entry_t: + # this kind of ep is to be injected before or after state updates + inject_before = False + + # this type of ep can have children + can_ramificate = True + + def __init__(self, ea: int, sub_ea: int = 0): + self.ea = ea # entry address + self.sub_ea = sub_ea # index of the sub minsn the entry is for + self.id = -1 # entry identifier + + # for entrypoints defining a structure (root ep) + self.struc_id = -1 + + # structure associated with this entrypoint + # the structure we will use to type this ep + self.struc: Optional[generation.structure_t] = None + self.struc_shift = 0 + + # data flow injection parameters + self.to_analyze = True # yet to analyze + + # list of operands accessing this ep fields + # a single op might reference multiple fields (offsets), like in STP, STM arm insns + self.operands: Dict[Tuple[int, int], Collection[int]] = defaultdict(list) # (ea, reg_id) -> [offsets] + + # list of the entries that can precede this one in a data flow + self.parents: Collection[Tuple[int, entry_t]] = deque() + + # list of entries we want to analyze following this one + self.children: Collection[Tuple[int, entry_t]] = deque() + + # entrypoint size + self.bounds: Optional[Tuple[int, int]] = None + + self.blocks: Optional[block_t] = None # list of blocks composing this ep + self.cblock: Optional[block_t] = None # current active block + + # does the entry point defines a structure to be generated + def is_root(self) -> bool: + return self.struc_id >= 0 + + def set_root(self, sid: int): + self.struc_id = sid + + def has_structure(self) -> bool: + return self.struc is not None + + def set_structure(self, shift: int, struc: "generation.structure_t"): + self.struc = struc + self.struc_shift = shift + struc.has_xrefs |= self.has_operands() + + # get the structure associated with the entry + def get_structure(self) -> Tuple[int, "generation.structure_t"]: + return (self.struc_shift, self.struc) + + # return the function containing this ep + def get_function(self) -> int: + return idaapi.BADADDR + + # get all the structures that flow through this ep + def get_flow(self) -> Collection[Tuple[int, "generation.structure_t"]]: + flow = set() + if self.is_root(): + flow.add(self.get_structure()) + for shift, parent in self.get_parents(): + flow.update([(shift + s_shift, s) for s_shift, s in parent.get_flow()]) + return flow + + def add_field(self, offset: int, size: int) -> field_t: + return self.cblock.add_field(offset, size) + + # get field at given offset + def get_field(self, offset: int) -> Optional[field_t]: + return self.cblock.get_field(offset) + + # get the latest type for a field + # scope: current entry (previous block & callees), at current state (not done analyzing) + def get_field_type(self, offset: int) -> Optional[ftype_t]: + ftype = None + current = self.cblock + while ftype is None and current is not None: + ftype = current.get_field_type(offset) + current = current.previous + + return ftype + + # get ep boundaries, min & max access on ep + def get_boundaries(self) -> Tuple[int, int]: + if self.bounds is None: + lower, upper = 0, 0 + + # ep own boundaries + current = self.blocks + while current is not None: + lower = min(lower, current.min) + upper = max(upper, current.max) + current = current.next + + # boundaries from ep children + for off, child in self.get_children(True): + ci, ca = child.get_boundaries() + lower = min(lower, ci + off) + upper = max(upper, ca + off) + + self.bounds = (lower, upper) + + return self.bounds + + # associated accessed operand with this ep + def add_operand(self, ea: int, off: int, regid: int): + self.operands[(ea, regid)].append(off) + + def get_operands(self) -> Generator[Tuple[int, int, Collection[int]], None, None]: + for (ea, regid), offs in self.operands.items(): + yield (ea, regid, offs) + + def has_operands(self) -> bool: + return len(self.operands) > 0 + + # does the given node precede this node in the data flow + def has_parent(self, parent: "entry_t") -> bool: + return self == parent or any([p.has_parent(parent) for _, p in self.get_parents()]) + + # add parent with given shift + def add_parent(self, parent: "entry_t", shift: int) -> bool: + if parent.has_parent(self): # loop check + return False + + if (shift, parent) not in self.parents: # duplicate check + self.parents.append((shift, parent)) + return True + + # add an entrypoint following this one in the data flow + def add_child(self, child: "entry_t", shift: int) -> bool: + if not child.add_parent(self, shift): + return False + + if (shift, child) not in self.children: + self.children.append((shift, child)) + return True + + # end the current block, with a call + # the callee represents an ep to be processed after the current block and before the next one + def end_block(self, callee: "entry_t", shift: int) -> bool: + if not callee.add_parent(self, shift): + return False + + self.cblock.set_callee(callee, shift) + self.cblock = self.cblock.get_next() + return True + + # get node's parents + # yields (shift, parent) + def get_parents(self) -> Generator[Tuple[int, "entry_t"], None, None]: + for off, p in self.parents: + yield (off, p) + + # get node's children + # if all is set, returns following children + end blocks callee children + # else only returns following children + def get_children(self, all: bool = False) -> Generator[Tuple[int, "entry_t"], None, None]: + if all: + assert self.blocks is not None + + current = self.blocks + while current.next is not None: + yield current.get_callee() + current = current.next + + for off, c in self.children: + yield (off, c) + + # get distance to given child + # assume self is parent of child + def distance_to(self, child: "entry_t") -> int: + q = deque() + + q.append((child, 0)) + while len(q) > 0: + current, distance = q.popleft() + if current == self: + return distance + + for _, p in current.get_parents(): + q.append((p, distance + 1)) + + raise Exception(f"{self.entry_id()} is not a parent of {child.entry_id()}") + + # inject entrypoint on given state + # return True if the ep had to be analyzed + def inject(self, state: cpustate.state_t, reset: bool = True) -> bool: + if reset: + self.reset() + had_to = self.to_analyze + self.to_analyze = False # is beeing analyzed + return had_to + + # reset non-cumulative states when re-propagating + def reset(self): + # reset blocks + self.blocks = block_t(self) + self.cblock = self.blocks + utils.g_logger.debug(f"Resetting entry {self.entry_id()}") + + # get unique key identifying the ep from others + # to be implemented by heirs + def get_key(self) -> Any: + raise Exception(f"{self.__class__} does not implement method get_key") + + # find name of the structure associated to this entry point + # using symbols information + # returns name, relevance (the least, the more relevant) + def find_name(self) -> Tuple[Optional[str], int]: + return None, 0 + + def entry_header(self) -> str: + return "Entry[sid=%d], ea: 0x%x, [%s]" % ( + self.id, + self.ea, + ("TO_ANALYZE" if self.to_analyze else "ANALYZED"), + ) + + def entry_id(self) -> str: + return f"ep_0x{self.ea:x}" + + def __eq__(self, other) -> bool: + return isinstance(other, entry_t) and other.id == self.id + + def __hash__(self) -> int: + return self.id + + def __str__(self) -> str: + out = "%s\n" % self.entry_header() + out += f"\t| Parents: ({', '.join([str(i.id) for i in self.get_parents()])})\n" + + if len(self.operands) > 0: + out += "\t| Operands:\n" + for ea, regid, offs in self.get_operands(): + for off in offs: + out += f"\t\t{ida_utils.addr_friendly_name(ea)}, ea: 0x{ea:x}, reg {idaapi.get_reg_name(regid,8)}({regid:#x}), off {off:#x}\n" + + if len(self.children) > 0: + out += "\t| Children:\n" + for offset, child in self.children: + out += f"\t\tentry[sid={child.id}], off: 0x{offset:x}, ea: 0x{child.ea:x}\n" + + return out + + +# travel the flows of nodes from given entrypoint +# yields (flow root, node, shift) +def flow_from_root(entry: entry_t, all_roots: bool = True) -> Generator[Tuple[entry_t, block_t, int], None, None]: + roots: Collection[Tuple[int, entry_t]] = deque() + roots.append((0, entry)) + + while len(roots) > 0: + rshift, root = roots.pop() + if all_roots: + roots.extend([(i + rshift, j) for i, j in root.get_children()]) + + blocks: Collection[int, block_t] = deque() + blocks.append((rshift, root.blocks)) + + while len(blocks) > 0: + bshift, node = blocks.pop() + yield root, node, bshift + + # record next block for latter + if node.next is not None: + blocks.append((bshift, node.next)) + + # process blocks from direct function call before + if node.has_callee(): + cshift, callee = node.get_callee() + blocks.append((bshift + cshift, callee.blocks)) + + # childrens are not in this direct flow (ex: virtual method recorded from vtable load) + # process them as differents roots + if all_roots: + roots.extend([(bshift + cshift + i, j) for i, j in callee.get_children()]) + + +# entrypoint as a method's argument +class arg_entry_t(entry_t): + inject_before = True + + def __init__(self, ea: int, index: int): + super().__init__(ea, -1) + self.index = index + + def get_function(self) -> int: + return self.ea + + def find_name(self) -> Tuple[Optional[str], int]: + if self.index != 0: # TODO use fct arguments types to find names of arguments that are not 'this' + return None, 0 + + fct_name = ida_utils.demangle_ea(self.ea) + return symbols.get_classname_from_ctor(fct_name), 1 + + def inject(self, state: cpustate.state_t) -> bool: + had_to = super().inject(state, False) + + vdloc = state.fct.get_argloc(self.index) + state.set_var_from_loc(vdloc, cpustate.sid_t(self.id)) + + return had_to + + def get_key(self) -> int: + return self.index + + def entry_id(self) -> str: + return f"ep_0x{self.ea:x}_arg{self.index}" + + def entry_header(self) -> str: + return "Entry[sid=%d], arg %d of %s (0x%x), [%s]" % ( + self.id, + self.index, + ida_utils.addr_friendly_name(self.ea), + self.ea, + ("TO_ANALYZE" if self.to_analyze else "ANALYZED"), + ) + + +# entry point in a variable (a micro operand) +# for destination operands (inject_before == False) +class dst_var_entry_t(entry_t): + def __init__(self, ea: int, sub_ea: int, fct_ea: int, mop: ida_hexrays.mop_t): + super().__init__(ea, sub_ea) + self.mop = ida_hexrays.mop_t(mop) # copy or it gets freed + assert self.mop.t in (ida_hexrays.mop_r, ida_hexrays.mop_S) + + if self.mop.t == ida_hexrays.mop_r: + self.key = ida_hexrays.get_mreg_name(self.mop.r, ida_utils.get_ptr_size()) + else: + self.key = f"stk:#{self.mop.s.off:x}" + + self.fct_ea = fct_ea + + def get_function(self) -> int: + return self.fct_ea + + def inject(self, state: cpustate.state_t) -> bool: + had_to = super().inject(state) + state.set_var_from_mop(self.mop, cpustate.sid_t(self.id)) + return had_to + + def get_key(self) -> str: + return self.key + + def entry_id(self) -> str: + return f"ep_0x{self.ea:x}_{self.get_key()}" + + def entry_header(self) -> str: + return "Entry[sid=%d], %s at ea: 0x%x(%s), [%s]" % ( + self.id, + self.get_key(), + self.ea, + ida_utils.addr_friendly_name(self.ea), + ("TO_ANALYZE" if self.to_analyze else "ANALYZED"), + ) + + +# entry point in a register +# as a src operand (inject_before == True) +class src_reg_entry_t(dst_var_entry_t): + # inject_before needs to be a static member + # because of its use in get_entry_by_key() + # thus two reg_entry_t classes are required + inject_before = True + + +# entry point as a value read from a structure +# can be used to propagate a structure ptr written & read from a structure +class read_entry_t(dst_var_entry_t): + can_ramificate = False + + def __init__(self, ea: int, sub_ea: int, fct_ea: int, mop: ida_hexrays.mop_t, source: entry_t, off: int): + super().__init__(ea, sub_ea, fct_ea, mop) + + # source ep & offset this ep was read from + self.src = source + self.src_off = off + + def entry_id(self) -> str: + return f"ep_rd_0x{self.ea:x}_{self.get_key()}" + + def add_parent(self, parent: "entry_t", shift: int) -> bool: + raise Exception("read_entry_t are not meant to be linked with parents") + + +# entry point as an allocation with known size +class alloc_entry_t(dst_var_entry_t): + def __init__(self, ea: int, sub_ea: int, size: int, mba: ida_hexrays.mba_t): + super().__init__(ea, sub_ea, mba.entry_ea, ida_hexrays.mop_t(mba.call_result_kreg, ida_utils.get_ptr_size())) + self.size = size + + # retrieve name from factory function + # this is not an accurate name for a structure, and is to be used as a last chance name + def find_name(self) -> Tuple[Optional[str], int]: + fct = idaapi.get_func(self.ea) + if fct is None: + utils.g_logger.error(f"No function for entry {self.entry_id()}, this should not happen") + return None, 0 + + # do not use 'sub_' function names + if not symbols.has_relevant_name(fct.start_ea): + return None, 0 + + fct_name = symbols.method_name_from_signature(ida_utils.demangle_ea(fct.start_ea)) + return f"struc_{fct_name}", 3 + + def add_field(self, offset: int, size: int) -> field_t: + if offset < 0 or offset + size > self.size: + return False + + return super().add_field(offset, size) + + +# constant root entry +# define a known structure we do not need to build on the way +class cst_entry_t(entry_t): + def __init__(self, ea: int): + super().__init__(ea) + + self.to_analyze = False + + # do not record accessed fields + def add_field(self, offset: int, size: int) -> None: + return None + + # a root has no parents + def has_parent(self, parent: entry_t) -> bool: + return False + + def add_parent(self, parent: entry_t, shift: int) -> bool: + return False + + def end_block(self, callee: entry_t, shift: int) -> bool: + return False + + def inject(self, state: cpustate.state_t) -> bool: + raise Exception(f"{self.entry_id()} is not to be injected in the data flow") + + +# vtable root entry +class vtbl_entry_t(cst_entry_t): + def __init__(self, vtbl: vtables.vtable_t): + super().__init__(vtbl.ea) + self.vtbl = vtbl + self.reset() # add default block + + # make fields + ptr_size = ida_utils.get_ptr_size() + for i, (fea, _) in enumerate(vtbl.get_members()): + field = entry_t.add_field(self, i * ptr_size, ptr_size) + field.set_type(ftype_fct_t(cpustate.mem_t(fea, fea, ptr_size))) + + # get most derived between self and other + def get_most_derived(self, other: "vtbl_entry_t") -> "vtbl_entry_t": + if self.vtbl.get_most_derived(other.vtbl) == self.vtbl: + return self + return other + + def get_key(self) -> Any: # ea is enough to discriminate vtables + return None + + def find_name(self) -> Tuple[Optional[str], int]: + return symbols.get_vtable_name_from_ctor(self.ea), 0 + + def entry_id(self) -> str: + return f"ep_0x{self.ea:x}_vtbl" + + def entry_header(self) -> str: + return f"vftable {ida_utils.demangle_ea(self.ea)}" + + +# records all entrypoints +class entry_record_t: + g_next_sid = -1 + + def __init__(self): + self.entries_per_sid: List[entry_t] = list() # entry per sid + + # sorted entries, by ea for quick access + # & by inject_before / inject_after + self.entries_per_ea: dict[Tuple[int, int, bool], Collection[entry_t]] = defaultdict(deque) + + # next entry point id + def next_id(self) -> int: + return len(self.entries_per_sid) + + def structures_count(self) -> int: + return entry_record_t.g_next_sid + 1 + + # add an entrypoint to the graph + def add_entry(self, entry: entry_t, as_root: bool = False, inc_sid: bool = True) -> entry_t: + existing = self.get_entry_by_key(entry.ea, entry.sub_ea, entry.__class__, entry.get_key()) + if existing is not None: + return existing + + key = (entry.ea, entry.sub_ea, entry.__class__.inject_before) + self.entries_per_ea[key].append(entry) + + entry.id = self.next_id() + self.entries_per_sid.append(entry) + + if as_root: + entry_record_t.g_next_sid += int(inc_sid) + entry.set_root(entry_record_t.g_next_sid) + + return entry + + # add an entry to the graph, as a child of another entry + def add_entry_as_child(self, parent: entry_t, child: entry_t, shift: int, block_end: bool) -> Optional[entry_t]: + # check if parent can have children + if not parent.__class__.can_ramificate: + return None + + effective = self.add_entry(child) + + if block_end: + parent.end_block(effective, shift) + else: + parent.add_child(effective, shift) + + return effective + + # remove an entry from the graph, and its successors + def remove_entry(self, entry: entry_t): + # entry should not have any parents + assert len(entry.parents) == 0 + + for shift, child in entry.get_children(True): + child.parents.remove((shift, entry)) + if len(child.parents) == 0: + self.remove_entry(child) + + self.entries_per_ea[(entry.ea, entry.sub_ea, entry.__class__.inject_before)].remove(entry) + self.entries_per_sid[entry.id] = None + + # all entries to inject at given ea + def get_entries_at(self, ea: int, sub_ea: int, inject_before: bool) -> Collection[entry_t]: + return self.entries_per_ea.get((ea, sub_ea, inject_before), tuple()) + + # entry by sid + def get_entry_by_id(self, sid: int) -> Optional[entry_t]: + if sid < 0 or sid >= len(self.entries_per_sid): + return None + return self.entries_per_sid[sid] + + # entry by ea, class & unique key identifier + def get_entry_by_key(self, ea: int, sub_ea: int, cls: type, key: Any = None) -> Optional[entry_t]: + eakey = (ea, sub_ea, cls.inject_before) + if eakey not in self.entries_per_ea: + return None + + c = filter( + lambda e: isinstance(e, cls) and e.get_key() == key, + self.entries_per_ea[eakey], + ) + + try: + return next(c) + except StopIteration: + return None + + def get_entries(self, analyzed: bool = True) -> Generator[entry_t, None, None]: + for entry in self.entries_per_sid: + if entry is not None and not (analyzed and entry.to_analyze): + yield entry + + # yield all unexplored entrypoints + # TODO: yield from most interesting function to less (fct having the most entrypoints) + def next_to_analyze(self) -> Generator[entry_t, None, None]: + current_len = len(self.entries_per_sid) + for i in range(current_len): + if self.entries_per_sid[i].to_analyze: + yield self.entries_per_sid[i] + + def __str__(self) -> str: + out = "" + for entry in self.entries_per_sid: + out += f"{str(entry)}\n" + return out + + +# information about a function's prototype +class prototype_t: + def __init__(self, ea: int): + self.ea = ea + + # structure returned by function + self.ret: Optional[entry_t] = None + + # is a virtual method + self.virtual = False + + def set_ret(self, ret: entry_t): + self.ret = ret + + def get_ret(self) -> Optional[entry_t]: + return self.ret + + def set_virtual(self): + self.virtual = True + + def is_virtual(self) -> bool: + return self.virtual + + def __eq__(self, other: object) -> bool: + return isinstance(other, prototype_t) and self.ea == other.ea + + +# global model +# groups information gathered by the analysis +class context_t: + # init from a list of entrypoints and propagation context + def __init__(self, entries: entry_record_t, allocators: Set[allocators.allocator_t]): + self.allocators = allocators # all registered allocators + self.functions: Dict[int, prototype_t] = dict() # ea -> prototype_t + self.graph = entries # entrypoints tree hierarchy + + # information gathered by data flow + # is to be deleted once propagation is done + self.dflow_info: Optional[cpustate.dflow_ctrl_t] + + # propagation context depth + self.follow_calls = True + + # dive into callee decision + self.dive_in: bool = False + + def get_function(self, ea: int) -> prototype_t: + if ea not in self.functions: + self.functions[ea] = prototype_t(ea) + return self.functions[ea] + + def get_functions(self) -> Collection[prototype_t]: + return self.functions.values() + + def get_entrypoints(self) -> entry_record_t: + return self.graph + + def get_allocators(self) -> Set[allocators.allocator_t]: + return self.allocators + + def can_follow_calls(self) -> bool: + return self.follow_calls + + def set_follow_calls(self, follow: bool): + self.follow_calls = follow + +``` + +`symless/model/entrypoints.py`: + +```py +import enum +from collections import defaultdict, deque +from typing import Collection, Dict, Iterable, Set, Tuple, Union + +import ida_hexrays +import idaapi + +import symless.allocators as allocators +import symless.cpustate.cpustate as cpustate +import symless.utils.ida_utils as ida_utils +import symless.utils.vtables as vtables +from symless.model import * + +""" Entry points from memory allocations """ + + +# Type of a memory allocation +class allocation_type(enum.Enum): + WRAPPED_ALLOCATION = 0 # allocator is just a wrap calling another allocator + STATIC_SIZE = 1 # static size allocation + UNKNOWN = 2 # any other case we do not handle + + +# Analyze a given call to a memory allocator +# defines if the caller is an allocator wrapper, or if the call is a static allocation (known size) +def analyze_allocation( + caller: idaapi.func_t, allocator: allocators.allocator_t, call_ea: int +) -> Tuple[allocation_type, Union[allocators.allocator_t, alloc_entry_t, None]]: + action, wrapper_args = None, None + + params = cpustate.dflow_ctrl_t(depth=0) + for ea, sub_ea, state in cpustate.generate_state(caller, params): + if ea == call_ea and state.has_call_info() and action is None: + action, wrapper_args = allocator.on_call(state) + + # caller calls allocator, with size argument passed through + if action == allocators.alloc_action_t.WRAPPED_ALLOCATOR: + pass + + # known size allocation + elif action == allocators.alloc_action_t.STATIC_ALLOCATION: + return (allocation_type.STATIC_SIZE, alloc_entry_t(ea, sub_ea, wrapper_args, state.mba)) + + # unknown size allocation + elif action == allocators.alloc_action_t.UNDEFINED: + return (allocation_type.UNKNOWN, None) + + # allocator wrapper returns the allocation + elif action and state.has_ret_info() and allocator.on_wrapper_ret(state, call_ea): + return (allocation_type.WRAPPED_ALLOCATION, allocator.get_child(caller.start_ea, wrapper_args)) + + return (allocation_type.UNKNOWN, None) + + +# Analyze all calls to a memory allocator and its wrappers +# returns a set of entrypoints (static allocations) made with this allocator +def analyze_allocator_heirs( + allocator: allocators.allocator_t, + allocs: Set[allocators.allocator_t], + entries: entry_record_t, +): + if allocator in allocs: # avoid infinite recursion if crossed xrefs + return + allocs.add(allocator) + + # for all xrefs to allocator + for allocation_ea in ida_utils.get_all_references(allocator.ea): + # function referencing the allocator + caller = idaapi.get_func(allocation_ea) + if caller is None: + continue + + # instruction referencing the allocator + call_insn = ida_utils.get_ins_microcode(allocation_ea) + if call_insn is None: + continue + + utils.g_logger.debug(f"Analyzing xref {allocation_ea:#x}: {call_insn.dstr()} to allocator {allocator}") + + # verify this is a call / jmp instruction + if call_insn.opcode not in (ida_hexrays.m_call, ida_hexrays.m_icall, ida_hexrays.m_goto, ida_hexrays.m_ijmp): + continue + + type, alloc = analyze_allocation(caller, allocator, allocation_ea) + + if type == allocation_type.WRAPPED_ALLOCATION: + utils.g_logger.debug(f"{allocation_ea:#x} is a wrap around {allocator}") + analyze_allocator_heirs(alloc, allocs, entries) + + elif type == allocation_type.STATIC_SIZE: + utils.g_logger.debug(f"{allocation_ea:#x} is a static allocation of {alloc.size:#x}") + entries.add_entry(alloc, True) + + +# get all entrypoints from defined allocators +def get_allocations_entrypoints( + imports: Iterable[allocators.allocator_t], entries: entry_record_t +) -> Set[allocators.allocator_t]: + allocators = set() + + for i in imports: + analyze_allocator_heirs(i, allocators, entries) + + return allocators + + +""" Entry points from ctors & dtors """ + + +# a constructor / destructor and the vtables it loads into the 'this' object +class ctor_t: + def __init__(self, func: idaapi.func_t): + self.func = func + + # vtables loaded into the 'this' object by this ctor + self.vtables: Dict[vtables.vtable_t, Optional[int]] = dict() # vtbl_ea -> load_offset + + # get what we think is the right vtable for the class associated with this ctor + def get_associated_vtable(self) -> Tuple[vtables.vtable_t, int]: + candidates = [(vtbl, off) for (vtbl, off) in self.vtables.items() if off is not None] + candidates.sort(key=lambda k: k[1], reverse=True) + + vtbl, off = candidates.pop() # at least one candidate should be present + while len(candidates) > 0: + vtbl2, off2 = candidates.pop() + if off2 != off: + break + vtbl = vtbl.get_most_derived(vtbl2) # conflict, try to find the inheriting vtable + + return (vtbl, off) + + +# analyse given ctor, returns True if it really is a ctor +def analyze_ctor(ctor: ctor_t) -> bool: + ptr_size = ida_utils.get_ptr_size() + yet_to_see = set(ctor.vtables.keys()) + + mba = ida_utils.get_func_microcode(ctor.func) + if mba is None: + return False + + params = cpustate.dflow_ctrl_t(depth=0) + state = cpustate.state_t(mba, params.get_function_for_mba(mba)) + + if state.fct.get_args_count() == 0: # function does not take a 'this' argument + return False + state.set_var_from_loc(state.fct.get_argloc(0), cpustate.sid_t(0)) + + ret = False + for _, _, state in cpustate.function_data_flow(state, params): + if len(yet_to_see) == 0: # nothing more to see + return ret + + for write in state.writes: + if write.size != ptr_size or not isinstance(write.value, cpustate.mem_t): + continue + + val = write.value.get_uval() + if val not in yet_to_see: # written value is one of our vtables ea + continue + yet_to_see.remove(val) + + # value is written in our 'this' object + if not isinstance(write.target, cpustate.sid_t): + continue + + # update shift for vtable + utils.g_logger.debug(f"Load for vtbl {val:#x} into this:{write.target.shift:x}") + ctor.vtables[val] = write.target.shift + ret = True + + return ret + + +# get ctors & dtors families +def get_ctors() -> Dict[int, Collection[ctor_t]]: + all_ctors: Dict[int, ctor_t] = dict() + ctors_for_family: Dict[int, Collection[ctor_t]] = defaultdict(deque) + + # make record of candidates ctors & the vtables they load + for vtbl in vtables.get_all_vtables(): + for xref in vtbl.get_loads(): + fct = idaapi.get_func(xref) # can not return None + if fct.start_ea not in all_ctors: + all_ctors[fct.start_ea] = ctor_t(fct) + all_ctors[fct.start_ea].vtables[vtbl] = None + + # analyze all ctors, find the base vtable for their class + for ctor in all_ctors.values(): + utils.g_logger.debug( + f"Analyzing fct {ctor.func.start_ea:#x} for {len(ctor.vtables.keys())} vtables loads into 'this'" + ) + if not analyze_ctor(ctor): + continue + + vtbl, offset = ctor.get_associated_vtable() + utils.g_logger.info(f"Found one ctor/dtor @ {ctor.func.start_ea:#x} for vtbl {vtbl.ea:#x} (off {offset:#x})") + + if offset != 0: # we are only interested in vtables loaded at off:0 + continue + + ctors_for_family[vtbl.ea].append(ctor) + + return ctors_for_family + + +# get all entrypoints from identified ctors / dtors +def get_ctors_entrypoints(entries: entry_record_t): + for fam in get_ctors().values(): + for i, ctor in enumerate(fam): + entries.add_entry(arg_entry_t(ctor.func.start_ea, 0), True, i == 0) + + +# find root entrypoints, from classes & allocators found in the base +def retrieve_entrypoints(imports: Iterable[allocators.allocator_t]) -> context_t: + entries = entry_record_t() + + allocators = get_allocations_entrypoints(imports, entries) + + get_ctors_entrypoints(entries) + + return context_t(entries, allocators) + +``` + +`symless/model/model.py`: + +```py +import idaapi + +import symless.cpustate.cpustate as cpustate +import symless.utils.ida_utils as ida_utils +import symless.utils.vtables as vtables +from symless.model import * +from symless.utils.utils import g_logger as logger + +""" Propagation actions handlers """ + + +# handle function ret, record ret type for function typing +def handle_ret(state: cpustate.state_t, ctx: context_t): + if state.ret is None: + return + + # only record struct pointer returned without shift + if not isinstance(state.ret, cpustate.sid_t) or state.ret.shift != 0: + return + + prot = ctx.get_function(state.get_fea()) + prot.set_ret(ctx.graph.get_entry_by_id(state.ret.sid)) + + +# Build model members from state access +def handle_access(ea: int, sub_ea: int, state: cpustate.state_t, ctx: context_t): + for access in state.accesses: + struc = access.target + + # a structure is accessed + if not isinstance(struc, cpustate.sid_t): + continue + + offset = access.target.shift + size = access.size + + # record field beeing accessed + # do not make field for access of unknown (0) size + entry = ctx.graph.get_entry_by_id(struc.sid) + if size != 0: + f = entry.add_field(offset, size) + logger.debug(f"{ea:#x}.{sub_ea:x}: adding {f} to {entry.entry_id()}") + + # record operand for the access, to type later + # we only care about regs operands + if access.loc.t == ida_hexrays.mop_r: + regid = ida_hexrays.mreg2reg(access.loc.r, access.loc.size) + if regid == -1: # special kreg, nothing to type + return + + logger.debug(f"{ea:#x}.{sub_ea:x}: add operand to {entry.entry_id()} at offset 0x{offset:x}") + entry.add_operand(ea, offset, regid) + + +# add arg_0 entry for each virtual method +# assuming every virtual method takes 'this' as first argument +# we are aware that in some cases this is not right ('this' unused by virtual method + optimization) +def analyze_virtual_methods(vtbl: vtables.vtable_t, current: entry_t, offset: int, ctx: context_t): + if not ctx.can_follow_calls(): + return + + for fea, _ in vtbl.get_members(): + fct = idaapi.get_func(fea) + if fct is None: # no body for virtual method, it may be an import + continue + + model = ctx.dflow_info.get_function(fct) + + # virtual method should have at least one argument (this) + # if not we have no interest in analysing it + if model is None or model.get_args_count() == 0: + continue + + # add entry to analyse + child = ctx.graph.add_entry_as_child(current, arg_entry_t(fea, 0), offset, False) + if child is not None: + logger.debug(f"Add virtual method 0x{fea:x}, {child.entry_id()}, as child of {current.entry_id()}") + + # mark function as virtual + prot = ctx.get_function(fea) + prot.set_virtual() + + +# Handle writes to struc members +def handle_write(ea: int, sub_ea: int, state: cpustate.state_t, ctx: context_t): + ptr_size = ida_utils.get_ptr_size() + + for write in state.writes: + struc = write.target + size = write.size + + # mov [sid + offset], mem -> ptr loaded + if not (isinstance(struc, cpustate.sid_t) and isinstance(write.value, cpustate.mem_t) and size == ptr_size): + continue + + offset = struc.shift + + value = write.value.get_uval() + entry = ctx.graph.get_entry_by_id(struc.sid) + + # check if addr is a vtable + vtbl = vtables.vtable_t(value) + + # value is not a vtable address + if not vtbl.valid(): + type = ftype_ptr_t(write.value) + logger.debug(f'{ea:#x}.{sub_ea:x}: add type "{type}" to field 0x{offset:x} of {entry.entry_id()}') + + else: + # record vtable loading sites + # used later in conflicts to differentiate a base vtable from an inherinting one + vtbl.search_loads() + + # get / create vtable entry point + vtbl_entry = ctx.graph.add_entry(vtbl_entry_t(vtbl), True) + type = ftype_struc_t(vtbl_entry) + + logger.debug( + f"{ea:#x}.{sub_ea:x} associate {vtbl_entry.entry_id()} to field 0x{offset:x} of {entry.entry_id()}" + ) + + # add entrypoints to analyze virtual methods + analyze_virtual_methods(vtbl, entry, offset, ctx) + + # type structure field with retrieved type + entry.get_field(offset).set_type(type) + + +# Handle read of struct members +def handle_read(ea: int, sub_ea: int, state: cpustate.state_t, ctx: context_t): + ptr_size = ida_utils.get_ptr_size() + + for read in state.reads: + struc = read.target + size = read.size + + # mov reg, [sid + offset] + if not isinstance(struc, cpustate.sid_t): + continue + + offset = struc.shift + + entry = ctx.graph.get_entry_by_id(struc.sid) + + # do not expand entries graph too much from read_entry_t leaves + if isinstance(entry, read_entry_t): + logger.debug(f"Ignoring read from {entry.entry_id()}") + continue + + # no fixed value, propagate read entrypoint + rtype = entry.get_field_type(offset) + if rtype is None and size == ptr_size: + r_entry = ctx.graph.add_entry(read_entry_t(ea, sub_ea, state.get_fea(), read.dst, entry, offset)) + logger.debug(f"{ea:#x}.{sub_ea:x}: type not known, propagating {r_entry.entry_id()}") + + elif rtype is None: + pass + + # a struc ptr is read + elif isinstance(rtype, ftype_struc_t): + r_entry = ctx.graph.add_entry_as_child( + rtype.entry, dst_var_entry_t(ea, sub_ea, state.get_fea(), read.dst), 0, False + ) + if r_entry is not None: + logger.debug(f"{ea:#x}.{sub_ea:x} from {rtype.entry.entry_id()}, propagating {r_entry.entry_id()}") + + # propagate any field + else: + state.set_var_from_mop(read.dst, rtype.get_propagated_value()) + logger.debug(f"{ea:#x}.{sub_ea:x}: propagating read type {rtype}") + + +# handle call, add entrypoints in callee +def handle_call(ea: int, sub_ea: int, state: cpustate.state_t, ctx: context_t): + if not ctx.can_follow_calls(): + return + + if state.call_to is not None: + ctx.dive_in = False # default: do not dive in every callee + call_ea = state.call_to.start_ea + + callee_model = ctx.dflow_info.get_function(state.call_to) + if callee_model is None: + return # function mba could not be generated, analysis is not possible + + # callsite nargs can differ from callee nargs + callee_nargs = min(len(state.call_args), callee_model.get_args_count()) + + # look for entries to be propagated as callee's arguments + epc = 0 + for i in range(callee_nargs): + arg = state.call_args[i] + + if not isinstance(arg, cpustate.sid_t): + continue + + entry = ctx.graph.get_entry_by_id(arg.sid) # current entry as caller-to-callee arg + + # create new arg entry point + # one entry point is restricted to be propagated in only one function + epc += int(ctx.graph.add_entry_as_child(entry, arg_entry_t(call_ea, i), arg.shift, True) is not None) + + logger.debug(f"{ea:#x}.{sub_ea:x}, {epc} entrypoints recorded") + + +# handle new cpu state +def handle_state(ea: int, sub_ea: int, state: cpustate.state_t, ctx: context_t): + handle_access(ea, sub_ea, state, ctx) + handle_write(ea, sub_ea, state, ctx) + handle_read(ea, sub_ea, state, ctx) + handle_call(ea, sub_ea, state, ctx) + handle_ret(state, ctx) + + +""" Entrypoints analysis & entries graph building """ + + +# diving decision callback - dive if we have sid to propagate +def dive_in(callee: cpustate.function_t, state: cpustate.state_t, ctx: context_t) -> bool: + dive = ctx.dive_in # get context dive_in decision + + # root function, propagate + if ctx.dflow_info.depth == ctx.dflow_info.max_depth: + dive = True + + if dive: + # arguments entries are to be built (again ?), reset their states + for ep in ctx.graph.get_entries_at(callee.ea, -1, arg_entry_t.inject_before): + ep.reset() + utils.g_logger.debug("Diving into fct 0x%x: %s" % (callee.ea, "YES" if dive else "NO")) + return dive + + +# injector callback, inject entrypoints into cpustate +def model_injector(state: cpustate.state_t, ea: int, sub_ea: int, before_update: bool, ctx: context_t): + for ep in ctx.graph.get_entries_at(ea, sub_ea, before_update): + utils.g_logger.debug(f"Injecting {ep.entry_id()} at {ea:#x}.{sub_ea:x}") + ctx.dive_in |= ep.inject(state) # dive in callee if new eps are to be analyzed + + +# entrypoints graph builder +# from original entrypoints, builds a propagation graph +# that can later be used to build structures +def analyze_entrypoints(ctx: context_t): + entries = ctx.get_entrypoints() + + # injector callback + def inject_cb(state: cpustate.state_t, ea: int, sub_ea: int, before_update: bool): + model_injector(state, ea, sub_ea, before_update, ctx) + + inject = cpustate.injector_t(inject_cb, 3) + + # follow callees, use dive_in() decisions + if ctx.can_follow_calls(): + ctx.dflow_info = cpustate.dflow_ctrl_t(inject, lambda callee, state: dive_in(callee, state, ctx)) + + # only propagate in root function + else: + ctx.dflow_info = cpustate.dflow_ctrl_t(inject, lambda callee, state: dive_in(callee, state, ctx), depth=0) + + # analyse entrypoints by waves + current_count = 1 + current_wave = 0 + while current_count > 0: + current_count = 0 + for entry in entries.next_to_analyze(): + current_count += 1 + + logger.debug(f"Analyzing entry {entry.entry_header()} ..") + + func = idaapi.get_func(entry.ea) + for ea, sub_ea, state in cpustate.generate_state(func, ctx.dflow_info): + handle_state(ea, sub_ea, state, ctx) + + # entrypoint was not injected + # this happens when the user selects an entrypoint that gets deleted from the mba after call analysis + # solution for now: let the user select another entrypoint, or update the mba + if entry.to_analyze: + logger.error( + f"Entry {entry.entry_header()} was not injected because it is unvalid - Redo the analysis." + ) + current_count = 0 + break + + logger.debug(f"Entrypoints wave {current_wave} has been analyzed (total: {current_count})") + current_wave += 1 + + # remove propagation data from model + del ctx.dflow_info + +``` + +`symless/plugins/__init__.py`: + +```py +# a plugin extension to be incorporated into symless plugin +class plugin_t: + # load the plugin + def __init__(self): + pass + + # the extension has been reloaded + def reload(self): + pass + + # terminate & clean the extension + def term(self): + pass + + +# entry function to be defined by every additional plugin +def get_plugin() -> plugin_t: + return None + +``` + +`symless/plugins/builder.py`: + +```py +import os +from collections import deque +from dataclasses import dataclass +from typing import Collection, Optional, Tuple + +import ida_hexrays +import idaapi +from PyQt5 import QtCore, QtGui, QtWidgets + +import symless +import symless.cpustate.arch as arch +import symless.generation.generate as generate +import symless.generation.structures as structures +import symless.model.model as model +import symless.utils.ida_utils as ida_utils +import symless.utils.utils as utils +from symless.plugins import * + +# builder window title +WINDOW_TITLE = "Symless structure builder" + +# fictive color_t used for tagging elements in our microcode view +COLOR_TARGET = idaapi.COLOR_OPND1 +SCOLOR_TARGET = chr(COLOR_TARGET) + + +# Structure builder plugin extension +class BuilderPlugin(plugin_t): + def __init__(self): + self.uihook = PopUpHook() + self.uihook.hook() + + def reload(self): + self.uihook.init_action() + + def term(self): + self.uihook.unhook() + if self.uihook.loaded: + self.uihook.term() + + +# retrieve the extension +def get_plugin() -> plugin_t: + return BuilderPlugin() + + +# a selected micro-operand for data flow entrypoint +@dataclass +class mop_sel_t: + ea: int # insn ea + sub_idx: int # sub insn idx + mop: ida_hexrays.mop_t # selected mop + as_dst: bool # is dst operand + + def __str__(self) -> str: + if self.mop.t == ida_hexrays.mop_r: + name = ida_hexrays.get_mreg_name(self.mop.r, self.mop.size) + elif self.mop.t == ida_hexrays.mop_S: + name = f"stk:#{self.mop.s.off:x}" + return f"{name} @ {self.ea:#x}.{self.sub_idx:x} ({'DST' if self.as_dst else 'SRC'})" + + +# context for parsing microinstruction and extracting operands +class minsn_parse_ctx_t: + def __init__(self, mba: ida_hexrays.mba_t, ea: int): + self.mba = mba + self.ea = ea + self.sub_idx = 0 + self.targets: Collection[mop_sel_t] = deque() + + def add_mop(self, mop: ida_hexrays.mop_t, as_dst: bool): + self.targets.append(mop_sel_t(self.ea, self.sub_idx, ida_hexrays.mop_t(mop), as_dst)) + + def next_subinsn(self): + self.sub_idx += 1 + + +# get simplified name for global at ea +# to be displayed in microcode view +def get_simplified_gbl_name(ea: int) -> str: + d = ida_utils.demangle_ea(ea) + if not len(d): + return f"off_{ea:#x}" + if "(" in d: + return d.split("(")[0] + return d + + +# parse a microcode operand +# returns its str representation & update the contained micro-operands in the context +# as_dst: operand value gets updated by the instruction +def parse_mop(ctx: minsn_parse_ctx_t, op: ida_hexrays.mop_t, as_dst: bool = False) -> Optional[str]: # noqa: C901 + if op.t == ida_hexrays.mop_z: # none + return None + + if op.t == ida_hexrays.mop_r: # micro register + ctx.add_mop(op, as_dst) # add as target variable + return idaapi.COLSTR(idaapi.COLSTR(ida_hexrays.get_mreg_name(op.r, op.size), SCOLOR_TARGET), idaapi.SCOLOR_REG) + + if op.t == ida_hexrays.mop_n: # immediate (number) + return f"#{idaapi.COLSTR(hex(op.signed_value()), idaapi.SCOLOR_NUMBER)}" + + if op.t == ida_hexrays.mop_str: # immediate (string) + return f'"{idaapi.COLSTR(op.cstr, idaapi.SCOLOR_STRING)}"' + + if op.t == ida_hexrays.mop_d: # result of another instruction + in_repr = parse_minsn(ctx, op.d, True) + ctx.next_subinsn() + return in_repr + + if op.t == ida_hexrays.mop_S: # local stack variable + member = op.s.get_stkvar(None) + if member in (None, -1): # happens + return idaapi.COLSTR(f"stk:#{op.s.off:x}", idaapi.SCOLOR_LOCNAME) + ctx.add_mop(op, as_dst) + + if isinstance(member, int): # IDA 9 API changed get_stkvar() prototype + m = idaapi.udm_t() + op.s.get_stkvar(m) + varname = m.name + + # IDA 8 case + else: + varname = idaapi.get_member_name(member.id) + + return idaapi.COLSTR(idaapi.COLSTR(varname, SCOLOR_TARGET), idaapi.SCOLOR_LOCNAME) + + if op.t == ida_hexrays.mop_v: # global variable + color = idaapi.SCOLOR_DNAME + if idaapi.get_func(op.g) is not None: + color = idaapi.SCOLOR_CNAME + return idaapi.COLSTR(get_simplified_gbl_name(op.g), color) + + if op.t == ida_hexrays.mop_b: # micro basic block + b = ctx.mba.get_mblock(op.b) + assert b.serial == op.b + return f"{b.start:#x}" # type == BLT_STOP -> ret + + if op.t == ida_hexrays.mop_f: # args list + return f"({', '.join([parse_mop(ctx, i) for i in op.f.args])})" + + if op.t == ida_hexrays.mop_l: # local variable + return idaapi.COLSTR("?", idaapi.SCOLOR_LOCNAME) # should only exist at MMAT_LVARS maturity + + if op.t == ida_hexrays.mop_a: # address of operand + return f"&({parse_mop(ctx, op.a)})" + + if op.t == ida_hexrays.mop_h: # helper function + return idaapi.COLSTR(op.helper, idaapi.SCOLOR_MACRO) + + if op.t == ida_hexrays.mop_c: # mcases + return idaapi.tag_remove(op.c._print()) # TODO test me + + if op.t == ida_hexrays.mop_fn: # floating constant + return idaapi.COLSTR(idaapi.tag_remove(op.fpc._print()), idaapi.SCOLOR_NUMBER) # TODO test me + + if op.t == ida_hexrays.mop_p: # operands pair + return f"({parse_mop(ctx, op.pair.lop, as_dst)}, {parse_mop(ctx, op.pair.hop, as_dst)})" + + if op.t == ida_hexrays.mop_sc: # scattered + return op.scif.name # TODO test me + + return "?" + + +# parse microcode instruction +# returns a str representation of the instruction + the variable it contains +# provides a simpler representation than the one given by insn._print() +def parse_minsn(ctx: minsn_parse_ctx_t, insn: ida_hexrays.minsn_t, inlined: bool = False) -> str: + ops_repr = filter( + lambda k: k is not None, + [parse_mop(ctx, i, j) for (i, j) in ((insn.l, False), (insn.r, False), (insn.d, insn.modifies_d()))], + ) + + repr_ = None # out string representation + par_ = ("(", ")") if inlined else ("", "") + padd = 0 if inlined else 9 + + # call special format + if insn.opcode == ida_hexrays.m_call: + repr_ = f"{idaapi.COLSTR(ida_utils.g_mcode_name[insn.opcode], idaapi.SCOLOR_INSN):{' '}<{padd}} {next(ops_repr)}{next(ops_repr, '()')}" + + # special "ret" goto + elif ( + insn.opcode == ida_hexrays.m_goto + and insn.l.t == ida_hexrays.mop_b + and ctx.mba.get_mblock(insn.l.b).type == ida_hexrays.BLT_STOP + ): + repr_ = idaapi.COLSTR("ret", idaapi.SCOLOR_INSN) + + # special embedded operations + elif inlined and insn.d.t == ida_hexrays.mop_z: + if insn.opcode == ida_hexrays.m_add: + repr_ = f"{next(ops_repr)}+{next(ops_repr)}" + elif insn.opcode == ida_hexrays.m_sub: + repr_ = f"{next(ops_repr)}-{next(ops_repr)}" + elif insn.opcode == ida_hexrays.m_mul: + repr_ = f"{next(ops_repr)}*{next(ops_repr)}" + elif insn.opcode == ida_hexrays.m_shl: + repr_ = f"{next(ops_repr)}<<{next(ops_repr)}" + elif insn.opcode == ida_hexrays.m_shr: + repr_ = f"{next(ops_repr)}>>{next(ops_repr)}" + elif insn.opcode == ida_hexrays.m_or: + repr_ = f"{next(ops_repr)}|{next(ops_repr)}" + elif insn.opcode == ida_hexrays.m_and: + repr_ = f"{next(ops_repr)}&{next(ops_repr)}" + elif insn.opcode == ida_hexrays.m_xor: + repr_ = f"{next(ops_repr)}^{next(ops_repr)}" + elif insn.opcode == ida_hexrays.m_ldx: + repr_ = f"{next(ops_repr)}:{next(ops_repr)}" + par_ = ("[", "]") + + # default repr + if repr_ is None: + repr_ = f"{idaapi.COLSTR(ida_utils.g_mcode_name[insn.opcode], idaapi.SCOLOR_INSN):{' '}<{padd}} {', '.join(ops_repr)}" + + # return insn representation within appropriate parentheses + return f"{par_[0]}{repr_}{par_[1]}" + + +def find_in_line_wrapper( + range: idaapi.tagged_line_section_t, line: idaapi.tagged_line_sections_t, tag: int +) -> idaapi.tagged_line_section_t: + if hasattr(line, "find_in"): + return line.find_in(range, tag) # IDA 8 + return line.nearest_after(range, range.start, tag) # IDA 9 + + +# view of the current function (simplified) microcode +# for the user to select the propagation entry variable +class MicrocodeViewer(idaapi.simplecustviewer_t): + def __init__(self, mba: ida_hexrays.mba_t, current_ea: int, hint: Tuple[int, int]): + super().__init__() + self.mba = mba + guess_mreg, guess_size = hint # guess for target variable + + # chosen target (insn ea, operand, is a dst operand ?) + self.chosen: Optional[mop_sel_t] = None + + # list of valid target operands for each line + self.ops_per_line: Collection[Optional[Collection[mop_sel_t]]] = list() + + self.Create("Symless microcode view") + + # fill view with microinstructions + block = self.mba.blocks + _jump = True + while block: + insn = block.head + while insn: + # print(idaapi.tag_remove(insn._print())) + + ctx = minsn_parse_ctx_t(self.mba, insn.ea) + insn_repr = parse_minsn(ctx, insn) + self.ops_per_line.append(ctx.targets) + + # set line in listing + if _jump and insn.ea >= current_ea: + self.AddLine(f"{idaapi.COLSTR(hex(insn.ea), idaapi.SCOLOR_INSN)} {insn_repr}") + self.Jump(self.Count() - 1) + _jump = False + else: + self.AddLine(f"{idaapi.COLSTR(hex(insn.ea), idaapi.SCOLOR_PREFIX)} {insn_repr}") + + # find target mvar from hint + # if multiple mvar match hint, the last one is selected + if insn.ea == current_ea and self.chosen is None: + for i, mvar in enumerate(ctx.targets): + if mvar.mop.t == ida_hexrays.mop_r and mvar.mop.r == guess_mreg and mvar.mop.size == guess_size: + self.set_chosen(self.Count() - 1, i) + break + + insn = insn.next + block = block.nextb + if block: # basic block boundaries + self.ops_per_line.append(None) # account for empty lines + self.AddLine("") + + # remove IDA status bar + qwidget = idaapi.PluginForm.TWidgetToPyQtWidget(self.GetWidget()) + for child in qwidget.children(): + if isinstance(child, QtWidgets.QStatusBar): + child.setMaximumHeight(0) + + qwidget.setMinimumWidth(512) + + # get index of given section in given line + # use improper method because tagged_line_sections_t are not iterable in IDA python + def index_of_sect_in_line(self, section: idaapi.tagged_line_section_t, line: idaapi.tagged_line_sections_t) -> int: + range = idaapi.tagged_line_section_t() + range.start = 0 + range.length = 0xFFFF + + # loop over the tagged sections of the line + i = 0 + current = find_in_line_wrapper(range, line, COLOR_TARGET) + while current and current.valid(): + if section.start == current.start and section.length == current.length: + break + i += 1 + range.start = current.start + current.length + current = find_in_line_wrapper(range, line, COLOR_TARGET) + return i + + # resfresh the view & try to disable (again) the default highlighting + def OnCursorPosChanged(self): + self.Refresh() + idaapi.set_highlight(self.GetWidget(), None, idaapi.HIF_LOCKED) + self.Close() + + # set chosen target variable to given (line, idx) variable + def set_chosen(self, line: int, idx: int): + # highligth selected var + old_line = self.GetLine(line)[0] + pat_off, pat_end = 0, 0 + for _ in range(idx + 1): + pat_off = old_line.find(idaapi.SCOLOR_ON + SCOLOR_TARGET, pat_end, len(old_line)) + pat_end = old_line.find(idaapi.SCOLOR_OFF + SCOLOR_TARGET, pat_off, len(old_line)) + 2 + self.EditLine( + line, + old_line[:pat_off] + + old_line[pat_off:pat_end].replace(SCOLOR_TARGET, idaapi.SCOLOR_ERROR) + + old_line[pat_end:], + ) + + # set appropriate chosen + self.chosen = self.ops_per_line[line][idx] + utils.g_logger.debug(f"selected variable is {self.chosen}") + + # forget current selection + def forget_chosen(self): + for i in range(self.Count()): + line = self.GetLine(i)[0] + if idaapi.SCOLOR_ERROR in line: + self.EditLine(i, line.replace(idaapi.SCOLOR_ERROR, SCOLOR_TARGET)) + self.Refresh() + + self.chosen = None + + # the user clicked (hopefully) a target variable + def OnClick(self, shift): + self.forget_chosen() # clear any previous selection + + # click location + loc = idaapi.listing_location_t() + if not idaapi.get_custom_viewer_location(loc, self.GetWidget(), idaapi.CVLF_USE_MOUSE): + return False + + # click location as coords + y, x, _ = self.GetPos() + + # get clicked variable + nearest = loc.tagged_sections.nearest_at(x, COLOR_TARGET) + if nearest is None or not nearest.valid(): + return False + + # get variable idx in line + var_idx = self.index_of_sect_in_line(nearest, loc.tagged_sections) + + self.set_chosen(y, var_idx) + return True + + +""" +class StackViewer(idaapi.simplecustviewer_t): + def __init__(self, fea: int): + super().__init__() + self.Create("Symless stack view") + self.selected = None + func = idaapi.get_func(fea) + frame = idaapi.get_frame(func) + if not frame: + utils.g_logger.warning("No frame found") + return + self.items = [] + for offset, name, size in idautils.StructMembers(frame.id): + # Get the member ID and type + mptr: idaapi.member_t = idaapi.get_member_by_name(frame, name) + tif = idaapi.tinfo_t() + idaapi.get_member_tinfo(tif, mptr) + mtype = idaapi.print_tinfo("", 0, 0, idaapi.PRTYPE_1LINE, tif, "", "") + if mtype is None: + mtype = "unknown" + + # Add the details to the items list + self.items.append([hex(offset), name, hex(size), mtype]) + self.AddLine( + f"{idaapi.COLSTR('rsp+0x{:04x}'.format(offset), idaapi.SCOLOR_KEYWORD)} " + f"{idaapi.COLSTR(name.ljust(10), idaapi.SCOLOR_DNAME)} " + f"{idaapi.COLSTR(mtype, idaapi.SCOLOR_NUMBER)}" + ) + + # remove status bar + qwidget = idaapi.PluginForm.TWidgetToPyQtWidget(self.GetWidget()) + for child in qwidget.children(): + if isinstance(child, QtWidgets.QStatusBar): + child.setMaximumHeight(0) + qwidget.setMinimumWidth(384) + + def OnClick(self, shift): + if self.selected is not None: + line, _, _ = self.GetLine(self.selected) + self.EditLine(self.selected, line[2:]) + + line_no, x, y = self.GetPos() + if line_no != self.selected: + self.EditLine(line_no, f"> {self.GetCurrentLine()}") + self.selected = line_no + self.Refresh() +""" + + +# a line in the structures list +class StrucSelItem(QtWidgets.QListWidgetItem): + def __init__(self, struc_name: str, size: int = 0): + super().__init__(struc_name) + self.name = struc_name + self.size = size + + def get_name(self) -> str: + return self.name + + def get_size(self) -> int: + return self.size + + +# default option for structure selector +class StrucSelDefaultItem(QtWidgets.QListWidgetItem): + def __init__(self): + super().__init__("New structure") + icon = QtGui.QIcon(os.path.join(os.path.abspath(symless.__path__[0]), "resources", "cross.png")) + self.setIcon(icon) + ft = QtGui.QFont() + ft.setBold(True) + self.setData(QtCore.Qt.FontRole, ft) + + def get_size(self) -> int: + return 0 + + +# structure selector (list) +class StrucSelWid(QtWidgets.QListWidget): + def __init__(self, parent: QtWidgets.QWidget = None): + super().__init__(parent) + + self.setWhatsThis("Select a structure to propagate.") + + # Get structures from local types + tif = idaapi.tinfo_t() + for id in range(1, idaapi.get_ordinal_count(None)): + if tif.get_numbered_type(None, id) and (tif.is_struct() or tif.is_forward_struct()): + local_type_name = idaapi.idc_get_local_type_name(id) + self.addItem(StrucSelItem(local_type_name, 0 if tif.is_forward_struct() else tif.get_size())) + self.sortItems() + + # default option + default = StrucSelDefaultItem() + self.insertItem(0, default) + self.setCurrentItem(default) + + def sizeHint(self) -> QtCore.QSize: + size = super().sizeHint() + size.setHeight(256) + return size + + +# base class for a tab in our plugin's UI +class BuilderTabBase(QtWidgets.QWidget): + def __init__(self, window: "BuilderMainWid", parent: QtWidgets.QWidget = None): + super().__init__(parent) + self.window = window + + # get form error, None if well filled + def get_error(self) -> Optional[str]: + return None + + +""" +# tab - propagate structure in stack +class BuilderFromStkTab(BuilderTabBase): + def __init__(self, fea: int, window: "BuilderMainWid", parent: QtWidgets.QWidget = None): + super().__init__(window, parent) + + # title + layout = QtWidgets.QVBoxLayout() + title = QtWidgets.QLabel(self) + title.setText("<h3>Select a stack offset</h3>") + layout.addWidget(title) + + self.stack = StackViewer(fea) + stackQTW = idaapi.PluginForm.TWidgetToPyQtWidget(self.stack.GetWidget()) + stackQTW.setWhatsThis("Choose the offset of your structure in the stack") + layout.addWidget(stackQTW, QtCore.Qt.AlignLeft) + + # size selector + self.size = QtWidgets.QLineEdit(self) + self.size.setText("0x0") + self.size.setMaxLength(16) + self.size.setValidator(QtGui.QRegularExpressionValidator(QtCore.QRegularExpression("^([0-9]+)|(0x[0-9a-fA-F]+)$"), self.size)) + self.size.setWhatsThis("Size of the in-stack structure.") + lsize = QtWidgets.QLabel(self) + lsize.setText("Structure size") + lsize.setWhatsThis("Size of the in-stack structure.") + lsize.setBuddy(self.size) + + # deep dive checkbox + self.chk = QtWidgets.QCheckBox("Spread in callees", self) + self.chk.setWhatsThis("Should propagation follow functions calls.") + self.chk.setChecked(True) + + lcenter = QtWidgets.QHBoxLayout() + lcenter.addWidget(lsize) + lcenter.addWidget(self.size) + lcenter.addStretch() + lcenter.addWidget(self.chk) + layout.addLayout(lcenter) + + self.setLayout(layout) + + def get_error(self) -> Optional[str]: + if self.get_stack_offset() is None: + return "Please select the offset of the structure in the stack." + + if self.get_structure_size() <= 0: + return "Please provide a valid structure size." + + return None + + def get_stack_offset(self) -> Optional[int]: + line_no = self.stack.selected + if line_no is not None and line_no < len(self.stack.items): + return int(self.stack.items[line_no][0], 16) + return None + + def get_structure_size(self) -> int: + sval = self.size.text() + try: + return int(sval, 16 if sval.startswith("0x") else 10) + except ValueError: + return -1 + + def set_structure_size(self, size: int): + self.size.setText(hex(size)) +""" + + +# tab - propagate structure pointer +class BuilderFromPtrTab(BuilderTabBase): + def __init__( + self, + mba: ida_hexrays.mba_t, + ea: int, + hint: Tuple[int, int], + window: "BuilderMainWid", + parent: QtWidgets.QWidget = None, + ): + super().__init__(window, parent) + + # title + layout = QtWidgets.QVBoxLayout() + title = QtWidgets.QLabel(self) + title.setText("<h3>Select an entry variable</h3>") + title.setAlignment(QtCore.Qt.AlignCenter) + layout.addWidget(title) + + # microcode view + self.microcodeViewer = MicrocodeViewer(mba, ea, hint) + microQTW = idaapi.PluginForm.TWidgetToPyQtWidget(self.microcodeViewer.GetWidget()) + microQTW.setWhatsThis( + "Select an entry point for the propagation. The entry point should be a variable having for value a pointer to the structure to propagate." + ) + layout.addWidget(microQTW, QtCore.Qt.AlignLeft) + + # shift selector + self.shift = QtWidgets.QLineEdit(self) + self.shift.setText("0x0") + self.shift.setMaxLength(16) + self.shift.setValidator( + QtGui.QRegularExpressionValidator(QtCore.QRegularExpression("^([0-9]+)|(0x[0-9a-fA-F]+)$"), self.shift) + ) + self.shift.setWhatsThis("Shift to apply to the propagated structure pointer.") + lshift = QtWidgets.QLabel(self) + lshift.setText("Shifted by") + lshift.setWhatsThis("Shift to apply to the propagated structure pointer.") + lshift.setBuddy(self.shift) + + # deep dive checkbox + self.chk = QtWidgets.QCheckBox("Spread in callees", self) + self.chk.setWhatsThis("Should propagation follow functions calls.") + self.chk.setChecked(True) + + lcenter = QtWidgets.QHBoxLayout() + lcenter.addWidget(lshift) + lcenter.addWidget(self.shift) + lcenter.addStretch() + lcenter.addWidget(self.chk) + layout.addLayout(lcenter) + + self.setLayout(layout) + + def get_error(self) -> Optional[str]: + if self.get_entry_variable() is None: + return "Please provide a variable as an entry point for the propagation." + + if self.get_shift() < 0: + return "Please provide a valid shift (negative values not supported)." + + return None + + def get_entry_variable(self) -> Optional[ida_hexrays.mop_t]: + if self.microcodeViewer.chosen is None: + return None + return self.microcodeViewer.chosen.mop # chosen op + + def get_shift(self) -> int: + sval = self.shift.text() + try: + return int(sval, 16 if sval.startswith("0x") else 10) + except ValueError: + return -1 + + +# plugin's main UI +class BuilderMainWid(QtWidgets.QDialog): + def __init__(self, mba: ida_hexrays.mba_t, ea: int, hint: Tuple[int, int], parent: QtWidgets.QWidget = None): + super().__init__(parent) + self.mba = mba + + # main layout + layout = QtWidgets.QVBoxLayout() + + # window's title + whint = QtWidgets.QLabel(self) + whint.setText("<h3>Select a structure</h3>") + whint.setAlignment(QtCore.Qt.AlignCenter) + layout.addWidget(whint) + layout.setAlignment(whint, QtCore.Qt.AlignTop) + + # structure selector + self.struct_selector = StrucSelWid(self) + layout.addWidget(self.struct_selector, QtCore.Qt.AlignLeft) + layout.setStretch(1, 1) + + # structure selector search bar + self.search_bar = QtWidgets.QLineEdit(self) + self.search_bar.setPlaceholderText("Structure name") + self.search_bar.setWhatsThis("Name of the structure (new or existing) to propagate.") + self.search_bar.textChanged.connect(self.search_for_structure) + layout.addWidget(self.search_bar) + self.search_bar.setFocus() + + # ctrl+f action + saction = QtWidgets.QAction(self) + saction.setShortcut(QtGui.QKeySequence.Find) + saction.triggered.connect(self.search_for) + self.addAction(saction) + + # tabs + self.tabs = QtWidgets.QTabWidget(self) + self.tabs.setMovable(False) + self.tabs.setTabsClosable(False) + self.tab0 = BuilderFromPtrTab(self.mba, ea, hint, self, self.tabs) + self.tabs.addTab(self.tab0, "From pointer") + # self.tab1 = BuilderFromStkTab(self.mba.entry_ea, self, self.tabs) + # self.tabs.addTab(self.tab1, "From stack") + layout.addWidget(self.tabs) + layout.setStretch(3, 2) + + # Cancel & Propagate buttons + lbottom = QtWidgets.QGridLayout() + cancel_btn = QtWidgets.QPushButton("Cancel", self) + cancel_btn.clicked.connect(self.reject) + ok_btn = QtWidgets.QPushButton("Propagate", self) + ok_btn.setDefault(True) + ok_btn.clicked.connect(self.execute) + lbottom.addWidget(cancel_btn, 0, 0) + lbottom.addWidget(ok_btn, 0, 1) + + layout.addLayout(lbottom) + layout.setAlignment(lbottom, QtCore.Qt.AlignBottom) + self.setLayout(layout) + + # window's properties + self.setWindowTitle(WINDOW_TITLE) + + # window's icon + icon = QtGui.QIcon(os.path.join(os.path.abspath(symless.__path__[0]), "resources", "champi.png")) + self.setWindowIcon(icon) + + # closing handler + self.finished.connect(self.on_finish) + + def get_error(self) -> Optional[str]: + struc = self.struct_selector.currentItem() + + if isinstance(struc, StrucSelDefaultItem) and len(self.search_bar.text()) == 0: + return "Please provide a name for the new structure." + + return None + + # 'propagate' was clicked + def execute(self): + tab = self.tabs.currentWidget() + error = self.get_error() or tab.get_error() + + if error is not None: + idaapi.warning(error) + return + + self.accept() + + # search for structure in list + def search_for_structure(self, key: str): + lkey = key.lower() + for i in range(self.struct_selector.count()): + current = self.struct_selector.item(i) + current.setHidden( + False if (not isinstance(current, StrucSelItem) or lkey in current.text().lower()) else True + ) + + # Ctrl+F action + def search_for(self): + self.search_bar.setFocus() + + # get structures (name) selected by user + def get_structure(self) -> str: + selected = self.struct_selector.currentItem() + if isinstance(selected, StrucSelDefaultItem): + return self.search_bar.text() + + return selected.get_name() + + # spread in callees checked by user + def get_dive(self) -> bool: + return self.tabs.currentWidget().chk.isChecked() + + # struc ptr shift specified by user + def get_shift(self) -> int: + tab = self.tabs.currentWidget() + return tab.get_shift() if isinstance(tab, BuilderFromPtrTab) else 0 + + # get the microcode variable the user selected as entry point + def get_entry_variable(self) -> Optional[ida_hexrays.mop_t]: + tab = self.tabs.currentWidget() + return tab.get_entry_variable() + + # ea of the selected entry point + def get_entry_ea(self) -> Tuple[int, int]: + tab = self.tabs.currentWidget() + return ( + (tab.microcodeViewer.chosen.ea, tab.microcodeViewer.chosen.sub_idx) + if isinstance(tab, BuilderFromPtrTab) + else (self.mba.entry_ea, 0) + ) + + # selected entry operand is a destination operand + def entry_is_dst_op(self) -> bool: + tab = self.tabs.currentWidget() + return tab.microcodeViewer.chosen.as_dst if isinstance(tab, BuilderFromPtrTab) else False + + # close custom viewers when window is close + # otherwise they will haunt IDA forever + def on_finish(self, result): + # TODO this does not seem to work + # old views still appears in "Synchronize with" + self.tab0.microcodeViewer.Close() + # self.tab1.stack.Close() + + +# Hook to attach new action to popup menu +class PopUpHook(idaapi.UI_Hooks): + loaded = False + + # triggered when all UI elements have been initialized + def ready_to_run(self): + self.init_action() + + def init_action(self): + if self.loaded: + return + + # check that the decompiler exists + if not idaapi.init_hexrays_plugin(): + utils.g_logger.error("You do not have the decompiler for this architecture, symless will not load") + self.unhook() + return + + icon_path = os.path.join(utils.get_resources_path(), "propag.png") + self.icon = idaapi.load_custom_icon(icon_path) + + self.action = idaapi.action_desc_t( + "symless:live", + "Propagate structure", + BuildHandler(), + "Shift+t", + "Build structure from selected variable", + self.icon, + idaapi.ADF_OWN_HANDLER, + ) + idaapi.register_action(self.action) + self.loaded = True + + def term(self): + idaapi.unregister_action(self.action.name) + idaapi.free_custom_icon(self.icon) + + # triggered on right click menu popup + def finish_populating_widget_popup(self, widget, popup, ctx: idaapi.action_ctx_base_t): + # window is (DISASM or PSEUDOCODE) & no selection + if ctx.widget_type not in (idaapi.BWN_DISASM, idaapi.BWN_PSEUDOCODE) or ctx.has_flag(idaapi.ACF_HAS_SELECTION): + return + + # we are inside a function + if ctx.cur_func is None: + return + + idaapi.attach_action_to_popup(widget, popup, self.action.name) + + +# context menu structure builder action +class BuildHandler(idaapi.action_handler_t): + def activate(self, ctx: idaapi.action_ctx_base_t) -> int: + hint_mreg = ida_hexrays.mr_none + hint_size = 0 + + if not arch.is_arch_supported(): + utils.g_logger.error("Unsupported arch (%s) or filetype" % arch.get_proc_name()) + return 0 + + mba = ida_utils.get_func_microcode(ctx.cur_func, True) + if not mba: + utils.g_logger.error(f"Could not generate microcode for function {ctx.cur_func.start_ea:#x}") + return 0 + + # guess the micro operand associated to user selection + if ctx.widget_type == idaapi.BWN_DISASM: + hint_mreg, hint_size = self.guess_selected_mop_from_assembly() + else: # idaapi.BWN_PSEUDOCODE + # TODO implement guessing for pseudocode view + pass + + # display plugin's UI + form = BuilderMainWid(mba, ctx.cur_ea, (hint_mreg, hint_size)) + code = form.exec() + + # cancel button was hit + if code == QtWidgets.QDialog.Rejected: + return 0 + + propagate_structure( + form.get_entry_ea(), + form.get_entry_variable(), + form.entry_is_dst_op(), + form.get_structure(), + form.get_shift(), + form.get_dive(), + ) + + return 1 # all IDA windows will be refreshed + + def update(self, ctx): + return idaapi.AST_ENABLE_ALWAYS + + # use the currently selected assembly operand to guess the corresponding microcode operand + # returns (mreg_t, size) + def guess_selected_mop_from_assembly(self) -> Tuple[int, int]: + mreg = ida_hexrays.mr_none + mreg_size = 0 + + cur_ea = idaapi.get_screen_ea() # current address + cur_op = idaapi.get_opnum() # current op idx + cur_insn = idaapi.insn_t() # current instruction + insn_len = idaapi.decode_insn(cur_insn, cur_ea) + + if insn_len == 0 or cur_op < 0 or cur_op > ida_utils.get_len_insn_ops(cur_insn): + return (mreg, mreg_size) + + op = cur_insn.ops[cur_op] + if op.type == idaapi.o_reg: + mreg = ida_hexrays.reg2mreg(op.reg) # mr_none if none + mreg_size = idaapi.get_dtype_size(op.dtype) + + elif op.type in [idaapi.o_phrase, idaapi.o_displ]: + mreg = ida_hexrays.reg2mreg(op.phrase) + mreg_size = ida_utils.get_ptr_size() + + if mreg_size: + utils.g_logger.debug(f"Guess for target mreg: {ida_hexrays.get_mreg_name(mreg, mreg_size)}") + + return (mreg, mreg_size) + + +# do the propagation & build the structure +def propagate_structure( + ea_couple: Tuple[int, int], mop: ida_hexrays.mop_t, dst_op: bool, strucname: str, shift: int, dive: bool +): + idaapi.show_wait_box("HIDECANCEL\nPropagating struct info..") + + ea, subea = ea_couple + + try: + # get containing function + fct = idaapi.get_func(ea) + + # entry is to be injected after minsn is processed + if dst_op: + entry = model.dst_var_entry_t(ea, subea, fct.start_ea, mop) + + # entry is to be injected before + else: + entry = model.src_reg_entry_t(ea, subea, fct.start_ea, mop) + + entry.struc_shift = shift # shift for associated structure + + # set root entries + entries = model.entry_record_t() + entries.add_entry(entry, True) + + # build entrypoints graph + ctx = model.context_t(entries, set()) + ctx.set_follow_calls(dive) + model.analyze_entrypoints(ctx) + + # define structures + strucs = structures.define_structures(ctx) + + # associate generated model with chosen structure + _, struc_model = entry.get_structure() + if struc_model is None: + pass # previous steps have failed (hopefully with an error msg) + + else: + struc_model.set_name(strucname) + struc_model.force_generation = True # generate even if empty + + # make sure not to reduce existing structure size by removing padding + struc = ida_utils.get_local_type(strucname) + if struc and not struc.is_forward_decl() and struc.get_size() > struc_model.get_size(): + struc_model.set_size(struc.get_size()) + + # import structures into IDA + generate.import_structures(strucs) + + # type operands with structures + generate.import_context(ctx) + + except Exception as e: + import traceback + + utils.g_logger.critical(repr(e) + "\n" + traceback.format_exc()) + + finally: + # no need to keep all mbas + ida_utils.g_microcode_cache.clear() + + idaapi.hide_wait_box() + +``` + +`symless/symbols/__init__.py`: + +```py +import re +from typing import Optional, Tuple + +import idaapi + +import symless.utils.ida_utils as ida_utils +import symless.utils.utils as utils + +# global expressions for symbol selection +re_ctors = re.compile(r"\b((?:[\w_]+::)*)([\S ]+)::\2(?:\(|$)") +re_vtable_single_msvc = re.compile(r"^const (.+)::`vftable'$") +re_vtable_single_gcc = re.compile(r"^`vtable for'([\s\S]+)$") +re_tinfo_gcc = re.compile(r"^`typeinfo for'([\s\S]+)$") +re_vtable_for_msvc = re.compile(r"^const (.+)::`vftable'{for `(.+)'}") + +# invalid structure names exps & replacements +re_invalid_struc_name = ( + (re.compile(r"[\s\*&]"), ""), + (re.compile(r"[,\-+]"), "_"), + (re.compile(r"[<>]"), "__"), +) + +# invalid method field names exps & replacements +re_invalid_method_name = ((re.compile(r"[\s]+"), "_"), (re.compile(r"[^0-9a-zA-Z~_-]"), "")) + + +# full method name from method signature +# i.e get Class::Initialize from Class::Initialize(Class*) +def full_method_name_from_signature(signature: str) -> str: + fct = signature.split("(")[0] + return fct + + +# method name from method signature +# i.e get Initialize from Class::Initialize(Class*) +def method_name_from_signature(signature: str) -> str: + full = full_method_name_from_signature(signature) + name = full.split("::")[-1] + for exp, repl in re_invalid_method_name: + name = exp.sub(repl, name) + return name.strip("_") + + +# replace unvalid characters from structure's name +def struc_name_cleanup(original: str) -> str: + out = original + for exp, repl in re_invalid_struc_name: + out = exp.sub(repl, out) + return out + + +# ea was given a (non-dummy) name +def has_relevant_name(ea: int): + flags = idaapi.get_flags(ea) + return idaapi.has_any_name(flags) and not idaapi.has_dummy_name(flags) + + +# get class name from its constructor signature +def get_classname_from_ctor(fct_name: str) -> Optional[str]: + if fct_name is None or "::" not in fct_name: + return None + + m = re_ctors.search(fct_name) + if m is None: + return None + + return m.group(1) + m.group(2) + + +# get vtable structure's name from its symbol +def get_vtable_name_from_ctor(vtable_ea: int) -> Optional[str]: + derived, parent = get_classnames_from_vtable(vtable_ea) + + if derived is None: + utils.g_logger.debug(f"No name found for vtable 0x{vtable_ea:x}") + return None + + if parent is None: + return f"{derived}{idaapi.VTBL_SUFFIX}" + + return f"{derived}_{parent}{idaapi.VTBL_SUFFIX}" + + +# get child & parent classes names from vtable symbol +def get_classnames_from_vtable(vtable_ea: int) -> Tuple[Optional[str], Optional[str]]: + for f in (get_classnames_from_vtable_gcc, get_classnames_from_vtable_msvc): + derived, parent = f(vtable_ea) + if derived is not None: + return derived, parent + + return None, None + + +# get child & parent classes names from vtable symbol for gcc compiled binaries +def get_classnames_from_vtable_gcc(vtable_ea: int) -> Tuple[Optional[str], Optional[str]]: + ptr_size = ida_utils.get_ptr_size() + + # use vtable symbol + label_ea = vtable_ea - (2 * ptr_size) + vtbl_name = ida_utils.demangle_ea(label_ea) + + m = re_vtable_single_gcc.search(vtbl_name) + if m is not None: + return (m.group(1), None) + + # fallback - use typeinfo symbol + tinfo_ea = ida_utils.__dereference_pointer(vtable_ea - ptr_size, ptr_size) + tinfo = ida_utils.demangle_ea(tinfo_ea) + + m = re_tinfo_gcc.search(tinfo) + if m is not None: + return (m.group(1), None) + + return (None, None) + + +# get child & parent classes names from vtable symbol for msvc compiled binaries +def get_classnames_from_vtable_msvc(vtable_ea: int) -> Tuple[Optional[str], Optional[str]]: + vtbl_name = ida_utils.demangle_ea(vtable_ea) + + if vtbl_name is None or "::" not in vtbl_name: + return (None, None) + + m = re_vtable_single_msvc.search(vtbl_name) + if m is not None: + return (m.group(1), None) + + m = re_vtable_for_msvc.search(vtbl_name) + if m is not None: + return (m.group(1), m.group(2)) + + return (None, None) + +``` + +`symless/symbols/rename.py`: + +```py +from typing import Collection, List, Tuple + +import symless.generation as generation +from symless.symbols import * + + +# update list of potential names with given candidate +def add_candidate_name(names: List[Tuple[int, str]], candidate: Tuple[int, str]): + relevance, name = candidate + + seeker = (i for i, (_, n) in enumerate(names) if (n == name)) + try: + idx = next(seeker) # name exists, update relevance + old_rel, _ = names[idx] + if old_rel > relevance: + names[idx] = (relevance, name) + except StopIteration: + names.append((relevance, name)) + + +# get a list of possible names for a structure +# ordered by preferences +def find_structure_name(struc: generation.structure_t) -> Collection[str]: + names: List[Tuple[int, str]] = list() + current_root, depth = None, 0 + + # loop over all nodes associated to the structure + # get names from the associated nodes + for root, block, shift in struc.node_flow(False): + if root != current_root: + current_root, depth = root, 0 + + if shift == 0 and depth <= 2: + name, relevance = block.get_owner().find_name() + if name is not None: # add possible name to the list + name = struc_name_cleanup(name) + relevance *= depth + 1 + add_candidate_name(names, (relevance, name)) + + depth += 1 + + # add name from associated vtable to the list + field = struc.get_field(0) + + # first field is a vtable pointer + if isinstance(field, generation.vtbl_ptr_field_t): + _, vtable = field.get_structure() + derived, _ = get_classnames_from_vtable(vtable.ea) + if derived is not None: + # names from first vtable are more accurate than names from ctors + # set best preference (0) + add_candidate_name(names, (0, struc_name_cleanup(derived))) + + names.sort(key=lambda k: k[0]) + return [n for _, n in names] + + +# name all structures from given record +# use symbols for naming +# TODO: select less derived when conflict on name +def define_structures_names(record: generation.structure_record_t): + all_names = set() # all given names record + + for struc in record.get_structures(include_discarded=False): + # define structure's fields names + struc.compute_names() + + # define structure's name + names = find_structure_name(struc) + if len(names) == 0: + continue + + # name conflict, make it unique + name = names[0] + if name in all_names: + name = f"{name}_0x{struc.ea:x}" + + all_names.add(name) + struc.set_name(name) + +``` + +`symless/utils/ida_utils.py`: + +```py +from collections import deque +from typing import Generator, List, Optional, Tuple + +import ida_hexrays +import idaapi +import idautils +import idc + +import symless.symbols as symbols +import symless.utils.utils as utils + +""" Imports utilities """ + + +def get_import_module_index(name: str) -> int: + for i in range(idaapi.get_import_module_qty()): + if idaapi.get_import_module_name(i) == name: + return i + return None + + +# Get ea of given import, from given module +def get_import_from_module(module: int, import_name: str) -> int: + import_ea = None + + def iterator(ea, name, ord): + nonlocal import_ea, import_name + if name.startswith(import_name): + import_ea = ea + return False + return True + + idaapi.enum_import_names(module, iterator) + return import_ea + + +""" Names utilities """ + + +def demangle(name: str, inf_attr=idc.INF_SHORT_DN) -> str: + demangled = idaapi.demangle_name(name, idc.get_inf_attr(inf_attr)) + if demangled: + return demangled + + return name + + +def demangle_ea(ea: int, inf_attr=idc.INF_SHORT_DN) -> str: + return demangle(idaapi.get_name(ea), inf_attr) + + +# retrieve a name in the form "fct+offset" +def addr_friendly_name(ea: int) -> str: + fct = idaapi.get_func(ea) + if fct is None: + return f"ea[0x{ea:x}]" + + offset = ea - fct.start_ea + fct_name = symbols.full_method_name_from_signature(demangle(idaapi.get_short_name(fct.start_ea))) + return "%s%s" % (fct_name, f"+{offset:x}" if offset != 0 else "") + + +""" Xrefs utilities """ + +# The following functions can be time-consuming when an address has numerous xref +# every xref has to be fetch using an API call + + +def get_references(address: int) -> List[int]: + return [ref for ref in idautils.CodeRefsTo(address, 0)] + + +def get_data_references(address: int) -> List[int]: + return [ref for ref in idautils.DataRefsTo(address)] + + +def get_all_references(address: int) -> set: + crefs = get_references(address) + drefs = get_data_references(address) + return set(crefs + drefs) + + +""" Pointers utilities """ + + +g_ptr_size = None + + +def get_ptr_size() -> int: + global g_ptr_size + g_ptr_size = ( + g_ptr_size if g_ptr_size else (8 if idaapi.inf_is_64bit() else (4 if idaapi.inf_is_32bit_or_higher else 2)) + ) + return g_ptr_size + + +def __dereference_pointer(addr: int, ptr_size: int) -> int: + return idaapi.get_qword(addr) if ptr_size == 8 else idaapi.get_dword(addr) + + +def dereference_pointer(addr: int) -> int: + return __dereference_pointer(addr, get_ptr_size()) + + +# get size bytes from given ea, if ea is initialized with a value +def get_nb_bytes(ea: int, size: int) -> int: + if not idaapi.is_loaded(ea): + return None + + if size == 8: + return idaapi.get_qword(ea) + if size == 4: + return idaapi.get_dword(ea) + if size == 2: + return idaapi.get_word(ea) + + return idaapi.get_byte(ea) + + +""" Type utilities """ + + +# get basic type +def get_basic_type(type: int) -> idaapi.tinfo_t: + tinfo = idaapi.tinfo_t() + tinfo.create_simple_type(type) + return tinfo + + +# returns void* tinfo_t +def void_ptr() -> idaapi.tinfo_t: + tinfo = get_basic_type(idaapi.BT_VOID) + tinfo.create_ptr(tinfo) + return tinfo + + +# local type by name +def get_local_type(name: str) -> Optional[idaapi.tinfo_t]: + tinfo = idaapi.tinfo_t() + if tinfo.get_named_type(idaapi.get_idati(), name): + return tinfo + return None + + +# convert a local variable forward ref into a real struct +def replace_forward_ref(tif: idaapi.tinfo_t): + ord, tname = tif.get_ordinal(), tif.get_type_name() + mudt = idaapi.udt_type_data_t() + tif.create_udt(mudt) + err = tif.set_numbered_type(None, ord, idaapi.NTF_REPLACE) + if err != idaapi.TERR_OK: + utils.g_logger.error(f'Could not convert forward ref to "{tname}" : {idaapi.tinfo_errstr(err)} ({err})') + + +# just a wrap around find_udm that returns BADADDR instead of -1 +def find_udm_wrap(struc: idaapi.tinfo_t, udm: idaapi.udm_t) -> int: + rc = struc.find_udm(udm, idaapi.STRMEM_OFFSET) + return idaapi.BADADDR if rc in (-1, idaapi.BADADDR) else rc + + +""" Function utilities """ + + +# creates funcarg_t type +def make_function_argument(typ: idaapi.tinfo_t, name: str = "") -> idaapi.funcarg_t: + farg = idaapi.funcarg_t() + farg.type = typ + farg.name = name + return farg + + +# shift pointer +def shift_ptr(ptr: idaapi.tinfo_t, parent: idaapi.tinfo_t, shift: int): + if shift == 0: + return + + ptr_data = idaapi.ptr_type_data_t() + if ptr.get_ptr_details(ptr_data): + ptr_data.taptr_bits |= idaapi.TAPTR_SHIFTED + ptr_data.delta = shift + ptr_data.parent = parent + ptr.create_ptr(ptr_data, idaapi.BT_PTR) + + +# add argument to function + shift ptr argument +def set_function_argument( + func_data: idaapi.func_type_data_t, + index: int, + typ: idaapi.tinfo_t, + shift: int = 0, + parent: Optional[idaapi.tinfo_t] = None, + name: Optional[str] = None, +): + while index > func_data.size(): + func_data.grow(make_function_argument(void_ptr(), f"arg_{func_data.size()}")) + + # apply __shifted + shift_ptr(typ, parent, shift) + + if name is None: + name = f"arg_{index}" + + arg = make_function_argument(typ, name) + if index == func_data.size(): + func_data.grow(arg) + else: + func_data[index] = arg + + +# creates a new valid func_type_data_t object +def new_func_data() -> idaapi.func_type_data_t: + func_data = idaapi.func_type_data_t() + + # ret type to void + ret_tinfo = idaapi.tinfo_t() + ret_tinfo.create_simple_type(idaapi.BT_VOID) + func_data.rettype = ret_tinfo + + # fastcall as default cc + func_data.cc = idaapi.CM_CC_FASTCALL + + return func_data + + +# get the tinfo for a given function +def get_fct_type(fea: int, force_decompile: bool = False) -> Optional[idaapi.tinfo_t]: + tinfo = idaapi.tinfo_t() + hf = ida_hexrays.hexrays_failure_t() + + if (not force_decompile) and idaapi.get_tinfo(tinfo, fea): + return tinfo + + utils.g_logger.info(f"Forcing decompilation of fct {fea:#x}") + cfunc = ida_hexrays.decompile_func(idaapi.get_func(fea), hf, ida_hexrays.DECOMP_NO_WAIT) + if cfunc is None: + utils.g_logger.warning(f"Could not decompile fct {fea:#x}: {hf.str} ({hf.code})") + return None + + if cfunc.get_func_type(tinfo): + return tinfo + + utils.g_logger.error(f"No tinfo_t for fea {fea:#x}") + return None + + +# get function type, create default one if none +def get_or_create_fct_type(fea: int) -> Tuple[idaapi.tinfo_t, idaapi.func_type_data_t]: + func_tinfo = get_fct_type(fea) + if func_tinfo is None: + return (idaapi.tinfo_t(), new_func_data()) + + func_data = idaapi.func_type_data_t() + if func_tinfo.get_func_details(func_data): + return (func_tinfo, func_data) + + return (func_tinfo, new_func_data()) + + +# yields one instruction operands +def get_insn_ops(insn: idaapi.insn_t) -> Generator[idaapi.op_t, None, None]: + i = 0 + while i < idaapi.UA_MAXOP and insn.ops[i].type != idaapi.o_void: + yield insn.ops[i] + i += 1 + + +# get instruction's operands count +def get_len_insn_ops(insn: idaapi.insn_t) -> int: + return len([i for i in get_insn_ops(insn)]) + + +# cache our mbas with their special kregs +g_microcode_cache = dict() + + +# get the microcode for a given function +def get_func_microcode(func: idaapi.func_t, analyze_calls: bool = False) -> Optional[ida_hexrays.mba_t]: + global g_microcode_cache + if func.start_ea in g_microcode_cache: + return g_microcode_cache[func.start_ea] + + # generate the function microcode + mbr = ida_hexrays.mba_ranges_t(func) + hf = ida_hexrays.hexrays_failure_t() + mba: ida_hexrays.mba_t = ida_hexrays.gen_microcode( + mbr, hf, None, ida_hexrays.DECOMP_NO_WAIT, ida_hexrays.MMAT_PREOPTIMIZED + ) + + if not mba: + utils.g_logger.error(f"Could generate mba for fct {func.start_ea:#x}: {hf.str} ({hf.code})") + return None + + # build cfg and define blocks relations + mba.build_graph() + + # resolve calls arguments and returns + if analyze_calls: + mba.analyze_calls(ida_hexrays.ACFL_GUESS) + + # only cache mba used by cpustate (without initial call analysis) + else: + g_microcode_cache[mba.entry_ea] = mba + + # allocate special kregs + # used to pass result of inline minsns to parent minsn + setattr(mba, "tmp_result_kregs", deque()) + for _ in range(8): + mba.tmp_result_kregs.append(mba.alloc_kreg(get_ptr_size())) + setattr(mba, "call_result_kreg", mba.alloc_kreg(get_ptr_size())) + + return mba + + +# analyze calls of given mba, make sure to have the correct args count for each +def mba_analyze_calls(mba: ida_hexrays.mba_t): + global g_microcode_cache + + if mba.callinfo_built(): # already done + return + + hf = ida_hexrays.hexrays_failure_t() + + # find all calls, decompile callees for accurate arguments count + for i in range(mba.qty): + mblock = mba.get_mblock(i) + minsn = mblock.head + + while minsn: + if minsn.is_unknown_call() and minsn.l.t == ida_hexrays.mop_v: + fct = idaapi.get_func(minsn.l.g) + if fct: + utils.g_logger.info(f"decompiling callee {fct.start_ea:#x} for accurate call info") + ida_hexrays.decompile_func(fct, hf, ida_hexrays.DECOMP_NO_WAIT) + + minsn = minsn.next + + # resolve call arguments (and ret) in mba + mba.analyze_calls(ida_hexrays.ACFL_GUESS) + + +# get the microcode block containing the specified ea +def get_block_microcode(fct: idaapi.func_t, ea: int) -> Optional[Tuple[ida_hexrays.mblock_t, ida_hexrays.mba_t]]: + # function microcode + mba = get_func_microcode(fct) + if not mba: + return None + + # return containing block + try: + return ( + next( + filter( + lambda b: (not (b.flags & ida_hexrays.MBL_FAKE)) and ea >= b.start and ea < b.end, + [mba.get_mblock(i) for i in range(mba.qty)], + ) + ), + mba, + ) + except StopIteration: # yes this can happen + return None + + +# lift the instruction at ea into a micro instruction +# returns a MMAT_PREOPTIMIZED minsn +def get_ins_microcode(ea: int) -> Optional[ida_hexrays.minsn_t]: + insn = idaapi.insn_t() + rvec = idaapi.rangevec_t() + hf = ida_hexrays.hexrays_failure_t() + + # get the instruction size + insn_size = idaapi.decode_insn(insn, ea) + if not insn_size: + utils.g_logger.warning(f"no instruction found at {ea:#x}") + return None + + # generate the mba + rvec.push_back(idaapi.range_t(insn.ea, insn.ea + insn_size)) + mbr = ida_hexrays.mba_ranges_t(rvec) + mba = ida_hexrays.gen_microcode( + mbr, hf, None, ida_hexrays.DECOMP_NO_WAIT | ida_hexrays.DECOMP_NO_FRAME, ida_hexrays.MMAT_PREOPTIMIZED + ) + if not mba: + utils.g_logger.error(f"Could not get minsn for ea {ea:#x}: {hf.str} ({hf.code})") + return None + + # find the minsn in the mba + # it should be the first instruction of the first non-fake block + minsn = next( + filter(lambda b: not (b.flags & ida_hexrays.MBL_FAKE), [mba.get_mblock(i) for i in range(mba.qty)]) + ).head + + return ida_hexrays.minsn_t(minsn) # original is freed with mba + + +""" Misc """ + + +# get root folder for local types, if supported +def get_local_types_folder() -> Optional[idaapi.dirtree_t]: + try: + return idaapi.get_std_dirtree(idaapi.DIRTREE_LOCAL_TYPES) + except AttributeError: + return None + + +# mcode_t values to microinstruction names +g_mcode_name = { + getattr(ida_hexrays, mcode): mcode[2:] for mcode in filter(lambda y: y.startswith("m_"), dir(ida_hexrays)) +} + + +# mopt_t values to operand type as str +g_mopt_name = [ + "mop_z", + "mop_r", + "mop_n", + "mop_str", + "mop_d", + "mop_S", + "mop_v", + "mop_b", + "mop_f", + "mop_l", + "mop_a", + "mop_h", + "mop_c", + "mop_fn", + "mop_p", + "mop_sc", +] + + +# get instruction + operands representation +def insn_str_full(insn: ida_hexrays.minsn_t) -> str: + return f"[{insn.dstr()}] = [{g_mcode_name[insn.opcode]} {', '.join([g_mopt_name[i.t] for i in (insn.l, insn.r, insn.d)])}]" + +``` + +`symless/utils/utils.py`: + +```py +import logging +import os + +import symless +import symless.config as config + + +def get_logger(level: int = config.g_settings.log_level): + logger = logging.getLogger("symless") + logger.setLevel(level) + logger.propagate = False + + # do not recreate handler when reloading utils module + if logger.hasHandlers(): + return logger + + # create console handler and set level to debug + ch = logging.StreamHandler() + ch.setLevel(level) + + # log format + formatter = logging.Formatter( + "{asctime} - {name} - {levelname:<8s} {filename:>15s}:{lineno:05} - {funcName:<30s} - {message}", + style="{", + ) + + # add formatter to ch + ch.setFormatter(formatter) + + # add ch to logger + logger.addHandler(ch) + + return logger + + +# initialize global logger +g_logger = get_logger() + + +# where to look for resources +def get_resources_path() -> str: + return os.path.join(symless.__path__[0], "resources") + + +# print completed action & time it took +def print_delay(prefix: str, start: float, end: float): + delay = int(end - start) + min = int(delay / 60) + sec = delay - (min * 60) + g_logger.info("%s in %s%s" % (prefix, "%d minutes and " % min if min > 0 else "", "%d seconds" % sec)) + + +# convert integer to given sign & size +def to_c_integer(value: int, sizeof: int, signed: bool = True) -> int: + mask = 1 << (sizeof * 8) + out = value & (mask - 1) + if signed and (out & (mask >> 1)): + out -= mask + return out + +``` + +`symless/utils/vtables.py`: + +```py +from collections import deque +from typing import Collection, Generator, Optional, Tuple + +import idaapi + +import symless.cpustate.cpustate as cpustate +import symless.utils.ida_utils as ida_utils +import symless.utils.utils as utils + +""" Utilities for identifying virtual tables """ + + +# model for a virtual table +class vtable_t: + def __init__(self, ea: int): + self.ea = ea + self.total_xrefs = 0 # total of xrefs on virtual methods + + # all virtual methods (fea, is_imported) + self.members: Collection[Tuple[int, bool]] = deque() + + for fea, is_import in vtable_members(ea): + self.members.append((fea, is_import)) + self.total_xrefs += len(ida_utils.get_data_references(fea)) + + # list of ea where the vtable is loaded + self.load_xrefs: Collection[int] = deque() + + def size(self) -> int: + return len(self.members) * ida_utils.get_ptr_size() + + def add_load(self, xref: int): + self.load_xrefs.append(xref) + + def get_loads(self) -> Collection[int]: + return self.load_xrefs + + # search for places where this vtable is loaded into a structure + def search_loads(self): + for x in search_xrefs_for_vtable_load(self.ea, self.ea): + self.add_load(x) + + # special RTTI case, vtable symbol may not point directly to the array of fct ptrs + # ctor loads the vtable in the object like this: + # lea rax, `vtable for'FooBar + # add rax, 10h + # mov [rbx], rax + if len(self.get_loads()) == 0: + for x in search_xrefs_for_vtable_load(self.ea - 2 * ida_utils.get_ptr_size(), self.ea): + self.add_load(x) + + def get_members(self) -> Collection[Tuple[int, bool]]: + return self.members + + def members_count(self) -> int: + return len(self.members) + + # only contain imported functions + def all_imports(self) -> bool: + return all([is_import for _, is_import in self.members]) + + # we think this really is a vtable + def valid(self) -> bool: + return self.members_count() > 0 and not self.all_imports() + + # get most derived vtable between self and other + # decision based on some not-so-accurate heuristics + def get_most_derived(self, other: "vtable_t") -> "vtable_t": + # biggest vtable is the most derived + if other.members_count() > self.members_count(): + return other + if other.members_count() < self.members_count(): + return self + + # vtable with the most referenced methods is the base one + # why ? its methods may be referenced from all inheriting vtables + if self.total_xrefs > other.total_xrefs: + return other + if other.total_xrefs > self.total_xrefs: + return self + + # most referenced vtable is the base one + # as it is more loaded (also loaded in child classes ctors/dtors) + if len(self.load_xrefs) > len(other.load_xrefs): + return other + + return self + + def __hash__(self): + return self.ea + + def __eq__(self, value): + return (isinstance(value, int) and self.ea == value) or (isinstance(value, vtable_t) and self.ea == value.ea) + + +# returns the next member for the given vftable, None if we reached the end +def next_vtable_member(vtbl_ea: int, member_ea: int, ptr_size: int) -> Optional[Tuple[int, bool]]: + fea = ida_utils.__dereference_pointer(member_ea, ptr_size) & ~1 # in case of thumb mode + func = idaapi.get_func(fea) + + # addr is a function entry point + if func and func.start_ea == fea: + imported = False + + # addr points to an import + elif idaapi.is_mapped(fea) and idaapi.getseg(fea).type == idaapi.SEG_XTRN: + imported = True + + else: + return None + + # if a reference is found on the member, consider it is not part of the current vtable + if vtbl_ea != member_ea and ( + idaapi.get_first_dref_to(member_ea) != idaapi.BADADDR or idaapi.get_first_cref_to(member_ea) != idaapi.BADADDR + ): + return None + + return fea, imported + + +# yield all members of given vtable +def vtable_members(vtbl_ea: int) -> Generator[Tuple[int, bool], None, None]: + ptr_size = ida_utils.get_ptr_size() + + current = vtbl_ea + r = next_vtable_member(vtbl_ea, current, ptr_size) + while r is not None: + yield r + current += ptr_size + r = next_vtable_member(vtbl_ea, current, ptr_size) + + +# does the given xref points to a loading of vtbl_ea into a struct +def is_vtable_loaded_at(fct: idaapi.func_t, xref_ea: int, vtbl_ea: int) -> bool: + block = ida_utils.get_block_microcode(fct, xref_ea) + if not block: + return False + mbb, mba = block + + # flow in xref basic block, see if vtable is loaded & stored to a struct + minsn = mbb.head + state = cpustate.state_t(mba, None) + + utils.g_logger.debug(f"Looking for a load of vtable {vtbl_ea:#x} at {xref_ea:#x}") + + while minsn: # for every bb's instructions + for subinsn in cpustate.flatten_minsn(minsn, mba): # for every sub instruction + cpustate.process_instruction(state, subinsn) + + # check for vtable ea to be stored + for write in state.writes: + if ( + write.size == ida_utils.get_ptr_size() + and isinstance(write.value, cpustate.mem_t) + and write.value.get_uval() == vtbl_ea + ): + return True + + minsn = minsn.next + + return False + + +# search the xrefs of given ea for loads of vtbl_ea +def search_xrefs_for_vtable_load(ea: int, vtbl_ea: int) -> Generator[int, None, None]: + for xref in ida_utils.get_data_references(ea): + fct = idaapi.get_func(xref) + + # referenced from other data + if fct is None and ida_utils.dereference_pointer(xref) == ea: + yield from search_xrefs_for_vtable_load(xref, vtbl_ea) + + elif fct and is_vtable_loaded_at(fct, xref, vtbl_ea): + yield xref + + +# get a model for the vtable at given address +# None if no vtable found at that address +def next_vtable(ea: int, end_ea: int) -> Tuple[Optional[vtable_t], int]: + if not idaapi.is_loaded(ea): + return None, idaapi.next_head(ea, end_ea) + + vtbl = vtable_t(ea) + if vtbl.members_count() == 0: # not an array of fct ptrs + return None, idaapi.next_head(ea, end_ea) + + if vtbl.all_imports(): # we are not sure any of these are fct ptrs + return None, ea + vtbl.size() + + # find vtable loading sites + vtbl.search_loads() + + # vtable only if it is loaded by code + if len(vtbl.get_loads()) != 0: + return vtbl, ea + vtbl.size() + + return None, ea + vtbl.size() + + +# scans given segment for vtables +def get_all_vtables_in(seg: idaapi.segment_t) -> Generator[vtable_t, None, None]: + utils.g_logger.info( + "scanning segment %s[%x, %x] for vtables" % (idaapi.get_segm_name(seg), seg.start_ea, seg.end_ea) + ) + + current = seg.start_ea + while current != idaapi.BADADDR and current < seg.end_ea: + # do not cross functions + chunk = idaapi.get_fchunk(current) + if chunk is not None: + current = chunk.end_ea + continue + + # is a vtable ? + vtbl, current = next_vtable(current, seg.end_ea) + if vtbl: + yield vtbl + + +# scans code segments for vtables +def get_all_vtables() -> Generator[vtable_t, None, None]: + seg = idaapi.get_first_seg() + while seg is not None: + # search for vtables in .data and .text segments + if seg.type == idaapi.SEG_CODE or seg.type == idaapi.SEG_DATA: + yield from get_all_vtables_in(seg) + + seg = idaapi.get_next_seg(seg.start_ea) + +``` \ No newline at end of file