Skip to content

Commit 1f70129

Browse files
committed
Add a disassembly command to CLI
* bdx/cli.py (search): Improve help message. (disass): New command. * bdx/index.py (SearchResult.dynamic_fields): Add `endaddress' field.
1 parent b2990e7 commit 1f70129

File tree

3 files changed

+109
-15
lines changed

3 files changed

+109
-15
lines changed

README.md

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,26 +74,36 @@ Available options:
7474
available, to find the source file for a compiled file, if it can't be found
7575
in any other way.
7676

77-
### Graph generation ###
77+
### Disassembling ###
7878

79-
Generate an SVG image showing at most 20 routes from symbol `main` in
80-
`main.o` to all symbols in section `.text` in files matching wildcard
81-
`Algorithms_*`:
79+
After a directory is indexed, you can disassemble symbols matching a search
80+
query.
8281

83-
bdx graph 'main path:main.o' 'section:".text" AND path:Algorithms*' -n 20 | dot -Tsvg > graph.svg
82+
You can set the command to disassemble with the `-D`, `--disassembler` option,
83+
which can contain `{}` placeholders for replacement.
8484

85-
Example graphs: ![ASTAR](./examples/astar.svg) ![BFS](./examples/bfs.svg) ![DFS](./examples/dfs.svg)
85+
```
86+
$ bdx disass tree node defp source:./gcc/cp/parser* section:.text
8687
87-
By default this generates paths by using the ASTAR algorithm, the `--algorithm
88-
BFS` or `--algorithm DFS` options will use
89-
breadth-first-search/depth-first-search algorithms which can generate different
90-
graphs and can be slower/faster depending on the index and the queries
91-
provided.
88+
/src/gcc-12/build/gcc/cp/parser.o: file format elf64-x86-64
89+
90+
91+
Disassembly of section .text:
92+
93+
000000000000a1d0 <defparse_location(tree_node*)>:
94+
a1d0: 48 8b 47 08 mov 0x8(%rdi),%rax
95+
a1d4: 48 8b 10 mov (%rax),%rdx
96+
a1d7: 48 8b 40 08 mov 0x8(%rax),%rax
97+
a1db: 8b 7a 04 mov 0x4(%rdx),%edi
98+
a1de: 8b 50 04 mov 0x4(%rax),%edx
99+
a1e1: 89 fe mov %edi,%esi
100+
a1e3: e9 00 00 00 00 jmp a1e8 <defparse_location(tree_node*)+0x18>
101+
```
92102

93103
### Searching ###
94104

95-
`bdx search` command accepts a query string. A simple query language is
96-
recognized.
105+
`bdx search` and other commands accepts a query string. A simple query
106+
language is recognized.
97107

98108
```
99109
$ bdx search -n 5 tree
@@ -183,6 +193,22 @@ $ bdx search -n 5 -f '0x{address:0>10x}|{section:<10}|{type:8}|{demangled}' tree
183193
bdx search section:.rodata AND size:1000..
184194

185195

196+
### Graph generation ###
197+
198+
Generate an SVG image showing at most 20 routes from symbol `main` in
199+
`main.o` to all symbols in section `.text` in files matching wildcard
200+
`Algorithms_*`:
201+
202+
bdx graph 'main path:main.o' 'section:".text" AND path:Algorithms*' -n 20 | dot -Tsvg > graph.svg
203+
204+
Example graphs: ![ASTAR](./examples/astar.svg) ![BFS](./examples/bfs.svg) ![DFS](./examples/dfs.svg)
205+
206+
By default this generates paths by using the ASTAR algorithm, the `--algorithm
207+
BFS` or `--algorithm DFS` options will use
208+
breadth-first-search/depth-first-search algorithms which can generate different
209+
graphs and can be slower/faster depending on the index and the queries
210+
provided.
211+
186212
## License ##
187213

188214
```

bdx/cli.py

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import json
44
import os
5+
import subprocess
56
import sys
67
import time
78
from functools import wraps
@@ -14,7 +15,7 @@
1415
from click.types import BoolParamType, IntRange
1516

1617
import bdx
17-
from bdx import debug, error, info, log, make_progress_bar
18+
from bdx import debug, error, info, log, make_progress_bar, trace
1819
# fmt: off
1920
from bdx.binary import BinaryDirectory, find_compilation_database
2021
from bdx.index import (IndexingOptions, SymbolIndex, index_binary_directory,
@@ -311,7 +312,10 @@ def convert(self, value, param, ctx):
311312
@click.option(
312313
"-f",
313314
"--format",
314-
help="Output format (json, sexp, or Python string format)",
315+
help=(
316+
"Output format (json, sexp, or Python string format). "
317+
"'{}'-placeholders are replaced with symbol fields."
318+
),
315319
type=SearchOutputFormatParamType(),
316320
nargs=1,
317321
default=None,
@@ -357,6 +361,69 @@ def search(_directory, index_path, query, num, format):
357361
exit(1)
358362

359363

364+
@cli.command()
365+
@_common_options(index_must_exist=True)
366+
@click.argument(
367+
"query",
368+
nargs=-1,
369+
)
370+
@click.option(
371+
"-n",
372+
"--num",
373+
help="Limit the number of results",
374+
type=click.IntRange(1),
375+
metavar="LIMIT",
376+
default=None,
377+
)
378+
@click.option(
379+
"-D",
380+
"--disassembler",
381+
help=(
382+
"The command to run to disassemble a symbol. "
383+
"'{}'-placeholders are replaced with search keys."
384+
),
385+
nargs=1,
386+
default=(
387+
"objdump -dC "
388+
"'{path}' "
389+
"--section '{section}' "
390+
"--start-address 0x{address:x} --stop-address 0x{endaddress:x}"
391+
),
392+
)
393+
def disass(_directory, index_path, query, num, disassembler):
394+
"""Search binary directory for symbols."""
395+
results = search_index(
396+
index_path=index_path, query=" ".join(query), limit=num
397+
)
398+
399+
while True:
400+
try:
401+
res = next(results)
402+
except QueryParser.Error as e:
403+
error(f"Invalid query: {str(e)}")
404+
exit(1)
405+
except StopIteration:
406+
break
407+
408+
data = res.asdict()
409+
data.update(res.dynamic_fields())
410+
411+
try:
412+
cmd = disassembler.format(**data)
413+
except (KeyError, ValueError, TypeError) as e:
414+
error(
415+
"Invalid format: {} in '{}'\nAvailable keys: {}",
416+
str(e),
417+
disassembler,
418+
list(data.keys()),
419+
)
420+
exit(1)
421+
422+
trace("Symbol: {}", res)
423+
debug("Running command: {}", cmd)
424+
subprocess.check_call(cmd, shell=True)
425+
426+
360427
@cli.command()
361428
@_common_options(index_must_exist=True)
362429
def files(_directory, index_path):

bdx/index.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,7 @@ def dynamic_fields(self) -> dict[str, Any]:
10931093
"""Return useful additional fields that are set dynamically."""
10941094
return {
10951095
"basename": self.symbol.path.name,
1096+
"endaddress": self.symbol.address + self.symbol.size,
10961097
}
10971098

10981099

0 commit comments

Comments
 (0)