{"record":{"id":"045756dd82909dcd","repo":"Tencent/tinker","slug":"unable-to-find-rodata-section","errorCode":null,"errorMessage":"Unable to find .rodata section.","messagePattern":"Unable to find \\.rodata section\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"warning","filePath":"tinker-android/tinker-android-loader-no-op/src/main/java/com/tencent/tinker/loader/shareutil/ShareOatUtil.java","lineNumber":55,"sourceCode":"     * Get instruction set used to generate {@code oatFile}.\n     *\n     * @param oatFile\n     *  the oat file.\n     * @return\n     *  the instruction used to generate this oat file, if the oat file does not\n     *  contain this value, an empty string will be returned.\n     *\n     * @throws IOException\n     *  If anything wrong when parsing the elf format or locating target field in oat header.\n     */\n    public static String getOatFileInstructionSet(File oatFile) throws Throwable {\n        ShareElfFile elfFile = null;\n        String result = \"\";\n        try {\n            elfFile = new ShareElfFile(oatFile);\n            final ShareElfFile.SectionHeader roDataHdr = elfFile.getSectionHeaderByName(\".rodata\");\n            if (roDataHdr == null) {\n                throw new IOException(\"Unable to find .rodata section.\");\n            }\n\n            final FileChannel channel = elfFile.getChannel();\n            channel.position(roDataHdr.shOffset);\n\n            final byte[] oatMagicAndVersion = new byte[8];\n            ShareElfFile.readUntilLimit(channel, ByteBuffer.wrap(oatMagicAndVersion), \"Failed to read oat magic and version.\");\n\n            if (oatMagicAndVersion[0] != 'o'\n                    || oatMagicAndVersion[1] != 'a'\n                    || oatMagicAndVersion[2] != 't'\n                    || oatMagicAndVersion[3] != '\\n') {\n                throw new IOException(\n                        String.format(\"Bad oat magic: %x %x %x %x\",\n                                oatMagicAndVersion[0],\n                                oatMagicAndVersion[1],\n                                oatMagicAndVersion[2],\n                                oatMagicAndVersion[3])","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/Tencent/tinker/blob/1b7ea02c239840f563ea64fb5bd286eb98d4011e/tinker-android/tinker-android-loader-no-op/src/main/java/com/tencent/tinker/loader/shareutil/ShareOatUtil.java#L37-L73","documentation":"ShareOatUtil.getOatFileInstructionSet parses an odex/oat file as ELF (via ShareElfFile) and looks up the section named '.rodata', where the OAT header with the instruction-set string lives. If no section named .rodata exists, it throws IOException('Unable to find .rodata section.'). This happens when the file being parsed is a plain ELF/odex without OAT packaging — e.g. some devices' odex files use different section layouts, or the file is a .so rather than an oat.","triggerScenarios":"getOatFileInstructionSet(oatFile) invoked on a file that is valid ELF but has no .rodata section — such as a native library passed by mistake, an odex produced by an ART version that strips/renames sections, or an uncompressed check-dex odex variant.","commonSituations":"Tinker's interpret-mode flow calling this on devices (some Meizu/Huawei ROMs) whose odex layout lacks .rodata; passing a .so path where an oat/odex path was expected; OS version changes to odex format.","solutions":["Call getOatFileInstructionSet defensively and catch Throwable — tinker itself expects IOException here and treats it as 'unknown instruction set'; fall back to Build.SUPPORTED_ABIS / the primary abi.","Verify the argument is really an odex/oat file (secondary.dex-like path under oat/) before calling.","On affected ROMs, disable interpret-only mode (useInterpretModeOnSupported32BitSystem = false) so the oat parsing path is avoided.","Update tinker — newer versions handle more odex layouts."],"exampleFix":"// before\nString isa = ShareOatUtil.getOatFileInstructionSet(oatFile); // may throw\n\n// after\nString isa;\ntry {\n    isa = ShareOatUtil.getOatFileInstructionSet(oatFile);\n} catch (Throwable t) {\n    isa = Build.SUPPORTED_ABIS[0]; // graceful fallback\n}","handlingStrategy":"fallback","validationCode":"if (oatFile == null || !oatFile.getName().endsWith(\".odex\")\n        || !ShareOatUtil.fileLooksLikeOat(oatFile)) {\n    return Build.SUPPORTED_ABIS[0]; // skip oat parsing entirely\n}","typeGuard":"public static boolean looksLikeOat(File f) throws IOException {\n    try (RandomAccessFile raf = new RandomAccessFile(f, \"r\")) {\n        byte[] magic = new byte[4];\n        raf.seek(0); // .rodata offset unknown pre-parse; at minimum require ELF magic\n        raf.readFully(magic);\n        return (magic[0] & 0xff) == 0x7f && magic[1] == 'E' && magic[2] == 'L' && magic[3] == 'F';\n    }\n}","tryCatchPattern":"String isa;\ntry {\n    isa = ShareOatUtil.getOatFileInstructionSet(oatFile);\n} catch (Throwable t) {\n    isa = Build.SUPPORTED_ABIS[0]; // some ROM odex layouts lack .rodata\n}","preventionTips":["Wrap oat-parsing calls in try-catch and fall back to SUPPORTED_ABIS","Disable interpret-only mode on ROMs known to ship odex files without .rodata","Keep tinker updated for new odex layout handling"],"tags":["tinker","android","oat","elf","instruction-set"],"backgroundTag":null,"analyzedSha":"1b7ea02c239840f563ea64fb5bd286eb98d4011e","analyzedAt":"2026-08-14T15:16:52.110Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}