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-home
pull/3137/head
patryk4815 6 months ago committed by GitHub
parent 22e26203a5
commit df12edc0d5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -21,14 +21,6 @@ jobs:
- name: Install dependencies
run: uv sync --group docs --all-extras
- name: Install lldb
run: |
# https://pwndbg.re/pwndbg/dev/contributing/setup-pwndbg-dev/#running-with-lldb
sudo apt update
sudo apt install -y gdb lldb-19 liblldb-19-dev
echo "/usr/lib/llvm-19/bin/" >> $GITHUB_PATH
echo "/usr/lib/llvm-19/bin/lldb-server" >> $GITHUB_PATH
- name: Verify docs are up to date with source
run: |
./scripts/verify-docs.sh

@ -6,7 +6,7 @@ Pwndbg relies on several environment variables to customize its behavior. Below
- `EDITOR`, `VISUAL`: Used by the `cymbol` command to open an editor.
- `HOME`, `XDG_CACHE_HOME`: Used by `lib.tempfile` to determine temporary file locations.
- `PWNDBG_VENV_PATH`: Specifies the virtual environment path for Pwndbg.
- Set to `PWNDBG_PLEASE_SKIP_VENV` if you don't want Pwndbg to use a python virtual environment. You can also get this behaviour by creating a file named `.skip-venv` in the project root.
- Set to `PWNDBG_PLEASE_SKIP_VENV` if you don't want Pwndbg to use a python virtual environment.
This effectively disables the use of `uv` in the project.
- `PWNDBG_DISABLE_COLORS`: Disables colored output in Pwndbg.
- `PWNDBG_LOGLEVEL`: Initial log level to use for log messages.

@ -1,113 +1,10 @@
from __future__ import annotations
import cProfile
import hashlib
import importlib.abc
import logging
import os
import shutil
import site
import subprocess
import sys
import time
import traceback
from glob import glob
from pathlib import Path
from typing import List
from typing import Tuple
import gdb
# 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())
def hash_file(file_path: str | Path) -> str:
with open(file_path, "rb") as f:
file_hash = hashlib.sha256()
while True:
chunk = f.read(8192)
if not chunk:
break
file_hash.update(chunk)
return file_hash.hexdigest()
def run_uv_install(
binary_path: os.PathLike[str], src_root: Path, dev: bool = False
) -> Tuple[str, str, int]:
# We don't want to quietly uninstall dependencies by just specifying
# `--extra gdb` so we will be conservative and pull all extras in.
command: List[str] = [str(binary_path), "sync", "--all-extras"]
if dev:
command.append("--all-groups")
logging.debug(f"Updating deps with command: {' '.join(command)}")
result = subprocess.run(command, capture_output=True, text=True, cwd=src_root)
return result.stdout.strip(), result.stderr.strip(), result.returncode
def find_uv(venv_path: Path) -> Path | None:
binary_path = shutil.which("uv", path=venv_path / "bin")
if binary_path is not None:
return Path(binary_path)
return None
def is_dev_mode(venv_path: Path) -> bool:
# If "dev.marker" exists in the venv directory, the user ran setup-dev.sh and is
# considered a developer
return (venv_path / "dev.marker").exists()
def update_deps(src_root: Path, venv_path: Path) -> None:
uv_lock_hash_path = venv_path / "uv.lock.hash"
current_hash = hash_file(src_root / "uv.lock")
logging.debug(f"Current uv.lock hash: {current_hash}")
stored_hash = None
if uv_lock_hash_path.exists():
stored_hash = uv_lock_hash_path.read_text().strip()
logging.debug(f"Stored uv.lock hash: {stored_hash}")
else:
logging.debug("No stored hash found")
# If the hashes don't match, update the dependencies
if current_hash == stored_hash:
return
print("Detected outdated Pwndbg dependencies (uv.lock). Updating.")
uv_path = find_uv(venv_path)
if uv_path is None:
print(
"'uv' was not found on the $PATH. Please ensure it is installed and on the path, "
"or run `./setup.sh` to manually update Python dependencies."
)
return
dev_mode = is_dev_mode(venv_path)
stdout, stderr, return_code = run_uv_install(uv_path, src_root, dev=dev_mode)
if return_code == 0:
uv_lock_hash_path.write_text(current_hash)
# Only print the uv output if anything was actually updated
if "No dependencies to install or update" not in stdout:
# The output is usually long and ends up paginated. This
# normally gets disabled later during initialization, but in
# this case we disable it here to avoid pagination.
gdb.execute("set pagination off", to_string=True)
print(stdout)
else:
print(stderr, file=sys.stderr)
def fixup_paths(src_root: Path, venv_path: Path):
@ -129,6 +26,7 @@ def fixup_paths(src_root: Path, venv_path: Path):
sys.path.insert(0, str(src_root))
# Push virtualenv's site-packages to the front
if site_pkgs_path in sys.path:
sys.path.remove(site_pkgs_path)
sys.path.insert(1, site_pkgs_path)
@ -141,123 +39,20 @@ def get_venv_path(src_root: Path):
return src_root / ".venv"
def skip_venv(src_root) -> bool:
return (
os.environ.get("PWNDBG_VENV_PATH") == "PWNDBG_PLEASE_SKIP_VENV"
or (src_root / ".skip-venv").exists()
)
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 rewire_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 main() -> None:
profiler = cProfile.Profile()
start_time = None
if os.environ.get("PWNDBG_PROFILE") == "1":
start_time = time.time()
profiler.enable()
rewire_exit()
check_doubleload()
handler = init_logger()
src_root = Path(__file__).parent.resolve()
if not skip_venv(src_root):
venv_path = get_venv_path(src_root)
if not venv_path.exists():
print(f"Cannot find Pwndbg virtualenv directory: {venv_path}. Please re-run setup.sh")
sys.exit(1)
no_auto_update = os.getenv("PWNDBG_NO_AUTOUPDATE")
if no_auto_update is None:
update_deps(src_root, venv_path)
fixup_paths(src_root, venv_path)
# 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}'")
# Add the original stdout methods back to gdb._GdbOutputFile for pwnlib colors
sys.stdout.isatty = sys.__stdout__.isatty
sys.stdout.fileno = sys.__stdout__.fileno
import pwndbg # noqa: F811
import pwndbg.dbg.gdb
pwndbg.dbg = pwndbg.dbg_mod.gdb.GDB()
pwndbg.dbg.setup()
import pwndbg.log
import pwndbg.profiling
print(
f"Cannot find Pwndbg virtualenv directory: {venv_path}. Please re-run setup.sh",
flush=True,
)
os._exit(1)
# ColorFormatter relies on pwndbg being loaded, so we can't set it up until now
handler.setFormatter(pwndbg.log.ColorFormatter())
fixup_paths(src_root, venv_path)
from pwndbginit.gdbinit import main_try
pwndbg.profiling.init(profiler, start_time)
if os.environ.get("PWNDBG_PROFILE") == "1":
pwndbg.profiling.profiler.stop("pwndbg-load.pstats")
pwndbg.profiling.profiler.start()
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()
# We've already imported this in `main`, but we reimport it here so that it's
# available at the global scope when some starts a Python interpreter in GDB
import pwndbg # noqa: F401
except Exception:
print(traceback.format_exc(), file=sys.stderr)
sys.exit(1)

