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
- Confirm the argument is the actual odex under /oat/<isa>/ and not another ELF.
- Inspect the file's sections (readelf -S) to see whether .rodata exists before calling.
- If the odex format is unrecognized, fall back to deriving the instruction set from Build.CPU_ABI instead of parsing the oat header.
- 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
- Confirm the target is the odex under /oat/<isa>/ before parsing.
- Treat missing .rodata as 'not a tinker-parsable oat' and switch to ABI mapping.
- Test oat parsing per supported Android version.
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
- Unable to find .rodata section.
- {} Rest bytes insufficient, expect to read {} bytes but only
- bad elf magic: %x %x %x %x.
- Bad oat magic: %x %x %x %x
- Bad oat version: {}
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/dbea3fd139961f22.
Report an issue: GitHub.