1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.compatibility.common.util; 18 19 import java.io.File; 20 import java.io.IOException; 21 import java.io.RandomAccessFile; 22 import java.util.HashMap; 23 import java.util.Map; 24 25 /** 26 * A poor man's implementation of the readelf command. This program is designed 27 * to parse ELF (Executable and Linkable Format) files. 28 */ 29 public class ReadElf implements AutoCloseable { 30 /** The magic values for the ELF identification. */ 31 private static final byte[] ELFMAG = { 32 (byte) 0x7F, (byte) 'E', (byte) 'L', (byte) 'F', }; 33 34 private static final int EI_NIDENT = 16; 35 36 private static final int EI_CLASS = 4; 37 private static final int EI_DATA = 5; 38 39 private static final int EM_386 = 3; 40 private static final int EM_MIPS = 8; 41 private static final int EM_ARM = 40; 42 private static final int EM_X86_64 = 62; 43 // http://en.wikipedia.org/wiki/Qualcomm_Hexagon 44 private static final int EM_QDSP6 = 164; 45 private static final int EM_AARCH64 = 183; 46 47 private static final int ELFCLASS32 = 1; 48 private static final int ELFCLASS64 = 2; 49 50 private static final int ELFDATA2LSB = 1; 51 private static final int ELFDATA2MSB = 2; 52 53 private static final int EV_CURRENT = 1; 54 55 private static final long PT_LOAD = 1; 56 57 private static final int SHT_SYMTAB = 2; 58 private static final int SHT_STRTAB = 3; 59 private static final int SHT_DYNAMIC = 6; 60 private static final int SHT_DYNSYM = 11; 61 62 public static class Symbol { 63 public static final int STB_LOCAL = 0; 64 public static final int STB_GLOBAL = 1; 65 public static final int STB_WEAK = 2; 66 public static final int STB_LOPROC = 13; 67 public static final int STB_HIPROC = 15; 68 69 public static final int STT_NOTYPE = 0; 70 public static final int STT_OBJECT = 1; 71 public static final int STT_FUNC = 2; 72 public static final int STT_SECTION = 3; 73 public static final int STT_FILE = 4; 74 public static final int STT_COMMON = 5; 75 public static final int STT_TLS = 6; 76 77 public final String name; 78 public final int bind; 79 public final int type; 80 Symbol(String name, int st_info)81 Symbol(String name, int st_info) { 82 this.name = name; 83 this.bind = (st_info >> 4) & 0x0F; 84 this.type = st_info & 0x0F; 85 } 86 87 @Override toString()88 public String toString() { 89 return "Symbol[" + name + "," + toBind() + "," + toType() + "]"; 90 } 91 toBind()92 private String toBind() { 93 switch (bind) { 94 case STB_LOCAL: 95 return "LOCAL"; 96 case STB_GLOBAL: 97 return "GLOBAL"; 98 case STB_WEAK: 99 return "WEAK"; 100 } 101 return "STB_??? (" + bind + ")"; 102 } 103 toType()104 private String toType() { 105 switch (type) { 106 case STT_NOTYPE: 107 return "NOTYPE"; 108 case STT_OBJECT: 109 return "OBJECT"; 110 case STT_FUNC: 111 return "FUNC"; 112 case STT_SECTION: 113 return "SECTION"; 114 case STT_FILE: 115 return "FILE"; 116 case STT_COMMON: 117 return "COMMON"; 118 case STT_TLS: 119 return "TLS"; 120 } 121 return "STT_??? (" + type + ")"; 122 } 123 } 124 125 private final String mPath; 126 private final RandomAccessFile mFile; 127 private final byte[] mBuffer = new byte[512]; 128 private int mEndian; 129 private boolean mIsDynamic; 130 private boolean mIsPIE; 131 private int mType; 132 private int mAddrSize; 133 134 /** Symbol Table offset */ 135 private long mSymTabOffset; 136 137 /** Symbol Table size */ 138 private long mSymTabSize; 139 140 /** Dynamic Symbol Table offset */ 141 private long mDynSymOffset; 142 143 /** Dynamic Symbol Table size */ 144 private long mDynSymSize; 145 146 /** Section Header String Table offset */ 147 private long mShStrTabOffset; 148 149 /** Section Header String Table size */ 150 private long mShStrTabSize; 151 152 /** String Table offset */ 153 private long mStrTabOffset; 154 155 /** String Table size */ 156 private long mStrTabSize; 157 158 /** Dynamic String Table offset */ 159 private long mDynStrOffset; 160 161 /** Dynamic String Table size */ 162 private long mDynStrSize; 163 164 /** Symbol Table symbol names */ 165 private Map<String, Symbol> mSymbols; 166 167 /** Dynamic Symbol Table symbol names */ 168 private Map<String, Symbol> mDynamicSymbols; 169 read(File file)170 public static ReadElf read(File file) throws IOException { 171 return new ReadElf(file); 172 } 173 main(String[] args)174 public static void main(String[] args) throws IOException { 175 for (String arg : args) { 176 ReadElf re = new ReadElf(new File(arg)); 177 re.getSymbol("x"); 178 re.getDynamicSymbol("x"); 179 re.close(); 180 } 181 } 182 isDynamic()183 public boolean isDynamic() { 184 return mIsDynamic; 185 } 186 getType()187 public int getType() { 188 return mType; 189 } 190 isPIE()191 public boolean isPIE() { 192 return mIsPIE; 193 } 194 ReadElf(File file)195 private ReadElf(File file) throws IOException { 196 mPath = file.getPath(); 197 mFile = new RandomAccessFile(file, "r"); 198 199 if (mFile.length() < EI_NIDENT) { 200 throw new IllegalArgumentException("Too small to be an ELF file: " + file); 201 } 202 203 readHeader(); 204 } 205 206 @Override close()207 public void close() { 208 try { 209 mFile.close(); 210 } catch (IOException ignored) { 211 } 212 } 213 214 @Override finalize()215 protected void finalize() throws Throwable { 216 try { 217 close(); 218 } finally { 219 super.finalize(); 220 } 221 } 222 readHeader()223 private void readHeader() throws IOException { 224 mFile.seek(0); 225 mFile.readFully(mBuffer, 0, EI_NIDENT); 226 227 if (mBuffer[0] != ELFMAG[0] || mBuffer[1] != ELFMAG[1] || 228 mBuffer[2] != ELFMAG[2] || mBuffer[3] != ELFMAG[3]) { 229 throw new IllegalArgumentException("Invalid ELF file: " + mPath); 230 } 231 232 int elfClass = mBuffer[EI_CLASS]; 233 if (elfClass == ELFCLASS32) { 234 mAddrSize = 4; 235 } else if (elfClass == ELFCLASS64) { 236 mAddrSize = 8; 237 } else { 238 throw new IOException("Invalid ELF EI_CLASS: " + elfClass + ": " + mPath); 239 } 240 241 mEndian = mBuffer[EI_DATA]; 242 if (mEndian == ELFDATA2LSB) { 243 } else if (mEndian == ELFDATA2MSB) { 244 throw new IOException("Unsupported ELFDATA2MSB file: " + mPath); 245 } else { 246 throw new IOException("Invalid ELF EI_DATA: " + mEndian + ": " + mPath); 247 } 248 249 mType = readHalf(); 250 251 int e_machine = readHalf(); 252 if (e_machine != EM_386 && e_machine != EM_X86_64 && 253 e_machine != EM_AARCH64 && e_machine != EM_ARM && 254 e_machine != EM_MIPS && 255 e_machine != EM_QDSP6) { 256 throw new IOException("Invalid ELF e_machine: " + e_machine + ": " + mPath); 257 } 258 259 // AbiTest relies on us rejecting any unsupported combinations. 260 if ((e_machine == EM_386 && elfClass != ELFCLASS32) || 261 (e_machine == EM_X86_64 && elfClass != ELFCLASS64) || 262 (e_machine == EM_AARCH64 && elfClass != ELFCLASS64) || 263 (e_machine == EM_ARM && elfClass != ELFCLASS32) || 264 (e_machine == EM_QDSP6 && elfClass != ELFCLASS32)) { 265 throw new IOException("Invalid e_machine/EI_CLASS ELF combination: " + 266 e_machine + "/" + elfClass + ": " + mPath); 267 } 268 269 long e_version = readWord(); 270 if (e_version != EV_CURRENT) { 271 throw new IOException("Invalid e_version: " + e_version + ": " + mPath); 272 } 273 274 long e_entry = readAddr(); 275 276 long ph_off = readOff(); 277 long sh_off = readOff(); 278 279 long e_flags = readWord(); 280 int e_ehsize = readHalf(); 281 int e_phentsize = readHalf(); 282 int e_phnum = readHalf(); 283 int e_shentsize = readHalf(); 284 int e_shnum = readHalf(); 285 int e_shstrndx = readHalf(); 286 287 readSectionHeaders(sh_off, e_shnum, e_shentsize, e_shstrndx); 288 readProgramHeaders(ph_off, e_phnum, e_phentsize); 289 } 290 readSectionHeaders(long sh_off, int e_shnum, int e_shentsize, int e_shstrndx)291 private void readSectionHeaders(long sh_off, int e_shnum, int e_shentsize, int e_shstrndx) 292 throws IOException { 293 // Read the Section Header String Table offset first. 294 { 295 mFile.seek(sh_off + e_shstrndx * e_shentsize); 296 297 long sh_name = readWord(); 298 long sh_type = readWord(); 299 long sh_flags = readX(mAddrSize); 300 long sh_addr = readAddr(); 301 long sh_offset = readOff(); 302 long sh_size = readX(mAddrSize); 303 // ... 304 305 if (sh_type == SHT_STRTAB) { 306 mShStrTabOffset = sh_offset; 307 mShStrTabSize = sh_size; 308 } 309 } 310 311 for (int i = 0; i < e_shnum; ++i) { 312 // Don't bother to re-read the Section Header StrTab. 313 if (i == e_shstrndx) { 314 continue; 315 } 316 317 mFile.seek(sh_off + i * e_shentsize); 318 319 long sh_name = readWord(); 320 long sh_type = readWord(); 321 long sh_flags = readX(mAddrSize); 322 long sh_addr = readAddr(); 323 long sh_offset = readOff(); 324 long sh_size = readX(mAddrSize); 325 326 if (sh_type == SHT_SYMTAB || sh_type == SHT_DYNSYM) { 327 final String symTabName = readShStrTabEntry(sh_name); 328 if (".symtab".equals(symTabName)) { 329 mSymTabOffset = sh_offset; 330 mSymTabSize = sh_size; 331 } else if (".dynsym".equals(symTabName)) { 332 mDynSymOffset = sh_offset; 333 mDynSymSize = sh_size; 334 } 335 } else if (sh_type == SHT_STRTAB) { 336 final String strTabName = readShStrTabEntry(sh_name); 337 if (".strtab".equals(strTabName)) { 338 mStrTabOffset = sh_offset; 339 mStrTabSize = sh_size; 340 } else if (".dynstr".equals(strTabName)) { 341 mDynStrOffset = sh_offset; 342 mDynStrSize = sh_size; 343 } 344 } else if (sh_type == SHT_DYNAMIC) { 345 mIsDynamic = true; 346 } 347 } 348 } 349 readProgramHeaders(long ph_off, int e_phnum, int e_phentsize)350 private void readProgramHeaders(long ph_off, int e_phnum, int e_phentsize) throws IOException { 351 for (int i = 0; i < e_phnum; ++i) { 352 mFile.seek(ph_off + i * e_phentsize); 353 354 long p_type = readWord(); 355 if (p_type == PT_LOAD) { 356 if (mAddrSize == 8) { 357 // Only in Elf64_phdr; in Elf32_phdr p_flags is at the end. 358 long p_flags = readWord(); 359 } 360 long p_offset = readOff(); 361 long p_vaddr = readAddr(); 362 // ... 363 364 if (p_vaddr == 0) { 365 mIsPIE = true; 366 } 367 } 368 } 369 } 370 readSymbolTable(long symStrOffset, long symStrSize, long tableOffset, long tableSize)371 private HashMap<String, Symbol> readSymbolTable(long symStrOffset, long symStrSize, 372 long tableOffset, long tableSize) throws IOException { 373 HashMap<String, Symbol> result = new HashMap<String, Symbol>(); 374 mFile.seek(tableOffset); 375 while (mFile.getFilePointer() < tableOffset + tableSize) { 376 long st_name = readWord(); 377 int st_info; 378 if (mAddrSize == 8) { 379 st_info = readByte(); 380 int st_other = readByte(); 381 int st_shndx = readHalf(); 382 long st_value = readAddr(); 383 long st_size = readX(mAddrSize); 384 } else { 385 long st_value = readAddr(); 386 long st_size = readWord(); 387 st_info = readByte(); 388 int st_other = readByte(); 389 int st_shndx = readHalf(); 390 } 391 if (st_name == 0) { 392 continue; 393 } 394 395 final String symName = readStrTabEntry(symStrOffset, symStrSize, st_name); 396 if (symName != null) { 397 Symbol s = new Symbol(symName, st_info); 398 result.put(symName, s); 399 } 400 } 401 return result; 402 } 403 readShStrTabEntry(long strOffset)404 private String readShStrTabEntry(long strOffset) throws IOException { 405 if (mShStrTabOffset == 0 || strOffset < 0 || strOffset >= mShStrTabSize) { 406 return null; 407 } 408 return readString(mShStrTabOffset + strOffset); 409 } 410 readStrTabEntry(long tableOffset, long tableSize, long strOffset)411 private String readStrTabEntry(long tableOffset, long tableSize, long strOffset) 412 throws IOException { 413 if (tableOffset == 0 || strOffset < 0 || strOffset >= tableSize) { 414 return null; 415 } 416 return readString(tableOffset + strOffset); 417 } 418 readHalf()419 private int readHalf() throws IOException { 420 return (int) readX(2); 421 } 422 readWord()423 private long readWord() throws IOException { 424 return readX(4); 425 } 426 readOff()427 private long readOff() throws IOException { 428 return readX(mAddrSize); 429 } 430 readAddr()431 private long readAddr() throws IOException { 432 return readX(mAddrSize); 433 } 434 readX(int byteCount)435 private long readX(int byteCount) throws IOException { 436 mFile.readFully(mBuffer, 0, byteCount); 437 438 int answer = 0; 439 if (mEndian == ELFDATA2LSB) { 440 for (int i = byteCount - 1; i >= 0; i--) { 441 answer = (answer << 8) | (mBuffer[i] & 0xff); 442 } 443 } else { 444 final int N = byteCount - 1; 445 for (int i = 0; i <= N; ++i) { 446 answer = (answer << 8) | (mBuffer[i] & 0xff); 447 } 448 } 449 450 return answer; 451 } 452 readString(long offset)453 private String readString(long offset) throws IOException { 454 long originalOffset = mFile.getFilePointer(); 455 mFile.seek(offset); 456 mFile.readFully(mBuffer, 0, (int) Math.min(mBuffer.length, mFile.length() - offset)); 457 mFile.seek(originalOffset); 458 459 for (int i = 0; i < mBuffer.length; ++i) { 460 if (mBuffer[i] == 0) { 461 return new String(mBuffer, 0, i); 462 } 463 } 464 465 return null; 466 } 467 readByte()468 private int readByte() throws IOException { 469 return mFile.read() & 0xff; 470 } 471 getSymbol(String name)472 public Symbol getSymbol(String name) { 473 if (mSymbols == null) { 474 try { 475 mSymbols = readSymbolTable(mStrTabOffset, mStrTabSize, mSymTabOffset, mSymTabSize); 476 } catch (IOException e) { 477 return null; 478 } 479 } 480 return mSymbols.get(name); 481 } 482 getDynamicSymbol(String name)483 public Symbol getDynamicSymbol(String name) { 484 if (mDynamicSymbols == null) { 485 try { 486 mDynamicSymbols = readSymbolTable( 487 mDynStrOffset, mDynStrSize, mDynSymOffset, mDynSymSize); 488 } catch (IOException e) { 489 return null; 490 } 491 } 492 return mDynamicSymbols.get(name); 493 } 494 } 495