@ -32,7 +32,7 @@ done
set -o xtrace
LINT_FILES="pwndbg tests *.py scripts"
LINT_FILES="pwndbg pwndbginit tests *.py scripts"
call_shfmt() {
local FLAGS=$1
@ -71,5 +71,5 @@ $UV_RUN_LINT vermin -vvv --no-tips -t=3.10- --eval-annotations --violations ${LI
# mypy is run in a separate step on GitHub Actions
if [[ -z "$GITHUB_ACTIONS" ]]; then
$UV_RUN_LINT mypy pwndbg gdbinit.py lldbinit.py pwndbg-lldb.py tests/host
$UV_RUN_LINT mypy pwndbg pwndbginit tests/host
fi

@ -61,7 +61,6 @@ in
nasm
gcc
curl
gdb
parallel
qemu
zig_0_13 # version match setup-dev.sh
@ -76,21 +75,11 @@ in
}
++ [
jemalloc-static
pkgs.gdb
pyEnv
(pkgs.writeShellScriptBin "pwndbg" ''
exec ${lib.getBin pkgs.gdb}/bin/gdb --quiet --nx --init-command="$REPO_ROOT/gdbinit.py" $@
'')
]
++ pkgs.lib.optionals isLLDB [
pkgs.lldb_20
(pkgs.writeShellScriptBin "pwndbg-lldb" (
(lib.optionalString (!pkgs.stdenv.isDarwin) ''
export LLDB_DEBUGSERVER_PATH=${lib.makeBinPath [ pkgs.lldb_20 ]}/lldb-server
'')
+ ''
exec ${lib.getBin pyEnv}/bin/python3 $REPO_ROOT/pwndbg-lldb.py $@
''
))
];
shellHook = ''
export PWNDBG_NO_AUTOUPDATE=1

@ -10,14 +10,13 @@
}:
let
lib = pkgs.lib;
binPath = lib.makeBinPath (
extraPackags =
[
python3.pkgs.pwntools # ref: https://github.com/pwndbg/pwndbg/blob/2023.07.17/pwndbg/wrappers/checksec.py#L8
]
++ lib.optionals pkgs.stdenv.isLinux [
python3.pkgs.ropper # ref: https://github.com/pwndbg/pwndbg/blob/2023.07.17/pwndbg/commands/ropper.py#L30
]
);
];
pyEnv = import ./pyenv.nix {
inherit
@ -37,92 +36,31 @@ let
in
version;
pwndbg = pkgs.callPackage (
pwndbg_gdb =
pkgs.runCommand "pwndbg"
{
stdenv,
makeWrapper,
}:
let
pwndbgName = if isLLDB then "pwndbg-lldb" else "pwndbg";
in
stdenv.mkDerivation {
name = pwndbgName;
version = pwndbgVersion;
src = lib.sourceByRegex inputs.self (
[
"pwndbg"
"pwndbg/.*"
]
++ (
if isLLDB then
[
"lldbinit.py"
"pwndbg-lldb.py"
]
else
[
"gdbinit.py"
]
)
);
nativeBuildInputs = [ makeWrapper ];
buildInputs = [ pyEnv ];
installPhase =
let
fix_init_script =
{ target, line }:
nativeBuildInputs = [ pkgs.pkgsBuildHost.makeWrapper ];
}
''
# Build self-contained init script for lazy loading from vanilla gdb
# I purposely use insert() so I can re-import during development without having to restart gdb
sed "${line} i import sys, os\n\
sys.path.insert(0, '${pyEnv}/${python3.sitePackages}')\n\
sys.path.insert(0, '$out/share/pwndbg/')\n\
os.environ['PATH'] += ':${binPath}'\n" -i ${target}
mkdir -p $out/bin/
makeWrapper ${pyEnv}/bin/pwndbg $out/bin/pwndbg \
--prefix PATH : ${lib.makeBinPath ([ gdb ] ++ extraPackags)}
'';
in
(
if isLLDB then
''
mkdir -p $out/share/pwndbg
mkdir -p $out/bin
cp -r lldbinit.py pwndbg $out/share/pwndbg
cp pwndbg-lldb.py $out/bin/${pwndbgName}
${fix_init_script {
target = "$out/bin/${pwndbgName}";
line = "4";
}}
touch $out/share/pwndbg/.skip-venv
wrapProgram $out/bin/${pwndbgName} \
--prefix PATH : ${lib.makeBinPath [ lldb ]} \
''
+ (lib.optionalString (!stdenv.isDarwin) ''
--set LLDB_DEBUGSERVER_PATH ${lib.makeBinPath [ lldb ]}/lldb-server \
'')
+ ''
--set PWNDBG_LLDBINIT_DIR $out/share/pwndbg
''
else
''
mkdir -p $out/share/pwndbg
cp -r gdbinit.py pwndbg $out/share/pwndbg
${fix_init_script {
target = "$out/share/pwndbg/gdbinit.py";
line = "2";
}}
touch $out/share/pwndbg/.skip-venv
makeWrapper ${gdb}/bin/gdb $out/bin/${pwndbgName} \
--add-flags "--quiet --nx --init-command=$out/share/pwndbg/gdbinit.py"
pwndbg_lldb =
pkgs.runCommand "pwndbg-lldb"
{
version = pwndbgVersion;
nativeBuildInputs = [ pkgs.pkgsBuildHost.makeWrapper ];
}
''
);
mkdir -p $out/bin/
makeWrapper ${pyEnv}/bin/pwndbg-lldb $out/bin/pwndbg-lldb \
--prefix PATH : ${lib.makeBinPath ([ lldb ] ++ extraPackags)}
'';
pwndbg_final = (if isLLDB then pwndbg_lldb else pwndbg_gdb) // {
meta = {
pwndbgVenv = pyEnv;
python3 = python3;
@ -130,7 +68,6 @@ let
lldb = lldb;
isLLDB = isLLDB;
};
}
) { };
};
in
pwndbg
pwndbg_final

