Tencent/tinker · error · IOException

Unable to find .rodata section.

Error message

Unable to find .rodata section.

What it means

ShareOatUtil.getOatFileInstructionSet opens an odex file with ShareElfFile and looks up the section named '.rodata' (where the OAT header lives inside the ELF). If no section header carries that name, it throws IOException("Unable to find .rodata section."). The file is a valid ELF but not an OAT container with the expected layout.

Source

Thrown at tinker-android/tinker-android-loader/src/main/java/com/tencent/tinker/loader/shareutil/ShareOatUtil.java:55

     * Get instruction set used to generate {@code oatFile}.
     *
     * @param oatFile
     *  the oat file.
     * @return
     *  the instruction used to generate this oat file, if the oat file does not
     *  contain this value, an empty string will be returned.
     *
     * @throws IOException
     *  If anything wrong when parsing the elf format or locating target field in oat header.
     */
    public static String getOatFileInstructionSet(File oatFile) throws Throwable {
        ShareElfFile elfFile = null;
        String result = "";
        try {
            elfFile = new ShareElfFile(oatFile);
            final ShareElfFile.SectionHeader roDataHdr = elfFile.getSectionHeaderByName(".rodata");
            if (roDataHdr == null) {
                throw new IOException("Unable to find .rodata section.");
            }

            final FileChannel channel = elfFile.getChannel();
            channel.position(roDataHdr.shOffset);

            final byte[] oatMagicAndVersion = new byte[8];
            ShareElfFile.readUntilLimit(channel, ByteBuffer.wrap(oatMagicAndVersion), "Failed to read oat magic and version.");

            if (oatMagicAndVersion[0] != 'o'
                    || oatMagicAndVersion[1] != 'a'
                    || oatMagicAndVersion[2] != 't'
                    || oatMagicAndVersion[3] != '\n') {
                throw new IOException(
                        String.format("Bad oat magic: %x %x %x %x",
                                oatMagicAndVersion[0],
                                oatMagicAndVersion[1],
                                oatMagicAndVersion[2],
                                oatMagicAndVersion[3])

View on GitHub (pinned to 1b7ea02c23)

Solutions

  1. Confirm the argument is the actual odex under /oat/<isa>/ and not another ELF.
  2. Inspect the file's sections (readelf -S) to see whether .rodata exists before calling.
  3. If the odex format is unrecognized, fall back to deriving the instruction set from Build.CPU_ABI instead of parsing the oat header.
  4. Catch Throwable from getOatFileInstructionSet (its signature throws Throwable) and skip oat probing.

Example fix

// before
String isa = ShareOatUtil.getOatFileInstructionSet(odexFile);
// after
String isa;
try {
    isa = ShareOatUtil.getOatFileInstructionSet(odexFile);
} catch (Throwable t) {
    isa = ShareTinkerInternals.getCurrentInstructionSet();
}
Defensive patterns

Strategy: fallback

Validate before calling

if (odexFile == null || !odexFile.exists() || odexFile.length() == 0) {
    // no odex to parse: use ABI-derived instruction set instead
}

Try / catch

try {
    isa = ShareOatUtil.getOatFileInstructionSet(odexFile);
} catch (Throwable t) {
    isa = ShareTinkerInternals.getCurrentInstructionSet(); // ABI fallback
}

Prevention

When it happens

Trigger: Calling getOatFileInstructionSet(oatFile) on an odex whose section table has no .rodata — e.g. a plain shared library, a stripped odex, or an ART output format that stores the oat header differently.

Common situations: Android O+ odex layout changes on newer ART versions; passing the wrong file (a .so or the base apk) where the odex was expected; odex produced by unconventional dexopt configurations.

Related errors


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