libvmcu - Virtual MCU Library (looking for contributors)

Thread Starter

Milo-D

Joined Jan 28, 2020
9
libvmcu - Virtual MCU Library

vcd_showcase.png


libvmcu is a library for static and dynamic analysis of AVR binaries.

It can be used to create regression tests for embedded systems or to analyze AVR programs on assembly level.
You can build your own tools too.

There is also a Java binding, that supports basic functionality.

Features:

Cycle accurate realtime Simulation

Cycle accurate realtime simulation of the microcontroller including its peripherals.

Decode, Decompose, Disassemble, Analyze

The pipeline offers an interface for each stage: decode, decompose, disassemble and analyze. Stages can either operate on a single opcode or a whole binary.

Combine Static and Dynamic Analysis

Perform a static analysis on your binary to receive additional information, for example which SFRs are used by the program. Then, use this information to improve your dynamic analysis.

No further dependencies

libvmcu comes with no further dependencies, thus allowing easy setup and easy usage.

Examples:

Printing disassembly of an intel hex file
C:
int main(void) {

    /* ignoring checks for this example */
    vmcu_report_t *report = vmcu_analyze_ihex("file.hex");

    for(int32_t i = 0; i < report->progsize; i++)
        printf("%s\n", report->disassembly[i].mnem);

    vmcu_report_dtor(report);
    return EXIT_SUCCESS;
}

Printing xrefs of potential labels
C:
int main(void) {
   
    /* ignoring checks for this example */
    vmcu_report_t *report = vmcu_analyze_ihex("file.hex");
   
    for(int32_t i = 0; i < report->nlabels; i++) {

        vmcu_label_t *lx = &report->labels[i];
        printf("0x%04x\tL%d\n\n", lx->addr, lx->id);

        for(int32_t j = 0; j < lx->nxrefs; j++) {

            vmcu_xref_t *x = &lx->xrefs[j];

            printf(" xref from 0x%04x ", x->p->addr);
            printf("(%s)\n", x->p->mnem);
        }

        printf("\n");
    }
   
    vmcu_report_dtor(report);
    return EXIT_SUCCESS;
}
Output:
Output:

0x0000  L0

xref from 0x0051 (jmp +0 ; PC <- 0x0)

0x0034  L1

xref from 0x0000 (jmp +52 ; PC <- 0x34)

0x0040  L2

xref from 0x0044 (brne -5 ; (Z = 0): PC <- PC - 0x5 + 1)

0x0042  L3

xref from 0x003f (rjmp +2 ; PC <- PC + 0x2 + 1)

0x0049  L4

xref from 0x004c (brne -4 ; (Z = 0): PC <- PC - 0x4 + 1)

0x004a  L5

xref from 0x0048 (rjmp +1 ; PC <- PC + 0x1 + 1)

0x0051  L6

xref from 0x0002 (jmp +81 ; PC <- 0x51)
xref from 0x0004 (jmp +81 ; PC <- 0x51)
xref from 0x0006 (jmp +81 ; PC <- 0x51)
xref from 0x0008 (jmp +81 ; PC <- 0x51)
xref from 0x000a (jmp +81 ; PC <- 0x51)
xref from 0x000c (jmp +81 ; PC <- 0x51)
xref from 0x000e (jmp +81 ; PC <- 0x51)
xref from 0x0010 (jmp +81 ; PC <- 0x51)
xref from 0x0012 (jmp +81 ; PC <- 0x51)
xref from 0x0014 (jmp +81 ; PC <- 0x51)
xref from 0x0016 (jmp +81 ; PC <- 0x51)
xref from 0x0018 (jmp +81 ; PC <- 0x51)
xref from 0x001a (jmp +81 ; PC <- 0x51)
xref from 0x001c (jmp +81 ; PC <- 0x51)
xref from 0x001e (jmp +81 ; PC <- 0x51)
xref from 0x0020 (jmp +81 ; PC <- 0x51)
xref from 0x0022 (jmp +81 ; PC <- 0x51)
xref from 0x0024 (jmp +81 ; PC <- 0x51)
xref from 0x0026 (jmp +81 ; PC <- 0x51)
xref from 0x0028 (jmp +81 ; PC <- 0x51)
xref from 0x002a (jmp +81 ; PC <- 0x51)
xref from 0x002e (jmp +81 ; PC <- 0x51)
xref from 0x0030 (jmp +81 ; PC <- 0x51)
xref from 0x0032 (jmp +81 ; PC <- 0x51)

