mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
Import docs
This commit is contained in:
@@ -268,6 +268,28 @@ find_package(Python REQUIRED)
|
||||
file(RELATIVE_PATH PYTHON_INSTALL_PATH "${CMAKE_INSTALL_PREFIX}"
|
||||
"${Python_SITELIB}")
|
||||
|
||||
#
|
||||
# Build docs
|
||||
#
|
||||
find_program(MKDOCS NAMES mkdocs)
|
||||
add_custom_command(
|
||||
OUTPUT share/doc/revng/html/404.html
|
||||
DEPENDS share/doc/revng/references/mime-types.md
|
||||
share/doc/revng/references/ptml.md
|
||||
share/doc/revng/user-manual/analyses.md
|
||||
share/doc/revng/user-manual/index.md
|
||||
share/doc/revng/user-manual/metaaddress.md
|
||||
share/doc/revng/user-manual/model.md
|
||||
share/doc/revng/user-manual/model-tutorial.md
|
||||
share/doc/revng/user-manual/working-environment.md
|
||||
share/doc/revng/what-is-revng.md
|
||||
share/doc/revng/index.md
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
COMMAND ${MKDOCS} build --quiet --strict --site-dir
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/share/doc/revng/html"
|
||||
COMMENT "Generating mkdocs")
|
||||
add_custom_target(mkdocs ALL DEPENDS share/doc/revng/html/404.html)
|
||||
|
||||
#
|
||||
# Proceed to subdirectories
|
||||
#
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
## MIME types
|
||||
|
||||
* MIMEs describe the content of the container in its entirety (not what's contained within)
|
||||
* Trying to conform as close as possible to [RFC2045](https://datatracker.ietf.org/doc/html/rfc2045) and [RFC6838](https://www.rfc-editor.org/rfc/rfc6838)
|
||||
* Types:
|
||||
* `text/*`: the output will be opened in a text editor
|
||||
* everything else: the output needs to be managed by an "external application"
|
||||
* Base MIMEs:
|
||||
* Built-in MIMEs:
|
||||
* `application/x-executable` -> a binary
|
||||
* `application/x-object` -> an object file
|
||||
* `text/plain` -> Plain text file
|
||||
* Custom MIMEs:
|
||||
* `text/x.c` -> C
|
||||
* `text/x.asm` -> Assembly
|
||||
* `text/x.yaml` -> Plain YAML
|
||||
* `image/svg` -> svg
|
||||
* `text/x.llvm.ir` -> LLVM IR
|
||||
* `application/x.llvm.bc` -> LLVM Bitcode
|
||||
|
||||
Mimes that are not `text/*` or `image/svg` will be transmitted over GraphQL via Base64 encoding
|
||||
|
||||
* Suffixes (order matters):
|
||||
* `+ptml` LHS is wrapped in PTML
|
||||
* `+yaml` LHS is wrapped in a YAML dictionary with keys being functions metaaddresses (soon: locations)
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
site_name: rev.ng docs
|
||||
docs_dir: share/doc/revng
|
||||
theme:
|
||||
name: material
|
||||
palette:
|
||||
scheme: revng
|
||||
font:
|
||||
text: DM Sans
|
||||
code: DM Mono
|
||||
logo: assets/logo.svg
|
||||
features:
|
||||
- navigation.footer
|
||||
- content.code.copy
|
||||
nav:
|
||||
- "What's rev.ng?": "what-is-revng.md"
|
||||
- "User's Manual":
|
||||
- Introduction: "user-manual/index.md"
|
||||
- "Getting a working environment": "user-manual/working-environment.md"
|
||||
- MetaAddress: "user-manual/metaaddress.md"
|
||||
- Model: "user-manual/model.md"
|
||||
- "Building a model from scratch": "user-manual/model-tutorial.md"
|
||||
- "Analyses": "user-manual/analyses.md"
|
||||
- "References":
|
||||
- "MIME types": references/mime-types.md
|
||||
- PTML: references/ptml.md
|
||||
markdown_extensions:
|
||||
- mkdocs_graphviz
|
||||
- pymdownx.superfences
|
||||
- pymdownx.tabbed:
|
||||
alternate_style: true
|
||||
- toc:
|
||||
permalink: "#"
|
||||
extra_css:
|
||||
- stylesheets/main.css
|
||||
- stylesheets/theme.css
|
||||
extra_javascript:
|
||||
- https://cdn.jsdelivr.net/gh/rod2ik/cdn@main/mkdocs/javascripts/mkdocs-graphviz.js
|
||||
@@ -171,6 +171,7 @@ set(REVNG_CLI_COMMANDS_MODULE_FILES
|
||||
revng/internal/cli/_commands/daemon.py
|
||||
revng/internal/cli/_commands/hard_purge.py
|
||||
revng/internal/cli/_commands/import_idb.py
|
||||
revng/internal/cli/_commands/test_docs.py
|
||||
revng/internal/cli/_commands/trace_run.py
|
||||
revng/internal/cli/_commands/model_compare.py)
|
||||
python_module(TARGET_NAME revng-python-cli-commands WHEEL revng_internal
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
#
|
||||
# This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
#
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
from abc import ABC
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
from typing import Mapping
|
||||
|
||||
import marko
|
||||
|
||||
from revng.internal.cli.commands_registry import Command, CommandsRegistry, Options
|
||||
|
||||
verbose = False
|
||||
|
||||
|
||||
def log(message):
|
||||
global verbose
|
||||
if verbose:
|
||||
sys.stderr.write(message + "\n")
|
||||
|
||||
|
||||
def run(working_directory, arguments):
|
||||
log(f"Running {shlex.join(arguments)}")
|
||||
process = subprocess.run(
|
||||
arguments, cwd=working_directory, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
|
||||
if process.returncode != 0:
|
||||
sys.stderr.write(f"The following program failed: {shlex.join(arguments)}\n")
|
||||
sys.stderr.flush()
|
||||
sys.stderr.buffer.write(process.stdout)
|
||||
sys.stderr.flush()
|
||||
sys.stderr.write(f"Process exited with code {process.returncode}\n")
|
||||
sys.stderr.write("Re-run with --verbose for further info\n")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class Doctest(ABC):
|
||||
def run(self, working_directory):
|
||||
pass
|
||||
|
||||
def process(self, code, extra=""):
|
||||
pass
|
||||
|
||||
|
||||
class PythonDoctest(Doctest):
|
||||
def __init__(self):
|
||||
self.script = ""
|
||||
|
||||
def run(self, working_directory):
|
||||
if not self.script:
|
||||
return
|
||||
|
||||
self.script = self.script.strip() + "\n"
|
||||
log(f"Running the following Python script:\n{textwrap.indent(self.script, ' ')}\n")
|
||||
|
||||
script_path = working_directory / "run.py"
|
||||
script_path.write_text(self.script)
|
||||
run(
|
||||
working_directory,
|
||||
["python", "-c", "import doctest; import sys; doctest.testfile('run.py')"],
|
||||
)
|
||||
|
||||
def process(self, code, extra=""):
|
||||
self.script += code + "\n"
|
||||
|
||||
|
||||
class BashDoctest(Doctest):
|
||||
def __init__(self):
|
||||
self.script = ""
|
||||
self.expected_output = ""
|
||||
|
||||
def run(self, working_directory):
|
||||
if not self.script and not self.expected_output:
|
||||
return
|
||||
|
||||
self.script = self.script.strip() + "\n"
|
||||
script_path = working_directory / "run.sh"
|
||||
script_path.write_text(self.script)
|
||||
|
||||
self.expected_output = self.expected_output.strip() + "\n"
|
||||
expected_output_path = working_directory / "expected_output.log"
|
||||
expected_output_path.write_text(self.expected_output)
|
||||
|
||||
log(f"Running the following bash script:\n{textwrap.indent(self.script, ' ')}\n")
|
||||
log(f"Expected output is:\n{textwrap.indent(self.expected_output, ' ')}\n")
|
||||
|
||||
run(
|
||||
working_directory,
|
||||
[
|
||||
"bash",
|
||||
"-c",
|
||||
"bash -euo pipefail ./run.sh |& tee output.log"
|
||||
+ " && diff -Bwu output.log expected_output.log",
|
||||
],
|
||||
)
|
||||
|
||||
def process(self, code, extra=""):
|
||||
next_line_is_command = False
|
||||
silent = "silent" in extra
|
||||
match = re.match('.*ignore="([^"]*)".*', extra)
|
||||
ignore_regexp = None
|
||||
if match:
|
||||
ignore_regexp = match.groups()[0]
|
||||
self.script += "( "
|
||||
|
||||
if silent:
|
||||
self.script += "( "
|
||||
|
||||
for line in code.split("\n"):
|
||||
if line.startswith("$ "):
|
||||
self.script += line[2:] + "\n"
|
||||
if line.endswith("\\"):
|
||||
next_line_is_command = True
|
||||
elif next_line_is_command:
|
||||
self.script += line + "\n"
|
||||
next_line_is_command = line.endswith("\\")
|
||||
else:
|
||||
if not ignore_regexp or not re.match(".*(" + ignore_regexp + ").*", line):
|
||||
self.expected_output += line + "\n"
|
||||
|
||||
if silent:
|
||||
self.script += " ) >& /dev/null\n"
|
||||
|
||||
if ignore_regexp:
|
||||
self.script += f" ) |& grep -vE '{ignore_regexp}'\n"
|
||||
|
||||
|
||||
def escape_js(string):
|
||||
assert type(string) is str
|
||||
return json.dumps(string)
|
||||
|
||||
|
||||
def emit_assertion(expected):
|
||||
return f"if (JSON.stringify(last) !== {escape_js(expected[:-1])})\n process.exit(1);\n"
|
||||
|
||||
|
||||
class TypeScriptDoctest(Doctest):
|
||||
def __init__(self):
|
||||
self.script = ""
|
||||
|
||||
def run(self, working_directory):
|
||||
if not self.script:
|
||||
return
|
||||
|
||||
self.script = self.script.strip() + "\n"
|
||||
log(f"Running the following TypeScript script:\n{textwrap.indent(self.script, ' ')}")
|
||||
script_path = working_directory / "run.ts"
|
||||
script_path.write_text(self.script)
|
||||
run(
|
||||
working_directory,
|
||||
[
|
||||
"bash",
|
||||
"-c",
|
||||
"""npm install typescript revng-model @types/node"
|
||||
+ " && ./node_modules/.bin/tsc run.ts"
|
||||
+ " && node run.js""",
|
||||
],
|
||||
)
|
||||
|
||||
def process(self, code, extra=""):
|
||||
output = "console.log = (x) => { return x; };\nlet last;\n"
|
||||
|
||||
last_output = ""
|
||||
lines = list(map(str.strip, code.split("\n")))[:-1]
|
||||
for line, next_line in zip(lines, lines[1:] + ["> "]):
|
||||
is_command = line.startswith("> ")
|
||||
next_is_command = next_line.startswith("> ")
|
||||
|
||||
if is_command:
|
||||
if last_output:
|
||||
output += emit_assertion(last_output)
|
||||
last_output = ""
|
||||
|
||||
line = line[2:]
|
||||
if next_is_command:
|
||||
output += f"{line}\n"
|
||||
else:
|
||||
output += f"last = {line}\n"
|
||||
|
||||
else:
|
||||
last_output += line + "\n"
|
||||
|
||||
if last_output:
|
||||
output += emit_assertion(last_output)
|
||||
|
||||
return output
|
||||
|
||||
|
||||
def only(entries):
|
||||
assert len(entries) == 1
|
||||
return entries[0]
|
||||
|
||||
|
||||
def handle_file(path: Path):
|
||||
handler_types = {
|
||||
"python": PythonDoctest,
|
||||
"bash": BashDoctest,
|
||||
"typescript": TypeScriptDoctest,
|
||||
}
|
||||
|
||||
handlers: Mapping[str, Doctest] = {
|
||||
language: constructor() for language, constructor in handler_types.items()
|
||||
}
|
||||
|
||||
document = marko.parse(path.read_text())
|
||||
|
||||
for block in document.children:
|
||||
if type(block) is not marko.block.FencedCode:
|
||||
continue
|
||||
|
||||
block.lang = block.lang.strip("{").strip("}")
|
||||
block.extra = block.extra.strip("{").strip("}")
|
||||
|
||||
if "notest" in block.extra:
|
||||
log(
|
||||
f"Ignoring fenced code of type {block.lang}"
|
||||
+ f' due to `notest` in "{block.extra}"'
|
||||
)
|
||||
continue
|
||||
|
||||
if "noorchestra" in block.extra:
|
||||
log(f'Disabling orchestra environment due to `noorchestra` in "{block.extra}"')
|
||||
handlers["bash"].process(
|
||||
"$ unset ORCHESTRA_DOTDIR;"
|
||||
+ " unset ORCHESTRA_ROOT;"
|
||||
+ " unset ORCHESTRA_NODE_CACHE;"
|
||||
+ " export ORCHESTRA_DOTDIR ORCHESTRA_ROOT ORCHESTRA_NODE_CACHE;"
|
||||
)
|
||||
|
||||
rawtext = only(block.children)
|
||||
assert type(rawtext) is marko.inline.RawText
|
||||
text = rawtext.children
|
||||
assert type(text) is str
|
||||
|
||||
match = re.match(r"title=([^ ]*\.[^ ]*)", block.extra)
|
||||
if match:
|
||||
append_to = match.groups()[0]
|
||||
log(f"Appending to {append_to}")
|
||||
if text.endswith("\n"):
|
||||
text = text[:-1]
|
||||
for line in text.split("\n"):
|
||||
handlers["bash"].process("$ echo " + shlex.quote(line) + " >> " + append_to)
|
||||
elif block.lang == "diff":
|
||||
log("Applying diff")
|
||||
handlers["bash"].process("""$ rm -f patch.patch""")
|
||||
for line in text.split("\n"):
|
||||
handlers["bash"].process("$ echo " + shlex.quote(line) + " >> patch.patch")
|
||||
handlers["bash"].process("""$ patch --quiet -p1 < patch.patch""")
|
||||
handlers["bash"].process("""$ rm patch.patch""")
|
||||
|
||||
elif block.lang in handlers:
|
||||
log(f"Handling {block.lang} snippet")
|
||||
handlers[block.lang].process(text, block.extra)
|
||||
|
||||
for language, handler in handlers.items():
|
||||
with TemporaryDirectory(
|
||||
prefix=f"revng-docs-test-{language}-{os.path.basename(path)}-"
|
||||
) as temporary_directory:
|
||||
handler.run(Path(temporary_directory))
|
||||
|
||||
|
||||
class TestDocsCommand(Command):
|
||||
def __init__(self):
|
||||
super().__init__(("test-docs",), "Test mkdocs files")
|
||||
|
||||
def register_arguments(self, parser):
|
||||
parser.add_argument("files", metavar="FILE", nargs="+", help="files to test.")
|
||||
parser.add_argument("--verbose", action="store_true", help="enable verbose output.")
|
||||
|
||||
def run(self, options: Options):
|
||||
args = options.parsed_args
|
||||
|
||||
global verbose
|
||||
verbose = args.verbose
|
||||
|
||||
for path in args.files:
|
||||
handle_file(Path(path))
|
||||
|
||||
|
||||
def setup(commands_registry: CommandsRegistry):
|
||||
commands_registry.register_command(TestDocsCommand())
|
||||
@@ -0,0 +1,19 @@
|
||||
<!--
|
||||
This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
-->
|
||||
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.2256 30.9965C6.16319 29.6929 0.800049 23.4939 0.800049 16.018C0.800049 13.0382 1.67621 10.2713 3.13647 7.9035L13.2256 30.9965ZM18.7745 30.9965C25.8369 29.6929 31.2001 23.4939 31.2001 16.018C31.2001 13.0382 30.3239 10.2713 28.8636 7.9035L18.7745 30.9965ZM16.0664 25.2765L25.359 4.01919C22.7836 1.99722 19.5445 0.799999 16.0133 0.799999C12.5883 0.799999 9.45542 1.94401 6.9066 3.85956L16.0664 25.2765Z" fill="url(#paint0_linear_997_74486)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_997_74486" x1="12.0752" y1="30.7473" x2="19.976" y2="1.32181" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#D56B36"/>
|
||||
<stop offset="0.0446233" stop-color="#D76331"/>
|
||||
<stop offset="0.2232" stop-color="#DF4820"/>
|
||||
<stop offset="0.3818" stop-color="#E33816"/>
|
||||
<stop offset="0.5031" stop-color="#E53212"/>
|
||||
<stop offset="0.6426" stop-color="#E02723"/>
|
||||
<stop offset="0.9161" stop-color="#D20A4E"/>
|
||||
<stop offset="1" stop-color="#CD005C"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,5 @@
|
||||
Welcome to the rev.ng documentation!
|
||||
|
||||
If you're new, consider starting from [What's rev.ng?](what-is-revng.md).
|
||||
|
||||
If you want to get your hands dirty, head directly to [User Manual's Introduction](/user-manual).
|
||||
@@ -0,0 +1,62 @@
|
||||
Running `revng artifact` you will get a list of artifacts that can be produced along with their [MIME types](https://en.wikipedia.org/wiki/Media_type):
|
||||
|
||||
```bash
|
||||
$ revng artifact
|
||||
USAGE: revng-artifact [options] <artifact> <binary>
|
||||
|
||||
<artifact> can be one of:
|
||||
|
||||
lift - text/x.llvm.ir
|
||||
isolate - text/x.llvm.ir
|
||||
enforce-abi - text/x.llvm.ir
|
||||
hexdump - text/x.hexdump+ptml
|
||||
render-svg-call-graph - image/svg
|
||||
render-svg-call-graph-slice - image/svg
|
||||
disassemble - text/x.asm+ptml+tar+gz
|
||||
render-svg-cfg - image/svg
|
||||
recompile - application/x-executable
|
||||
recompile-isolated - application/x-executable
|
||||
emit-cfg - text/x.yaml
|
||||
make-segment-ref - text/x.llvm.ir
|
||||
decompile - text/x.c+ptml+tar+gz
|
||||
decompile-to-single-file - text/x.c+ptml
|
||||
emit-helpers-header - text/x.c+ptml
|
||||
emit-model-header - text/x.c+ptml
|
||||
emit-type-definitions - text/x.c+tar+gz
|
||||
convert-to-mlir - text/mlir
|
||||
```
|
||||
|
||||
## MIME types
|
||||
|
||||
When choosing MIME types for artifacts, we trying to conform as close as possible to [RFC2045](https://datatracker.ietf.org/doc/html/rfc2045) and [RFC6838](https://www.rfc-editor.org/rfc/rfc6838).
|
||||
|
||||
There are two fundamental distinctions in MIME types:
|
||||
|
||||
* `text/*`: the output can be opened in a text editor;
|
||||
* everything else: the output needs to be managed by an "external application";
|
||||
|
||||
The most common *base* MIME types we use are:
|
||||
|
||||
* `application/x-executable`: an executable program.
|
||||
* `application/x-object`: an object file.
|
||||
* `text/plain`: a plain text file.
|
||||
* `text/x.c`: C source code (e.g., decompiled code).
|
||||
* `text/x.asm`: assembly code.
|
||||
* `image/svg`: an SVG image.
|
||||
* `text/x.llvm.ir`: LLVM IR in its textual representation.
|
||||
* `application/x.llvm.bc`: LLVM IR in its binary representation (also known as bitcode).
|
||||
* `text/x.hexdump`: an ASCII representation of raw bytes.
|
||||
* `text/mlir`: MLIR IR in its textual representation.
|
||||
* `text/x.yaml`: a YAML dictionary, with one key for each function.
|
||||
|
||||
MIME types that are not `text/*` or `image/svg` will be transmitted over GraphQL via Base64 encoding
|
||||
|
||||
Some of these MIME types can be further wrapped in another format.
|
||||
To make this explicit, we add suffixes, specifically:
|
||||
|
||||
* `$PREFIX+ptml`: `$PREFIX` is wrapped in [PTML](ptml.md).
|
||||
* `$PREFIX+tar`: `$PREFIX` is wrapped in a `tar` file containing one file for each function.
|
||||
* `$PREFIX+gzip`: `$PREFIX` is compressed using `gzip`.
|
||||
|
||||
For instance: `text/x.c+tar+gz` means that the artifact is GZip-compressed `tar` archive, containing C code for each function in the binary.
|
||||
While, `text/x.asm+ptml+tar+gz` represents a GZip-compressed `tar` archive containing one file per function, which is in turn assembly code wrapped in PTML.
|
||||
@@ -0,0 +1,299 @@
|
||||
PTML stands for Plain Text Markup Language.
|
||||
It's an XML format aimed at enriching a plain text file with meta information useful for syntax highlighting, navigation and have anchors to trigger actions.
|
||||
|
||||
## Basic usage from the terminal
|
||||
|
||||
When dealing with PTML on the terminal you either want to strip all the markup:
|
||||
|
||||
```bash
|
||||
$ echo '<span data-token="asm.register">rax</span>' | orc shell revng ptml
|
||||
rax
|
||||
```
|
||||
|
||||
You can add `--color` to render the syntax highlighting using terminal coloring.
|
||||
|
||||
## Characteristics
|
||||
|
||||
* Whitespaces are very relevant. PTML is designed to gracefully degrade into plain text by simply stripping all the XML elements.
|
||||
* It can be easily embedded into HTML. All the elements are either `<div>` or `<span>`.
|
||||
* It is used to represent in a uniform way source code in different languages (the *underlying language*).
|
||||
For instance, we use it to represtent C and various flavor of assembly code.
|
||||
If a text editor or a viewer supports PTML, it does not need any further understanding of the underlying language.
|
||||
* Contains metadata for navigation/highlighting in XML elements/attributes.
|
||||
* It's easy to emit: just power up your string concatenation with tags.
|
||||
An equivalent approach would be to have a separate place for metadata, associated to offset ranges in the original document.
|
||||
This approach would be significantly more difficult to emit.
|
||||
* Potential frontends:
|
||||
* Web browsers, using CSS for styling and JS for interactivity.
|
||||
* Rich text editors (e.g, [Monaco](https://microsoft.github.io/monaco-editor/))
|
||||
* Terminals. `revng ptml` can turn a PTML document into syntax highlighted output on a terminal.
|
||||
* PTML is *not* designed to have frontends add inline text upon rendering.
|
||||
In this way, its bounding box can be easily computed by the emitter (e.g., for graph layout purposes).
|
||||
Dropping text is possible, but discouraged.
|
||||
* Attributes are single-valued, unless stated otherwise.
|
||||
In case an attribute needs to represent multiple values, it will do so by concatenating the elements with `,` (U+002C).
|
||||
|
||||
Attributes defined in PTML can be organize in the following categories:
|
||||
|
||||
1. Syntax attributes: used for syntax highlighting purposes.
|
||||
2. Navigation attributes: used for navigation purposes (e.g., going from a function call to its definition).
|
||||
3. Action attributes: used to provide a context for certain *actions* (e.g., `rename`) on a certain portion of text.
|
||||
|
||||
## Syntax attributes
|
||||
|
||||
### `data-scope`
|
||||
|
||||
```xml title="Example"
|
||||
<div data-scope="asm.basic-block">
|
||||
```
|
||||
|
||||
States that the enclosed portion of text is of a specific type.
|
||||
`data-scope`s can be nested, for example:
|
||||
|
||||
* `<div data-scope="asm.basic-block">` can contain `<div data-scope="asm.instruction">`.
|
||||
* `<div data-scope="c.scope">` can contain another `<div data-scope="c.scope">`.
|
||||
|
||||
The nestability of scopes is dependent on the underlying language.
|
||||
The main use case for this attribute is folding ranges (e.g., in [VSCode](https://code.visualstudio.com/docs/editor/codebasics#_folding)).
|
||||
|
||||
### `data-token`
|
||||
|
||||
```xml title="Example"
|
||||
<span data-token="asm.register">
|
||||
```
|
||||
|
||||
States the type of the enclosed text.
|
||||
`data-token`s are mutually exclusive, that is, each part of the text can be enclosed in at most one of these attributes.
|
||||
The main use case for this attribute is syntax highlighting.
|
||||
|
||||
### Language references
|
||||
|
||||
#### Common
|
||||
|
||||
Some `data-token`s are common in all programming languages and, as such, are shared:
|
||||
|
||||
* `indentation`: used to indicate that the whitespace inside is indentation
|
||||
* `comment`: the text inside is a comment (including the language-specific comment marker)
|
||||
|
||||
#### ASM
|
||||
|
||||
The assembly language defines the following `data-scope`s:
|
||||
|
||||
* `asm.function`
|
||||
* `asm.basic-block`
|
||||
* `asm.instruction`
|
||||
|
||||
```asm title="Example"
|
||||
printf_core_: # - -
|
||||
push r15 # <-| | |
|
||||
mov r15, rdi # <-| | |
|
||||
# instructions --| - |
|
||||
# basic-block --| |
|
||||
# |
|
||||
basic_block_at_0x402ac5_Code_x86_64: # - |
|
||||
cmp dword ptr [rsp + 0x4], 0x0 # <-| | |
|
||||
js 0x402af2 # <-| | |
|
||||
# instructions --| - |
|
||||
# basic-block --| |
|
||||
# |
|
||||
basic_block_at_0x402af4_Code_x86_64: # - |
|
||||
cmp byte ptr [rbp], 0x0 # <-| | |
|
||||
je 0x40321e # <-| | |
|
||||
# instructions --| - |
|
||||
# basic-block --| -
|
||||
# function --|
|
||||
```
|
||||
|
||||
The assembly language defines the following `data-token`s:
|
||||
|
||||
* `asm.label`: the label of a basic block.
|
||||
* `asm.label-indicator`: terminator for an `asm.label` (usually `:`).
|
||||
* `asm.mnemonic`: the mnemonic of an assembly instruction (e.g., `mov`).
|
||||
* `asm.mnemonic-prefix`, `asm.mnemonic-suffix`: in case a mnemonic can be split into a prefix and a suffix, the two respective parts (e.g., the `ne` in `jne`).
|
||||
* `asm.immediate-value`: used to represent integers.
|
||||
* `asm.memory-operand`: used to indicate memory operands.
|
||||
* `asm.register`: the name of a register.
|
||||
* `asm.helper`: a macro-like function for specifying some information in a more human-readable fashion (e.g., `offset_to` to get the offset of a global).
|
||||
|
||||
```asm title="Example"
|
||||
# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv--------- asm.label
|
||||
# v-------- asm.label-indicator
|
||||
basic_block_at_0x402ac5_Code_x86_64:
|
||||
# vvv--------------------------------------- asm.mnemonic
|
||||
# vvv---------vvv------------ asm.register
|
||||
# v---------v----------------- asm.memory-operand
|
||||
# vvv------------------ asm.immediate-value
|
||||
add dword ptr [rsp + 0x4], ebx
|
||||
|
||||
# v----------------------------------------- asm.mnemonic
|
||||
# vv--------------------------------------- asm.mnemonic-suffix
|
||||
# vvvvvvvv------------------------------ asm.immediate-value
|
||||
jge 0x402af2
|
||||
# vvvvvvvvv------------------------ asm.helper
|
||||
mov rax, offset_to(some_global)
|
||||
```
|
||||
|
||||
#### C
|
||||
|
||||
The C language defines the following `data-scope`s:
|
||||
|
||||
* `c.function`: a function, including its prototype.
|
||||
* `c.function_body`: the body of a function, i.e., the text just after the function declaration and `{` to just before the last `}`.
|
||||
* `c.scope`: any scope inside a function (e.g., `if`, `switch`).
|
||||
* `c.struct`: the body of a `struct`.
|
||||
* `c.union`: the body of a `union`.
|
||||
|
||||
Additionally, there are:
|
||||
|
||||
* `c.type_declarations_list`: a group of type declarations.
|
||||
* `c.function_declarations_list`: a group of function declarations.
|
||||
* `c.dynamic_function_declarations_list`: a group of dynamic function declarations.
|
||||
* `c.segment_declarations_list`: a group of segment declarations.
|
||||
|
||||
```c title="Example"
|
||||
struct example_struct {/* begin c.struct */
|
||||
int x;
|
||||
int y;
|
||||
/* end c.struct */};
|
||||
|
||||
union example_union {/* begin c.union */
|
||||
float value;
|
||||
double bigvalue;
|
||||
/* end c.union */};
|
||||
|
||||
/* begin c.function */ int foo(); /* end c.function */
|
||||
|
||||
/* begin c.function */ int foo() {/* begin c.function-body */
|
||||
int i = 0;
|
||||
for(i; i < 8; i++) {/* begin c.scope */
|
||||
if(i % 2 == 0) {/* begin c.scope */
|
||||
baz(i);
|
||||
/* end c.scope */}
|
||||
/* end c.scope */}
|
||||
return i;
|
||||
/* end c.function-body */}/* end c.function */
|
||||
```
|
||||
|
||||
The C language defines the following `data-token`s:
|
||||
|
||||
* `c.function`: the name of a function.
|
||||
* `c.type`: a type name (e.g. `uint8_t` or `struct_1000101`).
|
||||
* `c.operator`: any unary or binary operator, e.g., `*`, `&`, `->`, `+`, `-`, `&`, `>>`, `>`, `!=`.
|
||||
* `c.function_parameter`: a function parameter.
|
||||
* `c.variable`: a local variable.
|
||||
* `c.field`: the name of a member of a `struct` or a `union`.
|
||||
* `c.constant`: a constant value, e.g. `1` or `0xDEADBEEF`.
|
||||
* `c.keyword`: a C reserved keyword, e.g., `const` and `volatile`.
|
||||
* `c.directive`: any preprocessor directive, e.g., `#include`, `#ifdef`.
|
||||
* `c.string_literal`: a string literal, e.g., `"DEADBEEF"`.
|
||||
|
||||
## Navigation attributes
|
||||
|
||||
PTML supports defining and referencing *locations*.
|
||||
|
||||
### Locations
|
||||
|
||||
Each *location* represent an abstract concept, such as a function or a byte range, which can be used for navigation, e.g., going from a function call to the definition of the called function.
|
||||
Each location can have zero or more parameters.
|
||||
|
||||
We currently implemented the following locations:
|
||||
|
||||
* Generic locations
|
||||
* `/binary`
|
||||
* `/function/<Function_MetaAddress>`
|
||||
* `/dynamic-function/<Name_string>`
|
||||
* `/type/<Type_Kind>-<Type_ID>`
|
||||
* `/segment/<StartAddress_MetaAddress>-<VirtualSize_uint64>`
|
||||
* `/struct-field/<Type_Kind>-<Type_ID>/<Offset_uint64>`
|
||||
* `/union-field/<Type_Kind>-<Type_ID>/<Index_uint64>`
|
||||
* `/enum-entry/<Type_Kind>-<Type_ID>/<Index_uint64>`
|
||||
* `/raw-argument/<Type_Kind>-<Type_ID>/<Register>`
|
||||
* `/cabi-argument/<Type_Kind>-<Type_ID>/<Index_uint64>`
|
||||
* `/return-value/<Type_Kind>-<Type_ID>`
|
||||
* `/return-register/<Type_Kind>-<Type_ID>/<Register>`
|
||||
* Byte-ranges related
|
||||
* `/raw-byte/<Start_MetaAddress>`
|
||||
* `/raw-byte-range/<Start_MetaAddress>/<End_MetaAddress>`
|
||||
* Assembly-related:
|
||||
* `/basic-block/<Function_MetaAddress>/<BasicBlock_MetaAddress>`
|
||||
* `/instruction/<Function_MetaAddress>/<BasicBlock_MetaAddress>/<Instruction_MetaAddress>`
|
||||
* C-related:
|
||||
* `/local-variable/<Function_MetaAddress>/<Name_string>`
|
||||
* `/helper-function/<Name_string>`
|
||||
* `/helper-struct-type/<Name_string>`
|
||||
* `/helper-structs-field/<Name_string>/<FieldName_string>`
|
||||
* `/dynamic-function-argument/<Name_string>/<Name_string>`
|
||||
* `/artificial-struct/<Type_Kind>-<Type_ID>`
|
||||
|
||||
When interacting with revng, there is a description file, which, among other information, states what locations each Kind *defines*, e.g.:
|
||||
|
||||
* `DecompiledC` provides:
|
||||
* `/function/<MA>`
|
||||
* `YieldAssembly` provides:
|
||||
* `/function/<MA>`
|
||||
* `/basic-block/<MA>/*`
|
||||
* `/instruction/<MA>/*/*`
|
||||
* `ModelHeader` provides:
|
||||
* `/types/*`
|
||||
|
||||
Some locations directly map to parts of the model, e.g.:
|
||||
|
||||
* `/types/$TYPE_ID` -> `/Types/$TYPE_ID`
|
||||
* `/function/$FUNCTION_ADDRESS` -> `/Functions/$FUNCTION_ADDRESS`
|
||||
* `/basic-block/...` -> `null`
|
||||
* `/instruction/...` -> `null`
|
||||
|
||||
### Attributes
|
||||
|
||||
The following PTML attributes are used to provide navigation via location string:
|
||||
|
||||
* `data-location-definition`: represents the *definition* of a location.
|
||||
|
||||
```xml
|
||||
<div data-location-definition="/function/0x1000">...</div>
|
||||
<div data-location-definition="/basic-block/0x1000/0x1004">...</div>
|
||||
<div data-location-definition="/instruction/0x1000/0x1004/0x1006">...</div>
|
||||
```
|
||||
Represents the destination to navigate to starting from a `data-location-reference` (see below).
|
||||
|
||||
* `data-location-references`:
|
||||
Represents one or more references to a location. Can be multivalued.
|
||||
```xml
|
||||
<span data-location-references="/basic-block/0x1000/0x1004,
|
||||
/basic-block/0x1000/0x1008">
|
||||
jmp rax
|
||||
</span>
|
||||
struct <span data-location-references="/types/0x1123412)">mtype</span> {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Action attributes
|
||||
|
||||
PTML defines a set of *actions*:
|
||||
|
||||
* `rename`: rename the current object.
|
||||
* `comment`: edit the comment of the current object.
|
||||
* `codeSwitch`: jump to an alternative representation of the current object (e.g. ASM <-> C).
|
||||
* `editType`: edit the type associated to the current object (e.g., for a function, its prototype).
|
||||
|
||||
Each of these actions requires knowledge of how the action is executed correctly, its implementation is optional and left at the discretion of each PTML viewer.
|
||||
Each location supports a subset of the above actions:
|
||||
|
||||
* `function`: supports `rename`, `comment` and `editType`.
|
||||
* `instruction`: supports `codeSwitch`.
|
||||
* `type`: supports `rename`, `comment` and `editType`.
|
||||
* `struct-field`: supports `rename` and `comment`.
|
||||
* `union-field`: supports `rename` and `comment`.
|
||||
* `enum-entry`: supports `rename` and `comment`.
|
||||
* `cabi-argument`: supports `rename` and `comment`.
|
||||
* `raw-argument`: supports `rename` and `comment`.
|
||||
* `return-value`: supports `comment`.
|
||||
* `return-register`: supports `rename` and `comment`.
|
||||
* `segment`: supports `rename`, `comment` and `editType`.
|
||||
* `dynamic-function`: supports `rename`, `comment` and `editType`.
|
||||
|
||||
An action is defined by the following attributes:
|
||||
|
||||
* `data-action-context-location`: indicates that the contained snippet has the specified context and allows the PTML viewer to activate the supported actions. Nested element can specify different context locations; in this case the viewer should pick the innermost one.
|
||||
* `data-allowed-actions`: in some cases, the set of posisble actions needs to be restricted to a subset of all possible actions, in this case this attribute is used.
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TYPO
|
||||
*/
|
||||
|
||||
body .md-typeset a.headerlink {
|
||||
text-decoration: none;
|
||||
-webkit-text-fill-color: initial;
|
||||
text-fill-color: initial;
|
||||
background: none;
|
||||
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: var(--md-accent-fg-color);
|
||||
box-shadow: 0 0 1px 1px var(--md-default-fg-color--lighter);
|
||||
background-color: var(--md-default-fg-color--lightest);
|
||||
padding: 0.3em 0.4em;
|
||||
border-radius: 8px;
|
||||
border-width: 0px;
|
||||
font-size: 0.85em;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.md-typeset h1 {
|
||||
margin: 0 0 1.2rem;
|
||||
|
||||
display: inline-block;
|
||||
background: linear-gradient(131.31deg, #f25b01 9.12%, #ee3220 50.55%, #d0075f 88.66%);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.md-typeset h2,
|
||||
.md-typeset h3 {
|
||||
margin: 1.6rem 0 1.2rem;
|
||||
}
|
||||
|
||||
.md-typeset h1 + h2,
|
||||
.md-typeset h1 + h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.md-typeset h1 code,
|
||||
.md-typeset h2 code,
|
||||
.md-typeset h3 code,
|
||||
.md-typeset h4 code,
|
||||
.md-typeset h5 code,
|
||||
.md-typeset h6 code {
|
||||
background-color: transparent;
|
||||
color: var(--md-typeset-color);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.md-typeset blockquote,
|
||||
.md-typeset dl,
|
||||
.md-typeset figure,
|
||||
.md-typeset ol,
|
||||
.md-typeset pre,
|
||||
.md-typeset ul,
|
||||
.md-typeset .tabbed-set {
|
||||
margin-top: 1.2rem;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
|
||||
.md-typeset a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.md-typeset a:hover,
|
||||
.md-typeset a:focus {
|
||||
color: var(--md-typeset-a-color);
|
||||
}
|
||||
|
||||
/*
|
||||
* CODE
|
||||
*/
|
||||
|
||||
.md-typeset code {
|
||||
border-radius: 8px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.md-typeset a code {
|
||||
color: var(--md-accent-fg-color);
|
||||
}
|
||||
|
||||
.md-typeset a:focus code,
|
||||
.md-typeset a:hover code {
|
||||
background-color: var(--md-code-bg-color);
|
||||
}
|
||||
|
||||
.md-typeset pre > code {
|
||||
background-color: var(--md-default-bg-color--lighter);
|
||||
border: 1px solid var(--md-typeset-a-color);
|
||||
padding: 8px 16px;
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
|
||||
.md-typeset table.highlighttable {
|
||||
margin-top: 1.2rem;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
|
||||
table.highlighttable td.linenos {
|
||||
background-color: var(--md-default-bg-color--lighter);
|
||||
border: 1px solid var(--md-typeset-a-color);
|
||||
border-right-width: 0px;
|
||||
padding: 8px 8px 8px 16px;
|
||||
border-radius: 8px 0 0 8px !important;
|
||||
}
|
||||
|
||||
table.highlighttable td.linenos .linenodiv {
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
table.highlighttable td.linenos pre {
|
||||
margin: 0;
|
||||
color: rgba(239, 249, 255, 0.4);
|
||||
}
|
||||
|
||||
table.highlighttable td.linenos + td.code pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.highlighttable .linenos + .code pre code {
|
||||
border-left-width: 0;
|
||||
border-bottom-left-radius: 0 !important;
|
||||
border-top-left-radius: 0 !important;
|
||||
}
|
||||
|
||||
.highlight span.filename {
|
||||
border-top-left-radius: 8px !important;
|
||||
border-top-right-radius: 8px !important;
|
||||
border: 1px solid var(--md-typeset-a-color);
|
||||
border-bottom-width: 0;
|
||||
color: var(--md-accent-fg-color);
|
||||
background-color: var(--md-default-bg-color--lighter);
|
||||
margin-top: 1.2rem;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.highlight span.filename + pre code {
|
||||
border-top-left-radius: 0 !important;
|
||||
border-top-right-radius: 0 !important;
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
.js .md-typeset .tabbed-labels:before {
|
||||
height: 100%;
|
||||
border-radius: 8px 8px 0 0;
|
||||
background-color: var(--md-code-hl-color);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 44.9375em) {
|
||||
.md-content__inner > .highlight {
|
||||
margin-left: 0px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* HEADER
|
||||
*/
|
||||
|
||||
.md-header--shadow {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.md-header__inner {
|
||||
padding: 0.8rem 0.4rem;
|
||||
}
|
||||
|
||||
.md-header__title {
|
||||
color: var(--md-accent-fg-color);
|
||||
}
|
||||
|
||||
[dir='rtl'] .md-header__title,
|
||||
[dir='ltr'] .md-header__title {
|
||||
margin-left: 0.6rem;
|
||||
margin-right: 0.6rem;
|
||||
}
|
||||
|
||||
.md-header__topic:first-child {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 60em) {
|
||||
.md-header__inner {
|
||||
padding-top: 0.6rem;
|
||||
padding-bottom: 0.6rem;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* NAVBAR
|
||||
*/
|
||||
|
||||
.md-nav__title {
|
||||
font-family: var(--md-code-font);
|
||||
text-transform: uppercase;
|
||||
color: var(--md-primary-bg-color);
|
||||
}
|
||||
|
||||
.md-nav__title .md-nav__button.md-logo img {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 76.25em) {
|
||||
.md-sidebar.md-sidebar--primary .md-nav__link {
|
||||
margin-top: 0.8rem;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* SEARCH
|
||||
*/
|
||||
|
||||
[data-md-toggle='search']:checked ~ .md-header .md-search__form,
|
||||
.md-search__form {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 1px 1px var(--md-default-fg-color--lighter);
|
||||
background-color: var(--md-accent-fg-color--transparent);
|
||||
}
|
||||
|
||||
.md-search-result .md-typeset,
|
||||
.md-search-result__icon,
|
||||
.md-search-result__more > summary > div {
|
||||
color: var(--md-default-fg-color);
|
||||
}
|
||||
|
||||
.md-search-result__icon {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
margin: 0.7rem 0.5rem;
|
||||
}
|
||||
|
||||
.md-search-result mark {
|
||||
font-weight: 700;
|
||||
color: var(--md-accent-fg-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 60em) {
|
||||
[data-md-toggle='search']:checked ~ .md-header .md-search__inner,
|
||||
.md-search__scrollwrap {
|
||||
width: 23.4rem !important;
|
||||
}
|
||||
|
||||
.md-search__input {
|
||||
border-radius: 8px;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.md-search__form:hover {
|
||||
background-color: var(--md-accent-fg-color--transparent);
|
||||
}
|
||||
|
||||
.md-search__input.focus-visible {
|
||||
border-color: var(--md-typeset-a-color);
|
||||
}
|
||||
|
||||
.md-search__output {
|
||||
margin-top: 1px;
|
||||
border-radius: 8px !important;
|
||||
box-shadow: 0 0 1px 1px var(--md-default-fg-color--lighter) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MAIN
|
||||
*/
|
||||
|
||||
.md-main {
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
.md-main__inner {
|
||||
margin-top: 3.2rem;
|
||||
}
|
||||
|
||||
.md-content__inner {
|
||||
margin-left: 1rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
/*
|
||||
* FOOTER
|
||||
*/
|
||||
.md-footer-meta .md-copyright {
|
||||
font-family: var(--md-code-font);
|
||||
}
|
||||
|
||||
.md-footer__title .md-footer__direction {
|
||||
font-family: var(--md-code-font);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.md-footer__link:focus,
|
||||
.md-footer__link:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.md-footer-meta__inner {
|
||||
padding: 0.4rem 0.2rem;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
*/
|
||||
|
||||
@media screen {
|
||||
[data-md-color-scheme='revng'] {
|
||||
color-scheme: dark;
|
||||
|
||||
--md-nav-icon--next: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4.00003 13L4.00003 11L16 11L10.5 5.50004L11.92 4.08004L19.84 12L11.92 19.92L10.5 18.5L16 13L4.00003 13Z"/></svg>');
|
||||
|
||||
--primary-color: #ee3220;
|
||||
|
||||
/* Primary color shades */
|
||||
--md-primary-fg-color: hsla(215, 21%, 11%, 1);
|
||||
--md-primary-fg-color--light: hsla(215, 15%, 15%, 1);
|
||||
--md-primary-fg-color--dark: hsla(216, 28%, 7%, 1);
|
||||
--md-primary-bg-color: hsla(203, 100%, 97%, 0.6);
|
||||
--md-primary-bg-color--light: hsla(202, 100%, 97%, 0.85);
|
||||
|
||||
--md-accent-fg-color: hsla(0, 0%, 100%, 1);
|
||||
--md-accent-fg-color--transparent: hsla(203, 100%, 97%, 0.05);
|
||||
--md-accent-bg-color: hsla(203, 100%, 97%, 0.6);
|
||||
--md-accent-bg-color--light: hsla(202, 100%, 97%, 0.85);
|
||||
|
||||
/* Default color shades */
|
||||
--md-default-fg-color: hsla(202, 100%, 97%, 0.85);
|
||||
--md-default-fg-color--light: hsla(0, 0%, 100%, 1);
|
||||
--md-default-fg-color--lighter: hsla(203, 100%, 97%, 0.1);
|
||||
--md-default-fg-color--lightest: hsla(203, 100%, 97%, 0.05);
|
||||
--md-default-bg-color: hsla(216, 28%, 7%, 1);
|
||||
--md-default-bg-color--light: hsla(215, 15%, 15%, 0.9);
|
||||
--md-default-bg-color--lighter: hsla(215, 15%, 15%, 0.7);
|
||||
--md-default-bg-color--lightest: hsla(215, 15%, 15%, 0.1);
|
||||
|
||||
/* Code */
|
||||
--md-code-fg-color: var(--md-accent-fg-color);
|
||||
--md-code-bg-color: hsla(203, 100%, 97%, 0.1);
|
||||
--md-code-hl-color: hsla(203, 100%, 97%, 0.1);
|
||||
--md-code-hl-operator-color: #ffffff;
|
||||
--md-code-hl-name-color: #ffffff;
|
||||
--md-code-hl-punctuation-color: #ffffff;
|
||||
--md-code-hl-generic-color: #ffffff;
|
||||
--md-code-hl-variable-color: #ffffff;
|
||||
--md-code-hl-string-color: #ffffff;
|
||||
--md-code-hl-keyword-color: #1a82fc;
|
||||
--md-code-hl-constant-color: #1a82fc;
|
||||
--md-code-hl-function-color: #a966ff;
|
||||
--md-code-hl-number-color: #ff5833;
|
||||
--md-code-hl-special-color: #ff5833;
|
||||
--md-code-hl-comment-color: hsla(203, 100%, 97%, 0.4);
|
||||
|
||||
--md-typeset-color: var(--md-default-fg-color);
|
||||
--md-typeset-a-color: var(--primary-color);
|
||||
|
||||
/* Footer color shades */
|
||||
--md-footer-fg-color: var(--md-accent-fg-color);
|
||||
--md-footer-fg-color--lighter: var(--md-primary-bg-color--light);
|
||||
--md-footer-bg-color: var(--md-primary-fg-color);
|
||||
--md-footer-bg-color--dark: var(--md-default-bg-color);
|
||||
}
|
||||
|
||||
.md-nav__item .md-nav__link--active,
|
||||
.md-typeset a {
|
||||
color: var(--md-typeset-a-color);
|
||||
}
|
||||
|
||||
.md-typeset a:hover {
|
||||
color: var(--md-typeset-a-color);
|
||||
}
|
||||
|
||||
.highlight .nd,
|
||||
.highlight .ni,
|
||||
.highlight .nl,
|
||||
.highlight .nt {
|
||||
color: var(--md-code-hl-special-color);
|
||||
}
|
||||
|
||||
.md-footer-meta .md-copyright a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
Clearly we don't want our users to write the model by hand.
|
||||
Therefore, as mentioned before, we developed a series of analyses which can automatically populate the model for you when you open a new project.
|
||||
|
||||
Consider the following simple program:
|
||||
|
||||
```c title="example.c"
|
||||
int main(int argc, char *argv[]) {
|
||||
return argc * 3;
|
||||
}
|
||||
```
|
||||
|
||||
Let's compile it:
|
||||
|
||||
```bash
|
||||
$ gcc example.c -o example -O2
|
||||
```
|
||||
|
||||
We can use the `import-binary` analysis to automatically collect all the loading information available in the ELF headers:
|
||||
|
||||
```{bash ignore="VirtualSize|FileSize"}
|
||||
$ revng analyze import-binary example -o example.yml
|
||||
$ grep -A7 'Segments:' example.yml
|
||||
Segments:
|
||||
- StartAddress: "0x400000:Generic64"
|
||||
VirtualSize: 2528
|
||||
StartOffset: 0
|
||||
FileSize: 2528
|
||||
IsReadable: true
|
||||
IsWriteable: false
|
||||
IsExecutable: true
|
||||
```
|
||||
|
||||
However, the typical workflow does not require the user to manually specify what analyses to run, but there's a set of predefined analyses that should be run on a new project, the *initial autoanalyses*.
|
||||
|
||||
```bash
|
||||
$ revng analyze \
|
||||
--resume=working-directory \
|
||||
revng-initial-auto-analysis \
|
||||
example \
|
||||
-o /dev/null
|
||||
$ revng analyze \
|
||||
--resume=working-directory \
|
||||
revng-c-initial-auto-analysis \
|
||||
example \
|
||||
-o /dev/null
|
||||
$ revng artifact \
|
||||
--resume working-directory \
|
||||
decompile-to-single-file \
|
||||
example \
|
||||
| revng ptml | grep -A2 -B1 '\bmain\b'
|
||||
_ABI(SystemV_x86_64)
|
||||
generic64_t main(generic64_t _argument0) {
|
||||
return _argument0 * 3 & 0xFFFFFFFF;
|
||||
}
|
||||
```
|
||||
|
||||
The commands above are *stateful*, they build on top of each other storing intermediate results into the directory specified by the `--resume` parameter.
|
||||
The first command runs the set of initial autoanalyses of `revng`, the open source component of rev.ng, the second the analyses of `revng-c`, in particular Data LayoutAnalysis, and the last one produces the decompiled code.
|
||||
|
||||
Alternatively, you can run all the analyses above *and* produce the artifact with a single command:
|
||||
|
||||
```bash
|
||||
$ revng artifact \
|
||||
--analyses-list=revng-initial-auto-analysis \
|
||||
--analyses-list=revng-c-initial-auto-analysis \
|
||||
decompile-to-single-file \
|
||||
example \
|
||||
| revng ptml \
|
||||
| grep -A2 -B1 '\bmain\b'
|
||||
_ABI(SystemV_x86_64)
|
||||
generic64_t main(generic64_t _argument0) {
|
||||
return _argument0 * 3 & 0xFFFFFFFF;
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
# User's manual
|
||||
|
||||
The user's manual describes concepts that are mostly useful for users.
|
||||
If you just want to use the UI, most of these are benficial but not strictly necessary.
|
||||
On other hand, they are vital to get a better understanding of rev.ng and in particular to approach scripting rev.ng.
|
||||
|
||||
The manual discusses the following topics:
|
||||
|
||||
1. [Getting a working environment](working-environment.md): explains preliminary steps necessary to start using rev.ng, both if you're using a binary distribution or if you prefer to build things from source.
|
||||
2. [The `MetaAddress`](metaaddress.md): a 64-bit address on steroids used to uniquely identify objects and code in the binary.
|
||||
3. [The model](model.md): a brief theoretical introduction to the model, the YAML document where rev.ng stores everything the user can customize, such as function names, prototypes and so on.
|
||||
You can think about it as rev.ng's project file.
|
||||
4. [Building a model from scratch](model-tutorial.md): a practical, step-by-step tutorial on how to build a model from scratch and how to produce artifacts such as disassembly and decompiled code.
|
||||
5. [Analyses](analyses.md): analyses are the part of rev.ng that automatically infer information about a binary and write them in the model, just as a user could do.
|
||||
<br />rev.ng provides analyses to detect function entry points, function prototypes, data structures and more.
|
||||
@@ -0,0 +1,89 @@
|
||||
Before we dig any deeper into the model, it is important to understand a concept that's ubiquous in rev.ng: the `MetaAddress`.
|
||||
|
||||
You can see the `MetaAddress` as a virtual address on steroids.
|
||||
It is basically a 64-bit integer plus some additional meta-information useful to capture the fact that the same 64-bit address might represent different things in rev.ng.
|
||||
|
||||
For example, in an ARM program, it is in principle possible that the code at a certain address is used both as regular ARM code and as ARM Thumb code. A `MetaAddress` captures this difference and enables us to neatly distinguish the two situations.
|
||||
|
||||
The other key situation in which the concept of `MetaAddress` turns out to be very useful is in programs that, at the same address, have different code at different points in time.
|
||||
This is typical if a program embeds a JIT compiler or if the program is using non-trivial packing techniques.
|
||||
Thanks to `MetaAddress`, rev.ng is able to analyze such programs as if that code was at different addresses.
|
||||
|
||||
## The components of a `MetaAddress`
|
||||
|
||||
In practice, a `MetaAddress` is a very simple data structure:
|
||||
|
||||
```c title="PlainMetaAddress.h"
|
||||
typedef struct {
|
||||
uint32_t Epoch;
|
||||
uint16_t AddressSpace;
|
||||
uint16_t Type;
|
||||
uint64_t Address;
|
||||
} PlainMetaAddress;
|
||||
```
|
||||
|
||||
Let's review each field in detail:
|
||||
|
||||
* `uint64_t Address`: the absolute address represented as a 64-bits integer;
|
||||
* `uint16_t Type`: the entry of an `enum` to distinguish what this `MetaAddress` is pointing to and the size of the pointer:
|
||||
|
||||
* `Invalid`: the pointer is not valid;
|
||||
* `Generic32`: a 32-bit pointer pointing at raw data (not code);
|
||||
* `Generic64`: as before, but the pointer is 64-bits wide;
|
||||
* `Code_x86`: a 32-bit pointer to 32-bit x86 code;
|
||||
* `Code_x86_64`: a 64-bit pointer to 64-bit x86 code;
|
||||
* `Code_arm`: a 32-bit pointer to regular ARM code;
|
||||
* `Code_arm_thumb`: a 32-bit pointer to regular ARM Thumb code;
|
||||
* `Code_*`: one entry for each other type of code we currently support;
|
||||
|
||||
* `uint32_t Epoch`: a progressive value that enables us to distinguish different things at the same address in different points during the execution of the program;
|
||||
* `uint16_t AddressSpace`: certain architectures can access different type of memories (e.g., a large ROM and a smaller RAM) through different instructions.
|
||||
The `AddressSpace` field enables us to distinguish them.
|
||||
Note that currently we do not attribute any particular meaning to `AddressSpace`, but in the future we might use them to support multi-binary analysis: every dynamic library you'll feed to rev.ng will be distinguished by a different value of `AddressSpace`.
|
||||
|
||||
## Textual representation
|
||||
|
||||
A `MetaAddress` has also a string representation:
|
||||
|
||||
```
|
||||
Address:Type[:Epoch[:AddressSpace]]
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
* `:Invalid`: an invalid `MetaAddress`;
|
||||
* `0x1000:Code_arm`: a pointer with value `0x1000` targeting regular ARM code;
|
||||
* `0x1000:Code_arm_thumb`: a pointer with value `0x1000` targeting regular ARM Thumb code;
|
||||
* `0x3000:Generic32`: a 32-bit pointer with value `0x3000` targeting raw data;
|
||||
* `0x1000:Code_x86:10:2`: a pointer to 32-bit x86 code at epoch 10 in address space 2;
|
||||
|
||||
## Scripting
|
||||
|
||||
Our Python library offers a `MetaAddress` `class`:
|
||||
|
||||
```python
|
||||
>>> from revng.model import MetaAddress, MetaAddressType
|
||||
>>> address = MetaAddress(Address=0x1000, Type=MetaAddressType.Code_arm)
|
||||
>>> str(address)
|
||||
'0x1000:Code_arm'
|
||||
>>> hex(address.Address)
|
||||
'0x1000'
|
||||
```
|
||||
|
||||
Our TypeScript library offers a `MetaAddress` `class` too:
|
||||
|
||||
```typescript
|
||||
> import { MetaAddress } from "revng-model"
|
||||
> let address = new MetaAddress("0x1000:Code_arm")
|
||||
> address.toString()
|
||||
"0x1000:Code_arm"
|
||||
> address.address.toString(16)
|
||||
"1000"
|
||||
```
|
||||
|
||||
These facilities are currently only available within a rev.ng development environment, i.e., an `orc shell`, but they will be generally available in the future.
|
||||
|
||||
### Relevant sources
|
||||
|
||||
* `include/revng/Runtime/PlainMetaAddress.h`
|
||||
* `include/revng/Support/MetaAddress.h`
|
||||
@@ -0,0 +1,244 @@
|
||||
An empty model is a valid model:
|
||||
|
||||
```bash
|
||||
$ revng model opt /dev/null -verify -Y
|
||||
---
|
||||
{}
|
||||
...
|
||||
```
|
||||
|
||||
Let's get started and try to decompile a very simple program.
|
||||
|
||||
```bash
|
||||
$ printf '\x48\x01\xf7\x48\x89\xf8\xc3\x90' > sum
|
||||
```
|
||||
|
||||
If you disassemble it as raw bytes you get the following:
|
||||
|
||||
```bash
|
||||
$ objdump -D -Mintel,x86-64 -b binary -m i386:x86-64 sum
|
||||
|
||||
sum: file format binary
|
||||
|
||||
|
||||
Disassembly of section .data:
|
||||
|
||||
0000000000000000 <.data>:
|
||||
0: 48 01 f7 add rdi,rsi
|
||||
3: 48 89 f8 mov rax,rdi
|
||||
6: c3 ret
|
||||
7: 90 nop
|
||||
```
|
||||
|
||||
Note that this file contains raw x86-64 instructions, it's not a valid program (e.g., an ELF).
|
||||
|
||||
Let's now create a simple model that enables us to decompile this simple function.
|
||||
|
||||
### Step 1: Loading
|
||||
|
||||
The first thing rev.ng needs to know is the architecture and the ABI of the program:
|
||||
|
||||
```yaml title="model.yml"
|
||||
Architecture: x86_64
|
||||
DefaultABI: SystemV_x86_64
|
||||
```
|
||||
|
||||
Then, we need to describe how to load the program.
|
||||
Let's pretend we want to load this code at address `0x400000`:
|
||||
|
||||
```yaml title="model.yml"
|
||||
Segments:
|
||||
- StartAddress: "0x400000:Generic64"
|
||||
VirtualSize: 7
|
||||
StartOffset: 0
|
||||
FileSize: 7
|
||||
IsReadable: true
|
||||
IsWriteable: false
|
||||
IsExecutable: true
|
||||
```
|
||||
|
||||
The piece of model above tells rev.ng to take 7 bytes from the file and load them at address `0x400000` as `+rx` data (i.e., code).
|
||||
|
||||
### Step 2: Function list
|
||||
|
||||
Most parts of rev.ng work on a function basis (e.g., we decompile one function at a time).
|
||||
Let's create an entry in the function list:
|
||||
|
||||
```yaml title="model.yml"
|
||||
Functions:
|
||||
- Entry: "0x400000:Code_x86_64"
|
||||
```
|
||||
|
||||
At a minimum, a function is identified by its entry [`MetaAddress`](metaaddress.md).
|
||||
Note how here the type of the `MetaAddress` is not `Generic64` but `Code_x86_64`, to indicate the type of code we can expect in the function.
|
||||
|
||||
### Step 3: Disassembly
|
||||
|
||||
At this point, we provided rev.ng enough information to be able to show us the disassembly of our program.
|
||||
|
||||
```bash
|
||||
$ revng artifact disassemble sum --model model.yml | revng ptml
|
||||
0x400000:Code_x86_64.asm.tar.gz: |-
|
||||
_function_0x400000_Code_x86_64:
|
||||
add rdi, rsi
|
||||
mov rax, rdi
|
||||
ret
|
||||
|
||||
|
||||
```
|
||||
|
||||
The output of the `revng artifact disassemble` command is a `tar.gz` composed by one file for each input function. Each file is an assembly listing decorated using [PTML](../references/ptml.md).
|
||||
<br />`revng ptml` strips away all this details and outputs a YAML dictionary with one entry for each disassembled function.
|
||||
|
||||
For further information on the file types emitted by `revng artifact`, see the [MIME types documentation](../references/mime-types.md).
|
||||
|
||||
### Step 4: Defining a function prototype
|
||||
|
||||
In order to decompile the function, we need to provide a function prototype.
|
||||
|
||||
By looking at the code above, we can see that the registers `rdi` and `rsi` are read at the entry of the function and the result of the computation is stored in `rax`: this function very much looks like a function taking two arguments and returning an integer in the x86-64 SystemV ABI!
|
||||
|
||||
Let's create such prototype then.
|
||||
|
||||
Specifically, we want to define the prototype for something that in C would look like the following:
|
||||
|
||||
```c
|
||||
uint64_t sum(uint64_t rdi, uint64_t rsi);
|
||||
```
|
||||
|
||||
First of all, let's populate the model type system with a bunch of *primitive types* such as `void`, `uint64_t`, `uint32_t` and so on.
|
||||
We could write them by hand, but the `add-primitive-types` analysis can help us with that:
|
||||
|
||||
```bash
|
||||
$ revng analyze add-primitive-types /dev/null \
|
||||
| grep -vE '^(---|\.\.\.)$' >> model.yml
|
||||
```
|
||||
|
||||
> **Note**: `revng analyze` is used to run *analyses* and automatically populate parts of the model. More on this in the [analyses page](analyses.md).
|
||||
|
||||
This will add to the model something similar to the following:
|
||||
|
||||
```yaml
|
||||
Types:
|
||||
- Kind: PrimitiveType
|
||||
ID: 1288
|
||||
PrimitiveKind: Unsigned
|
||||
Size: 8
|
||||
```
|
||||
|
||||
Here we are defining a *primitive* type (such as `void`, an integral type or a floating-point type) with size 8 bytes (64 bits) and *kind* `Unsigned`. Basically, an `uint64_t`.
|
||||
|
||||
> **Note**: the first 1808 type IDs are currently reserved for `PrimitiveType`s, which have a deterministic ID.
|
||||
> All other types, can have an arbitrary type ID, but tools usually adopt progressive type IDs.
|
||||
> <br />In the future, there will be no reserved type IDs since we plan to restructure how `PrimitiveType`s are handled.
|
||||
|
||||
Now that we have our `uint64_t`, we can define the function prototype.
|
||||
In the model type system, it looks like this:
|
||||
|
||||
```yaml title="model.yml"
|
||||
- Kind: CABIFunctionType
|
||||
ABI: SystemV_x86_64
|
||||
ID: 1809
|
||||
Arguments:
|
||||
- Index: 0
|
||||
Type:
|
||||
UnqualifiedType: "/Types/1288-PrimitiveType"
|
||||
- Index: 1
|
||||
Type:
|
||||
UnqualifiedType: "/Types/1288-PrimitiveType"
|
||||
ReturnType:
|
||||
UnqualifiedType: "/Types/1288-PrimitiveType"
|
||||
```
|
||||
|
||||
OK, there's a lot here. Let's go through it:
|
||||
|
||||
* `Kind: CABIFunctionType`: we're defining a type, specifically a C prototype associated to an ABI;
|
||||
* `ABI: SystemV_x86_64`: the chosen ABI is the x86-64 SystemV one;
|
||||
* `ID: ...`: a unique identifier;
|
||||
* `Arguments`: we have two arguments (index `0` and index `1`);
|
||||
* `Type`: a qualified type, i.e., a type plus (optionally) one or more qualifiers such as `const`, pointer (`*`) and so on;
|
||||
* `UnqualifiedType: "/Types/PrimitiveType-1288"`: a reference to the actual, unqualified, type; in this case, it's a reference to the *primitive type* with ID 1288, i.e., the `uint64_t` defined above;
|
||||
* `ReturnType`: again, a reference to `uint64_t`;
|
||||
|
||||
At this point, we can associate the function prototype with the previously defined function:
|
||||
|
||||
```diff
|
||||
--- a/model.yml
|
||||
+++ b/model.yml
|
||||
@@ -10,6 +10,7 @@
|
||||
IsExecutable: true
|
||||
Functions:
|
||||
- Entry: "0x400000:Code_x86_64"
|
||||
+ Prototype: "/Types/1809-CABIFunctionType"
|
||||
Types:
|
||||
- Kind: PrimitiveType
|
||||
ID: 1288
|
||||
```
|
||||
|
||||
Basically, we added to our function definition a reference to the prototype we created above.
|
||||
|
||||
### Step 5: Decompiling
|
||||
|
||||
At this point, we have all the information we need to successfully decompile our example program:
|
||||
|
||||
```c
|
||||
$ revng artifact decompile sum --model model.yml | revng ptml
|
||||
0x400000:Code_x86_64:
|
||||
uint64_t function_0x400000_Code_x86_64(uint64_t unnamed_arg_0, uint64_t unnamed_arg_1) {
|
||||
return (uint64_t) ((generic64_t) unnamed_arg_0 + (generic64_t) unnamed_arg_1);
|
||||
}
|
||||
```
|
||||
|
||||
> **Note**: the open source release of rev.ng does not currently provide the `decompile` artifact. You need access to the binary distribution to run the command above. However, we plan to move the emission of this (and any other) artifact into the open source revng project.
|
||||
|
||||
### Step 6: Renaming
|
||||
|
||||
One of the main activities of a reverse engineer is giving things a name, just like Adam in the Genesis.
|
||||
<br />Let's try to give a name to our function:
|
||||
|
||||
```diff
|
||||
--- a/model.yml
|
||||
+++ b/model.yml
|
||||
@@ -11,6 +11,7 @@ Segments:
|
||||
Functions:
|
||||
- Entry: "0x400000:Code_x86_64"
|
||||
Prototype: "/Types/1809-CABIFunctionType"
|
||||
+ CustomName: Sum
|
||||
Types:
|
||||
- Kind: PrimitiveType
|
||||
ID: 256
|
||||
```
|
||||
|
||||
Almost everything in the model can have a name. Let's add a name to the function arguments:
|
||||
|
||||
```diff
|
||||
--- a/model.yml
|
||||
+++ b/model.yml
|
||||
@@ -119,12 +120,14 @@ Types:
|
||||
- Kind: CABIFunctionType
|
||||
ABI: SystemV_x86_64
|
||||
ID: 1809
|
||||
Arguments:
|
||||
- Index: 0
|
||||
Type:
|
||||
UnqualifiedType: "/Types/1288-PrimitiveType"
|
||||
+ CustomName: FirstAddend
|
||||
- Index: 1
|
||||
Type:
|
||||
UnqualifiedType: "/Types/1288-PrimitiveType"
|
||||
+ CustomName: SecondAddend
|
||||
ReturnType:
|
||||
UnqualifiedType: "/Types/1288-PrimitiveType"
|
||||
```
|
||||
|
||||
Here's what we get now if we try to decompile again:
|
||||
|
||||
```bash
|
||||
$ revng artifact decompile sum --model model.yml | revng ptml
|
||||
0x400000:Code_x86_64.c.ptml: |-
|
||||
_ABI(SystemV_x86_64)
|
||||
uint64_t Sum(uint64_t FirstAddend, uint64_t SecondAddend) {
|
||||
return (uint64_t) ((generic64_t) FirstAddend + (generic64_t) SecondAddend);
|
||||
}
|
||||
|
||||
```
|
||||
@@ -0,0 +1,46 @@
|
||||
The *model* is a YAML document that contains information about the binary that the user might want to customize.
|
||||
You can think about the model as a sort of interchange format for reverse engineering: it is not specific to a binary image format (e.g., ELF) or an architecture.
|
||||
|
||||
It includes:
|
||||
|
||||
* **Loading Information**: a description of which parts of the input file should be loaded and at which address.
|
||||
* **The list of functions**: for each function we have an entry address, the layout of its stack frame (where local variables reside), its prototype and more.
|
||||
* **The type system**: includes all the `struct`s, `enum`s and function prototypes for the binary.
|
||||
|
||||
On the other hand, it *does not* include things such as the **control-flow graph**: the control-flow graph is rather complicated to maintain for the user.
|
||||
<br />For instance, if a user wants to mark a function as `noreturn`, in order to obtain a valid model it'd need to update the CFG of all of its callers.
|
||||
However, the user might be sometimes interested in providing extra information about the control-flow of a program, which is a job more suitable for rev.ng internals.
|
||||
|
||||
The model must be a valid YAML document, but that's not enough.
|
||||
In order to be consumed by rev.ng, a model needs to be valid.
|
||||
You can check if a model is valid as follows:
|
||||
|
||||
```{bash notest}
|
||||
$ revng model opt -verify mymodel.yml
|
||||
```
|
||||
|
||||
If the command succeeds, the tool will print the model again.
|
||||
|
||||
## Who uses the model?
|
||||
|
||||
The model has a couple of different users:
|
||||
|
||||
* **The end user**: the user, as part of his analysis activities, makes changes to the model, e.g., renaming a function, adding arguments, introducing new functions, creating new data structures.
|
||||
<br />The user interacts with the model either through the UI or manually or through scripting wrappers that enable easy maninpulation of the model.<br />
|
||||
We currently provide wrappers for Python and TypeScript.
|
||||
* **Importers/exporters**: the model is designed to be a sort of interchange format and, as such, it's not strictly rev.ng-specific.
|
||||
This means that's possible to implement importers from other formats.
|
||||
<br />For example, we provide out of the box importers for the most common binary formats (ELF, PE/COFF, MachO), debug info formats (DWARF and PDB) and other binary analysis tools such as IDA Pro's `.idb`s.
|
||||
<br />Users can easily implement new importers by simply manipulating the model (which is a plain YAML file) in their favorite scripting language.
|
||||
<br />In the future, we also plan to implement *exporters*, e.g., produce DWARF debug info that enable advanced debugging using information available in the model (e.g., arguments, data structures...) using off-the-shelf debuggers on the target binary.
|
||||
* **Pipes**: rev.ng provides a set of *pipelines*, composed by *pipes*, that produce *artifacts*. Most pipes, read the model for various reasons.
|
||||
For example, there's a pipeline responsible for generating a C header file declaring all the functions in the binary.
|
||||
In order to produce this artifact, the pipeline inspects the model and nothing else.
|
||||
Other pipelines might inspect the model, other previously generated artifacts and the input binary itself.
|
||||
* **Analyses**: rev.ng also provides a set of *analyses* that can automatically recover high-level information by analyzing the binary or artifacts produced by a pipeline.
|
||||
The final goal of an analysis is to make changes to the model.
|
||||
<br />For instance, rev.ng provides an analysis that automatically detects the arguments of a function: when you run such an analysis, it will go through the list of functions in the model, analyze each function and enrich each function that did not initially have a prototype, with arguments and return values.
|
||||
|
||||
## Relevant sources
|
||||
|
||||
* `include/revng/Model/Binary.h`
|
||||
@@ -0,0 +1,102 @@
|
||||
## Binary distribution
|
||||
|
||||
If you have access to a binary release of rev.ng just run the following command:
|
||||
|
||||
```{bash notest}
|
||||
$ tar xaf revng-*.tar.gz
|
||||
$ cd revng
|
||||
$ export PATH="$PWD:$PATH"
|
||||
$ revng artifact --help
|
||||
```
|
||||
|
||||
## Using orchestra
|
||||
|
||||
In order to build from source, you need to install our package manager, orchestra.
|
||||
|
||||
First of all, let's clone the orchestra configuration:
|
||||
|
||||
```{bash noorchestra}
|
||||
$ git clone https://github.com/revng/orchestra
|
||||
Cloning into 'orchestra'...
|
||||
$ cd orchestra
|
||||
```
|
||||
|
||||
Let's now install the actual package manager application, `revng-orchestra`.
|
||||
<br />You can either install it globally for the current user or in a virtualenv.
|
||||
|
||||
```{bash notest title="Install globally for the current user"}
|
||||
$ python3 -m pip install --user --force-reinstall https://github.com/revng/revng-orchestra/archive/master.zip
|
||||
$ export PATH="$HOME/.local/bin:$PATH"
|
||||
```
|
||||
|
||||
```{bash title="Install in a virtualenv" notest}
|
||||
$ python -m venv venv
|
||||
$ source venv/bin/activate
|
||||
$ pip install --upgrade pip wheel certifi
|
||||
$ pip install https://github.com/revng/revng-orchestra/archive/master.zip
|
||||
```
|
||||
|
||||
At this point, from the `orchestra` repository, we can sync all the information with the remote repositories:
|
||||
|
||||
```{bash silent}
|
||||
$ orc update
|
||||
```
|
||||
|
||||
To install `revng` and its dependencies from the binary archives run the following command:
|
||||
|
||||
```{bash notest}
|
||||
$ orc install revng
|
||||
```
|
||||
|
||||
Now you can enter the environment where you can use `revng`:
|
||||
|
||||
```{bash notest}
|
||||
$ orc shell
|
||||
$ revng artifact --help
|
||||
$ cat .orchestra/config/user_options.yml
|
||||
```
|
||||
|
||||
### Building from source
|
||||
|
||||
Add the following to `.orchestra/config/user_options.yml`:
|
||||
|
||||
```diff
|
||||
--- a/.orchestra/config/user_options.yml
|
||||
+++ b/.orchestra/config/user_options.yml
|
||||
@@ -11,6 +11,6 @@
|
||||
binary_archives:
|
||||
- origin: "https://rev.ng/gitlab/revng/binary-archives.git"
|
||||
|
||||
-#! #@overlay/replace
|
||||
-#! build_from_source:
|
||||
-#! - component-name
|
||||
+#@overlay/replace
|
||||
+build_from_source:
|
||||
+ - revng
|
||||
```
|
||||
|
||||
We can now install and test `revng`:
|
||||
|
||||
```{bash notest}
|
||||
$ orc install --test revng
|
||||
```
|
||||
|
||||
This will clone the sources into `sources/revng`, build it, install it (in `root/`) and run the test suite.
|
||||
|
||||
### rev.ng Developers
|
||||
|
||||
If you have access to the private rev.ng repositories (mainly, `revng-c`), you can build from source that too:
|
||||
|
||||
```diff
|
||||
--- a/.orchestra/config/user_options.yml
|
||||
+++ b/.orchestra/config/user_options.yml
|
||||
@@ -14,3 +14,4 @@
|
||||
#@overlay/replace
|
||||
build_from_source:
|
||||
- revng
|
||||
+ - revng-c
|
||||
```
|
||||
|
||||
```{bash notest}
|
||||
$ orc install --test revng-c
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
# What is rev.ng?
|
||||
|
||||
rev.ng stands for *next-generation reverse engineering*.
|
||||
It is pronounced *revenge* (/ɹɪˈvɛndʒ/).
|
||||
Its key features are:
|
||||
|
||||
* **Decompilation**: rev.ng features a full-fledged decompiler emitting C code.
|
||||
The C code we emit is designed to be valid and recompilable without significant warnings.
|
||||
* **VSCode-based UI**: we provide a UI based on VSCode hiding away all the complexity of rev.ng internals and wrapping everything in a nice, usable UI.
|
||||
* **Interactivity**: rev.ng is structured to automatically infer as much information as possible from the binary, but lets the user customize it by simply editing a YAML file, aka *the model*.
|
||||
Every change to the model automatically invalidates certain things previously produced by rev.ng, which will be automatically recomputed when needed.<br />
|
||||
We offer Python and TypeScript wrappers to interact with rev.ng.
|
||||
* **Focus on data structures**: the most innovative feature of rev.ng is its focus on data structures. Thanks to Data Layout Analysis, we can automatically infer data structures by analyzing how a pointer is used by one or more functions.
|
||||
* **Compatibility**: rev.ng can import from IDA's `.idb`/`.i64`, Linux/macOS debug information (DWARF) and Windows debug information (PDB).
|
||||
|
||||
In terms of design choices, its distinctive characteristics are:
|
||||
|
||||
* **Openness**: all the decompilation pipeline is (going to be) open source.
|
||||
Certain analyses, such as Data Layout Analysis will be commercially available.
|
||||
* **Based on QEMU**: it employs QEMU to lift assembly code to an architecture-independent representation.
|
||||
Note that we never emulate any code, we simply use QEMU as a lifter.
|
||||
* **Based on LLVM**: all of our analyses are LLVM passes.
|
||||
This enables to use existing, powerful analyses and optimizations and ensures scalability.
|
||||
* **Client/server Architecture**: scripting and automation takes place in your favorite environment by connecting to a GraphQL API over the network or locally, possibly through our wrappers.
|
||||
Reference in New Issue
Block a user