Skip to content

Commit

Permalink
Add mechanism to let architectures specify whether they support a gd…
Browse files Browse the repository at this point in the history
…b arch (#822)

* Fix gdb arch parsing for auto-detected archs

* Add mechanism to let architectures specify whether they support a gdb arch

* Add documentation about adding architectures and supports_gdb_arch()

* Address review comments
  • Loading branch information
Bobo1239 committed Feb 22, 2022
1 parent 2b7f315 commit 18c40b6
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 9 deletions.
19 changes: 19 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,22 @@ args.foo --> [3, 14, 159, 2653] # a List(int) from user input
args.bleh --> "" # the default value
args.blah --> True # set to True because user input declared the option (would have been False otherwise)
```

### Adding new architectures

Support for new architectures can be added by inheriting from the `Architecture` class. To register the new architecture with gef, the decorator `@register_architecture` has to be added to the class. Examples can be found in [gef-extras](https://github.com/hugsy/gef-extras/tree/dev/archs).

Sometimes architectures can more precisely determine whether they apply to the current target by looking at the architecture determined by gdb. For these cases the custom architecture may implement the `supports_gdb_arch()` static function to signal that they should be used instead of the default. The function receives only one argument:
- `gdb_str` (of type `str`) which is the architecture name as reported by GDB.

The function **must** return:
- `True` if the current `Architecture` class supports the target binary; `False` otherwise.
- `None` to simply ignore this check and let GEF try to determine the architecture.

One example is the ARM Cortex-M architecture which in some cases should rather be used than the generic ARM one:

```python
@staticmethod
def supports_gdb_arch(gdb_arch: str) -> Optional[bool]:
return bool(re.search("^armv.*-m$", gdb_arch))
```
31 changes: 22 additions & 9 deletions gef.py
Original file line number Diff line number Diff line change
Expand Up @@ -2067,13 +2067,15 @@ def get_arch() -> str:

arch_str = gdb.execute("show architecture", to_string=True).strip()
if "The target architecture is set automatically (currently " in arch_str:
arch_str = arch_str.split("(currently ", 1)[1]
arch_str = arch_str.split(")", 1)[0]
arch_str = arch_str.lstrip("(currently ").rstrip(")")
elif "The target architecture is assumed to be " in arch_str:
arch_str = arch_str.replace("The target architecture is assumed to be ", "")
arch_str = arch_str.lstrip("The target architecture is assumed to be ")
elif "The target architecture is set to " in arch_str:
# GDB version >= 10.1
arch_str = re.findall(r"\"(.+)\"", arch_str)[0]
if '"auto"' in arch_str:
arch_str = re.findall(r"currently \"(.+)\"", arch_str)[0]
else:
arch_str = re.findall(r"\"(.+)\"", arch_str)[0]
else:
# Unknown, we throw an exception to be safe
raise RuntimeError(f"Unknown architecture: {arch_str}")
Expand Down Expand Up @@ -2135,6 +2137,13 @@ def register_architecture(cls: Type["Architecture"]) -> Type["Architecture"]:
class Architecture(metaclass=abc.ABCMeta):
"""Generic metaclass for the architecture supported by GEF."""

@staticmethod
def supports_gdb_arch(gdb_arch: str) -> Optional[bool]:
"""If implemented by a child `Architecture`, this function dictates if the current class
supports the loaded ELF file (which can be accessed via `gef.binary`). This callback
function will override any assumption made by GEF to determine the architecture."""
return None

@abc.abstractproperty
def all_registers(self) -> List[str]: pass
@abc.abstractproperty
Expand Down Expand Up @@ -3163,6 +3172,9 @@ class MIPS64(MIPS):
mode = "MIPS64"
ptrsize = 8

@staticmethod
def supports_gdb_arch(gdb_arch: str) -> Optional[bool]:
return gdb_arch.startswith("mips") and gef.binary.e_class == Elf.Class.ELF_64_BITS

def copy_to_clipboard(data: str) -> None:
"""Helper function to submit data to the clipboard"""
Expand Down Expand Up @@ -3757,12 +3769,13 @@ def reset_architecture(arch: Optional[str] = None, default: Optional[str] = None
if not gef.binary:
gef.binary = get_elf_headers()

arch_name = gef.binary.e_machine if gef.binary else get_arch()
gdb_arch = get_arch()
arch_name = gef.binary.e_machine if gef.binary else gdb_arch

if ((arch_name == "MIPS" or arch_name == Elf.Abi.MIPS)
and (gef.binary is not None and gef.binary.e_class == Elf.Class.ELF_64_BITS)):
# MIPS64 = arch(MIPS) + 64b flag
arch_name = "MIPS64"
preciser_arch = next((a for a in arches.values() if a.supports_gdb_arch(gdb_arch)), None)
if preciser_arch:
gef.arch = preciser_arch()
return

try:
gef.arch = arches[arch_name]()
Expand Down

0 comments on commit 18c40b6

Please sign in to comment.