Extracting details from opcode
C:
int main(void) {

    vmcu_plain_t p;
    vmcu_disassembly_bytes(0xd8e0, &p);

    const VMCU_IKEY key    = p.key;        // VMCU_LDI

    const uint32_t opcode  = p.opcode;     // 0xe0d8 (big endian)
    const uint16_t addr    = p.addr;       // 0x0000 (undefined)

    const bool dword       = p.dword;      // false
    const bool exec        = p.exec;       // true

    const char *mnemonic   = p.mnem;       // ldi r29, 0x08

    vmcu_operand_t *src    = &p.src;
    vmcu_operand_t *dest   = &p.dest;

    VMCU_OPTYPE src_type   = src.type;     // VMCU_IMM8
    VMCU_OPTYPE dest_type  = dest.type;    // VMCU_REGISTER

    const uint8_t src_val  = src.value;    // 0x08
    const uint8_t dest_val = dest.value;   // (r)29

    return EXIT_SUCCESS;
}

Unittest: Testing interrupt frequency of driver/led/led.hex
C:
#define TESTFILE  "../../driver/led/led.hex"

#define PORTB     0x0025
#define PB5       0x05

#define bit(v, b) ((v & (1 << b)) >> b)

int main(const int argc, const char **argv) {

    uint8_t led;

    /* ignoring checks for this example */
    vmcu_report_t *report = vmcu_analyze_ihex(TESTFILE);
    vmcu_system_t *sys    = vmcu_system_ctor(report);

    do {

        vmcu_system_step(sys);
        led = vmcu_system_read_data(sys, PORTB);

    } while(bit(led, PB5) == 0x00);

    const double f    = 16000000U;
    const double c    = sys->cycles;
    const double time = (c / f);

    assert((0.95 <= time) && (time <= 1.05));
    printf("Time between LED toggle: %lf [s]\n", time);

    vmcu_report_dtor(report);
    vmcu_system_dtor(sys);

    return EXIT_SUCCESS;
}
Output:
Time between LED toggle: 1.000021 [s]

This library is still a work in progress. That's why I am posting here. I am looking for contributors who are interested in developing bindings, tests and drivers (just to name few examples). Feedback is also always welcome.

https://github.com/Milo-D/libvmcu-Virtual-MCU-Library
 

Thread Starter

Milo-D

Joined Jan 28, 2020
9
Below I will give a few details on how you can contribute.

Bindings

I am planning a binding for python.

A python binding would allow us
to examine and simulate AVR binaries (or parts of it) without having to
compile and link a program. It would be a very interactive way of analyzing
and simulating binaries.

Bindings need to be maintained, since the libvcmu interface can change over time.

In general, a binding forms a subsystem of the repository for which
you would be responsible.

If the binding can not be built for a longer time, it is no problem, the binding becomes
archived until someone agrees to resume.

Bindings for other languages are not planned, but I would have
nothing against it, as long as these are also maintained. Currently pointbazaar maintains the Java Binding.

Bindings require in general, because the library interface
can change and you have to adjust the binding, high effort.

Drivers

Drivers, on the other hand, are less expensive in time. If you have little time
but still want to contribute, creating a driver might be the right choice.

A driver is an utility or example program, that fulfills certain functions by using the libvmcu library.

An example of a driver would be the "findisr" driver, which
locates ISRs in an AVR binary by using libvmcu.

There are no precise specifications for the drivers. You can create anything you want as long as
it combines libvmcu functions in order to achieve something new.

I will maintain the drivers afterwards to ensure that they withstand library changes.

Tests

More testing.
Generally there are so many tests
needed that I won't be able to keep up with it on my own.

Thanks for reading.
 
Top