- Add LICENSES directory - Download additional licenses used - Add .reuse directory - Add doc about SPDX/reuse Co-authored-by: Florian Märkl <info@florianmaerkl.de>
304 lines
No EOL
11 KiB
Markdown
304 lines
No EOL
11 KiB
Markdown
# DEVELOPERS
|
|
|
|
This file is aimed at developers who want to work on the Rizin code base.
|
|
|
|
## Documentation
|
|
There is support for Doxygen document generation in this repo.
|
|
By running `doxygen` in the root of this repository, it will autodetect the
|
|
Doxyfile and generate HTML documentation into
|
|
[doc/doxygen/html/index.html](./doc/doxygen/html/index.html)
|
|
|
|
If you're contributing code or willing to update existing code, you can use the
|
|
doxygen C-style comments to improve documentation and comments in code.
|
|
See the [Doxygen Manual](http://www.doxygen.nl/manual/index.html)
|
|
for more info. Example usage can be found [here](http://www.doxygen.nl/manual/docblocks.html)
|
|
```c
|
|
/**
|
|
* \brief Find the min and max addresses in an RzList of maps.
|
|
* \param maps RzList of maps that will be searched through
|
|
* \param min Pointer to a ut64 that the min will be stored in
|
|
* \param max Pointer to a ut64 that the max will be stored in
|
|
* \param skip How many maps to skip at the start of an iteration
|
|
* \param width Divisor for the return value
|
|
* \return (max-min)/width
|
|
*
|
|
* Used to determine the min & max addresses of maps and
|
|
* scale the ascii bar to the width of the terminal
|
|
*/
|
|
static int findMinMax(RzList *maps, ut64 *min, ut64 *max, int skip, int width);
|
|
```
|
|
|
|
## Code style
|
|
|
|
### C
|
|
|
|
In order to contribute with patches or plugins, we encourage you to use the same
|
|
coding style as the rest of the code base.
|
|
|
|
* Use git-clang-format 11 to format your code. You should invoke it as below
|
|
(after making sure that your local copy of `dev` is up-to-date and your branch
|
|
is up-to-date with `dev`):
|
|
|
|
```bash
|
|
git-clang-format-11 --extensions c,cpp,h,hpp,inc --style file dev
|
|
```
|
|
|
|
* Lines should be at most 100 chars. A tab is considered as 8 chars. If it makes
|
|
things more readable, you can use more than 100 characters, but this should be
|
|
the exception, not the rule.
|
|
|
|
* Always use braces for if and while.
|
|
|
|
* In general, don't use goto. The goto statement only comes in handy when a
|
|
function exits from multiple locations and some common work such as cleanup
|
|
has to be done. If there is no cleanup needed, then just return directly.
|
|
|
|
Choose label names which say what the goto does or why the goto exists. An
|
|
example of a good name could be "out_buffer:" if the goto frees "buffer".
|
|
Avoid using GW-BASIC names like "err1:" and "err2:".
|
|
|
|
* Use `rz_return_*` functions to check preconditions that are caused by
|
|
programmers' errors. Please note the difference between conditions that should
|
|
never happen, and that are handled through `rz_return_*` functions, and
|
|
conditions that can happen at runtime (e.g. `malloc()` returns `NULL`, input coming
|
|
from user, etc.), and should be handled in the usual way through if-else.
|
|
|
|
```c
|
|
int check(RzCore *c, int a, int b) {
|
|
rz_return_val_if_fail(c, false);
|
|
rz_return_val_if_fail(a >= 0 && b >= 1, false);
|
|
|
|
if (a == 0) {
|
|
/* do something */
|
|
...
|
|
}
|
|
... /* do something else */
|
|
}
|
|
```
|
|
|
|
* Split long conditional expressions into small `static inline` functions to make them more readable:
|
|
|
|
```diff
|
|
+static inline bool inRange(RzBreakpointItem *b, ut64 addr) {
|
|
+ return (addr >= b->addr && addr < (b->addr + b->size));
|
|
+}
|
|
+
|
|
+static inline bool matchProt(RzBreakpointItem *b, int rwx) {
|
|
+ return (!rwx || (rwx && b->rwx));
|
|
+}
|
|
+
|
|
RZ_API RzBreakpointItem *rz_bp_get_in(RzBreakpoint *bp, ut64 addr, int rwx) {
|
|
RzBreakpointItem *b;
|
|
RzListIter *iter;
|
|
rz_list_foreach (bp->bps, iter, b) {
|
|
- if (addr >= b->addr && addr < (b->addr+b->size) && \
|
|
- (!rwx || rwx&b->rwx))
|
|
+ if (inRange(b, addr) && matchProt(b, rwx)) {
|
|
return b;
|
|
+ }
|
|
}
|
|
return NULL;
|
|
}
|
|
```
|
|
|
|
* Structure in the C files
|
|
|
|
The structure of the C files in Rizin must be like this:
|
|
|
|
```c
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
/* Copyright ... */ ## copyright
|
|
#include <rz_core.h> ## includes
|
|
static int globals ## const, define, global variables
|
|
static void helper(void) {} ## static functions
|
|
RZ_IPI void internal(void) {} ## internal apis (used only inside the library)
|
|
RZ_API void public(void) {} ## public apis starting with constructor/destructor
|
|
```
|
|
|
|
* Why return int vs enum
|
|
|
|
The reason why many places in Rizin-land functions return int instead of an enum type is because enums can't be OR'ed; otherwise, it breaks the usage within a switch statement and swig can't handle that stuff.
|
|
|
|
```
|
|
rz_core_wrap.cxx:28612:60: error: assigning to 'RzRegisterType' from incompatible type 'long'
|
|
arg2 = static_cast< long >(val2); if (arg1) (arg1)->type = arg2; resultobj = SWIG_Py_Void(); return resultobj; fail:
|
|
^ ~~~~
|
|
rz_core_wrap.cxx:32103:61: error: assigning to 'RzDebugReasonType' from incompatible type 'int'
|
|
arg2 = static_cast< int >(val2); if (arg1) (arg1)->type = arg2; resultobj = SWIG_Py_Void(); return resultobj; fail:
|
|
^ ~~~~
|
|
3 warnings and 2 errors generated.
|
|
````
|
|
|
|
* Do not use `assert.h`, use `rz_util/rz_assert.h` instead.
|
|
|
|
* You can use `export RZ_DEBUG_ASSERT=1` to set a breakpoint when hitting an assert.
|
|
|
|
* Function names should be explicit enough to not require a comment
|
|
explaining what it does when seen elsewhere in code.
|
|
|
|
* Use `RZ_API` define to mark exportable (public) methods only for module APIs
|
|
|
|
* The rest of functions must be static, to avoid polluting the global space.
|
|
|
|
* Avoid using global variables, they are evil.
|
|
|
|
* Do not write ultra-large functions: split them into multiple or simplify
|
|
the algorithm, only external-copy-pasted-not-going-to-be-maintained code
|
|
can be accepted in this way (gnu code, external disassemblers, etc..)
|
|
|
|
* Use the Rizin types instead of the ones in `<stdint.h>`, which are known to cause some
|
|
portability issues. So, instead of `uint8_t`, use `ut8`, etc.. As a bonus point they
|
|
are shorter to write.
|
|
|
|
* Never ever use `%lld` or `%llx`. This is not portable. Always use the `PFMT64x`
|
|
macros. Those are similar to the ones in GLIB. See all macroses in `librz/include/rz_types.h`.
|
|
|
|
### Shell Scripts
|
|
|
|
* Use `#!/bin/sh`
|
|
|
|
* Do not use bashisms `[[`, `$'...'` etc.
|
|
|
|
* Use our [shellcheck.sh](https://github.com/rizinorg/rizin/blob/master/sys/shellcheck.sh) script to check for problems and for bashisms
|
|
|
|
# Manage Endianness
|
|
|
|
As hackers, we need to be aware of endianness.
|
|
|
|
Endianness can become a problem when you try to process buffers or streams
|
|
of bytes and store intermediate values as integers with width larger than
|
|
a single byte.
|
|
|
|
It can seem very easy to write the following code:
|
|
```c
|
|
ut8 opcode[4] = { 0x10, 0x20, 0x30, 0x40 };
|
|
ut32 value = *(ut32*)opcode;
|
|
```
|
|
... and then continue to use "value" in the code to represent the opcode.
|
|
|
|
This needs to be avoided!
|
|
|
|
Why? What is actually happening?
|
|
|
|
When you cast the opcode stream to a unsigned int, the compiler uses the endianness
|
|
of the host to interpret the bytes and stores it in host endianness. This leads to
|
|
very unportable code, because if you compile on a different endian machine, the
|
|
value stored in "value" might be 0x40302010 instead of 0x10203040.
|
|
|
|
## Solution
|
|
|
|
Use bitshifts and OR instructions to interpret bytes in a known endian.
|
|
Instead of casting streams of bytes to larger width integers, do the following:
|
|
```c
|
|
ut8 opcode[4] = { 0x10, 0x20, 0x30, 0x40 };
|
|
ut32 value = opcode[0] | opcode[1] << 8 | opcode[2] << 16 | opcode[3] << 24;
|
|
```
|
|
or if you prefer the other endian:
|
|
```c
|
|
ut32 value = opcode[3] | opcode[2] << 8 | opcode[1] << 16 | opcode[0] << 24;
|
|
```
|
|
This is much better because you actually know which endian your bytes are stored in
|
|
within the integer value, REGARDLESS of the host endian of the machine.
|
|
|
|
## Endian helper functions
|
|
|
|
Rizin now uses helper functions to interpret all byte streams in a known endian.
|
|
|
|
Please use these at all times, eg:
|
|
```c
|
|
val32 = rz_read_be32(buffer) // reads 4 bytes from a stream in BE
|
|
val32 = rz_read_le32(buffer) // reads 4 bytes from a stream in LE
|
|
val32 = rz_read_ble32(buffer, isbig) // reads 4 bytes from a stream:
|
|
// if isbig is true, reads in BE
|
|
// otherwise reads in LE
|
|
```
|
|
There are a number of helper functions for 64, 32, 16, and 8 bit reads and writes.
|
|
|
|
(Note that 8 bit reads are equivalent to casting a single byte of the buffer
|
|
to a `ut8` value, ie endian is irrelevant).
|
|
|
|
## Packed structures
|
|
|
|
Due to the various differences between platforms and compilers Rizin
|
|
has a special helper macro - `RZ_PACKED()`. Instead of non-portable
|
|
`#pragma pack` or `__attribute__((packed))` it is advised to use this macro
|
|
instead. To wrap the code inside of it you just need to write:
|
|
```c
|
|
RZ_PACKED(union mystruct {
|
|
int a;
|
|
char b;
|
|
})
|
|
```
|
|
or in case of typedef:
|
|
```c
|
|
RZ_PACKED(typedef structmystruct {
|
|
int a;
|
|
char b;
|
|
})
|
|
```
|
|
|
|
## Modules
|
|
|
|
The Rizin code base is modularized into different libraries that are
|
|
found in `librz/` directory. The `binrz/` directory contains the programs
|
|
which use the libraries.
|
|
|
|
Hint: To find both the declaration and definition of a function named
|
|
*func_name*, you can use the following `git grep` command:
|
|
|
|
```bash
|
|
git grep -nWG "^[^[:blank:]].*func_name("
|
|
```
|
|
|
|
## JSON
|
|
|
|
Since many places in Rizin output JSON the special API was created, **PJ** which means "Print Json".
|
|
It allows to create nested JSON structs with a simple and short API. Full API reference is
|
|
available in `librz/include/rz_util/pj.h`.
|
|
|
|
Here is the short example of how we usually use **PJ**:
|
|
```c
|
|
PJ *pj = NULL;
|
|
if (mode == RZ_OUTPUT_MODE_JSON) {
|
|
pj = pj_new(); // creates a new instance of the API
|
|
if (!pj) {
|
|
return false;
|
|
}
|
|
}
|
|
// ... some other logic
|
|
// Creating the JSON structure
|
|
if (mode == RZ_OUTPUT_MODE_JSON) {
|
|
pj_o(pj); // creates a JSON list
|
|
pj_ki(pj, "id", some->id); // creates an element like "id": 6
|
|
pj_ks(pj, "name", some->name); // creates an element like "name": "bla"
|
|
pj_end(pj); // closes a JSON list
|
|
}
|
|
// ... some other logic
|
|
// Printing the JSON on the screen
|
|
if (mode == RZ_OUTPUT_MODE_JSON) {
|
|
rz_cons_println(pj_string(pj));
|
|
pj_free(pj); // free the instance of the API
|
|
}
|
|
```
|
|
It will produce the following output:
|
|
```json
|
|
{"id":6,"name":"bla"}
|
|
```
|
|
|
|
## Licenses
|
|
|
|
Rizin is trying to comply with the Software Package Data Exchange® (SPDX®),
|
|
an open standard to communicate in a clear way licenses and copyrights, among
|
|
other things, of a software. All files in the repository should either have
|
|
an header specifying the copyright and the license that apply or an entry in
|
|
.reuse/dep5 file. All pieces of code copied from other projects should have
|
|
a license/copyright entry as well.
|
|
|
|
In particular, the SPDX header may look like:
|
|
```C
|
|
// SPDX-FileCopyrightText: 2021 RizinOrg <info@rizin.re>
|
|
// SPDX-License-Identifier: LPGL-3.0-only
|
|
```
|
|
|
|
You can use the [REUSE Software](https://reuse.software/) to check the
|
|
compliance of the project and get the licenses/copyright of each file. |