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 - name: Install dependencies
run: uv sync --group docs --all-extras 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 - name: Verify docs are up to date with source
run: | run: |
./scripts/verify-docs.sh ./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. - `EDITOR`, `VISUAL`: Used by the `cymbol` command to open an editor.
- `HOME`, `XDG_CACHE_HOME`: Used by `lib.tempfile` to determine temporary file locations. - `HOME`, `XDG_CACHE_HOME`: Used by `lib.tempfile` to determine temporary file locations.
- `PWNDBG_VENV_PATH`: Specifies the virtual environment path for Pwndbg. - `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. This effectively disables the use of `uv` in the project.
- `PWNDBG_DISABLE_COLORS`: Disables colored output in Pwndbg. - `PWNDBG_DISABLE_COLORS`: Disables colored output in Pwndbg.
- `PWNDBG_LOGLEVEL`: Initial log level to use for log messages. - `PWNDBG_LOGLEVEL`: Initial log level to use for log messages.

@ -1,113 +1,10 @@
from __future__ import annotations from __future__ import annotations
import cProfile
import hashlib
import importlib.abc
import logging
import os import os
import shutil
import site import site
import subprocess
import sys import sys
import time
import traceback
from glob import glob from glob import glob
from pathlib import Path 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): def fixup_paths(src_root: Path, venv_path: Path):
@ -129,7 +26,8 @@ def fixup_paths(src_root: Path, venv_path: Path):
sys.path.insert(0, str(src_root)) sys.path.insert(0, str(src_root))
# Push virtualenv's site-packages to the front # Push virtualenv's site-packages to the front
sys.path.remove(site_pkgs_path) if site_pkgs_path in sys.path:
sys.path.remove(site_pkgs_path)
sys.path.insert(1, 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" 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: 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() src_root = Path(__file__).parent.resolve()
if not skip_venv(src_root): venv_path = get_venv_path(src_root)
venv_path = get_venv_path(src_root) if not venv_path.exists():
if not venv_path.exists(): print(
print(f"Cannot find Pwndbg virtualenv directory: {venv_path}. Please re-run setup.sh") f"Cannot find Pwndbg virtualenv directory: {venv_path}. Please re-run setup.sh",
sys.exit(1) flush=True,
no_auto_update = os.getenv("PWNDBG_NO_AUTOUPDATE") )
if no_auto_update is None: os._exit(1)
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
# 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()
fixup_paths(src_root, venv_path)
from pwndbginit.gdbinit import main_try
# We wrap everything in try/except so that we can exit GDB with an error code main_try()
# 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: main()
print(traceback.format_exc(), file=sys.stderr)
sys.exit(1)

@ -32,7 +32,7 @@ done
set -o xtrace set -o xtrace
LINT_FILES="pwndbg tests *.py scripts" LINT_FILES="pwndbg pwndbginit tests *.py scripts"
call_shfmt() { call_shfmt() {
local FLAGS=$1 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 # mypy is run in a separate step on GitHub Actions
if [[ -z "$GITHUB_ACTIONS" ]]; then 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 fi

@ -61,7 +61,6 @@ in
nasm nasm
gcc gcc
curl curl
gdb
parallel parallel
qemu qemu
zig_0_13 # version match setup-dev.sh zig_0_13 # version match setup-dev.sh
@ -76,21 +75,11 @@ in
} }
++ [ ++ [
jemalloc-static jemalloc-static
pkgs.gdb
pyEnv pyEnv
(pkgs.writeShellScriptBin "pwndbg" ''
exec ${lib.getBin pkgs.gdb}/bin/gdb --quiet --nx --init-command="$REPO_ROOT/gdbinit.py" $@
'')
] ]
++ pkgs.lib.optionals isLLDB [ ++ pkgs.lib.optionals isLLDB [
pkgs.lldb_20 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 = '' shellHook = ''
export PWNDBG_NO_AUTOUPDATE=1 export PWNDBG_NO_AUTOUPDATE=1

@ -10,14 +10,13 @@
}: }:
let let
lib = pkgs.lib; 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 python3.pkgs.pwntools # ref: https://github.com/pwndbg/pwndbg/blob/2023.07.17/pwndbg/wrappers/checksec.py#L8
] ]
++ lib.optionals pkgs.stdenv.isLinux [ ++ lib.optionals pkgs.stdenv.isLinux [
python3.pkgs.ropper # ref: https://github.com/pwndbg/pwndbg/blob/2023.07.17/pwndbg/commands/ropper.py#L30 python3.pkgs.ropper # ref: https://github.com/pwndbg/pwndbg/blob/2023.07.17/pwndbg/commands/ropper.py#L30
] ];
);
pyEnv = import ./pyenv.nix { pyEnv = import ./pyenv.nix {
inherit inherit
@ -37,100 +36,38 @@ let
in in
version; version;
pwndbg = pkgs.callPackage ( pwndbg_gdb =
{ pkgs.runCommand "pwndbg"
stdenv, {
makeWrapper, version = pwndbgVersion;
}: nativeBuildInputs = [ pkgs.pkgsBuildHost.makeWrapper ];
let }
pwndbgName = if isLLDB then "pwndbg-lldb" else "pwndbg"; ''
in mkdir -p $out/bin/
stdenv.mkDerivation { makeWrapper ${pyEnv}/bin/pwndbg $out/bin/pwndbg \
name = pwndbgName; --prefix PATH : ${lib.makeBinPath ([ gdb ] ++ extraPackags)}
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 }:
''
# 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}
'';
in
(
if isLLDB then
''
mkdir -p $out/share/pwndbg
mkdir -p $out/bin
cp -r lldbinit.py pwndbg $out/share/pwndbg pwndbg_lldb =
cp pwndbg-lldb.py $out/bin/${pwndbgName} 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)}
'';
${fix_init_script { pwndbg_final = (if isLLDB then pwndbg_lldb else pwndbg_gdb) // {
target = "$out/bin/${pwndbgName}"; meta = {
line = "4"; pwndbgVenv = pyEnv;
}} python3 = python3;
gdb = gdb;
touch $out/share/pwndbg/.skip-venv lldb = lldb;
wrapProgram $out/bin/${pwndbgName} \ isLLDB = isLLDB;
--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"
''
);
meta = {
pwndbgVenv = pyEnv;
python3 = python3;
gdb = gdb;
lldb = lldb;
isLLDB = isLLDB;
};
}
) { };
in in
pwndbg pwndbg_final

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

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

@ -1,20 +1,15 @@
from __future__ import annotations from __future__ import annotations
import cProfile
import hashlib import hashlib
import logging
import os import os
import shutil import shutil
import site
import subprocess import subprocess
import sys import sys
import time
from glob import glob
from pathlib import Path from pathlib import Path
from typing import List from typing import List
from typing import Tuple from typing import Tuple
import lldb
def hash_file(file_path: str | Path) -> str: def hash_file(file_path: str | Path) -> str:
with open(file_path, "rb") as f: 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 binary_path: os.PathLike[str], src_root: Path, dev: bool = False
) -> Tuple[str, str, int]: ) -> Tuple[str, str, int]:
# We don't want to quietly uninstall dependencies by just specifying # 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"] command: List[str] = [str(binary_path), "sync", "--all-extras"]
if dev: if dev:
command.append("--all-groups") 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) result = subprocess.run(command, capture_output=True, text=True, cwd=src_root)
return result.stdout.strip(), result.stderr.strip(), result.returncode 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" uv_lock_hash_path = venv_path / "uv.lock.hash"
current_hash = hash_file(src_root / "uv.lock") current_hash = hash_file(src_root / "uv.lock")
logging.debug(f"Current uv.lock hash: {current_hash}")
stored_hash = None stored_hash = None
if uv_lock_hash_path.exists(): if uv_lock_hash_path.exists():
stored_hash = uv_lock_hash_path.read_text().strip() 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 the hashes don't match, update the dependencies
if current_hash == stored_hash: if current_hash == stored_hash:
@ -87,30 +87,6 @@ def update_deps(src_root: Path, venv_path: Path) -> None:
print(stderr, file=sys.stderr) 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): def get_venv_path(src_root: Path):
venv_path_env = os.environ.get("PWNDBG_VENV_PATH") venv_path_env = os.environ.get("PWNDBG_VENV_PATH")
if venv_path_env: if venv_path_env:
@ -122,46 +98,25 @@ def get_venv_path(src_root: Path):
def skip_venv(src_root) -> bool: def skip_venv(src_root) -> bool:
return ( return (
os.environ.get("PWNDBG_VENV_PATH") == "PWNDBG_PLEASE_SKIP_VENV" 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: def verify_venv():
if "pwndbg" in sys.modules: src_root = Path(__file__).parent.parent.resolve()
print("Detected double-loading of Pwndbg.") if skip_venv(src_root):
print("This should not happen. Please report this issue if you're not sure how to fix it.") return
sys.exit(1)
profiler = cProfile.Profile()
start_time = None
if os.environ.get("PWNDBG_PROFILE") == "1":
start_time = time.time()
profiler.enable()
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() venv_path = get_venv_path(src_root)
pwndbg.dbg.setup(debugger, __name__, debug=debug) if not venv_path.exists():
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) update_deps(src_root, venv_path)
if os.environ.get("PWNDBG_PROFILE") == "1":
pwndbg.profiling.profiler.stop("pwndbg-load.pstats")
pwndbg.profiling.profiler.start()

@ -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 argparse
import os import os
import re import re
import shutil
import subprocess import subprocess
import sys import sys
from typing import List from typing import List
@ -58,16 +59,30 @@ def find_lldb_python_path() -> str:
return folder 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() args = PARSER.parse_args()
debug = args.verbose debug = args.verbose
# Find the path for the LLDB Python bindings. if sys.platform == "linux" and "LLDB_DEBUGSERVER_PATH" not in os.environ:
path = find_lldb_python_path() os.environ["LLDB_DEBUGSERVER_PATH"] = shutil.which("lldb-server")
sys.path.append(path)
if debug:
print(f"[-] Launcher: LLDB Python path: {path}")
# Older LLDB versions crash newer versions of CPython on import, so check # Older LLDB versions crash newer versions of CPython on import, so check
# for it, and stop early with an error message. # 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) print("LLDB 18 and earlier is incompatible with Python 3.12 and later", file=sys.stderr)
sys.exit(1) sys.exit(1)
# Start up LLDB and create a new debugger object. try:
import lldb 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() lldb.SBDebugger.Initialize()
debugger = lldb.SBDebugger.Create() debugger = lldb.SBDebugger.Create()
# Resolve the location of lldbinit.py based on the environment, if needed. from pwndbginit import lldbinit
lldbinit_dir = os.path.dirname(sys.argv[0]) from pwndbginit import pwndbglldbhandler
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")
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 {pwndbglldbhandler.__file__}")
debugger.HandleCommand(f"command script import {lldbinit_path}")
# Initialize the debugger, proper. # Initialize the debugger, proper.
import lldbinit
if debug: if debug:
print("[-] Launcher: Initializing Pwndbg") print("[-] Launcher: Initializing Pwndbg")
lldbinit.main(debugger, lldb_version[0], lldb_version[1], debug=debug) 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. # Dispose of our debugger and terminate LLDB.
lldb.SBDebugger.Destroy(debugger) lldb.SBDebugger.Destroy(debugger)
lldb.SBDebugger.Terminate() 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 = [ lldb = [
# The LLDB REPL requires readline. # The LLDB REPL requires readline.
'gnureadline>=8.2.10,<9; sys_platform != "win32"', '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] [dependency-groups]
dev = [ dev = [
@ -128,10 +131,14 @@ builtins-ignorelist = [
] ]
[tool.hatch.build.targets.sdist] [tool.hatch.build.targets.sdist]
include = ["/pwndbg"] include = ["/pwndbg", "/pwndbginit"]
[tool.hatch.build.targets.wheel] [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] [build-system]
requires = ["hatchling"] requires = ["hatchling"]

@ -1,23 +1,5 @@
#!/usr/bin/env bash #!/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" source "$(dirname "$0")/../common.sh"
cd $PWNDBG_ABS_PATH cd $PWNDBG_ABS_PATH
@ -26,16 +8,16 @@ cd $PWNDBG_ABS_PATH
# the documentation. Do this from each debugger. # the documentation. Do this from each debugger.
export PWNDBG_DOCGEN_DBGNAME="gdb" 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" \ -iex "set exception-verbose on" \
-ix ./scripts/_docs/extract_command_docs.py \ -ix ./scripts/_docs/extract_command_docs.py \
-ix ./scripts/_docs/extract_configuration_docs.py \ -ix ./scripts/_docs/extract_configuration_docs.py \
-ix ./scripts/_docs/extract_function_docs.py \ -ix ./scripts/_docs/extract_function_docs.py \
-nx || exit 1 || exit 1
export PWNDBG_DOCGEN_DBGNAME="lldb" export PWNDBG_DOCGEN_DBGNAME="lldb"
{ {
$UV_RUN_DOCS python pwndbg-lldb.py << EOF $UV_RUN_DOCS pwndbg-lldb << EOF
set show-tips off set show-tips off
command script import ./scripts/_docs/extract_command_docs.py command script import ./scripts/_docs/extract_command_docs.py
command script import ./scripts/_docs/extract_configuration_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" PWNDBG_VENV_PATH="${PWNDBG_ABS_PATH}/.venv"
fi 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 # We are using the dependencies as installed on the system
# so we shouldn't use uv (and can't, since it's not installed). # so we shouldn't use uv (and can't, since it's not installed).
UV="" UV=""

@ -349,25 +349,3 @@ if linux; then
configure_venv configure_venv
fi fi
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: if args.nix:
# Use pwndbg, as build by nix. # Use pwndbg, as build by nix.
use_gdbinit = False
gdb_path = local_pwndbg_root / "result" / "bin" / "pwndbg" gdb_path = local_pwndbg_root / "result" / "bin" / "pwndbg"
if not gdb_path.exists(): if not gdb_path.exists():
print("ERROR: No nix-compatible pwndbg found. Run nix build .#pwndbg-dev") print("ERROR: No nix-compatible pwndbg found. Run nix build .#pwndbg-dev")
sys.exit(1) sys.exit(1)
elif args.group == Group.CROSS_ARCH_USER: elif args.group == Group.CROSS_ARCH_USER:
# Cross-arch requires 'gdb-multiarch'. # Some systems don't ship 'gdb-multiarch', but support multiple
use_gdbinit = True # architectures in their regular binaries. Try the regular GDB.
if (gdb_multiarch := shutil.which("gdb-multiarch")) is not None: 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 = Path(gdb_multiarch)
gdb_path_str = shutil.which("pwndbg")
if gdb_path_str is None:
print("ERROR: No 'pwndbg' executables in path")
sys.exit(1)
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: else:
# Some systems don't ship 'gdb-multiarch', but support multiple print("ERROR: 'pwndbg' does not support cross architecture targets")
# architectures in their regular binaries. Try the regular GDB. sys.exit(1)
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")
if gdb_path_str is None:
print("ERROR: No 'gdb-multiarch' or 'gdb' executables in path")
sys.exit(1)
result = subprocess.run([gdb_path_str, "-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"
)
sys.exit(1)
else: else:
# Use the regular system GDB. # Use the regular system GDB.
use_gdbinit = True gdb_path_str = shutil.which("pwndbg")
gdb_path_str = shutil.which("gdb")
if gdb_path_str is None: if gdb_path_str is None:
print("ERROR: No 'gdb' executable in path") print("ERROR: No 'gdb' executable in path")
sys.exit(1) 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.library(),
local_pwndbg_root / args.group.binary_dir(), local_pwndbg_root / args.group.binary_dir(),
gdb_path, gdb_path,
use_gdbinit,
) )

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

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

@ -6,7 +6,6 @@ set -o pipefail
source "$(dirname "$0")/../../../scripts/common.sh" source "$(dirname "$0")/../../../scripts/common.sh"
ROOT_DIR=$PWNDBG_ABS_PATH ROOT_DIR=$PWNDBG_ABS_PATH
GDB_INIT_PATH="$ROOT_DIR/gdbinit.py"
COVERAGERC_PATH="$ROOT_DIR/pyproject.toml" COVERAGERC_PATH="$ROOT_DIR/pyproject.toml"
VMLINUX_LIST=($(basename -a "${TESTING_KERNEL_IMAGES_DIR}"/vmlinux*)) VMLINUX_LIST=($(basename -a "${TESTING_KERNEL_IMAGES_DIR}"/vmlinux*))
@ -135,23 +134,19 @@ run_gdb() {
exit 1 exit 1
fi fi
else else
gdb_load_pwndbg=(--command "$GDB_INIT_PATH") gdb_load_pwndbg=()
if [[ "${arch}" == x86_64 ]]; then GDB=pwndbg
GDB=gdb
else
GDB=gdb-multiarch
fi
fi fi
if [ $should_drop_to_pdb -eq 1 ]; then if [ $should_drop_to_pdb -eq 1 ]; then
# $GDB --nx --nh "${gdb_load_pwndbg[@]}" \ # $GDB --nx "${gdb_load_pwndbg[@]}" \
# -ex "set exception-verbose on" "$@" # -ex "set exception-verbose on" "$@"
echo "Run: " 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" read -p "Press enter to continue"
else 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 -ex "set exception-verbose on" "$@" -ex "quit" 2> /dev/null
fi fi
return $? 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" }, { 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]] [[package]]
name = "ghp-import" name = "ghp-import"
version = "2.1.0" version = "2.1.0"
@ -647,6 +674,33 @@ version = "3.0.1"
source = { registry = "https://pypi.org/simple" } 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" } 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]] [[package]]
name = "mako" name = "mako"
version = "1.3.9" version = "1.3.9"
@ -1258,8 +1312,12 @@ dependencies = [
] ]
[package.optional-dependencies] [package.optional-dependencies]
gdb = [
{ name = "gdb-for-pwndbg" },
]
lldb = [ lldb = [
{ name = "gnureadline", marker = "sys_platform != 'win32'" }, { name = "gnureadline", marker = "sys_platform != 'win32'" },
{ name = "lldb-for-pwndbg" },
{ name = "pyreadline3", marker = "sys_platform == 'win32'" }, { name = "pyreadline3", marker = "sys_platform == 'win32'" },
] ]
@ -1303,8 +1361,10 @@ tests = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "capstone", specifier = "==6.0.0a4" }, { 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 = "gnureadline", marker = "sys_platform != 'win32' and extra == 'lldb'", specifier = ">=8.2.10,<9" },
{ name = "ipython", specifier = ">=8.27.0,<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 = "psutil", specifier = ">=6.1.1,<7" },
{ name = "pt", git = "https://github.com/martinradev/gdb-pt-dump?rev=50227bda0b6332e94027f811a15879588de6d5cb" }, { name = "pt", git = "https://github.com/martinradev/gdb-pt-dump?rev=50227bda0b6332e94027f811a15879588de6d5cb" },
{ name = "pwntools", specifier = ">=4.14.0,<5" }, { name = "pwntools", specifier = ">=4.14.0,<5" },

Loading…
Cancel
Save