Move everything to the toplevel
This commit is contained in:
parent
efe56d1011
commit
b9e56b5add
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,5 +1,6 @@
|
|||
.envrc
|
||||
.venv
|
||||
*.so
|
||||
*.dylib
|
||||
**/target/
|
||||
target/
|
||||
*.egg-info
|
||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -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"] }
|
||||
|
|
|
@ -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"] }
|
|
@ -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()
|
|
@ -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"
|
Loading…
Reference in a new issue