Repository: pwndbg/pwndbg
Pull request: Fix #2614: distance command works with function symbols now
Fixes #2614



Problem statement:
dist error Cannot convert value to long
### Description

```
pwndbg> dist 0x55555555cbe8 win
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ ------------------------/pwndbg/pwndbg/dbg/gdb.py:1033 in __int__                                │
│                                                                                                  │
│   1030 │   @override                                                                             │
│   1031 │   def __int__(self) -> int:                                                             │
│   1032 │   │   try:                                                                              │
│ ❱ 1033 │   │   │   return int(self.inner)                                                        │
│   1034 │   │   except gdb.error as e:                                                            │
│   1035 │   │   │   raise pwndbg.dbg_mod.Error(e)                                                 │
│   1036                                                                                           │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
error: Cannot convert value to long.

During handling of the above exception, another exception occurred:

╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ ------------------------/pwndbg/pwndbg/commands/__init__.py:145 in invoke                        │
│                                                                                                  │
│   142 │   │   │   return                                                                         │
│   143 │   │                                                                                      │
│   144 │   │   try:                                                                               │
│ ❱ 145 │   │   │   args, kwargs = self.split_args(argument)                                       │
│   146 │   │   except SystemExit:                                                                 │
│   147 │   │   │   # Raised when the usage is printed by an ArgparsedCommand                      │
│   148 │   │   │   return                                                                         │
│                                                                                                  │
│ ------------------------/pwndbg/pwndbg/commands/__init__.py:608 in split_args                    │
│                                                                                                  │
│   605 │                                                                                          │
│   606 │   def split_args(self, argument: str):                                                   │
│   607 │   │   argv = pwndbg.dbg.lex_args(argument)                                               │
│ ❱ 608 │   │   return (), vars(self.parser.parse_args(argv))                                      │
│   609                                                                                            │
│   610                                                                                            │
│   611 class ArgparsedCommand:                                                                    │
│                                                                                                  │
│ /usr/lib/python3.12/argparse.py:1895 in parse_args                                               │
│                                                                                                  │
│   1892 │   # Command line argument parsing methods                                               │
│   1893 │   # =====================================                                               │
│   1894 │   def parse_args(self, args=None, namespace=None):                                      │
│ ❱ 1895 │   │   args, argv = self.parse_known_args(args, namespace)                               │
│   1896 │   │   if argv:                                                                          │
│   1897 │   │   │   msg = _('unrecognized arguments: %s') % ' '.join(argv)                        │
│   1898 │   │   │   if self.exit_on_error:                                                        │
│                                                                                                  │
│ /usr/lib/python3.12/argparse.py:1931 in parse_known_args                                         │
│                                                                                                  │
│   1928 │   │   # parse the arguments and exit if there are any errors                            │
│   1929 │   │   if self.exit_on_error:                                                            │
│   1930 │   │   │   try:                                                                          │
│ ❱ 1931 │   │   │   │   namespace, args = self._parse_known_args(args, namespace)                 │
│   1932 │   │   │   except ArgumentError as err:                                                  │
│   1933 │   │   │   │   self.error(str(err))                                                      │
│   1934 │   │   else:                                                                             │
│                                                                                                  │
│ /usr/lib/python3.12/argparse.py:2171 in _parse_known_args                                        │
│                                                                                                  │
│   2168 │   │   │   start_index = consume_optional(start_index)                                   │
│   2169 │   │                                                                                     │
│   2170 │   │   # consume any positionals following the last Optional                             │
│ ❱ 2171 │   │   stop_index = consume_positionals(start_index)                                     │
│   2172 │   │                                                                                     │
│   2173 │   │   # if we didn't consume all the argument strings, there were extras                │
│   2174 │   │   extras.extend(arg_strings[stop_index:])                                           │
│                                                                                                  │
│ /usr/lib/python3.12/argparse.py:2127 in consume_positionals                                      │
│                                                                                                  │
│   2124 │   │   │   │   │   │   │   │   │   │   │   │    start_index + arg_count) >= 0):          │
│   2125 │   │   │   │   │   │   args.remove('--')                                                 │
│   2126 │   │   │   │   start_index += arg_count                                                  │
│ ❱ 2127 │   │   │   │   take_action(action, args)                                                 │
│   2128 │   │   │                                                                                 │
│   2129 │   │   │   # slice off the Positionals that we just parsed and return the                │
│   2130 │   │   │   # index at which the Positionals' string args stopped                         │
│                                                                                                  │
│ /usr/lib/python3.12/argparse.py:1991 in take_action                                              │
│                                                                                                  │
│   1988 │   │                                                                                     │
│   1989 │   │   def take_action(action, argument_strings, option_string=None):                    │
│   1990 │   │   │   seen_actions.add(action)                                                      │
│ ❱ 1991 │   │   │   argument_values = self._get_values(action, argument_strings)                  │
│   1992 │   │   │                                                                                 │
│   1993 │   │   │   # error if this argument is not allowed with other previously                 │
│   1994 │   │   │   # seen arguments                                                              │
│                                                                                                  │
│ /usr/lib/python3.12/argparse.py:2535 in _get_values                                              │
│                                                                                                  │
│   2532 │   │   # single argument or optional argument produces a single value                    │
│   2533 │   │   elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]:                  │
│   2534 │   │   │   arg_string, = arg_strings                                                     │
│ ❱ 2535 │   │   │   value = self._get_value(action, arg_string)                                   │
│   2536 │   │   │   self._check_value(action, value)                                              │
│   2537 │   │                                                                                     │
│   2538 │   │   # REMAINDER arguments convert all values, checking none                           │
│                                                                                                  │
│ /usr/lib/python3.12/argparse.py:2568 in _get_value                                               │
│                                                                                                  │
│   2565 │   │                                                                                     │
│   2566 │   │   # convert the value to the appropriate type                                       │
│   2567 │   │   try:                                                                              │
│ ❱ 2568 │   │   │   result = type_func(arg_string)                                                │
│   2569 │   │                                                                                     │
│   2570 │   │   # ArgumentTypeErrors indicate errors                                              │
│   2571 │   │   except ArgumentTypeError as err:                                                  │
│                                                                                                  │
│ ------------------------/pwndbg/pwndbg/commands/__init__.py:278 in fix_int_reraise               │
│                                                                                                  │
│   275                                                                                            │
│   276                                                                                            │
│   277 def fix_int_reraise(*a, **kw) -> int:                                                      │
│ ❱ 278 │   return fix_int(*a, reraise=True, **kw)                                                 │
│   279                                                                                            │
│   280                                                                                            │
│   281 def OnlyWhenLocal(function: Callable[P, T]) -> Callable[P, Optional[T]]:                   │
│                                                                                                  │
│ ------------------------/pwndbg/pwndbg/commands/__init__.py:274 in fix_int                       │
│                                                                                                  │
│   271                                                                                            │
│   272                                                                                            │
│   273 def fix_int(*a, **kw) -> int:                                                              │
│ ❱ 274 │   return int(fix(*a, **kw))                                                              │
│   275                                                                                            │
│   276                                                                                            │
│   277 def fix_int_reraise(*a, **kw) -> int:                                                      │
│                                                                                                  │
│ ------------------------/pwndbg/pwndbg/dbg/gdb.py:1035 in __int__                                │
│                                                                                                  │
│   1032 │   │   try:                                                                              │
│   1033 │   │   │   return int(self.inner)                                                        │
│   1034 │   │   except gdb.error as e:                                                            │
│ ❱ 1035 │   │   │   raise pwndbg.dbg_mod.Error(e)                                                 │
│   1036 │                                                                                         │
│   1037 │   @override                                                                             │
│   1038 │   def cast(self, type: pwndbg.dbg_mod.Type | Any) -> pwndbg.dbg_mod.Value:              │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
Error: Cannot convert value to long.
```

