3.8 KiB
Usage of the C API
Simple example to disassemble a word
#include "rabbitizer.h"
#include <stdlib.h>
int main() {
RabbitizerInstruction instr;
uint32_t word = 0x8D4A7E18;
uint32_t vram = 0x80000000;
char *buffer;
size_t bufferSize;
RabbitizerInstruction_init(&instr, word, vram);
RabbitizerInstruction_processUniqueId(&instr);
bufferSize = RabbitizerInstruction_getSizeForBuffer(&instr, 0, 0);
buffer = malloc(bufferSize + 1);
RabbitizerInstruction_disassemble(&instr, buffer, NULL, 0, 0);
printf("%s\n", buffer);
free(buffer);
RabbitizerInstruction_destroy(&instr);
return 0;
}
Compiling and running the above C code prints the following:
lw $t2, 0x7E18($t2)
Please note many safe-guards were removed from this example for simplicity, like
checking if malloc
returned a NULL
pointer.
Let's break up the example and explain each part:
- The stack
The RabbitizerInstruction
type is the type rabbitizer
uses to represent an
instruction. It is a simple struct which doesn't need dynamic memory
allocation of any kind, so it can be declared as an automatic variable and live
in the stack, without worrying about pointers and such.
The other stack variables should be self-explanatory. word
is a 32-bit word
representing a raw MIPS instruction (spoiler, it is an lw
). rabbitizer
needs to know the vram
address of the instruction it is decoding, so we
initialize with a place-holder one. buffer
and bufferSize
will be used for
storing the disassembled string.
int main() {
RabbitizerInstruction instr;
uint32_t word = 0x8D4A7E18;
uint32_t vram = 0x80000000;
char *buffer;
size_t bufferSize;
- Initializing
To initialize our instr
we need to call the pair RabbitizerInstruction_init
and RabbitizerInstruction_processUniqueId
. RabbitizerInstruction_init
initialises all the members of the struct so it doesn't contain garbage data
anymore, while RabbitizerInstruction_processUniqueId
does the heavy lifting of
identifying the actual instruction id out of the word
we passed.
A RabbitizerInstruction
variable is not considered fully initialized until it
has been passed to this pair of functions.
RabbitizerInstruction_init(&instr, word, vram);
RabbitizerInstruction_processUniqueId(&instr);
- Disassembling into a string
To disassemble the passed word as a string we can call
RabbitizerInstruction_disassemble
. This function expects a char
buffer to
fill, which should have enough space to hold the resulting string. To know how
big this buffer needs to be we should use the
RabbitizerInstruction_getSizeForBuffer
function which calculates a size big
enough to hold the disassembled string for the passed instruction (without
taking into account the finalizing NUL character, similar to how strlen
behaves).
With this information we can just malloc
our buffer and call
RabbitizerInstruction_disassemble
to get our disassembled instruction.
(Ignore the extra 0
and NULL
arguments for now, they will be discussed later)
bufferSize = RabbitizerInstruction_getSizeForBuffer(&instr, 0, 0);
buffer = malloc(bufferSize + 1);
RabbitizerInstruction_disassemble(&instr, buffer, NULL, 0, 0);
- Printing
Not much to say here, just print the disassembled instruction to stdout
.
printf("%s\n", buffer);
- Clean-up
Finally since we know we won't be using the produced string or the instruction
we just free
and RabbitizerInstruction_destroy
them.
As a curiosity, RabbitizerInstruction_destroy
currently does nothing, but
exists in case some destruction is needed in the future, so it recommended to
call this function as a future-proof method.
free(buffer);
RabbitizerInstruction_destroy(&instr);
return 0;
}