Repository: pwndbg/pwndbg

Issue:
Feature request: RVA-based breakpoints
**Is your feature request related to a problem? Please describe.**
I am missing a command that would allow creating a breakpoint relative to some image - either main program or a library - for example at offset 0x1337 from libc base.

**Describe the solution you'd like**
A new command that would enable this, for example

````
bp libc.so+0x1337
````


**Describe alternatives you've considered**
The alternative is to manually find the image bases which is tedious.



Discussion:
Hey,

1) We have such feature but only for the base binary address. You can do `break *$rebase(0x1234)`
2) It should be super easy to add this, check out https://github.com/pwndbg/pwndbg/blob/dev/pwndbg/gdblib/functions.py#L51-L55

You would have to implement it as:
```py
@GdbFunction(only_when_running=True)
def base(objfile: str) -> int:
    base = next(p for p in pwndbg.gdblib.vmmap.get() if objfile in p.objfile)
    return base
```

Probably also wrapping it in `try: ... except: ...` in case when vmmap is not available or there is no such mapping? And displaying the issue to the user? Or maybe an exception is desirable. To be checked.
Also, the adventage of doing it as a GDB function is that it would work with any other command within GDB/Pwndbg.
@nikoshell Btw I think u made some wrapper over Pwndbg where u handle stdin in a script.

I wonder if we could provide an extra UX this way by detecting `<something>.so` in the input line typed by the user and replacing it with `$base(<something>.so)`? But do things like tab completion works then?
@chivay any chance you can send a PR with the `base` function above and also test it by yourself? (ideally, add a testcase to Pwndbg tests, but skip it if u don't have time/don't want to deal with it)

Resolving patch:
diff --git a/pwndbg/gdblib/functions.py b/pwndbg/gdblib/functions.py
--- a/pwndbg/gdblib/functions.py
+++ b/pwndbg/gdblib/functions.py
@@ -53,3 +53,16 @@ def rebase(addr: gdb.Value | int) -> int:
     """Return rebased address."""
     base = pwndbg.gdblib.elf.exe().address
     return base + int(addr)
+
+
+@GdbFunction(only_when_running=True)
+def base(name_pattern: gdb.Value | str) -> int:
+    if isinstance(name_pattern, gdb.Value):
+        name = name_pattern.string()
+    else:
+        name = name_pattern
+
+    for p in pwndbg.gdblib.vmmap.get():
+        if name in p.objfile:
+            return p.vaddr
+    raise ValueError(f"No mapping named {name}")


Test patch:
diff --git a/tests/gdb-tests/tests/test_function_base.py b/tests/gdb-tests/tests/test_function_base.py
new file mode 100644
--- /dev/null
+++ b/tests/gdb-tests/tests/test_function_base.py
@@ -0,0 +1,15 @@
+from __future__ import annotations
+
+import gdb
+
+import tests
+
+REFERENCE_BINARY = tests.binaries.get("reference-binary.out")
+
+
+def test_function_base(start_binary):
+    start_binary(REFERENCE_BINARY)
+
+    result = gdb.execute('p/x $base("reference-binary")', to_string=True).strip()
+
+    assert result.startswith("$1 = 0x") and result.endswith("000")
diff --git a/tests/gdb-tests/tests/test_loads.py b/tests/gdb-tests/tests/test_loads.py
--- a/tests/gdb-tests/tests/test_loads.py
+++ b/tests/gdb-tests/tests/test_loads.py
@@ -9,7 +9,7 @@
 
 HELLO = [
     "pwndbg: loaded ### pwndbg commands and ### shell commands. Type pwndbg [--shell | --all] [filter] for a list.",
-    "pwndbg: created $rebase, $ida GDB functions (can be used with print/break)",
+    "pwndbg: created $rebase, $base, $ida GDB functions (can be used with print/break)",
 ]
 
 BINARY = tests.binaries.get("div_zero.out")