Hallo,
dies soll der neue Update-Log Thread werden, da ich das User Interface
rausgeworfen habe und es somit nicht mehr viel mit einem Debugger zu tun
hat.
Übrig geblieben ist nun die Engine für Simulation und Analyse von AVR
Binaries. Und um die soll es nun hier gehen. Das ganze habe ich libvmcu
(Library for Virtual Microcontroller) genannt.
Features:
--------------
- statische Analyse (SFRs, ISRs, mögliche Labels, Cycle Analyse, ...)
- Echtzeit Simulation inklusive Peripherie und Backstepping
- Java Binding
Usecases:
--------------
libvmcu kann verwendet werden um
- Regression Tests für AVR Projekte zu erstellen
- eigene Tools (Simulatoren oder kleine Utilities) zu erstellen
- detaillierte Informationen aus dem Binary zu erhalten
Beispiel:
--------------
Dieses Beispiel stellt einen möglichen Regressiontest für ein ATmega328P
Programm dar.
Das zu testende Programm: Eine angeschlossene LED an PB5. Die LED soll
im Takt von einer Sekunde getoggled werden. Dazu wird der Timer0 im
Normal Mode verwendet und nach einer gewissen Anzahl an Overflows wird
die LED getoggled. Der Timer wurde mit berechneten Werten konfiguriert,
somit ist zu erwarten, dass dieser auch im richtigen Takt toggled.
Dies soll nun dennoch verifiziert werden, dabei ist eine Abweichung von
maximal +-0.05 Sekunden erlaubt. Der Test ist also erfolgreich, wenn die
Zeit zwischen dem LED toggling zwischen 0.95 und 1.05 Sekunden liegt.
1
#define TESTFILE "../../driver/led/led.hex"
2
3
#define PORTB 0x0025
4
#define PB5 0x05
5
6
#define bit(v, b) ((v & (1 << b)) >> b)
7
8
intmain(constintargc,constchar**argv){
9
10
uint8_tled;
11
12
/* ignoring checks for this example */
13
vmcu_report_t*report=vmcu_analyze_ihex(TESTFILE);
14
vmcu_system_t*sys=vmcu_system_ctor(report);
15
16
do{
17
18
vmcu_system_step(sys);
19
led=vmcu_system_read_data(sys,PORTB);
20
21
}while(bit(led,PB5)==0x00);
22
23
constdoublef=16000000U;
24
constdoublec=sys->cycles;
25
constdoubletime=(c/f);
26
27
assert((0.95<=time)&&(time<=1.05));
28
printf("Time between LED toggle: %lf [s]\n",time);
Update (v.0.7.3)
-> libvmcu bietet nun xrefs an (cross references). Der Analyzer kann nun
Referenzen setzten, sodass man diese dann später einsehen kann um
beispielsweise herauszufinden, welche Instruktion ein potentielles Label
referenziert.
-> VCD Trace Tool (driver) wurde hinzugefügt. Dieser driver wird wie
folgt aufgerufen: ./vcdump <file.hex> <cycles> <dataspace address>
Dabei wird die Datei <file.hex> solange simuliert bis <cycles> erreicht
wird. Währenddessen wird der Wert der <dataspace address> aufgezeichnet
(es können mehrere Addressen angegeben werden) und als .vcd exportiert.
Diese kann dann mit bspw. gtkwave interpretiert werden (siehe Screenshot
im Anhang).
-> ein kleiner Bugfix, welches die 'or' Instruktion betrifft.
xref Beispiel:
----------------
Ich suche zurzeit noch nach Leuten, die gerne was zum Projekt beitragen
würden.
Bei folgenden Teilen des Projekts bräuchte ich noch Unterstützung:
Bindings
------------------
Ein Python Binding ist zurzeit in Planung. Ein Python Binding würde es
ermöglichen, sehr schnell kleine Skripte zu schreiben, die mit der AVR
binary interagieren.
Das Binding müsste dann auch maintained werden, da sich die libvmcu
library in C auch verändern kann.
Prinzipiell bildet ein Binding ein Subsystem der Repository für welches
man dann selbst verantwortlich wäre.
Sollte mal das Binding eine längere Zeit nicht gebaut werden können, ist
dies zwar Schade aber kein großes Problem. Das Binding wird dann einfach
archiviert bis sich wieder einer bereit erklärt die Arbeit am Binding
wieder aufzunehmen.
Bindings für andere Sprachen sind zwar nicht geplant, jedoch hätte ich
nichts dagegen, solange diese auch gepflegt werden. Zurzeit wird das
Java Binding von pointbazaar maintained.
Bindings generell sind, dadurch dass sich das Library Interface
verändern kann und man somit das Binding adjustieren muss, ein etwas
höherer Aufwand.
Driver
--------------
Driver dagegen sind weniger aufwendig. Wenn man nur wenig Zeit hat aber
dennoch etwas beitragen möchte, sind Driver genau richtig.
Ein Driver ist eine Art Hilfsprogramm (utility) oder Beispielprogramm,
das gewisse Funktionen erfüllt indem es die libvmcu library benutzt.
Ein Beispiel für einen Driver wäre der "findisr" driver, welcher
mithilfe von libvmcu die ISRs in einer AVR binary ausfindig macht.
Bei den Drivers gibt es keine genauen Vorgaben. Man kann schreiben wozu
man Lust hat, solange es Funktionen aus der libvmcu verwendet und
kombiniert um neue Funktionen zu kreieren. Dabei müssen die Funktionen
keineswegs kompliziert sein.
Die Driver werden von mir im Nachhinein noch maintained, sodass sie auch
Veränderungen an der Library überstehen und nicht kaputt gehen.
Tests
------------
Weitere Tests werden ebenfalls benötigt. Generell werden so viele Tests
benötigt, dass ich alleine da nicht mehr hinterher kommen werde.
Würde mich freuen falls jemand Interesse daran hätte.
Update (v.0.7.4)
-> Die vmcu_sfr_t Struktur wurde dem Analyzer Report hinzugefügt. Diese
beinhaltet eine SFR ID und eine Liste an cross references (xrefs). Die
ID gibt an um welches konkrete SFR es sich handelt und die xrefs sind
eine Referenz (bzw. Zeiger) auf die Stelle im Disassembly an dem dieses
SFR angesprochen wird.
-> Es existiert nun ein neues enum (SFREGISTER). Die bereits oben
erwähnte SFR ID ist von genau diesem Typ. Dies ermöglicht es sehr
einfach SFRs zu filtern. Ein Beispiel wäre VMCU_EECR oder VMCU_RESERVED.
-> Weitere Verbesserungen am SFR Analyzer.
-> Kleinere Änderungen an der Namenskonvention von vmcu_report_t
membern. Listen erhalten den Namen der Structure im Singular. Die Listen
Counter erhalten den Namen der Structure im Singular inklusive des
Prefix n_
-> Bugfix im occurence driver
-> COM(A/B)x bits überschreiben nun auch die normale Portfunktion
xref (SFR) Beispiel
-----------------------
Update (v.0.8.0)
-> Devices und Device Model hinzugefügt.
Ein Device Model ist eine Abstraktion über verschiedene AVRs. Es
beinhaltet Mikrocontroller spezifische Daten, wie beispielsweise
Speichersektionen und Speicherlayouts.
Jedes implementierte device besitzt einen Device-Loader, welches das
Device Model mit relevanten Daten bezüglich des AVR Typen füttert. Das
Model kann dem Analyzer übergeben werden. Dieser analysiert dann das
Programm im Bezug auf das übergebene Model.
Dieses neue Feature vereinfacht es neue AVR Typen in die statische
Analyse zu integrieren.
Schritte zum hinzufügen eines neuen AVRs:
1.) erstelle das neue device arch/device/new_avr.c
2.) device-loader Funktion load_device_new_avr in
arch/device/new_avr.c
3.) befülle das device model mit den Daten zum neuen AVR
4.) verbinde die device-loader Funktion im Konstruktor des device
models
Damit wurde nun der AVR new_avr der stattischen Analyse hinzugefügt.
Bei einigen AVRs müsste man sicherlich noch das device model ein wenig
adjustieren, da dieser noch nicht alle Typen berücksichtigt.
-> vmcu_ prefix fehlte bei einigen Funktionen noch. Dies wurde hiermit
ergänzt. Damit dürften nun keinen Nameclashes mehr auftauchen (es sei
denn natürlich man verwendet absichtlich denselben prefix).
-> weitere tests für den Decoder (com -> sbic)
-> einige Bugfixes
Beispiel Device Models
-------------------------
Update (v.0.8.1)
-> Hinzugefügt: Gruppen für Instruktionen. Diese richten sich nach dem
offiziellen Instruction Set Manual. Bis auf, dass die "cp" (compare)
Instruktionen in die VMCU_GROUP_MATH_LOGIC Gruppe aufgenommen wurden, da
diese nun wirklich nichts in der Flow Gruppe zu suchen haben. Wer auch
immer auf die Idee gekommen ist.
1.) arithmetic/logic instructions (VMCU_GROUP_MATH_LOGIC)
2.) data transfer instructions (VMCU_GROUP_TRANSFER)
3.) MCU control instructions (VMCU_GROUP_SYS_CTRL)
4.) flow control instructions (VMCU_GROUP_FLOW)
5.) bit and bit-test instructions (VMCU_GROUP_BIT)
-> vmcu_plain_t nach vmcu_instr_t umbenannt
Beispiel (Filtern von FLOW Instruktionen in file.hex)
-------------------------------------------------------
Update (v.0.8.2)
-> Interrupt Vektor Analyse wurde hinzugefügt. Es werden nun Vektoren
erkannt und im Report zur Verfügung gestellt.
Bedingungen für einen Vektor:
[1] Die Instruktion muss im (default) Vectortable Segment liegen (nur
default, da BOOTRESET von Parametern abhängt).
[2] Die Instruktion muss entweder ein jmp oder ein rjmp sein
[3] Die Instruktion darf nicht zwischen zwei Vektoren liegen, falls ein
Vektor 32-bit breit ist.
[4] Es muss mindestens einmal ein "sei" im disassembly vorkommen, da man
sonst davon ausgehen muss, dass Interrupts gesperrt bleiben.
Eine zukünftige extra Bedingung wird sich die verwendeten SFRs anschauen
um festzustellen, ob für den potentiellen Vektor die erforderlichen
Konfigurationen vorgenommen werden.
Beispiel: Ausgeben von Interrupt Vektoren und deren ISR
------------------------------------------------------------
Update v.0.8.6
-> Submodul für String Analyse wurde hinzugefügt. Die im binary
vorhandenen Strings werden in die report structure abgelegt. Zurzeit nur
ASCII encoding, jedoch eventuell bald auch UTF16.
-> Dabei besteht die string structure (vmcu_string_t) aus einer
Stringadresse (Startadresse im FLASH), einer Stringlänge und einem
Zeichenbuffer der den String selbst repräsentiert. Eine Folge an bytes
wird als String erkannt, wenn sie mindestens 2 Zeichen lang ist (+ null
byte).
-> Der Nutzer muss non printable characters mittels einer kleinen
Hilfsfunktion selber handlen.
-> Ein kleiner Fehler im intel hex reader wurde behoben.
ASCII strings aus einer binary lesen
------------------------------------
v.0.8.8
-> vmcu_mnemonic_t structure zu vmcu_instr_t hinzugefügt. Diese Struktur
löst den "char *mnem" member ab. vmcu_mnemonic_t enthält 4 strings:
- base (z.B. "ldi", "movw", etc.)
- src (z.B. "r29", "0xff", etc.)
- dest ("r29", "0xff", etc.)
- commment (z.B. "; r21 <- 0xab", "IF <- 0", etc.)
-> disasm driver hinzugefügt. Dies ist ein kleiner disassembler mit
Syntax Highlighting, der die Vorteile der obigen Änderungen nutzt.
-> Außerdem wurde der interne disassembler überarbeitet. Diese
Überarbeitung war eine notwendige Anpassung an die obigen Änderungen.
-> Bestehende driver und die README wurden angepasst.
Dieses Update macht die Ausgabe vom disassembly viel flexibler.
Beispielsweise kann nun zwischen AT&T und Intel Syntax gewechselt werden
oder ganz bestimmte Teile können sehr einfach mit Syntax Highlighting
hervorgehoben werden.
Der Nachteil ist der, dass nun eine zusätzliche printer Fuktion
geschrieben werden muss um das disassembly auszugeben.
Diese Funktion könnte so aussehen (intel syntax)
-----------------------------
1
/* this snippet can be used to assemble and print an instruction */
2
3
voidprint_instruction(vmcu_instr_t*instr){
4
5
vmcu_mnemonic_t*mnem=&instr->mnem;
6
7
printf("%s ",mnem->base);
8
printf("%s",mnem->dest);
9
10
if(instr->dest.type!=VMCU_OP_NONE)
11
printf(", ");
12
13
printf("%s ",mnem->src);
14
printf("%s\n",mnem->comment);
15
}
Dies ist das neue Layout von vmcu_instr_t
-----------------------------
charcomment[40];///< comment string (todo: make comments optional)
9
10
}vmcu_mnemonic_t;
Wie man sehen kann, nimmt der Kommentar den meisten Speicher innerhalb
der Struktur ein. Um Speicher zu sparen könnte man mit bedingter
Kompilierung anfangen um einzelne Komponenten ein- bzw. auszuschalten.
Somit wären Kommentare optional und würden nur bei tatsächlichem Bedarf
Speicher verbrauchen.
Feedback/Verbesserungsvorschläge sind natürlich gern gesehen :)
v.0.8.9 - Überarbeitung von Operanden und Operandentypen
-> VMCU_OP wurde überarbeitet und in VMCU_OPTYPE umgenannt. VMCU_OPTYPE
wurde nun nach Spezifikation
(https://en.wikipedia.org/wiki/Atmel_AVR_instruction_set) erstellt (mit
einigen wenigen Änderungen).
Die Operandentypen sehen nun wie folgt aus:
- VMCU_OPTYPE_NONE = -1 (no operand, therefore no type)
- VMCU_OPTYPE_R, (register operand)
- VMCU_OPTYPE_RP, (registerpair operand)
- VMCU_OPTYPE_X, (x pointer operand)
- VMCU_OPTYPE_Y, (y pointer operand)
- VMCU_OPTYPE_Z, (z pointer operand)
- VMCU_OPTYPE_B, (bit number 0-7)
- VMCU_OPTYPE_K4, (4-bit immediate)
- VMCU_OPTYPE_K6, (6-bit immediate)
- VMCU_OPTYPE_K8, (8-bit immediate)
- VMCU_OPTYPE_IO5, (5-bit I/O address)
- VMCU_OPTYPE_IO6, (6-bit I/O address)
- VMCU_OPTYPE_D7, (7-bit data address)
- VMCU_OPTYPE_D16, (16-bit data address)
- VMCU_OPTYPE_P22, (22-bit program address)
- VMCU_OPTYPE_S7, (7-bit signed displacement, program memory)
- VMCU_OPTYPE_S12 (12-bit signed displacement, program memory)
-> VMCU_REGISTER enumeration für die GPRs (r0-r31) hinzugefügt.
-> vmcu_registerpair_t structure hinzugefügt. Diese Structure
repräsentiert ein gewöhnliches Registerpaar der Form Rh:Rl. Die Struktur
besteht aus 2 VMCU_REGISTER Variablen.
-> vmcu_operand_t wurde ebenfalls überarbeitet. Es enthält immernoch ein
Feld für den Operandentypen (VMCU_OPTYPE) und neuerdings eine C-Union um
die verschiedenen Datentypen darzustellen.
Die Union enthält folgende Datentypen:
- uint8_t k for type = K4, K6, K8
- uint8_t b for type = B
- uint8_t io for type = IO5, IO6
- uint16_t d for type = D7, D16
- uint32_t p for type = P22
- int16_t s for type = S7, S12
-> endloop driver (driver/endloop/) verbessert. Der endloop driver kann
nun zwischen bedingten und unbedingten Endlosschleifen unterscheiden und
ermittelt diese mittels statischer Analyse.
- Driver Code wurden an v.0.8.9 angepasst
vmcu_operand_t nach Überarbeitung
------------------------
1
typedefstructvmcu_operand{
2
3
union{
4
5
uint8_tk;
6
uint8_tb;
7
uint8_tio;
8
9
uint16_td;
10
uint32_tp;
11
int16_ts;
12
13
VMCU_REGISTERr;
14
vmcu_registerpair_trp;
15
};
16
17
VMCU_OPTYPEtype;
18
19
}vmcu_operand_t;
VMCU_OP nach Überarbeitung
------------------------
v.0.8.10
- Added: vmcu_access_t
- vmcu_access_t holds bitfields (representing booleans)
- following bitfields are available
- registers (true if instr reads/writes gpr)
- flash (true if instr reads/writes flash)
- stack (true if instr reads/writes stack)
- io (true if instr reads/writes io segment)
- ds (true if instr reads/writes data segment)
- sp (true if instr reads/writes stack pointer)
- pc (true if instr reads/writes program counter)
- c_flag (true if instr reads/writes carry flag)
- z_flag (true if instr reads/writes zero flag)
- n_flag (true if instr reads/writes negative flag)
- v_flag (true if instr reads/writes overflow flag)
- s_flag (true if instr reads/writes sign flag)
- h_flag (true if instr reads/writes halfcarry flag)
- t_flag (true if instr reads/writes t flag)
- i_flag (true if instr reads/writes interrupt flag)
- NOTE: Although (stack, io, registers ⊂ ds), (stack = true) or (io =
true)
or (registers = true) does not imply (ds = true).
- Added: annotator stage in pipeline
- this stage annotates instructions by adding additional information
about
the instruction itself, like groups and explicit/implicit read/write
access.
- Added: rwaccess driver (driver/rwaccess/)
- Adjusted drivers, etc.
Example
------------
1
/* A possible implementation of print_instruction can be found above */
v.0.8.11 - Finally added the control flow graph (cfg)
libvmcu has now control flow graphs
- added: CFG - control flow graph
- vmcu_cfg_t is the controlflow graph of libvmcu. It holds,
- vmcu_cfg_t node*, a dynamic array of vmcu_node_t structures
- node[0] is the entry node
- a node consists of
- vmcu_xref_t xto, a cross-reference to the corresponding
instruction (xref-to)
- vmcu_cfg_node_t *t, a node pointer for the true branch
- vmcu_cfg_node_t *f, a node pointer for the false branch
- int32_t used, a simple node counter, should be equal to
report.progsize
- added: CFG example driver (driver/cfg/)
- using CMake now
- added syntax highlight for stepper driver
A controlflow graph will allow us to analyze and model functions, loops,
cycles, ...
Printing control flow of a binary
------------------------------------
1
/* A possible implementation of print_instruction can be found in repository */