Tencent/tinker · error · IOException

Unexpected elf class: {}

Error message

Unexpected elf class: {}

What it means

In ShareElfFile.ElfHeader's constructor, after reading e_entry/e_phoff/e_shoff the switch on eIndent[EI_CLASS] falls through to default and throws IOException("Unexpected elf class") for any class byte other than ELFCLASS32/ELFCLASS64. In practice the preceding assertInRange already rejects such values, so this branch is defensive; seeing it means the class byte was mutated between the two reads or assertInRange was bypassed.

Source

Thrown at tinker-android/tinker-android-loader/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 as a corrupted-file signal: pull the file and validate with readelf -h to inspect the class byte.
  2. Delete the corrupted odex/oat so the system regenerates it, or reinstall the app.
  3. Wrap ShareElfFile/ShareOatUtil usage in try-catch and skip oat parsing on failure.
  4. Verify file integrity (length, md5) before parsing.
Defensive patterns

Strategy: try-catch

Validate before calling

int eiClass = readEiClassByte(file); // read e_indent[4] yourself
if (eiClass != 1 && eiClass != 2) {
    // reject file before ShareElfFile parses it
}

Try / catch

try {
    elfFile = new ShareElfFile(file);
} catch (IOException e) {
    // covers 'Unexpected elf class' and magic errors: treat file as corrupt, regenerate
}

Prevention

When it happens

Trigger: Parsing an ELF whose EI_CLASS byte is neither 1 (32-bit) nor 2 (64-bit) at the point e_entry/e_phoff/e_shoff are decoded — normally impossible because assertInRange(EI_CLASS, ELFCLASS32, ELFCLASS64, ...) runs first.

Common situations: Corrupted ELF file where header bytes are inconsistent; concurrent modification of the file during parsing; extremely rarely, an OEM odex container with non-standard identification bytes.

Related errors


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