Tencent/tinker · error · IOException

Unexpected elf class: {}

Error message

Unexpected elf class: {}

What it means

While reading the remainder of the ELF header, ShareElfFile switches on e_ident[EI_CLASS] to read e_entry/e_phoff/e_shoff with the right widths (int for 32-bit, long for 64-bit). Any value other than ELFCLASS32(1) or ELFCLASS64(2) reaches the default branch and throws IOException('Unexpected elf class:'). Although ElfHeader.assertInRange normally rejects bad classes earlier, this branch covers arithmetic-injected or mutated class bytes and defends the width-sensitive reads.

Source

Thrown at tinker-android/tinker-android-loader-no-op/src/main/java/com/tencent/tinker/loader/shareutil/ShareElfFile.java:241

            eType = restBuffer.getShort();
            eMachine = restBuffer.getShort();

            eVersion = restBuffer.getInt();
            assertInRange(eVersion, EV_CURRENT, EV_CURRENT, "bad elf version: " + eVersion);

            switch (eIndent[EI_CLASS]) {
                case ELFCLASS32:
                    eEntry = restBuffer.getInt();
                    ePhOff = restBuffer.getInt();
                    eShOff = restBuffer.getInt();
                    break;
                case ELFCLASS64:
                    eEntry = restBuffer.getLong();
                    ePhOff = restBuffer.getLong();
                    eShOff = restBuffer.getLong();
                    break;
                default:
                    throw new IOException("Unexpected elf class: " + eIndent[EI_CLASS]);
            }
            eFlags = restBuffer.getInt();
            eEhSize = restBuffer.getShort();
            ePhEntSize = restBuffer.getShort();
            ePhNum = restBuffer.getShort();
            eShEntSize = restBuffer.getShort();
            eShNum = restBuffer.getShort();
            eShStrNdx = restBuffer.getShort();
        }
    }

    public static class ProgramHeader {
        // Segment types.
        public static final int PT_NULL = 0;
        public static final int PT_LOAD = 1;
        public static final int PT_DYNAMIC = 2;
        public static final int PT_INTERP = 3;
        public static final int PT_NOTE = 4;

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Treat the file as corrupt: cleanPatch() and re-apply a verified patch.
  2. Add md5 verification of patch contents before load so corrupted .so files are rejected before ELF parsing.
  3. Re-download the patch from the server instead of reusing a locally corrupted copy.
Defensive patterns

Strategy: try-catch

Validate before calling

int elfClass = eIndent[ShareElfFile.ElfHeader.EI_CLASS] & 0xff;
if (elfClass != ShareElfFile.ElfHeader.ELFCLASS32
        && elfClass != ShareElfFile.ElfHeader.ELFCLASS64) {
    throw new IOException("invalid elf class byte: " + elfClass);
}

Try / catch

catch (IOException e) {
    if (e.getMessage() != null && e.getMessage().contains("elf class")) {
        // corrupt ELF: re-fetch the file
    }
}

Prevention

When it happens

Trigger: Parsing a file whose ELF magic is valid but whose EI_CLASS byte at offset 4 is neither 1 nor 2 — e.g. corrupted ELF, fuzzed/mutated binaries, or files crafted with invalid class identifiers.

Common situations: Bit-rot or partial overwrites of .so files on disk; transferring patches through channels that corrupt binary data; security research/fuzzing inputs fed to ShareElfFile.

Related errors


AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14). Data as JSON: /api/errors/7c0353bc7386918c. Report an issue: GitHub.