Tencent/tinker · error · IOException

bad elf magic: %x %x %x %x.

Error message

bad elf magic: %x %x %x %x.

What it means

ShareElfFile.ElfHeader reads the first 16 bytes (e_ident) of the file and requires the ELF magic 0x7F 'E' 'L' 'F'. A mismatch throws IOException('bad elf magic: %x %x %x %x.') showing the actual first four bytes. Tinker parses ELF when checking native libraries in patches (and OAT files), so this means the file being parsed is simply not an ELF binary.

Source

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

        public final short eType;
        public final short eMachine;
        public final int eVersion;
        public final long eEntry;
        public final long ePhOff;
        public final long eShOff;
        public final int eFlags;
        public final short eEhSize;
        public final short ePhEntSize;
        public final short ePhNum;
        public final short eShEntSize;
        public final short eShNum;
        public final short eShStrNdx;

        private ElfHeader(FileChannel channel) throws IOException {
            channel.position(0);
            channel.read(ByteBuffer.wrap(eIndent));
            if (eIndent[0] != 0x7F || eIndent[1] != 'E' || eIndent[2] != 'L' || eIndent[3] != 'F') {
                throw new IOException(String.format("bad elf magic: %x %x %x %x.", eIndent[0], eIndent[1], eIndent[2], eIndent[3]));
            }

            assertInRange(eIndent[EI_CLASS], ELFCLASS32, ELFCLASS64, "bad elf class: " + eIndent[EI_CLASS]);
            assertInRange(eIndent[EI_DATA], ELFDATA2LSB, ELFDATA2MSB, "bad elf data encoding: " + eIndent[EI_DATA]);

            final ByteBuffer restBuffer = ByteBuffer.allocate(eIndent[EI_CLASS] == ELFCLASS32 ? 36 : 48);
            restBuffer.order(eIndent[EI_DATA] == ELFDATA2LSB ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
            readUntilLimit(channel, restBuffer, "failed to read rest part of ehdr.");

            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();

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Inspect the offending file's first bytes (xxd file | head -1) to identify what it actually is.
  2. Rebuild the patch ensuring only genuine ELF .so files for the target ABIs are included (match ABIs with the base APK).
  3. Verify patch download integrity (md5/length) before apply so proxy/error payloads never enter lib/.
  4. cleanPatch() to remove the bad state after the failure.

Example fix

// before
ShareElfFile elf = new ShareElfFile(libFile); // IOException: bad elf magic

// after: cheap magic guard
private static boolean looksLikeElf(File f) throws IOException {
    try (RandomAccessFile raf = new RandomAccessFile(f, "r")) {
        byte[] m = new byte[4];
        raf.readFully(m);
        return (m[0] & 0xff) == 0x7f && m[1] == 'E' && m[2] == 'L' && m[3] == 'F';
    }
}
Defensive patterns

Strategy: type-guard

Validate before calling

try (RandomAccessFile raf = new RandomAccessFile(f, "r")) {
    byte[] m = new byte[4];
    raf.readFully(m);
    boolean isElf = (m[0] & 0xff) == 0x7f && m[1] == 'E' && m[2] == 'L' && m[3] == 'F';
    if (!isElf) throw new IOException("not an ELF file: " + f);
}

Type guard

public static boolean isElfFile(File f) throws IOException {
    if (f.length() < 16) return false;
    try (RandomAccessFile raf = new RandomAccessFile(f, "r")) {
        byte[] m = new byte[4];
        raf.readFully(m);
        return (m[0] & 0xff) == 0x7f && m[1] == 'E' && m[2] == 'L' && m[3] == 'F';
    }
}

Try / catch

catch (IOException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("bad elf magic")) {
        // wrong file type: fix packaging, don't retry
    }
}

Prevention

When it happens

Trigger: Constructing ShareElfFile over a file whose first 4 bytes are not 7f 45 4c 46 — e.g. a plain dex/jar/zip named .so, a text file, a compressed archive, or an already-stripped fake library placed in the patch's lib directory.

Common situations: Patch built with mismatched ABIs or a placeholder .so; gradle plugin packaging a non-ELF file into lib/; pointing tinker's lib check at odex/dex artifacts; downloading patches through a proxy that returns an HTML error page saved with the .so name.

Related errors


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