Skip to content

system_calls: add reverse syscall lookup (number -> name) #127

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 42 additions & 7 deletions system_calls/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import importlib
import os
from typing import overload

from system_calls.tables.names import syscalls_names
from system_calls.linux_version import linux_version
Expand Down Expand Up @@ -42,6 +43,7 @@ def __init__(self):
self._names = syscalls_names
self._default_arch = os.uname().machine
self._loaded_arch_tables = {}
self._loaded_reverse_arch_tables = {}
self._names = syscalls_names
self.linux_version = linux_version

Expand All @@ -61,11 +63,31 @@ def load_arch_table(self, arch: str):
f"'{arch}': {e}") from e
return self._loaded_arch_tables[arch]

def __getitem__(self, syscall_name: str) -> int:
"""Returns number for requested system call.
def load_reverse_arch_table(self, arch: str):
"""Loads a reverse architecture table dynamically."""
if arch not in self._loaded_reverse_arch_tables:
arch_table = self.load_arch_table(arch)
self._loaded_reverse_arch_tables[arch] = {number: name for name, number in arch_table.items()}
return self._loaded_reverse_arch_tables[arch]

@overload
def __getitem__(self, syscall: str) -> int:
...

@overload
def __getitem__(self, syscall: int) -> str:
...

def __getitem__(self, syscall):
"""Returns number or name for requested system call.
Host architecture would be used.
"""
return self.get(syscall_name)
if isinstance(syscall, str):
return self.get(syscall)
elif isinstance(syscall, int):
return self.get_name(syscall)

raise TypeError

def get(self, syscall_name: str, arch: str = "") -> int: # type: ignore
"""Returns number for requested system call.
Expand All @@ -76,10 +98,7 @@ def get(self, syscall_name: str, arch: str = "") -> int: # type: ignore
arch = self._default_arch

# First, try to load/get the architecture's table
try:
arch_table = self.load_arch_table(arch)
except NoSuchArchitecture:
raise NoSuchArchitecture
arch_table = self.load_arch_table(arch)

try:
return arch_table[syscall_name]
Expand All @@ -89,6 +108,22 @@ def get(self, syscall_name: str, arch: str = "") -> int: # type: ignore
else:
raise NotSupportedSystemCall

def get_name(self, syscall_number: int, arch: str = "") -> str: # type: ignore
"""Returns name for requested system call number.
Architecture can be provided by second argument (optional, host
architecture would be used by default).
"""
if arch == "" or arch is None:
arch = self._default_arch

# First, try to load/get the architecture's reverse table
reverse_arch_table = self.load_reverse_arch_table(arch)

try:
return reverse_arch_table[syscall_number]
except KeyError:
raise NoSuchSystemCall

def archs(self) -> list:
"""Returns list of architectures supported by class.
Some entries are no longer supported by mainline Linux kernel.
Expand Down
22 changes: 20 additions & 2 deletions system_calls/tests/test_syscalls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,30 @@
("listmount", "riscv64", 458),
]
)
def test_get_valid_syscalls(syscall_name, arch, expected_number):
"""Tests for system calls which exist on tested architecture.
def test_get_valid_syscall_names(syscall_name, arch, expected_number):
"""Tests for system call names which exist on tested architecture.
"""
assert syscalls.get(syscall_name, arch) == expected_number


@pytest.mark.parametrize(
"syscall_number, arch, expected_name",
[
(2, "x86_64", "open"),
(257, "x86_64", "openat"),
(56, "arm64", "openat"),
(0, "x86_64", "read"),
(1, "x86_64", "write"),
(60, "x86_64", "exit"),
(458, "riscv64", "listmount"),
]
)
def test_get_valid_syscall_numbers(syscall_number, arch, expected_name):
"""Tests for system call numbers which exist on tested architecture.
"""
assert syscalls.get_name(syscall_number, arch) == expected_name


@pytest.mark.parametrize(
"syscall_name, arch",
[
Expand Down
Loading