Update newshell documentation (#88)
This commit is contained in:
parent
9ce5003ca6
commit
a223c7f4a2
2 changed files with 100 additions and 163 deletions
258
doc/newshell.md
258
doc/newshell.md
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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: >
|
||||
|
|
|
|||
Loading…
Reference in a new issue