mirror of https://github.com/pwndbg/pwndbg.git
Use gdb/lldb from pypi (#3119)
* add lldb/gdb pypi * add gdb * debug * debug * fix paths * fix lldb * add lldb-server * bump lock * fix nix * fix lint * rename gdbinit * fix pwndbg nix * revert missing files * fix docs * fix docs * remove .skip-venv, not needed anymore * cleanup version comment * fix lint script * lint * fix nix develop * fix docs * fix docs script * use 'pwndbg' binary for tests * lint * fix kernel tests * fix ubuntu22.04 * bump lldb * refactor gdbinit/lldbinit * test1 * fix logger * fix tests no-homepull/3137/head
parent
22e26203a5
commit
df12edc0d5
@ -0,0 +1,90 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import cProfile
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
import gdb
|
||||||
|
|
||||||
|
from pwndbginit import gdbpatches # noqa: F401
|
||||||
|
from pwndbginit.common import verify_venv
|
||||||
|
|
||||||
|
|
||||||
|
def init_logger():
|
||||||
|
log_level_env = os.environ.get("PWNDBG_LOGLEVEL", "WARNING")
|
||||||
|
log_level = getattr(logging, log_level_env.upper())
|
||||||
|
|
||||||
|
root_logger = logging.getLogger()
|
||||||
|
root_logger.setLevel(log_level)
|
||||||
|
|
||||||
|
# Add a custom StreamHandler we will use to customize log message formatting. We
|
||||||
|
# configure the handler later, after pwndbg has been imported.
|
||||||
|
handler = logging.StreamHandler()
|
||||||
|
root_logger.addHandler(handler)
|
||||||
|
|
||||||
|
return handler
|
||||||
|
|
||||||
|
|
||||||
|
def check_doubleload():
|
||||||
|
if "pwndbg" in sys.modules:
|
||||||
|
print(
|
||||||
|
"Detected double-loading of Pwndbg (likely from both .gdbinit and the Pwndbg portable build)."
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"To fix this, please remove the line 'source your-path/gdbinit.py' from your .gdbinit file."
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
handler = init_logger()
|
||||||
|
profiler = cProfile.Profile()
|
||||||
|
|
||||||
|
start_time = None
|
||||||
|
if os.environ.get("PWNDBG_PROFILE") == "1":
|
||||||
|
start_time = time.time()
|
||||||
|
profiler.enable()
|
||||||
|
|
||||||
|
check_doubleload()
|
||||||
|
verify_venv()
|
||||||
|
|
||||||
|
# Force UTF-8 encoding (to_string=True to skip output appearing to the user)
|
||||||
|
try:
|
||||||
|
gdb.execute("set target-wide-charset UTF-8", to_string=True)
|
||||||
|
gdb.execute("set charset UTF-8", to_string=True)
|
||||||
|
except gdb.error as e:
|
||||||
|
print(f"Warning: Cannot set gdb charset: '{e}'")
|
||||||
|
|
||||||
|
import pwndbg # noqa: F811
|
||||||
|
import pwndbg.dbg.gdb
|
||||||
|
|
||||||
|
pwndbg.dbg = pwndbg.dbg_mod.gdb.GDB()
|
||||||
|
pwndbg.dbg.setup()
|
||||||
|
|
||||||
|
import pwndbg.log
|
||||||
|
import pwndbg.profiling
|
||||||
|
|
||||||
|
# ColorFormatter relies on pwndbg being loaded, so we can't set it up until now
|
||||||
|
handler.setFormatter(pwndbg.log.ColorFormatter())
|
||||||
|
|
||||||
|
pwndbg.profiling.init(profiler, start_time)
|
||||||
|
if os.environ.get("PWNDBG_PROFILE") == "1":
|
||||||
|
pwndbg.profiling.profiler.stop("pwndbg-load.pstats")
|
||||||
|
pwndbg.profiling.profiler.start()
|
||||||
|
|
||||||
|
# We need reimport it here so that it's available at the global scope
|
||||||
|
# when some starts a Python interpreter in GDB
|
||||||
|
gdb.execute("py import pwndbg")
|
||||||
|
|
||||||
|
|
||||||
|
def main_try():
|
||||||
|
# We wrap everything in try/except so that we can exit GDB with an error code
|
||||||
|
# This is used by tests to check if gdbinit.py failed
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except Exception:
|
||||||
|
print(traceback.format_exc(), file=sys.stderr, flush=True)
|
||||||
|
os._exit(1)
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import importlib.abc
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
import gdb
|
||||||
|
|
||||||
|
|
||||||
|
def fix_exit():
|
||||||
|
major_ver = int(gdb.VERSION.split(".")[0])
|
||||||
|
if major_ver <= 15:
|
||||||
|
# On certain verions of gdb (used on ubuntu 24.04) using sys.exit() can cause
|
||||||
|
# a segfault. See:
|
||||||
|
# https://github.com/pwndbg/pwndbg/pull/2900#issuecomment-2825456636
|
||||||
|
# https://sourceware.org/bugzilla/show_bug.cgi?id=31946
|
||||||
|
def _patched_exit(exit_code):
|
||||||
|
# argparse requires a SystemExit exception, otherwise our CLI commands will exit incorrectly on invalid arguments
|
||||||
|
stack_list = traceback.extract_stack(limit=2)
|
||||||
|
if len(stack_list) == 2:
|
||||||
|
p = stack_list[0]
|
||||||
|
if p.filename.endswith("/argparse.py"):
|
||||||
|
raise SystemExit()
|
||||||
|
|
||||||
|
sys.stdout.flush()
|
||||||
|
sys.stderr.flush()
|
||||||
|
os._exit(exit_code)
|
||||||
|
|
||||||
|
sys.exit = _patched_exit
|
||||||
|
|
||||||
|
|
||||||
|
def fix_stdout():
|
||||||
|
# Add the original stdout methods back to gdb._GdbOutputFile for pwnlib colors
|
||||||
|
sys.stdout.isatty = sys.__stdout__.isatty
|
||||||
|
sys.stdout.fileno = sys.__stdout__.fileno
|
||||||
|
|
||||||
|
|
||||||
|
def fix_readline():
|
||||||
|
# Fix gdb readline bug: https://github.com/pwndbg/pwndbg/issues/2232#issuecomment-2542564965
|
||||||
|
class GdbRemoveReadlineFinder(importlib.abc.MetaPathFinder):
|
||||||
|
def find_spec(self, fullname, path=None, target=None):
|
||||||
|
if fullname == "readline":
|
||||||
|
raise ImportError("readline module disabled under GDB")
|
||||||
|
return None
|
||||||
|
|
||||||
|
sys.meta_path.insert(0, GdbRemoveReadlineFinder())
|
||||||
|
|
||||||
|
|
||||||
|
fix_stdout()
|
||||||
|
fix_readline()
|
||||||
|
fix_exit()
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import cProfile
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
import lldb
|
||||||
|
|
||||||
|
from pwndbginit.common import verify_venv
|
||||||
|
|
||||||
|
|
||||||
|
def main(debugger: lldb.SBDebugger, major: int, minor: int, debug: bool = False) -> None:
|
||||||
|
if "pwndbg" in sys.modules:
|
||||||
|
print("Detected double-loading of Pwndbg.")
|
||||||
|
print("This should not happen. Please report this issue if you're not sure how to fix it.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
verify_venv()
|
||||||
|
profiler = cProfile.Profile()
|
||||||
|
|
||||||
|
start_time = None
|
||||||
|
if os.environ.get("PWNDBG_PROFILE") == "1":
|
||||||
|
start_time = time.time()
|
||||||
|
profiler.enable()
|
||||||
|
|
||||||
|
import pwndbg # noqa: F811
|
||||||
|
import pwndbg.dbg.lldb
|
||||||
|
|
||||||
|
pwndbg.dbg_mod.lldb.LLDB_VERSION = (major, minor)
|
||||||
|
|
||||||
|
pwndbg.dbg = pwndbg.dbg_mod.lldb.LLDB()
|
||||||
|
pwndbg.dbg.setup(debugger, "pwndbglldbhandler", debug=debug)
|
||||||
|
|
||||||
|
import pwndbg.profiling
|
||||||
|
|
||||||
|
pwndbg.profiling.init(profiler, start_time)
|
||||||
|
if os.environ.get("PWNDBG_PROFILE") == "1":
|
||||||
|
pwndbg.profiling.profiler.stop("pwndbg-load.pstats")
|
||||||
|
pwndbg.profiling.profiler.start()
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import sysconfig
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
|
||||||
|
def get_gdb_version(path: str) -> Tuple[str, str]:
|
||||||
|
result = subprocess.run(
|
||||||
|
[
|
||||||
|
path,
|
||||||
|
"-nx",
|
||||||
|
"--batch",
|
||||||
|
"-iex",
|
||||||
|
"py import sysconfig; print(sysconfig.get_config_var('INSTSONAME'), sysconfig.get_config_var('VERSION'))",
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
return tuple(result.stdout.strip().split(" ", 2))
|
||||||
|
|
||||||
|
|
||||||
|
def get_venv_bin_path():
|
||||||
|
bin_dir = "Scripts" if os.name == "nt" else "bin"
|
||||||
|
return os.path.join(sys.prefix, bin_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def prepend_venv_bin_to_path():
|
||||||
|
# Set virtualenv's bin path (needed for utility tools like ropper, pwntools etc)
|
||||||
|
venv_bin = get_venv_bin_path()
|
||||||
|
path_elements = os.environ.get("PATH", "").split(os.pathsep)
|
||||||
|
if venv_bin in path_elements:
|
||||||
|
return
|
||||||
|
|
||||||
|
path_elements.insert(0, venv_bin)
|
||||||
|
os.environ["PATH"] = os.pathsep.join(path_elements)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
prepend_venv_bin_to_path()
|
||||||
|
|
||||||
|
gdb_argv = [
|
||||||
|
sys.argv[0],
|
||||||
|
"-q",
|
||||||
|
"-nx",
|
||||||
|
"-iex",
|
||||||
|
"py import pwndbginit.gdbinit; pwndbginit.gdbinit.main_try()",
|
||||||
|
*sys.argv[1:],
|
||||||
|
]
|
||||||
|
sys.argv = gdb_argv
|
||||||
|
|
||||||
|
try:
|
||||||
|
from gdb_for_pwndbg.gdb import main
|
||||||
|
|
||||||
|
main()
|
||||||
|
return
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
gdb_path = shutil.which("gdb")
|
||||||
|
if not gdb_path:
|
||||||
|
print("ERROR: Could not find 'gdb' binary")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
envs = os.environ.copy()
|
||||||
|
envs["PYTHONNOUSERSITE"] = "1"
|
||||||
|
envs["PYTHONPATH"] = ":".join(sys.path)
|
||||||
|
envs["PYTHONHOME"] = f"{sys.prefix}:{sys.exec_prefix}"
|
||||||
|
|
||||||
|
expected = (sysconfig.get_config_var("INSTSONAME"), sysconfig.get_config_var("VERSION"))
|
||||||
|
have = get_gdb_version(gdb_path)
|
||||||
|
if have != expected:
|
||||||
|
print(
|
||||||
|
f"ERROR: GDB is compiled for Python {have}, but your Python interpreter is {expected}"
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
os.execve(gdb_path, sys.argv, env=envs)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@ -0,0 +1 @@
|
|||||||
|
# This file is used just to handle lldb-commands, it must be empty
|
||||||
Loading…
Reference in new issue