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