You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Steffen Winterfeldt 0aed125dcc
Merge pull request #28 from wfeldt/sw_03
1 year ago
demo add '--32' option to demo program to start in 32-bit mode 1 year ago
include rework (i)mul instructions to set flags correctly 1 year ago
test add (i)mul tests 1 year ago
.gitignore prepare for obs auto-submission 6 years ago
.travis.yml Add Travis CI configuration file and a badge in the README 1 year ago
LICENSE add proper LICENSE file (containing the actual license) and add it consistently to all source files 3 years ago
LICENSE_INFO add proper LICENSE file (containing the actual license) and add it consistently to all source files 3 years ago
Makefile Introduce LDFLAGS variable in Makefiles 1 year ago update README to explain about library versions 1 year ago
api.c ops2: Introduce a callback function for MSR access 1 year ago
decode.c adjust code to compile with older C standards 1 year ago
git2log update git2log script 2 years ago
mem.c - reverse logic: tag library symbols to be exported instead of tagging internal symbols 2 years ago
ops.c rework (i)mul instructions to set flags correctly 1 year ago
ops2.c adjust code to compile with older C standards 1 year ago
prim_ops.c rework (i)mul instructions to set flags correctly 1 year ago

x86 emulation library

Build Status

libx86emu is a small library to emulate x86 instructions. The focus here is not a complete emulation (go for qemu for this) but to cover enough for typical firmware blobs.

At the moment 'regular' 32-bit instructions are covered together with basic protected mode support.

Not done are fpu, mmx, or any of the other instruction set extensions.

The library lets you

  • intercept any memory access or directly map real memory ranges
  • intercept any i/o access, map real i/o ports, or block any real i/o
  • intercept any interrupt
  • provides hook to run after each instruction
  • recognizes a special x86 instruction that can trigger logging
  • has integrated logging to
    • trace code execution, including register content and decoded instruction
    • trace memory and i/o accesses
    • provide statistics about accessed memory locations, i/o ports, and interrupts

Major versions

Programs should generally work fine with newer library versions without any changes (except re-compiling).

Version 3

Extend API to include CPUID and MSR handlers.

Version 2

Essentially the same API as version 1. The major difference is that version 2 is re-entrant (no global state variable).

Version 1

Version 1 relies internally on a global variable x86emu holding the emulator state. It has been eliminated in version 2.


Get the latest version from the openSUSE Build Service.


Have a look at this minimalistic demo program.

The library is used by hwinfo to emulate Video BIOS (VBE) calls.

API functions


Create new emulation object

x86emu_t *x86emu_new(unsigned def_mem_perm, unsigned def_io_perm);

def_mem_perm are the default permissions for memory accesses, def_io_perm for io. See x86emu_set_perm(), x86emu_set_io_perm().

Free object later with x86emu_done().


Delete emulation object

x86emu_t *x86emu_done(x86emu_t *emu);

Frees all memory; returns NULL;


Clone emulation object

x86emu_t *x86emu_clone(x86emu_t *emu);

Creates a copy of emu. Free the copy later with x86emu_done().


Reset cpu state

void x86emu_reset(x86emu_t *emu);

Does a normal cpu reset (clear registers, set cs:eip).


Start emulation

unsigned x86emu_run(x86emu_t *emu, unsigned flags);


  • X86EMU_RUN_TIMEOUT: set emu->timeout to max. seconds to run.
  • X86EMU_RUN_MAX_INSTR: set emu->max_instr to max. instructions to emulate.

Return value indicates why x86emu_run() stopped (see flags).


Stop emulation

void x86emu_stop(x86emu_t *emu);

Use this function in callbacks (e.g. interrupt handler) to tell the emulator to stop. The emulator returns from x86emu_run() when the current instruction has been finished.


Set log buffer

void x86emu_set_log(x86emu_t *emu, char *buffer, unsigned buffer_size, x86emu_flush_func_t flush);
typedef void (* x86emu_flush_func_t)(x86emu_t *emu, char *buf, unsigned size);

If the log buffer is full, flush() is called (if not NULL). The buffer is freed in x86emu_done().


Write to log

void x86emu_log(x86emu_t *emu, const char *format, ...) __attribute__ ((format (printf, 1, 2)));


Clear log

void x86emu_clear_log(x86emu_t *emu, int flush);

Clear log buffer. If flush != 0, write current log via flush() function (see x86emu_set_log()).


Dump emulator state

void x86emu_dump(x86emu_t *emu, int flags);



Writes emulator state to log.


Memory permissions

void x86emu_set_perm(x86emu_t *emu, unsigned start, unsigned end, unsigned perm);

perm is a bitmask of:

  • X86EMU_PERM_{R,W,X}: memory is readable, writable, executable
  • X86EMU_PERM_VALID: memory has been initialized (say, been written to)
  • X86EMU_ACC_{R,W,X}: memory has been read, written, executed
  • X86EMU_ACC_INVALID: there was an invalid access (e.g. tried to read but not readable)


Direct memory access

void x86emu_set_page(x86emu_t *emu, unsigned offset, void *address);

Map memory area of X86EMU_PAGE_SIZE size at address into emulator at offset. offset must be X86EMU_PAGE_SIZE aligned, address needs not.

Memory permissions still apply (via x86emu_set_perm()).

If address is NULL, switch back to emulated memory.


io permissions

void x86emu_set_io_perm(x86emu_t *emu, unsigned start, unsigned end, unsigned perm);

perm: see x86emu_set_perm().


Reset memory access statistics

