Skip to content

Latest commit

 

History

History
176 lines (138 loc) · 7.28 KB

File metadata and controls

176 lines (138 loc) · 7.28 KB

Lingue: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية

← ABI dei plugin NeverC

Plugin Python

NeverC può caricare un file sorgente Python tramite la stessa opzione -fplugin= usata dai plugin nativi. Una normale build da sorgente abilita per default i plugin Python e l'installazione del runtime incluso:

cmake -S llvm -B build -C neverc/cmake/caches/NeverC.cmake \
  -DCMAKE_INSTALL_PREFIX="$PWD/neverc-install"
cmake --build build --target install

Le nuove build usano NEVERC_ENABLE_PYTHON_PLUGINS=ON e NEVERC_BUNDLE_PYTHON_RUNTIME=ON per default. CMake può usare il Python di sistema per gli script di build, ma quell'interprete non seleziona l'ABI dei plugin. NeverC scarica separatamente una distribution development/runtime CPython 3.12.10 fissa e verificata con SHA-256, collega il bridge dei plugin, la prepara in build/python e installa lo stesso runtime nella directory adiacente python/. Le normali build da sorgente e gli archivi ufficiali eseguono quindi i plugin con CPython 3.12.10 senza Python runtime esterno, PYTHONHOME o PYTHONPATH.

Per una build offline, impostare -DNEVERC_MANAGED_PYTHON_ROOT=/path/to/cpython-3.12.10 su un albero development/runtime CPython 3.12.10 esatto già estratto. NeverC lo verifica e lo copia nella build senza modificare la directory sorgente fornita.

Su Linux il bundler d'installazione richiede patchelf in PATH. Poiché CMake esegue una sonda ABI, le build con plugin Python gestiti devono essere native; una cross build deve disabilitare Python o usare una fase di packaging nativa sulla piattaforma target. Per un compiler senza Python, passare insieme -DNEVERC_ENABLE_PYTHON_PLUGINS=OFF e -DNEVERC_BUNDLE_PYTHON_RUNTIME=OFF.

Installare il pacchetto di authoring con python3 -m pip install ./pluginsdk/python, aggiungere la directory a PYTHONPATH, oppure compilare e installare il componente neverc-pluginsdk. NeverC rileva anche l'SDK preparato in <directory di neverc>/../pluginsdk/python.

Plugin minimo

from neverc_plugin import Plugin


@Plugin(id="com.example.minimal", name="Minimal Python Plugin", version="1.0.0")
class MinimalPlugin:
    def on_process_begin(self, ctx):
        ctx.state = {"sessions": 0}

Caricarlo tramite il percorso nel filesystem:

neverc -fplugin=/absolute/path/to/minimal.py -fsyntax-only input.c

Il decorator accetta un ID canonico, un nome visualizzato non vuoto e una versione semantica rigorosa. Uno script dichiara esattamente una classe plugin. Script diversi sono moduli indipendenti e possono essere combinati con plugin nativi.

Ciclo di vita

Tutti gli hook sono opzionali:

  • on_process_begin(ctx) e on_destroy(ctx) racchiudono il processo compiler.
  • register(ctx) registra opzioni e observer prima del freeze del grafo delle fasi.
  • on_session_begin(ctx) e on_session_end(ctx) racchiudono un'invocazione.
  • on_task_begin(ctx) e on_task_end(ctx) racchiudono un'unità di compilazione.

Un hook begin può restituire un valore Python o assegnare ctx.state; l'hook end corrispondente può leggerlo. Gli altri hook e callback observer devono restituire None. Per impostazione predefinita sono session-serial e non rientranti; @Plugin può scegliere gli stessi modelli di un plugin nativo.

Opzioni e observer

from neverc_plugin import Plugin
from neverc_plugin.domains import driver


@Plugin(id="com.example.trace", name="Trace", version="1.0.0")
class TracePlugin:
    def register(self, ctx):
        ctx.option(
            "--trace-python",
            kind="flag",
            value_type="bool",
            help="Trace raw driver arguments",
        )
        ctx.observer(
            driver.RAW_ARGUMENTS,
            when=("before", "after"),
            fn=self.observe,
        )

    def observe(self, frame):
        if frame.option_values("--trace-python"):
            frame.check_cancelled()
            frame.emit_remark(f"arguments: {frame.arguments}", code=1001)

neverc_plugin.phases contiene tutte le 130 costanti delle fasi integrate, generate dallo schema normativo. I frame observer espongono dati di fase e route, handle opachi di input/output, opzioni analizzate, diagnostica, controllo della cancellazione e argomenti grezzi per driver.RAW_ARGUMENTS. Gli handle nativi verificano la propria lifetime: usare un oggetto conservato dopo il callback genera RuntimeError.

I kind delle opzioni sono flag, joined, separate e multi_arg; i tipi sono bool, int, uint, string, enum e path; le molteplicità sono single, last_wins e append. Una enum riceve il mapping enum_values={nome: intero}. argument_count vale solo per multi_arg.

Accesso completo alla ABI C

Gli helper per ciclo di vita, opzioni e observer si basano sull’intera ABI pubblica dei plugin C. neverc_plugin.abi contiene le definizioni ctypes generate, mentre i moduli in neverc_plugin.domains interrogano tutte le tabelle ufficiali verificandone versione e dimensione. bind_callbacks collega i callback Python tramite trampoline native con firma esatta.

from neverc_plugin import abi
from neverc_plugin.domains import ir
from neverc_plugin.ffi import bind_callbacks, require_ok


def register(self, context):
    scope = context.ffi
    core = ir.CORE.query(scope)
    builder = ir.BUILDER.query(scope)
    passes = ir.PASS.query(scope)

Esempio OLLVM in Python

L’SDK include in pluginsdk/python/examples/ollvm un esempio scritto esclusivamente contro questo binding pubblico che implementa in modo deterministico instruction substitution (SUB), bogus control flow (BCF) e control-flow flattening (FLA):

neverc -fplugin=/path/to/ollvm_plugin.py \
  --ollvm-sub --ollvm-bcf --ollvm-fla \
  --ollvm-seed 42 --ollvm-probability 80 \
  input.c -o output

Errori, sicurezza e ambito attuale

Un'eccezione Python non gestita diventa NEVERC_STATUS_PLUGIN_EXCEPTION. Durante un callback session/task attivo, NeverC emette il traceback formattato come diagnostica strutturata; gli errori di import e activation lo includono nell'errore del loader. L'interprete embedded è condiviso nel processo e non viene intenzionalmente finalizzato, mentre gli oggetti del plugin vengono rilasciati all'unload.

I plugin Python sono estensioni compiler fidate. Vengono eseguiti nel processo, possono importare moduli arbitrari e hanno gli stessi permessi di NeverC. Non è presente una sandbox.

Il binding Python non è un'API ridotta: le definizioni ctypes generate e i trampoline nativi coprono tutte le 36 tabelle ABI C ufficiali, i record, le funzioni e le famiglie di callback, incluse mutazioni, interceptor e provider. Lifetime, transaction e continuation sono verificati. Un esempio OLLVM Python completo con SUB, BCF e FLA è in pluginsdk/python/examples/ollvm/. Le definizioni raw sono in neverc_plugin.abi e i descrittori delle tabelle in neverc_plugin.domains.