Repository: pwndbg/pwndbg

Issue:
Fix cymbol command on Ubuntu 18.04
There's a cymbol test that fails on the Ubuntu 18.04 CI, this issue is there to track it


Discussion:
The cymbol functionality does not work on old GDB versions because the `add-symbol-file` command has different interface in old GDBs: it requires an address argument, while in newer GDBs its optional.

This can be seen below.

Older GDB versions:
```
pwndbg> help add-symbol-file
Load symbols from FILE, assuming FILE has been dynamically loaded.
Usage: add-symbol-file FILE ADDR [-readnow | -readnever | -s SECT-NAME SECT-ADDR]...
ADDR is the starting address of the file's text.
Each '-s' argument provides a section name and address, and
should be specified if the data and bss segments are not contiguous
with the text.  SECT-NAME is a section name to be loaded at SECT-ADDR.
The '-readnow' option will cause GDB to read the entire symbol file
immediately.  This makes the command slower, but may make future operations
faster.
The '-readnever' option will prevent GDB from reading the symbol file's
symbolic debug information.
pwndbg> version
Gdb:      8.1.1
Python:   3.6.9 (default, Jun 29 2022, 11:45:57)  [GCC 8.4.0]
Pwndbg:   1.1.1 build: c5d8800
Capstone: 4.0.1024
Unicorn:  2.0.7
```

Newer GDB versions:
```
pwndbg> help add-symbol-file
Load symbols from FILE, assuming FILE has been dynamically loaded.
Usage: add-symbol-file FILE [-readnow | -readnever] [-o OFF] [ADDR] [-s SECT-NAME SECT-ADDR]...
ADDR is the starting address of the file's text.
Each '-s' argument provides a section name and address, and
should be specified if the data and bss segments are not contiguous
with the text.  SECT-NAME is a section name to be loaded at SECT-ADDR.
OFF is an optional offset which is added to the default load addresses
of all sections for which no other address was specified.
The '-readnow' option will cause GDB to read the entire symbol file
immediately.  This makes the command slower, but may make future operations
faster.
The '-readnever' option will prevent GDB from reading the symbol file's
symbolic debug information.
pwndbg> version
Gdb:      12.1
Python:   3.10.6 (main, Nov  2 2022, 18:53:38) [GCC 11.3.0]
Pwndbg:   1.1.1 build: c5d8800
Capstone: 4.0.1024
Unicorn:  2.0.0
pwndbg>
```

Resolving patch:
diff --git a/pwndbg/commands/cymbol.py b/pwndbg/commands/cymbol.py
--- a/pwndbg/commands/cymbol.py
+++ b/pwndbg/commands/cymbol.py
@@ -181,7 +181,10 @@ def load_custom_structure(custom_structure_name, custom_structure_path):
     pwndbg_debug_symbols_output_file = generate_debug_symbols(custom_structure_path)
     if not pwndbg_debug_symbols_output_file:
         return  # generate_debug_symbols prints on failures
-    gdb.execute(f"add-symbol-file {pwndbg_debug_symbols_output_file}", to_string=True)
+    # Old GDB versions (e.g. 8.2) requires addr argument in add-symbol-file
+    # we set that address to which to load the symbols to 0 since it doesn't matter here
+    # (because we are only loading types information)
+    gdb.execute(f"add-symbol-file {pwndbg_debug_symbols_output_file} 0", to_string=True)
     loaded_symbols[custom_structure_name] = pwndbg_debug_symbols_output_file
     print(message.success("Symbols are loaded!"))
 


Test patch:
diff --git a/tests/gdb-tests/tests/test_cymbol.py b/tests/gdb-tests/tests/test_cymbol.py
--- a/tests/gdb-tests/tests/test_cymbol.py
+++ b/tests/gdb-tests/tests/test_cymbol.py
@@ -51,9 +51,12 @@ def test_cymbol(start_binary):
     assert pwndbg.commands.cymbol.loaded_symbols.get("example") is not None
     # Test whether the returned type is what we expect (on x86-64).
     assert (
-        "example_t\n    +0x0000 a                    : int\n    +0x0004 b                    : char [16]\n    +0x0018 c                    : char *\n    +0x0020 d                    : void *"
-        in pwndbg.gdblib.dt.dt("example_t").strip()
-    )
+        "example_t\n"
+        "    +0x0000 a                    : int\n"
+        "    +0x0004 b                    : char [16]\n"
+        "    +0x0018 c                    : char *\n"
+        "    +0x0020 d                    : void *"
+    ) == pwndbg.gdblib.dt.dt("example_t").strip()
 
     # Test whether unload_loaded_symbol() works properly.
     pwndbg.commands.cymbol.unload_loaded_symbol("example")