### My setup
Gdb:      15.2
Python:   3.12.7
Pwndbg:   2024.08.29 build: 1c3e9507
Capstone: 5.0.1280
Unicorn:  2.1.0

Hints from issue discussion:
`win` is struct/value, not an address 🤔 
I don't know, how to know if you(or user) mean comparing `win` as address, or comparing value from `win`
We should throw under the bus, current `arg` implementation xD
> win is struct/value, not an address 🤔

yeah win is a function. it'd be nice if the `dist` function supported such arguments but if not it should at least give a clean error.

Commit message: gdb: suggest &main instead of main (address of symbol) in commands

Patch to review:
diff --git a/pwndbg/commands/__init__.py b/pwndbg/commands/__init__.py
index cf9d7d4e797..9eb478d50fb 100644
--- a/pwndbg/commands/__init__.py
+++ b/pwndbg/commands/__init__.py
@@ -524,8 +524,15 @@ def fix_int_reraise_arg(arg) -> int:
         fixed = fix_reraise_arg(arg)
         return int(fixed)
     except pwndbg.dbg_mod.Error as e:
+        try:
+            address_value = int(fix_reraise_arg(f"&{arg}"))
+            colored_arg = message.notice(f"&{arg}")
+            suggestion = f"\nDid you mean to use {colored_arg} ? (&{arg} = {address_value:#x})"
+        except:
+            raise
+            suggestion = ""
         raise argparse.ArgumentTypeError(
-            f"couldn't convert '{arg}' ({fixed.type.name_to_human_readable}) to int: {e}"
+            f"couldn't convert '{arg}' ({fixed.type.name_to_human_readable}) to int: {e}{suggestion}"
         )
 
 


Code review comments:
diff_hunk: @@ -524,8 +524,15 @@ def fix_int_reraise_arg(arg) -> int:
         fixed = fix_reraise_arg(arg)
         return int(fixed)
     except pwndbg.dbg_mod.Error as e:
+        try:
+            address_value = int(fix_reraise_arg(f"&{arg}"))
+            colored_arg = message.notice(f"&{arg}")
+            suggestion = f"\nDid you mean to use {colored_arg} ? (&{arg} = {address_value:#x})"
+        except:
+            raise
+            suggestion = ""
path: pwndbg/commands/__init__.py
text: @user1:
The assignment to 'suggestion' after the 'raise' statement is unreachable code. Remove or reposition this statement to ensure it executes as intended.
```suggestion

```