Update newshell documentation (#88)

This commit is contained in:
Riccardo Schirone 2020-11-16 10:36:54 +01:00 committed by GitHub
parent 9ce5003ca6
commit a223c7f4a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 100 additions and 163 deletions

View file

@ -79,77 +79,49 @@ hashtable, using their names as keys.
## How to write a new command?
Let's make an example and suppose we want to add the `sky` command, which would
find all occurrences of the word "sky" in a binary. By looking at
[cmd.c](https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/core/cmd.c#L7118)
we can find the right function where to define this new command. In this case,
it would be the `s` branch, which does some additional initialization in
`cmd_seek_init`. `cmd_seek_init` is defined
[here](https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/core/cmd_seek.c#L65)
and it gets called with `parent` being the command descriptor of the `s`
command. We of course want our `sky` command to be a child of `s` and be shown
under `s?` (assuming `sk` does not exist, otherwise we probably want to
consider adding it there if it makes sense).
Let's make an example and suppose we want to add the `sky` command, which
would find all occurrences of the word "sky" in a binary. The first thing to
do is to insert the `sky` command in
[`librz/core/cmd_descs.yaml`](https://github.com/rizinorg/rizin/blob/9ce5003ca647cdfc181ac3c0f6206762ebb9e3e9/librz/core/cmd_descs.yaml). That
file respects the same tree structure used when executing rizin and seeing
its help, so it should be simple to see where to place it. If we want to
place it under the `s` sub-tree, we just need to define a new descriptor
under `subcommands`, with at least `name`, `cname`, `summary` and a list of
`args` accepted by the command.
Now we need to choose what kind of `RzCmdDesc` we want to have. We can see the
various types in the
Now we need to choose what kind of command (`type` field in YAML) we want to
have. We can see the various types in the
[`RzCmdDescType`](https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/include/rz_cmd.h#L135-L151)
enum.
enum, however let's assume we want a regular command, which is the default
one, so no action is required in this regard.
For now, suppose we simply want a regular `sky` command under `s`. It is enough
to add a line with `DEFINE_CMD_ARGV_DESC (core, sky, parent);`. This macro
expects some things in place:
If our new `sky` command accepts a numeric argument, we can specify it in the
`args` list, by using the type `RZ_CMD_ARG_TYPE_NUM`.
Then we only need to write the actual code that performs the command's job.
You have to call it according to the `cname` field you previously set for the
`sky` command, appending `_handler` to that string.
- a handler named `sky_handler` of the type
[`RzCmdArgvCb`](https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/include/rz_cmd.h#L29),
which gets argc/argv and does the actual job of the `sky` command. See
[`RzCmdStatus`](https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/include/rz_cmd.h#L20-L26) enum for a list of possible status the command handler can
return.
- a
[`RzCmdDescHelp`](https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/include/rz_cmd.h#L86-L133)
structure named `sky_help`, which shall be defined/declared together with all
others in `cmd_helps.c`/`cmd_helps.h`. This structure provides all the strings
that are useful to understand what a command does and they can be queried by
using `<cmd>?` or `<cmd>??`.
Below you can see how the code for adding the `sky` command would look like:
```C
// cmd_helps.h
extern const RzCmdDescHelp sky_help;
```
```C
// cmd_helps.c
const RzCmdDescDetailEntry sky_help_examples[] = {
{ .text = "sky", .comment = "Find the first occurence of the word \"sky\"" },
{ .text = "sky", .comment = "Find the first two occurrences of the word \"sky\"" },
{ 0 },
};
const RzCmdDescDetailEntry sky_help_env[] = {
{ .text = "RZ_SKY_ICASE", .comment = "If defined, `sky` command ignores the case while searching." },
{ 0 },
};
const RzCmdDescDetail sky_help_details[] = {
{ .name = "Examples", .entries = sky_help_examples },
{ .name = "Enviroment variables", .entries = sky_help_env },
{ 0 },
};
const RzCmdDescHelp sky_help = {
.args_str = " [limit]", // for the sake of the example we assume the `sky` command accepts one optional argument
.summary = "Find occurrences of word \"sky\"",
.description = "It prints the addresses, one per line, of each occurrence of the "sky" word in the currently opened file. If `limit` is provided, at most `limit` lines are printed. If RZ_SKY_ICASE environment variable is set, it ignore the case while searching.",
.details = sky_help_details,
};
```YAML
- name: s
cname: cmd_seek
summary: Seek to address
type: RZ_CMD_DESC_TYPE_OLDINPUT
subcommands:
- name: sky
cname: sky
summary: Find all occurrences of the work "sky" in the opened file
args:
- name: n
type: RZ_CMD_ARG_TYPE_NUM
optional: true
```
```C
// cmd_seek.c (example, real place depends on the parent command)
static RzCmdStatus sky_handler(RzCore *core, int argc, const char **argv) {
// argc/argv is like in main(), i.e. argv[0] always contains the command name
if (argc > 2) {
return RZ_CMD_STATUS_WRONG_ARGS;
}
int limit = -1;
if (argc > 1) {
limit = rz_num_math (core->num, argv[1]);
@ -157,115 +129,77 @@ static RzCmdStatus sky_handler(RzCore *core, int argc, const char **argv) {
// ... add the logic of your command
return RZ_CMD_STATUS_OK;
}
static void cmd_seek_init(RzCore *core, RzCmdDesc *parent) {
DEFINE_CMD_ARGV_DESC (core, sky, parent);
}
```
### Grouped commands
If at some point we want to make `sky` a group and add some sub-commands to it
(e.g. `sky`, `skyl`, `skyf`, `skyp`) we have to change its type to
`RZ_CMD_DESC_TYPE_GROUP` by using `DEFINE_CMD_ARGV_GROUP_EXEC` instead of
`DEFINE_CMD_ARGV_DESC`. So we would use something like:
```C
DEFINE_CMD_ARGV_GROUP_EXEC (core, sky, parent);
DEFINE_CMD_ARGV_DESC (core, skyl, sky_cd);
DEFINE_CMD_ARGV_DESC (core, skyf, sky_cd);
DEFINE_CMD_ARGV_DESC (core, skyp, sky_cd);
The YAML file is used at built-time (by meson only) to autogenerate two
files: `cmd_descs.c` and `cmd_descs.h`. To make sure the Make build system
still works, update the committed versions of these files with the following
command:
```
$ ./librz/core/cmd_descs_generate.py --output-dir ./librz/core/ ./librz/core/cmd_descs.yaml
```
`DEFINE_CMD_ARGV_GROUP_EXEC (core, sky, parent)` expects:
- a handler named `sky_handler`, as before
- a `RzCmdDescHelp` structure named `sky_help`. This is used to describe details
and help messages of the `sky` command itself and its arguments, as before. It
should explain how the `sky` command works, what are its arguments, etc..
- a `RzCmdDescHelp` structure named `sky_group_help` which is used to describe
`sky` as a group. For example, the summary of the group could be `Commands to
work with the sky, planets and stars`.
## Where is the handler of command `x`?
If we wanted to have a group named `sky` without an actual `sky` command, we
would instead use `DEFINE_CMD_ARGV_GROUP` macro. In this case, the handler
`sky_handler` is not expected/required.
## How to convert an oldinput command descriptor to argv?
If we want to convert a particular sub-command, just add the command we want to
convert as explained in the previous section. `RzCmd` will automatically select
the new handler if it can finds one.
If we want to convert an entire sub-tree of the available commands (e.g. we want
to convert all `y` sub-tree), we have to start by adapting
[`rz_core_cmd_init`](https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/core/cmd.c#L7176),
by making sure to specify a `descriptor_init` if not available, help structures
as required, the type of the command descriptor (very likely it will be a
`RZ_CMD_DESC_TYPE_GROUP`), and the command handler, in case the name of the
group is used to also identify a command (see
[cmd.c:7174](https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/core/cmd.c#L7174)
for an example).
At this point we can either convert in one shot all existing subcommands to use
`RZ_CMD_DESC_TYPE_ARGV`/`GROUP` as appropriate (this is the state we want to be
in, but it may require time to convert everything), or we could do the
transition gradually and define the subcommands as `RZ_CMD_DESC_TYPE_OLDINPUT`.
If we take the `RZ_CMD_DESC_TYPE_ARGV`/`GROUP` approach, we just have to create
new commands like explained above. If possible, try to refactor the code to
share as much as possible with existing handlers and avoid duplication. If the
command to implement is simple enough, don't waste too much time with this, as
`cfg.newshell` is anyway the default.
Otherwise, `RZ_CMD_DESC_TYPE_OLDINPUT` is used to describe command handlers that
do the parsing themselves, like the existing ones, and handlers of this type
have the signature `typedef int (*RzCmdCb) (void *user, const char *input)`. We
can define children of the command descriptor with `DEFINE_CMD_OLDINPUT_DESC`
and mostly re-use existing code. As an example, see [commit
cde558e6e5788d0a6d544ab975b144ed59190676](https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/core/cmd_write.c#L2174).
In that case, only some commands (`w0`, `w1+`, `w6`, `wB`, etc.) were converted
to the newshell style, while `wh`, `we`, `wp`, etc. were still handled by the
existing handlers. Existing code has been refactored so that the code could be
easily shared (see [`cmd_write`
function](https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/core/cmd_write.c#L2048)).
To define the command descriptor as `RZ_CMD_DESC_TYPE_OLDINPUT`, we can use
`DEFINE_CMD_OLDINPUT_DESC (core, yz, parent)`, which expects:
- a handler named `yz_handler_old` of the type
[`RzCmdCb`](https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/include/rz_cmd.h#L28),
which gets `RzCore` as first argument and a pointer to the first character
after `yz` (the name of the command) of the input string.
- a `RzCmdDescHelp` structure named `yz_help`, which shall be defined/declared
together with all others in `cmd_helps.c`/`cmd_helps.h`. This structure
provides all the strings that are useful to understand what a command does and
they can be queried by using `<cmd>?` or `<cmd>??`.
We probably won't need to create `yz_handler_old` from scratch, as there is
already code that handles that, though it is probably nested in some
switch-cases (e.g.
https://github.com/rizinorg/rizin/blob/cde558e6e5788d0a6d544ab975b144ed59190676/librz/core/cmd.c#L878).
In such a case, we are expected to do a bit of refactoring and extract pieces of
code in a separate function, named `yz_handler_old`, and use it in the
switch-case code.
## Where is the help/handler of command `x`?
If you are looking for the help of command `x`, there are some conventions we
are trying to use to make it easier to locate its info. Help descriptions of `x`
can be found in `cmd_helps.c`/`cmd_helps.h` with the name `x_help` (or
`x_group_help` if `x` represents also a group of sub-commands). The handler of
`x` instead can be in one of the various `cmd_*.c` files, depending on what it
does, but it should be named `x_handler`.
If you are looking for the handler of command `x`, you just have to look at
the file
[`librz/core/cmd_descs.yaml`](https://github.com/rizinorg/rizin/blob/9ce5003ca647cdfc181ac3c0f6206762ebb9e3e9/librz/core/cmd_descs.yaml).
By looking at the `cname` field of the command descriptor, you can see what
is the name of the handler of the `x` command. If the `cname` is `hex` and
the type is `RZ_CMD_DESC_TYPE_OLDINPUT`, then the handler will be named
`rz_hex`. In all other cases, the handler will be named `rz_hex_handler`.
Some examples:
- command: `wv`, handler: `cmd_write.c:wv_handler()`, help: `cmd_helps.c:wv_help`, group help: `cmd_helps.c:wv_group_help`;
- command: `w6d`, handler: `cmd_write.c:w6d_handler()`, help: `cmd_helps.c:w6d_help`.
- command: `wv`, type: unspecified (default to `RZ_CMD_DESC_TYPE_ARGV`), handler: `rz_write_value_handler`
- command: `w6d`, type: unspecified (default to `RZ_CMD_DESC_TYPE_ARGV`), handler: `rz_write_base64_decode_handler`
- command: `s`, type: `RZ_CMD_DESC_TYPE_OLDINPUT`, handler: `rz_cmd_seek`
When a command `x` contains special characters that cannot be used as
variable/function names in C, we convert them to reasonable strings. `*` becomes
`_star_`, `.` becomes `_dot_`, `%` becomes `_percentage_`, etc.
## How to improve the help messages of a command
Some examples:
- command: `wB-`, handler: `cmd_write.c:wB_minus_handler()`, help: `cmd_helps.c:wB_minus_help`
- command: `z*`, handler: `cmd_zign.c:z_star_handler()`, help: `cmd_helps.c:z_star_help`.
Find the command in
[`librz/core/cmd_descs.yaml`](https://github.com/rizinorg/rizin/blob/9ce5003ca647cdfc181ac3c0f6206762ebb9e3e9/librz/core/cmd_descs.yaml),
then fix/improve the `summary` and/or `description` fields. Once you have
done that, make sure to test your changes are effective by compiling as usual
and then run:
```
$ ./librz/core/cmd_descs_generate.py --output-dir ./librz/core/ ./librz/core/cmd_descs.yaml
```
The above rules should help you find the relevant part in the code for each command.
## How to show examples of a command or additional details
You may notice some commands like `env`, `%`, `*` and others have additional
sections when you show the extensive help with e.g. `env??` (or `%??`, etc.).
Those additional secions are called `details` and they can be specified,
again, in the file
[`librz/core/cmd_descs.yaml`](https://github.com/rizinorg/rizin/blob/9ce5003ca647cdfc181ac3c0f6206762ebb9e3e9/librz/core/cmd_descs.yaml).
The structure is explained at the beginning of the file and it can be seen in
existing commands (e.g.
https://github.com/rizinorg/rizin/blob/9ce5003ca647cdfc181ac3c0f6206762ebb9e3e9/librz/core/cmd_descs.yaml#L325
). The result, looks something like:
```
[0x00000000]> %??
Usage: %[varname[=varvalue]] # get/set environment variables
Examples:
| % # list all environment variables
| %SHELL # print value of SHELL variable
| %TMPDIR=/tmp # set TMPDIR to "/tmp"
| envSHELL # same as `%SHELL`
Environment:
| RZ_FILE # currently opened file name
| RZ_OFFSET # 10base offset 64bit value
| RZ_BYTES # TODO: variable with bytes in curblock
| RZ_XOFFSET # same as above, but in 16 base
| RZ_BSIZE # block size
| RZ_ENDIAN # 'big' or 'little'
| RZ_IOVA # is io.va true? virtual addressing (1,0)
| RZ_DEBUG # debug mode enabled? (1,0)
| RZ_BLOCK # TODO: dump current block to tmp file
| RZ_SIZE # file size
| RZ_ARCH # value of asm.arch
| RZ_BITS # arch reg size (8, 16, 32, 64)
| RZ_BIN_LANG # assume this lang to demangle
| RZ_BIN_DEMANGLE # demangle or not
| RZ_BIN_PDBSERVER # e pdb.server
```

View file

@ -1,6 +1,9 @@
# Format of RzCmdDesc/RzCmdDescHelp descriptor
# - name: same as RzCmdDesc.name (mandatory)
# cname: name used in the generated C code for structures, handlers, etc.
# cname: >
# name used in the generated C code for structures, handlers, etc. Make sure
# this is a valid C name. It cannot contain special characters like `$`, `"`,
# `=`, etc.
# summary: same as RzCmdDescHelp.summary (mandatory)
# description: same as RzCmdDescHelp.description
# type: >