You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pwndbg/scripts/_docs/extract_function_docs.py

105 lines
2.8 KiB
Python

#!/usr/bin/env python
from __future__ import annotations
import json
import re
from dataclasses import asdict
from inspect import getdoc
from inspect import signature
import pwndbg
from scripts._docs.function_docs_common import ExtractedFunction
from scripts._docs.function_docs_common import extracted_filename
from scripts._docs.gen_docs_generic import get_debugger
if pwndbg.dbg.is_gdblib_available():
from pwndbg.gdblib.functions import GdbFunction as ConvFunction
else:
# Convenience Function - dummy class for debuggers
# that don't support it.
class ConvFunction:
pass
def sanitize_signature(func_name: str, sig: str) -> str:
"""
We need to strip ' from type annotations, and cleanup
some functions that don't display properly.
"""
sig = sig.replace("'", "")
# Fixup default values. Example, change this:
# fsbase(offset: gdb.Value = <gdb.Value object at 0x7fb49fd2b9b0>) -> int
# into
# fsbase(offset: gdb.Value = gdb.Value(0)) -> int
# Unfortunately, I don't know how to extract the `0` from gdb.Value(0), and
# the number can be any arbitrary number so I cannot just count on it being zero.
# Thus, we will hardcode the doc fixes here (thankfully there aren't too many
# functions like this).
gdb_value_fixups: dict[str, int] = {
# convenience function name: the value inside gdb.Value(<this one>)
"fsbase": 0,
"gsbase": 0,
"heap": 0,
"stack": 0,
"bss": 0,
"got": 0,
}
if func_name in gdb_value_fixups:
sig = re.sub(
r"<gdb\.Value object at 0x[0-9a-fA-F]+>",
f"gdb.Value({gdb_value_fixups[func_name]})",
sig,
)
return sig
def extract_functions() -> list[ConvFunction]:
"""
Returns a dictionary that mapes function names to
the corresponding _GdbFunction objects.
"""
if pwndbg.dbg.is_gdblib_available():
functions = pwndbg.gdblib.functions.functions
else:
functions = []
return functions
def distill_sources(funcs: list[ConvFunction]) -> list[ExtractedFunction]:
result: list[ExtractedFunction] = []
for func in funcs:
name = func.name
signa = sanitize_signature(name, str(signature(func.func)))
docstr = getdoc(func)
result.append(ExtractedFunction(name, signa, docstr))
return result
def main():
print("\n== Extracting Functions ==")
debugger = get_debugger()
funcs = extract_functions()
extracted = distill_sources(funcs)
result = [asdict(x) for x in extracted]
# Write to file.
out_path = extracted_filename(debugger)
with open(out_path, "w") as file:
json.dump(result, file)
print("== Finished Extracting Functions ==")
# Not checking __name__ due to lldb
# (even though it doesn't support functions /shrug).
main()