tursodatabase/turso · critical · SystemExit

self-test failed: expected export was not found

Error message

self-test failed: expected export was not found

What it means

The script's built-in --self-test generates a synthetic minimal ELF64 containing one dynsym symbol and verifies the checker finds it. This error means the self-test failed in the positive case: missing_exports() reported the genuinely-present symbol as missing, so the parsing logic is broken. It is an internal invariant failure of the tool itself, not of your ELF file.

Source

Thrown at bindings/dotnet/scripts/check_elf_dynsym_exports.py:38

        raise SystemExit("usage: check_elf_dynsym_exports.py <elf> <symbol>...")

    path = Path(sys.argv[1])
    needed = sys.argv[2:]
    missing = missing_exports(path, needed)
    if missing:
        raise SystemExit(f"{path} is missing dynsym exports: {', '.join(missing)}")


def run_self_test() -> None:
    import tempfile

    symbol = "turso_sync_database_new"
    with tempfile.NamedTemporaryFile(delete=False, suffix=".so") as handle:
        handle.write(elf64_with_dynsym(symbol))
        path = Path(handle.name)
    try:
        if missing_exports(path, [symbol]):
            raise SystemExit("self-test failed: expected export was not found")
        if not missing_exports(path, ["turso_sync_database_missing"]):
            raise SystemExit("self-test failed: missing export was not reported")
    finally:
        path.unlink()


def missing_exports(path: Path, needed: list[str]) -> list[str]:
    names = dynsym_names(path)
    return [symbol for symbol in needed if symbol not in names]


def elf64_with_dynsym(symbol: str) -> bytes:
    shstrtab = b"\0.shstrtab\0.dynstr\0.dynsym\0"
    dynstr = b"\0" + symbol.encode("ascii") + b"\0"
    dynsym = bytes(24) + struct.pack("<IBBHQQ", 1, 0x12, 0, 1, 0x1000, 8)

    shstrtab_offset = 64
    dynstr_offset = shstrtab_offset + len(shstrtab)

View on GitHub (pinned to 492c4a71cd)

Solutions

  1. Check recent edits to elf64_with_dynsym(), elf_layout(), find_dynsym(), or names_from_dynsym() for format/offset regressions
  2. Verify struct format strings match the section/symbol layouts (ELF64 section header is IIQQQQIIQQ, symbol is IBBHQQ)
  3. Run the script under a debugger or add prints in names_from_dynsym() to see what names were actually parsed
  4. Confirm entry_size and section offsets/alignment (dynsym_pad) are still computed correctly

Example fix

// before
symbols = struct.unpack_from(layout["symbol_format"], data, offset)
// wrong field index for name after changing struct layout
// after
symbol = struct.unpack_from(layout["symbol_format"], data, offset)
raw = string_table[symbol[NAME]:]  # NAME == 0, keep constants in sync
Defensive patterns

Strategy: try-catch

Validate before calling

subprocess.run(["check_elf_dynsym_exports.py", "--self-test"], check=True)  # gate edits on self-test passing

Try / catch

try:
    subprocess.run(["check_elf_dynsym_exports.py", "--self-test"], check=True)
except subprocess.CalledProcessError as e:
    print("self-test failed, checker logic is broken:", e)
    raise

Prevention

When it happens

Trigger: Running `check_elf_dynsym_exports.py --self-test` when missing_exports(path, [symbol]) returns non-empty for the synthetic ELF built by elf64_with_dynsym() — i.e. the parser failed to read back the symbol it just wrote.

Common situations: Someone edited elf64_with_dynsym(), the struct formats, section constants (NAME/TYPE/OFFSET/SIZE/LINK/ENTRY_SIZE), or names_from_dynsym() and broke the round-trip; also a Python/struct behavior change on an exotic platform.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-09-13). Data as JSON: /api/errors/39113e96e3fabd6f. Report an issue: GitHub.