void x86emu_reset_access_stats(x86emu_t *emu);

Resets the X86EMU_ACC_* bits for the whole memory (see x86emu_set_perm()).


Execution hook

x86emu_code_handler_t x86emu_set_code_handler(x86emu_t *emu, x86emu_code_handler_t handler);
typedef int (* x86emu_code_handler_t)(x86emu_t *emu);

If defined, the function is called before a new instruction is decoded and emulated. If logging is enabled the current cpu state has already been logged. If the function returns a value != 0, the emulation is stopped.


Set interrupt handler

x86emu_intr_handler_t x86emu_set_intr_handler(x86emu_t *emu, x86emu_intr_handler_t handler);
typedef int (* x86emu_intr_handler_t)(x86emu_t *emu, u8 num, unsigned type);



and bitmask of:


If defined, the interrupt handler is called at the start of the interrupt handling procedure. The handler should return 1 to indicate the interrupt handling is complete and the emulator can skip its own interrupt processing or 0 to indicate the emulator should continue with normal interrupt processing.


Set alternative callback function that handles memory and io accesses

x86emu_memio_handler_t x86emu_set_memio_handler(x86emu_t *emu, x86emu_memio_handler_t handler);

typedef unsigned (* x86emu_memio_handler_t)(x86emu_t *emu, u32 addr, u32 *val, unsigned type);

type: one of


and one of:


Returns old function.


Execution hook

x86emu_cpuid_handler_t x86emu_set_cpuid_handler(x86emu_t *emu, x86emu_cpuid_handler_t handler);
typedef void (* x86emu_cpuid_handler_t)(x86emu_t *emu);

Set a callback function that handles the CPUID instruction. Allows the user to use the host's CPUID or provide a custom implementation to emulate a specific CPU.

Returns old function.

There's no default implementation. Without the handler installed the programm will raise an #UD exception.


Execution hook

x86emu_rdmsr_handler_t x86emu_set_rdmsr_handler(x86emu_t *emu, x86emu_rdmsr_handler_t handler);
typedef void (* x86emu_rdmsr_handler_t)(struct x86emu_s *);

Set alternative callback function that handles the RDMSR instruction. Allows the user to use the host's MSR or provide a custom implementation to emulate a specific platform.

Returns old function.

The default callback function uses the msr array in the x86emu_t structure to read MSRs from and updates the msr_perm array.


Execution hook

x86emu_wrmsr_handler_t x86emu_set_wrmsr_handler(x86emu_t *emu, x86emu_wrmsr_handler_t handler);
typedef void (* x86emu_wrmsr_handler_t)(struct x86emu_s *);

Set alternative callback function that handles the WRMSR instruction. Allows the user to use the host's MSR or provide a custom implementation to emulate a specific platform.

Returns old function.

The default callback function uses the msr array in the x86emu_t structure to write MSRs to and updates the msr_perm array.


Raise an interrupt

void x86emu_intr_raise(x86emu_t *emu, u8 intr_nr, unsigned type, unsigned err);

The interrupt is handled before the next instruction. For type see x86emu_set_intr_func(); if INTR_MODE_ERRCODE is set, err is the error code pushed to the stack.

memory access functions

unsigned x86emu_read_byte(x86emu_t *emu, unsigned addr);
unsigned x86emu_read_byte_noperm(x86emu_t *emu, unsigned addr);
unsigned x86emu_read_word(x86emu_t *emu, unsigned addr); 
unsigned x86emu_read_dword(x86emu_t *emu, unsigned addr);
void x86emu_write_byte(x86emu_t *emu, unsigned addr, unsigned val); 
void x86emu_write_byte_noperm(x86emu_t *emu, unsigned addr, unsigned val); 
void x86emu_write_word(x86emu_t *emu, unsigned addr, unsigned val); 
void x86emu_write_dword(x86emu_t *emu, unsigned addr, unsigned val);

Convenience functions to access emulator memory. Memory access restrictions (see x86emu_set_perm()) apply except for x86emu_*_noperm() which do not check permissions.


Set segment register

void x86emu_set_seg_register(x86emu_t *emu, sel_t *seg, u16 val);


x86emu_set_seg_register(emu, emu->x86.R_CS_SEL, 0x7c0);

Debug instruction

If the X86EMU_TRACE_DEBUG flags is set, the emulator interprets a special debug instruction:

db 0x67, 0xeb, LEN, DATA...

which is basically a short jump with address size prefix (an instruction normally not used). LEN is the size of DATA.

DATA can be:

db 0x01

Print STRING to log. STRING is not 0-zerminated.

db 0x02
dd flags

Set trace flags.

db 0x03
dd flags

Clear trace flags.

db 0x04
dd flags

Dump emulator state. For flags, see x86emu_dump().

db 0x05

Reset memory access stats. See x86emu_reset_access_stats().

openSUSE Development

To build, simply run make. Install with make install.

Basically every new commit into the master branch of the repository will be auto-submitted to all current SUSE products. No further action is needed except accepting the pull request.

Submissions are managed by a SUSE internal jenkins node in the InstallTools tab.

Each time a new commit is integrated into the master branch of the repository, a new submit request is created to the openSUSE Build Service. The devel project is system:install:head.

*.changes and version numbers are auto-generated from git commits, you don't have to worry about this.

The spec file is maintained in the Build Service only. If you need to change it for the master branch, submit to the devel project in the build service directly.

Development happens exclusively in the master branch. The branch is used for all current products.

You can find more information about the changes auto-generation and the tools used for jenkis submissions in the linuxrc-devtools documentation.