@ -134,6 +134,8 @@ let
paramiko = dummy;
pip = dummy;
uv = dummy;
gdb-for-pwndbg = dummy;
lldb-for-pwndbg = dummy;
psutil = pkgs.callPackage (
{
@ -336,6 +338,9 @@ let
++ lib.optionals isLLDB [
"lldb"
]
++ lib.optionals (!isLLDB) [
"gdb"
]
++ lib.optionals isDev [
"dev"
"tests"

@ -9,8 +9,8 @@ def build_id() -> str:
Returns pwndbg commit id if git is available.
"""
pwndbg_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
# If we install pwndbg into site-packages, then `gdbinit.py` is missing.
if not os.path.exists(os.path.join(pwndbg_dir, "gdbinit.py")):
# If we install pwndbg into site-packages, then `.pwndbg_root` is missing.
if not os.path.exists(os.path.join(pwndbg_dir, ".pwndbg_root")):
return ""
try:

@ -1,20 +1,15 @@
from __future__ import annotations
import cProfile
import hashlib
import logging
import os
import shutil
import site
import subprocess
import sys
import time
from glob import glob
from pathlib import Path
from typing import List
from typing import Tuple
import lldb
def hash_file(file_path: str | Path) -> str:
with open(file_path, "rb") as f:
@ -31,10 +26,11 @@ def run_uv_install(
binary_path: os.PathLike[str], src_root: Path, dev: bool = False
) -> Tuple[str, str, int]:
# We don't want to quietly uninstall dependencies by just specifying
# `--extra lldb` so we will be conservative and pull all extras in.
# `--extra gdb` so we will be conservative and pull all extras in.
command: List[str] = [str(binary_path), "sync", "--all-extras"]
if dev:
command.append("--all-groups")
logging.debug(f"Updating deps with command: {' '.join(command)}")
result = subprocess.run(command, capture_output=True, text=True, cwd=src_root)
return result.stdout.strip(), result.stderr.strip(), result.returncode
@ -57,10 +53,14 @@ def update_deps(src_root: Path, venv_path: Path) -> None:
uv_lock_hash_path = venv_path / "uv.lock.hash"
current_hash = hash_file(src_root / "uv.lock")
logging.debug(f"Current uv.lock hash: {current_hash}")
stored_hash = None
if uv_lock_hash_path.exists():
stored_hash = uv_lock_hash_path.read_text().strip()
logging.debug(f"Stored uv.lock hash: {stored_hash}")
else:
logging.debug("No stored hash found")
# If the hashes don't match, update the dependencies
if current_hash == stored_hash:
@ -87,30 +87,6 @@ def update_deps(src_root: Path, venv_path: Path) -> None:
print(stderr, file=sys.stderr)
def fixup_paths(src_root: Path, venv_path: Path):
site_pkgs_path = glob(str(venv_path / "lib/*/site-packages"))[0]
# add virtualenv's site-packages to sys.path and run .pth files
site.addsitedir(site_pkgs_path)
# remove existing, system-level site-packages from sys.path
for site_packages in site.getsitepackages():
if site_packages in sys.path:
sys.path.remove(site_packages)
# Set virtualenv's bin path (needed for utility tools like ropper, pwntools etc)
bin_path = str(venv_path / "bin")
os.environ["PATH"] = bin_path + os.pathsep + os.environ.get("PATH", "")
# Add pwndbg directory to sys.path so it can be imported
sys.path.insert(0, str(src_root))
# Push virtualenv's site-packages to the front
if site_pkgs_path in sys.path:
sys.path.remove(site_pkgs_path)
sys.path.insert(1, site_pkgs_path)
def get_venv_path(src_root: Path):
venv_path_env = os.environ.get("PWNDBG_VENV_PATH")
if venv_path_env:
@ -122,46 +98,25 @@ def get_venv_path(src_root: Path):
def skip_venv(src_root) -> bool:
return (
os.environ.get("PWNDBG_VENV_PATH") == "PWNDBG_PLEASE_SKIP_VENV"
or (src_root / ".skip-venv").exists()
or not (src_root / ".pwndbg_root").exists()
)
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)
profiler = cProfile.Profile()
start_time = None
if os.environ.get("PWNDBG_PROFILE") == "1":
start_time = time.time()
profiler.enable()
def verify_venv():
src_root = Path(__file__).parent.parent.resolve()
if skip_venv(src_root):
return
src_root = Path(__file__).parent.resolve()
if not skip_venv(src_root):
venv_path = get_venv_path(src_root)
if not venv_path.exists():
print(f"Cannot find Pwndbg virtualenv directory: {venv_path}. Please re-run setup.sh")
sys.exit(1)
no_auto_update = os.getenv("PWNDBG_NO_AUTOUPDATE")
if no_auto_update is None:
update_deps(src_root, venv_path)
fixup_paths(src_root, venv_path)
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, __name__, debug=debug)
print(
f"Cannot find Pwndbg virtualenv directory: {venv_path}. Please re-run setup.sh",
flush=True,
)
os._exit(1)
import pwndbg.profiling
no_auto_update = os.getenv("PWNDBG_NO_AUTOUPDATE") is not None
if no_auto_update:
return
pwndbg.profiling.init(profiler, start_time)
if os.environ.get("PWNDBG_PROFILE") == "1":
pwndbg.profiling.profiler.stop("pwndbg-load.pstats")
pwndbg.profiling.profiler.start()
update_deps(src_root, venv_path)

@ -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()

@ -5,6 +5,7 @@ from __future__ import annotations
import argparse
import os
import re
import shutil
import subprocess
import sys
from typing import List
@ -58,16 +59,30 @@ def find_lldb_python_path() -> str:
return folder
if __name__ == "__main__":
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()
args = PARSER.parse_args()
debug = args.verbose
# Find the path for the LLDB Python bindings.
path = find_lldb_python_path()
sys.path.append(path)
if debug:
print(f"[-] Launcher: LLDB Python path: {path}")
if sys.platform == "linux" and "LLDB_DEBUGSERVER_PATH" not in os.environ:
os.environ["LLDB_DEBUGSERVER_PATH"] = shutil.which("lldb-server")
# Older LLDB versions crash newer versions of CPython on import, so check
# for it, and stop early with an error message.
@ -82,35 +97,26 @@ if __name__ == "__main__":
print("LLDB 18 and earlier is incompatible with Python 3.12 and later", file=sys.stderr)
sys.exit(1)
# Start up LLDB and create a new debugger object.
try:
import lldb
except ImportError:
# Find the path for the LLDB Python bindings.
path = find_lldb_python_path()
sys.path.append(path)
if debug:
print(f"[-] Launcher: LLDB Python path: {path}")
import lldb
# Start up LLDB and create a new debugger object.
lldb.SBDebugger.Initialize()
debugger = lldb.SBDebugger.Create()
# Resolve the location of lldbinit.py based on the environment, if needed.
lldbinit_dir = os.path.dirname(sys.argv[0])
if "PWNDBG_LLDBINIT_DIR" in os.environ:
lldbinit_dir = os.environ["PWNDBG_LLDBINIT_DIR"]
lldbinit_dir = os.path.abspath(lldbinit_dir)
lldbinit_path = os.path.join(lldbinit_dir, "lldbinit.py")
from pwndbginit import lldbinit
from pwndbginit import pwndbglldbhandler
if debug:
print(f"[-] Launcher: Importing main LLDB module at '{lldbinit_path}'")
if not os.path.exists(lldbinit_path):
print(f"Could not find '{lldbinit_path}, please specify it with PWNDBG_LLDBINIT_DIR")
sys.exit(1)
if lldbinit_path not in sys.path:
sys.path.append(lldbinit_dir)
# Load the lldbinit module we just found.
debugger.HandleCommand(f"command script import {lldbinit_path}")
debugger.HandleCommand(f"command script import {pwndbglldbhandler.__file__}")
# Initialize the debugger, proper.
import lldbinit
if debug:
print("[-] Launcher: Initializing Pwndbg")
lldbinit.main(debugger, lldb_version[0], lldb_version[1], debug=debug)
@ -175,3 +181,7 @@ if __name__ == "__main__":
# Dispose of our debugger and terminate LLDB.
lldb.SBDebugger.Destroy(debugger)
lldb.SBDebugger.Terminate()
if __name__ == "__main__":
main()

@ -0,0 +1 @@
# This file is used just to handle lldb-commands, it must be empty

@ -34,9 +34,12 @@ dependencies = [
lldb = [
# The LLDB REPL requires readline.
'gnureadline>=8.2.10,<9; sys_platform != "win32"',
'pyreadline3>=3.5.4,<4; sys_platform == "win32"'
'pyreadline3>=3.5.4,<4; sys_platform == "win32"',
"lldb-for-pwndbg>=21.1.6",
]
gdb = [
"gdb-for-pwndbg>=16.2.6",
]
gdb = []
[dependency-groups]
dev = [
@ -128,10 +131,14 @@ builtins-ignorelist = [
]
[tool.hatch.build.targets.sdist]
include = ["/pwndbg"]
include = ["/pwndbg", "/pwndbginit"]
[tool.hatch.build.targets.wheel]
include = ["/pwndbg"]
include = ["/pwndbg", "/pwndbginit"]
[project.scripts]
pwndbg = "pwndbginit.pwndbg_gdb:main"
pwndbg-lldb = "pwndbginit.pwndbg_lldb:main"
[build-system]
requires = ["hatchling"]

@ -1,23 +1,5 @@
#!/usr/bin/env bash
# Check that supported LLDB is installed.
if ! command -v lldb &> /dev/null; then
echo "Cannot reliably extract information from sources because LLDB"
echo "is not installed. See installation instructions:"
echo "https://pwndbg.re/pwndbg/dev/contributing/setup-pwndbg-dev/#running-with-lldb"
exit 3
else
version=$(lldb --version | awk '{print $3}')
major_version=${version%%.*}
if [ "$major_version" -lt 19 ]; then
echo "Cannot reliably extract information from sources because your LLDB"
echo "version (${version}) is too old. Supported is LLDB >= 19. See installation instructions:"
echo "https://pwndbg.re/pwndbg/dev/contributing/setup-pwndbg-dev/#running-with-lldb"
exit 4
fi
fi
source "$(dirname "$0")/../common.sh"
cd $PWNDBG_ABS_PATH
@ -26,16 +8,16 @@ cd $PWNDBG_ABS_PATH
# the documentation. Do this from each debugger.
export PWNDBG_DOCGEN_DBGNAME="gdb"
$UV_RUN_DOCS gdb --batch -nx -ix ./gdbinit.py \
$UV_RUN_DOCS pwndbg -nx --batch \
-iex "set exception-verbose on" \
-ix ./scripts/_docs/extract_command_docs.py \
-ix ./scripts/_docs/extract_configuration_docs.py \
-ix ./scripts/_docs/extract_function_docs.py \
-nx || exit 1
|| exit 1
export PWNDBG_DOCGEN_DBGNAME="lldb"
{
$UV_RUN_DOCS python pwndbg-lldb.py << EOF
$UV_RUN_DOCS pwndbg-lldb << EOF
set show-tips off
command script import ./scripts/_docs/extract_command_docs.py
command script import ./scripts/_docs/extract_configuration_docs.py

@ -10,7 +10,7 @@ if [[ -z "${PWNDBG_VENV_PATH}" ]]; then
PWNDBG_VENV_PATH="${PWNDBG_ABS_PATH}/.venv"
fi
if [[ "$PWNDBG_VENV_PATH" == "PWNDBG_PLEASE_SKIP_VENV" || -f "${PWNDBG_ABS_PATH}/.skip-venv" || "$PWNDBG_NO_UV" == "1" ]]; then
if [[ "$PWNDBG_VENV_PATH" == "PWNDBG_PLEASE_SKIP_VENV" || "$PWNDBG_NO_UV" == "1" ]]; then
# We are using the dependencies as installed on the system
# so we shouldn't use uv (and can't, since it's not installed).
UV=""

@ -349,25 +349,3 @@ if linux; then
configure_venv
fi
fi
# LLDB is needed for docs generation. Tell the user
# if they don't have it installed or if it's an unsupported version.
if ! command -v lldb &> /dev/null; then
echo "WARNING: lldb not found in PATH, some functionality"
echo "(e.g. docs generation) will not be available."
echo "See installation instructions:"
echo "https://pwndbg.re/pwndbg/dev/contributing/setup-pwndbg-dev/#running-with-lldb"
else
version=$(lldb --version | awk '{print $3}')
major_version=${version%%.*}
if [ "$major_version" -ge 19 ]; then
echo "Supported LLDB installed. All good!"
else
echo "WARNING: lldb found in PATH, but the version is too old."
echo "Installed: ${version}. Supported: >= 19."
echo "Some functionality (e.g. docs generation) will not be available."
echo "See installation instructions:"
echo "https://pwndbg.re/pwndbg/dev/contributing/setup-pwndbg-dev/#running-with-lldb"
fi
fi

@ -133,40 +133,31 @@ def get_gdb_host(args: argparse.Namespace, local_pwndbg_root: Path) -> TestHost:
"""
if args.nix:
# Use pwndbg, as build by nix.
use_gdbinit = False
gdb_path = local_pwndbg_root / "result" / "bin" / "pwndbg"
if not gdb_path.exists():
print("ERROR: No nix-compatible pwndbg found. Run nix build .#pwndbg-dev")
sys.exit(1)
elif args.group == Group.CROSS_ARCH_USER:
# Cross-arch requires 'gdb-multiarch'.
use_gdbinit = True
if (gdb_multiarch := shutil.which("gdb-multiarch")) is not None:
gdb_path = Path(gdb_multiarch)
else:
# Some systems don't ship 'gdb-multiarch', but support multiple
# architectures in their regular binaries. Try the regular GDB.
supports_arches = "py import os; archs = ['i386', 'aarch64', 'arm', 'mips', 'riscv', 'sparc']; os._exit(3) if len([arch for arch in archs if arch in gdb.architecture_names()]) == len(archs) else os._exit(2)"
gdb_path_str = shutil.which("gdb")
gdb_path_str = shutil.which("pwndbg")
if gdb_path_str is None:
print("ERROR: No 'gdb-multiarch' or 'gdb' executables in path")
print("ERROR: No 'pwndbg' executables in path")
sys.exit(1)
result = subprocess.run([gdb_path_str, "-ex", supports_arches], capture_output=True)
result = subprocess.run([gdb_path_str, "-nx", "-ex", supports_arches], capture_output=True)
# GDB supports cross architecture targets
if result.returncode == 3:
gdb_path = Path(gdb_path_str)
else:
print(
"ERROR: 'gdb-multiarch' not found, and 'gdb' does not support cross architecture targets"
)
print("ERROR: 'pwndbg' does not support cross architecture targets")
sys.exit(1)
else:
# Use the regular system GDB.
use_gdbinit = True
gdb_path_str = shutil.which("gdb")
gdb_path_str = shutil.which("pwndbg")
if gdb_path_str is None:
print("ERROR: No 'gdb' executable in path")
sys.exit(1)
@ -179,7 +170,6 @@ def get_gdb_host(args: argparse.Namespace, local_pwndbg_root: Path) -> TestHost:
local_pwndbg_root / args.group.library(),
local_pwndbg_root / args.group.binary_dir(),
gdb_path,
use_gdbinit,
)

@ -20,13 +20,11 @@ class GDBTestHost(TestHost):
pytest_root: Path,
binaries_root: Path,
gdb_path: Path,
use_gdbinit: bool,
):
self._pwndbg_root = pwndbg_root
self._pytest_root = pytest_root
self._binaries_root = binaries_root
self._gdb_path = gdb_path
self._use_gdbinit = use_gdbinit
def _run_gdb(
self,
@ -39,10 +37,9 @@ class GDBTestHost(TestHost):
# Prepare the GDB command line.
gdb_args = ["--command", str(target)]
if self._use_gdbinit:
gdb_args.extend(["--init-command", str(self._pwndbg_root / "gdbinit.py")])
return subprocess.run(
[str(self._gdb_path), "--silent", "--nx", "--nh"]
[str(self._gdb_path), "--silent", "--nx"]
+ gdb_args_before
+ gdb_args
+ ["--eval-command", "quit"],
@ -77,10 +74,8 @@ class GDBTestHost(TestHost):
env["COVERAGE_PROCESS_START"] = str(self._pwndbg_root / "pyproject.toml")
env["PWNDBG_LAUNCH_TEST"] = case
env["PWNDBG_DISABLE_COLORS"] = "1"
env["GDB_INIT_PATH"] = str(self._pwndbg_root / "gdbinit.py")
env["GDB_BIN_PATH"] = str(self._gdb_path)
env["TEST_BINARIES_ROOT"] = str(self._binaries_root)
env["TEST_USE_GDBINIT"] = "1" if self._use_gdbinit else "0"
if interactive:
env["USE_PDB"] = "1"

@ -5,9 +5,7 @@ import os
import re
import subprocess
gdb_init_path = os.environ.get("GDB_INIT_PATH", "../gdbinit.py")
gdb_bin_path = os.environ.get("GDB_BIN_PATH", "gdb")
TEST_USE_GDBINIT = os.environ.get("TEST_USE_GDBINIT", "0")
gdb_bin_path = os.environ.get("GDB_BIN_PATH", "pwndbg")
def run_gdb_with_script(
@ -25,14 +23,11 @@ def run_gdb_with_script(
pybefore = ([pybefore] if isinstance(pybefore, str) else pybefore) or []
pyafter = ([pyafter] if isinstance(pyafter, str) else pyafter) or []
command = [gdb_bin_path, "--silent", "--nx", "--nh"]
command = [gdb_bin_path, "--silent", "--nx"]
for cmd in pybefore:
command += ["--init-eval-command", cmd]
if gdb_init_path and TEST_USE_GDBINIT == "1":
command += ["--command", gdb_init_path]
if binary:
command += [binary]

@ -6,7 +6,6 @@ set -o pipefail
source "$(dirname "$0")/../../../scripts/common.sh"
ROOT_DIR=$PWNDBG_ABS_PATH
GDB_INIT_PATH="$ROOT_DIR/gdbinit.py"
COVERAGERC_PATH="$ROOT_DIR/pyproject.toml"
VMLINUX_LIST=($(basename -a "${TESTING_KERNEL_IMAGES_DIR}"/vmlinux*))
@ -135,23 +134,19 @@ run_gdb() {
exit 1
fi
else
gdb_load_pwndbg=(--command "$GDB_INIT_PATH")
gdb_load_pwndbg=()
if [[ "${arch}" == x86_64 ]]; then
GDB=gdb
else
GDB=gdb-multiarch
fi
GDB=pwndbg
fi
if [ $should_drop_to_pdb -eq 1 ]; then
# $GDB --nx --nh "${gdb_load_pwndbg[@]}" \
# $GDB --nx "${gdb_load_pwndbg[@]}" \
# -ex "set exception-verbose on" "$@"
echo "Run: "
echo "$GDB --nx --nh ${gdb_load_pwndbg[@]} -ex \"set exception-debugger on\" -ex \"file ${TESTING_KERNEL_IMAGES_DIR}/vmlinux-${kernel_type}-${kernel_version}-${arch}\" -ex \"target remote :${GDB_PORT}\""
echo "$GDB --nx ${gdb_load_pwndbg[@]} -ex \"set exception-debugger on\" -ex \"file ${TESTING_KERNEL_IMAGES_DIR}/vmlinux-${kernel_type}-${kernel_version}-${arch}\" -ex \"target remote :${GDB_PORT}\""
read -p "Press enter to continue"
else
$UV_RUN $GDB --silent --nx --nh "${gdb_load_pwndbg[@]}" \
$UV_RUN_TEST $GDB --silent --nx "${gdb_load_pwndbg[@]}" \
-ex "set exception-verbose on" "$@" -ex "quit" 2> /dev/null
fi
return $?

@ -445,6 +445,33 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/89/ec/00d68c4ddfedfe64159999e5f8a98fb8442729a63e2077eb9dcd89623d27/filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338", size = 16164, upload-time = "2025-01-21T20:04:47.734Z" },
]
[[package]]
name = "gdb-for-pwndbg"
version = "16.2.6"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1a/62/1ea477664656fc0168f6711593f806621a5bf11928dc89e98c55d79a2061/gdb_for_pwndbg-16.2.6-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:3419c23a5626bf1205f038dad1910587657eb200ce312b411f7a3f817d3055cf", size = 9566628, upload-time = "2025-06-21T00:33:05.063Z" },
{ url = "https://files.pythonhosted.org/packages/42/bd/9b4ffa675a3c09bfb2ab8d40d6f335f8d037e2c6aef7de182df3311d2268/gdb_for_pwndbg-16.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4b4662a490bd5efb5dea2f67e92b3ab3dea55711db46ca762dc3ae585eb1f25c", size = 8926465, upload-time = "2025-06-20T23:56:33.087Z" },
{ url = "https://files.pythonhosted.org/packages/05/82/ea63ae8470638776d01b65078b057d0f98319a678ec01fab37cdc0eb73c4/gdb_for_pwndbg-16.2.6-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ec2ce78e6f467a37367931b9a89dd327e7ff9e5f9f2875e2ad9c2bbc130e7a56", size = 10370599, upload-time = "2025-06-20T23:51:53.476Z" },
{ url = "https://files.pythonhosted.org/packages/1b/ef/1362f839d0185efa2a3066524e5d6de090d0ae15c69113b53923166dba52/gdb_for_pwndbg-16.2.6-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c4492dc4d351a30f8056d159c1393a67c1a71e6f255befe8e31b038602108e8a", size = 10772789, upload-time = "2025-06-21T00:20:56.409Z" },
{ url = "https://files.pythonhosted.org/packages/96/18/f725ee11625831b3e8c372a06e3167f17c6cdb595fa70e4cbfc1f0fad5ab/gdb_for_pwndbg-16.2.6-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:28e338c6d1686d6bf5806a20b4a0976f7e235e7fae4c07ab54b92324ad393b20", size = 9507625, upload-time = "2025-06-21T00:33:06.653Z" },
{ url = "https://files.pythonhosted.org/packages/6d/78/92cf4d29496a651649d512096d8b32c57f844ba4ddea6e99e8f91e755080/gdb_for_pwndbg-16.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:eb32a6492c47127fcc72b19d96b8c8f7f8c939391ac1cbe25429fb502ce6df4f", size = 8868503, upload-time = "2025-06-20T23:56:34.626Z" },
{ url = "https://files.pythonhosted.org/packages/4d/2e/c557da35be3809057ecb07706b9b327ddc4eda14ce533c7ff17fc37966e9/gdb_for_pwndbg-16.2.6-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bd3ca455f9071506cf5afcde54e434677f5610fc2383888c4991f6b25e941d83", size = 10370642, upload-time = "2025-06-20T23:51:55.549Z" },
{ url = "https://files.pythonhosted.org/packages/43/e2/960414fc5e5226e9dc705501b28b39de289430b13b5b79fa957694a7c838/gdb_for_pwndbg-16.2.6-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:52b275e16aa56036180621ed5c339c3e0ce48797ff2992348e7739ace91e086e", size = 10772221, upload-time = "2025-06-21T00:20:58.524Z" },
{ url = "https://files.pythonhosted.org/packages/f1/5f/1e03daf58fdd2502a06e2c2baddaf7053f36dcb646c1a16627691d38d06b/gdb_for_pwndbg-16.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b555ea708b25f51fb9fa3e23a3ec97ba7ddabc98ff2da3ba16c1d417ac5a047d", size = 9509413, upload-time = "2025-06-21T00:33:08.617Z" },
{ url = "https://files.pythonhosted.org/packages/d9/40/b52f2cd54f511decaf7d933059847552babb2b6097f710890287b6de4124/gdb_for_pwndbg-16.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2dff1d2e6ffbe0097eb6c9119755d626d758a6415010e7a931b84c435616f868", size = 8872898, upload-time = "2025-06-20T23:56:36.488Z" },
{ url = "https://files.pythonhosted.org/packages/53/c6/870a1e737681d18a0974cb5a6c3d3160ab0c2762e24ca2d63eacf15f2974/gdb_for_pwndbg-16.2.6-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:86a465af1dd20db9ca51d6b854d153ab20fed9f731c5e02aef96b61b7b0e9a67", size = 10370761, upload-time = "2025-06-20T23:51:57.445Z" },
{ url = "https://files.pythonhosted.org/packages/26/99/2dc67b26c462d49bf9c76c33917b321ddf3e4e8e5702040ef42fc221a51b/gdb_for_pwndbg-16.2.6-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:8ce931356f2d572baafa461b0f6f6b60b0ce6f551ed0ebae3e9f468d479596db", size = 10775617, upload-time = "2025-06-21T00:21:00.64Z" },
{ url = "https://files.pythonhosted.org/packages/b8/42/0196a76fbeb1e1abf319ed19e145628a5b99b3381ea6a93f63a9f240c5c3/gdb_for_pwndbg-16.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9d6e8981d6e9e7f9e4469dc203904249e6cc0113b662489b9ab7c7d45cf93099", size = 9509319, upload-time = "2025-06-21T00:33:10.247Z" },
{ url = "https://files.pythonhosted.org/packages/80/be/de6307ed842d11e9f77232ba85450825afd535387545173d22285783dc97/gdb_for_pwndbg-16.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6fbcf374854b59daa6e607321f187e04126866c1b469f7d08cedd371458d8b3a", size = 8872560, upload-time = "2025-06-20T23:56:38.03Z" },
{ url = "https://files.pythonhosted.org/packages/dc/be/91f627b25fe9f53c65300e479c4c03d78626a33d4779df2558a800409a1d/gdb_for_pwndbg-16.2.6-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:6640e6996b7f908d6546f4945939bf08d9106e2289b03d58c5e780ba7b7693a0", size = 10370635, upload-time = "2025-06-20T23:51:59.014Z" },
{ url = "https://files.pythonhosted.org/packages/d4/2b/5611f627dfecb579b573fbe9498eaf56c362b320e85d4c4a6fc661270a5b/gdb_for_pwndbg-16.2.6-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:7b1aabafc806ddf3cb7a14626049134396d55975ada022eb9262b52fb039d023", size = 10775314, upload-time = "2025-06-21T00:21:02.444Z" },
{ url = "https://files.pythonhosted.org/packages/16/0b/2c02585d824521322f8562362b46a097e0429364dfe63119cd043f2c0513/gdb_for_pwndbg-16.2.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ef16fb07dfb69939907d3c7123b2c6506765b0847ab804f7039ee162d380e770", size = 9509613, upload-time = "2025-06-21T00:33:12.168Z" },
{ url = "https://files.pythonhosted.org/packages/24/04/6635e680ff564728813fcdde5d6894708c8cb0733ea2e5dc86d1d27253d7/gdb_for_pwndbg-16.2.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5acf4fe2a606bba5c352889eda2840b29ea4f05dd1df33e25ee5d58b58c44c75", size = 8871613, upload-time = "2025-06-20T23:56:39.506Z" },
{ url = "https://files.pythonhosted.org/packages/14/ec/0c614233df49fefea248e4e34e3441f815eb727876db44af5750840274d8/gdb_for_pwndbg-16.2.6-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:bf98ab117d0b9dea808a162cf815e9b954e7a528f39550805f26c11ffcb534d6", size = 10371486, upload-time = "2025-06-20T23:52:01.092Z" },
{ url = "https://files.pythonhosted.org/packages/04/dc/9744c1b546d556d17dbd7b77b6ffc55ef55f22589e95b0644154763ae2af/gdb_for_pwndbg-16.2.6-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:998c33ae5888d2053f01b08e66ab1a4ddd2dd0d67e086f0f195113068b544e50", size = 10775521, upload-time = "2025-06-21T00:21:04.667Z" },
]
[[package]]
name = "ghp-import"
version = "2.1.0"
@ -647,6 +674,33 @@ version = "3.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/5e/73/e01e4c5e11ad0494f4407a3f623ad4d87714909f50b17a06ed121034ff6e/jsmin-3.0.1.tar.gz", hash = "sha256:c0959a121ef94542e807a674142606f7e90214a2b3d1eb17300244bbb5cc2bfc", size = 13925, upload-time = "2022-01-16T20:35:59.13Z" }
[[package]]
name = "lldb-for-pwndbg"
version = "21.1.6"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5b/73/098d652f01f25b073a0948ae76407328d86085ca335663d3b073786e1a34/lldb_for_pwndbg-21.1.6-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:26f0e67c2d178447e5ab48a902f4e683b1737ce628e1961ef9dd08d754ccf22b", size = 51328759, upload-time = "2025-06-21T03:32:12.875Z" },
{ url = "https://files.pythonhosted.org/packages/57/95/5085b0a213fc16caf7c8c1f36e5b1968d0a4edadaaebceb37f6f0a70f88f/lldb_for_pwndbg-21.1.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1f02516b5212c6c8db352fd52cd69c3cc259abf4010fb0e3c8969a3cb515c1d9", size = 49041162, upload-time = "2025-06-21T03:17:35.13Z" },
{ url = "https://files.pythonhosted.org/packages/47/40/862c3ccc547eadb431631cd071e6ebf2295b658c030ca417ec09670056c8/lldb_for_pwndbg-21.1.6-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:8f6544ed6fc4ce7ab883e3532bba15f7ca8e0938d1ee1f11b29b89751b70fff2", size = 61864873, upload-time = "2025-06-21T01:34:07.088Z" },
{ url = "https://files.pythonhosted.org/packages/eb/3b/ec5b4cfaad2b3f0b9dd5fb73debadd12517c9450b8dbcfefa013d902306a/lldb_for_pwndbg-21.1.6-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:9db2de4ce2e2c8ae874f85f2f9012c7934f6db0ce5002f6c6fc615f648c4608c", size = 63479959, upload-time = "2025-06-21T02:33:06.264Z" },
{ url = "https://files.pythonhosted.org/packages/f7/30/3908eeb40c364ecced27dd47c62a2b2aa3ac6098296a2840bfcc1a87c9a5/lldb_for_pwndbg-21.1.6-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:df8555c6b0a54c080ed4a7905764711ee4047419921abc6ccb88cc00b8b7aad7", size = 51328788, upload-time = "2025-06-21T02:11:03.275Z" },
{ url = "https://files.pythonhosted.org/packages/79/46/73d7eb0905fe3fd012a01eaf493188d3ded298bb3768dbd40de7169c53bd/lldb_for_pwndbg-21.1.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:322351d66d5cf9da2acb6af8b54dc9c089d67ee536d590b7a6f1626205454bb2", size = 49042027, upload-time = "2025-06-21T01:11:54.985Z" },
{ url = "https://files.pythonhosted.org/packages/2f/69/cde7560b23f5487bf77c9e9410f823bb3a2e66970ecc9605c562042653da/lldb_for_pwndbg-21.1.6-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:20c18b4c8d1ca3cd72652b3e1c2e887a1d690b7325815030bccfc387cd5bdcac", size = 61864970, upload-time = "2025-06-21T01:33:27.861Z" },
{ url = "https://files.pythonhosted.org/packages/c3/3f/5bece6c56f0da4069a75683df0d802ea99a5c655df37ea31fc6cc8b703dd/lldb_for_pwndbg-21.1.6-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:3b59ef4bb58d85dc189baf243cbe9c999f62f224f6e90f75a94b308c9834f161", size = 63479939, upload-time = "2025-06-21T02:33:35.455Z" },
{ url = "https://files.pythonhosted.org/packages/c1/a4/5dae9dffae06801a42f2f21b3c9b55dee42e4af39aa66655d8601db3ef41/lldb_for_pwndbg-21.1.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:95eb493d5039eae2fe472f6f90e38bddec8736b67a922c128dc476078fbb7d1e", size = 51334493, upload-time = "2025-06-21T02:17:53.867Z" },
{ url = "https://files.pythonhosted.org/packages/74/c9/22aa3664c9f26fa0fcf73c97f433ac85c19768dd4e78325915f785ebf81d/lldb_for_pwndbg-21.1.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ffaa159ffee102a256dcebba96be8b340f7bd2df3128551ad1a7a1d42369d5b", size = 49040572, upload-time = "2025-06-21T03:16:24.476Z" },
{ url = "https://files.pythonhosted.org/packages/d9/03/f5d204aa3a2d55a93809021dfefcb7d36365f1cf676cc1bb782b999ae53d/lldb_for_pwndbg-21.1.6-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e1baa5eba96f4a98bb6f969a31e0e66f208c1dd79f7ceb15a63872f926e458c3", size = 61868081, upload-time = "2025-06-21T01:33:54.812Z" },
{ url = "https://files.pythonhosted.org/packages/02/04/230e9c752b1aea1e7a057d37ac846c214d379c0c90c38f43ec043f685eee/lldb_for_pwndbg-21.1.6-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ec8765769182faaa520180f3eeeb10db0fa2fbbd0fd6c3d53adb20c7380713fb", size = 63482490, upload-time = "2025-06-21T02:34:40.229Z" },
{ url = "https://files.pythonhosted.org/packages/1e/49/b2635d5a7208834a424988a2189a4c3998c4b265ac56100c0a90335e4ccf/lldb_for_pwndbg-21.1.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:40a5cb02af8ae8523fcff348cf1508d57f95db9f6eb5df835532f7a5fabfc004", size = 51335177, upload-time = "2025-06-21T01:57:48.322Z" },
{ url = "https://files.pythonhosted.org/packages/02/72/616715547dd75ddfeb44442c6cacbddb0731498ea9361674dae8dde6efd9/lldb_for_pwndbg-21.1.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6be469993255d76b8915a37b697c421307a736262c8e2ff6259e1aa7fbc45139", size = 49040350, upload-time = "2025-06-21T02:15:03.288Z" },
{ url = "https://files.pythonhosted.org/packages/d9/3d/395c60d8a9280b05f47c27c951cd6eb949b277d710071ea2c6bfbddb4296/lldb_for_pwndbg-21.1.6-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:d36d87d3961852e835ebf542f80d494bf5a3c565b07829b1947ac940fbc1a354", size = 61865868, upload-time = "2025-06-21T01:36:03.207Z" },
{ url = "https://files.pythonhosted.org/packages/6c/7a/cce245a6c0e54048a9662a8cc6ea72c5c82a5f1826196ec014482b38d799/lldb_for_pwndbg-21.1.6-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:fe99e73b5d3c74ff64049b57eb59a0e9d1c85f1f8603ce0cfde4c2284042dda2", size = 63482224, upload-time = "2025-06-21T02:32:18.325Z" },
{ url = "https://files.pythonhosted.org/packages/24/a4/70ce7bee6104fd299c85d9f20f911169e5fd72a9abdeda7fb060f6f1d167/lldb_for_pwndbg-21.1.6-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172cc2a3cac07693aa76c7c68cc3e16929322899572599a6a66537ae24f4d10a", size = 51334764, upload-time = "2025-06-21T04:28:45.136Z" },
{ url = "https://files.pythonhosted.org/packages/a9/01/6543f27307c1b2a4e1bdbf9e28721affbcaf6e8f950e4cead148e23b57c1/lldb_for_pwndbg-21.1.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:52979645b92ee8e1a9f05e579952240e9930200d590b9ab6fedcd1b90d474fbd", size = 49042237, upload-time = "2025-06-21T01:38:18.508Z" },
{ url = "https://files.pythonhosted.org/packages/e5/9d/b678e50da95a76f073f0a4cdce22d5b9310804e838a32c25452dcc5c1591/lldb_for_pwndbg-21.1.6-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:abd14e8ec43c40054450b2cf52090ee92bb500ac8423bb13b86171a3bb4775da", size = 61863596, upload-time = "2025-06-21T01:37:13.644Z" },
{ url = "https://files.pythonhosted.org/packages/fa/80/33084781f4f5f7a4b56bad8d3cddfb431358f84fdc6c2723647b8ba45e95/lldb_for_pwndbg-21.1.6-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:472906c8f9d2389c08b4c479e3912f95bd90a5ba51f42d7727f837a7d269ebb4", size = 63482322, upload-time = "2025-06-21T02:32:09.511Z" },
]
[[package]]
name = "mako"
version = "1.3.9"
@ -1258,8 +1312,12 @@ dependencies = [
]
[package.optional-dependencies]
gdb = [
{ name = "gdb-for-pwndbg" },
]
lldb = [
{ name = "gnureadline", marker = "sys_platform != 'win32'" },
{ name = "lldb-for-pwndbg" },
{ name = "pyreadline3", marker = "sys_platform == 'win32'" },
]
@ -1303,8 +1361,10 @@ tests = [
[package.metadata]
requires-dist = [
{ name = "capstone", specifier = "==6.0.0a4" },
{ name = "gdb-for-pwndbg", marker = "extra == 'gdb'", specifier = ">=16.2.6" },
{ name = "gnureadline", marker = "sys_platform != 'win32' and extra == 'lldb'", specifier = ">=8.2.10,<9" },
{ name = "ipython", specifier = ">=8.27.0,<9" },
{ name = "lldb-for-pwndbg", marker = "extra == 'lldb'", specifier = ">=21.1.6" },
{ name = "psutil", specifier = ">=6.1.1,<7" },
{ name = "pt", git = "https://github.com/martinradev/gdb-pt-dump?rev=50227bda0b6332e94027f811a15879588de6d5cb" },
{ name = "pwntools", specifier = ">=4.14.0,<5" },

Loading…
Cancel
Save