1
Fork 0

Move everything to the toplevel

This commit is contained in:
Jan-Erik Rediger 2023-12-09 19:11:13 +01:00
parent efe56d1011
commit b9e56b5add
14 changed files with 13 additions and 382 deletions

3
.gitignore vendored
View file

@ -1,5 +1,6 @@
.envrc
.venv
*.so
*.dylib
**/target/
target/
*.egg-info

View file

@ -1,6 +1,12 @@
[workspace]
resolver = "2"
[package]
name = "goblin-pyo3"
version = "0.0.2"
edition = "2021"
members = [
"crates/goblin-pyo3"
]
[lib]
name = "oelf"
crate-type = ["cdylib"]
[dependencies]
goblin = "0.7.1"
pyo3 = { version = "0.20.0", features = ["extension-module"] }

View file

@ -1,12 +0,0 @@
[package]
name = "goblin-pyo3"
version = "0.0.2"
edition = "2021"
[lib]
name = "oelf"
crate-type = ["cdylib"]
[dependencies]
goblin = "0.7.1"
pyo3 = { version = "0.20.0", features = ["extension-module"] }

View file

@ -1,347 +0,0 @@
import sys
import apsw
import apsw.shell
import goblin
from dataclasses import dataclass
from enum import Flag, auto
from typing import Any, Callable, Iterator, Sequence, Tuple, cast
connection = apsw.Connection(":memory:")
@dataclass
class Generator:
"""A generator for the virtual table SQLite module.
This class is needed because apsw wants to assign columns and
column_access to the generator function itself."""
columns: Sequence[str]
column_access: apsw.ext.VTColumnAccess
callable: Callable[[], Iterator[dict[str, Any]]]
def __call__(self) -> Iterator[dict[str, Any]]:
"""Call the generator should return an iterator of dictionaries.
The dictionaries should have keys that match the column names."""
return self.callable()
@staticmethod
def make_generator(
columns: list[str], generator: Callable[[], Iterator[dict[str, Any]]]
):
"""Create a generator from a callable that returns
an iterator of dictionaries."""
return Generator(columns, apsw.ext.VTColumnAccess.By_Name, generator)
class CacheFlag(Flag):
NONE = 0
DYNAMIC_ENTRIES = auto()
HEADERS = auto()
INSTRUCTIONS = auto()
SECTIONS = auto()
EXPORTS = auto()
IMPORTS = auto()
SYMBOLS = auto()
RPATHS = auto()
LIBS = auto()
STRINGS = auto()
VERSION_REQUIREMENTS = auto()
VERSION_DEFINITIONS = auto()
DWARF_DIE = auto()
DWARF_DIE_CALL_GRAPH = auto()
@classmethod
def from_string(cls, str: str):
"""Convert a string to a CacheFlag.
This also specially handles 'ALL' which returns all the flags."""
if str == "ALL":
return cls.ALL()
try:
return cls[str]
except KeyError:
raise ValueError(f"{str} is not a valid CacheFlag")
@classmethod
def ALL(cls):
retval = cls.NONE
for member in cls.__members__.values():
retval |= member
return retval
def register_generator(
connection: apsw.Connection,
generator: Generator,
table_name: str,
generator_flag: CacheFlag,
cache_flags: CacheFlag,
) -> None:
"""Register a virtual table generator.
This method does a bit of duplicate work which checks if we need to cache
the given generator.
If so we rename the table with a prefix 'raw' and then create a temp table"""
original_table_name = table_name
if generator_flag in cache_flags:
table_name = f"raw_{table_name}"
apsw.ext.make_virtual_module(connection, table_name, generator)
if generator_flag in cache_flags:
connection.execute(
f"""CREATE TABLE {original_table_name}
AS SELECT * FROM {table_name};"""
)
def register_headers(
gob: goblin.Object, connection: apsw.Connection, cache_flags: CacheFlag
) -> None:
def dynamic_entries_generator() -> Iterator[dict[str, Any]]:
header = gob.header
yield {
"path": gob.path,
"magic": header.magic,
"cputype": header.cputype,
"cpusubtype": header.cpusubtype,
"filetype": header.filetype,
"ncmds": header.ncmds,
"sizeofcmds": header.sizeofcmds,
"flags": header.flags,
"reserved": header.reserved,
}
generator = Generator.make_generator(
[
"path",
"magic",
"cputype",
"cpusubtype",
"filetype",
"ncmds",
"sizeofcmds",
"flags",
"reserved",
],
dynamic_entries_generator,
)
register_generator(
connection,
generator,
"macho_headers",
CacheFlag.HEADERS,
cache_flags,
)
def register_symbols(
gob: goblin.Object, connection: apsw.Connection, cache_flags: CacheFlag
) -> None:
def dynamic_entries_generator() -> Iterator[dict[str, Any]]:
for sym in gob.symbols():
yield {
"name": sym.name,
"type": sym.typ,
"global": sym.is_global,
"weak": sym.weak,
"undefined": sym.undefined,
"stab": sym.stab,
}
generator = Generator.make_generator(
["name", "type", "global", "weak", "undefined", "stab"],
dynamic_entries_generator,
)
register_generator(
connection,
generator,
"macho_symbols",
CacheFlag.SYMBOLS,
cache_flags,
)
def register_sections(
gob: goblin.Object, connection: apsw.Connection, cache_flags: CacheFlag
) -> None:
def dynamic_entries_generator() -> Iterator[dict[str, Any]]:
for sect in gob.sections():
yield {
"name": sect.name,
"segment": sect.segment,
"addr": sect.addr,
"size": sect.size,
"offset": sect.offset,
"align": sect.align,
"reloff": sect.reloff,
"nreloc": sect.nreloc,
"flags": sect.flags,
}
generator = Generator.make_generator(
[
"name",
"segment",
"addr",
"size",
"offset",
"align",
"reloff",
"nreloc",
"flags",
],
dynamic_entries_generator,
)
register_generator(
connection,
generator,
"macho_sections",
CacheFlag.SECTIONS,
cache_flags,
)
def register_exports(
gob: goblin.Object, connection: apsw.Connection, cache_flags: CacheFlag
) -> None:
def dynamic_entries_generator() -> Iterator[dict[str, Any]]:
for exp in gob.exports():
yield {
"name": exp.name,
"size": exp.size,
"offset": exp.offset,
"type": str(exp.info.typ),
"address": exp.info.address,
"flags": exp.info.flags,
"lib": exp.info.lib,
"lib_symbol_name": exp.info.lib_symbol_name,
}
generator = Generator.make_generator(
[
"name",
"size",
"offset",
"type",
"address",
"flags",
"lib",
"lib_symbol_name",
],
dynamic_entries_generator,
)
register_generator(
connection,
generator,
"macho_exports",
CacheFlag.EXPORTS,
cache_flags,
)
def register_imports(
gob: goblin.Object, connection: apsw.Connection, cache_flags: CacheFlag
) -> None:
def dynamic_entries_generator() -> Iterator[dict[str, Any]]:
for imp in gob.imports():
yield {
"name": imp.name,
"dylib": imp.dylib,
"lazy": imp.is_lazy,
"offset": imp.offset,
"size": imp.size,
"address": imp.address,
"addend": imp.addend,
"is_weak": imp.is_weak,
"start_of_sequence_offset": imp.start_of_sequence_offset,
}
generator = Generator.make_generator(
[
"name",
"dylib",
"lazy",
"offset",
"size",
"address",
"addend",
"is_weak",
"start_of_sequence_offset",
],
dynamic_entries_generator,
)
register_generator(
connection,
generator,
"macho_imports",
CacheFlag.IMPORTS,
cache_flags,
)
def register_rpaths(
gob: goblin.Object, connection: apsw.Connection, cache_flags: CacheFlag
) -> None:
def dynamic_entries_generator() -> Iterator[dict[str, Any]]:
for rpath in gob.rpaths:
yield {"path": g.path, "rpath": rpath}
generator = Generator.make_generator(
["path", "rpath"],
dynamic_entries_generator,
)
register_generator(
connection,
generator,
"macho_rpaths",
CacheFlag.RPATHS,
cache_flags,
)
def register_libs(
gob: goblin.Object, connection: apsw.Connection, cache_flags: CacheFlag
) -> None:
def dynamic_entries_generator() -> Iterator[dict[str, Any]]:
for lib in gob.libs:
yield {"path": g.path, "lib": lib}
generator = Generator.make_generator(
["path", "lib"],
dynamic_entries_generator,
)
register_generator(
connection,
generator,
"macho_libs",
CacheFlag.RPATHS,
cache_flags,
)
path = sys.argv[1]
g = goblin.Object(path)
register_headers(g, connection, CacheFlag.HEADERS)
register_symbols(g, connection, CacheFlag.SYMBOLS)
register_sections(g, connection, CacheFlag.SECTIONS)
register_exports(g, connection, CacheFlag.EXPORTS)
register_imports(g, connection, CacheFlag.IMPORTS)
register_rpaths(g, connection, CacheFlag.RPATHS)
register_libs(g, connection, CacheFlag.LIBS)
shell = apsw.shell.Shell(db=connection, stdin=sys.stdin)
shell.command_prompt(["ölf> "])
shell.cmdloop()

View file

@ -1,17 +0,0 @@
[project]
name = "oelf"
version = "0.1"
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
dependencies = [
"apsw",
]
[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"