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 android.os.cts; 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 27 * designed 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[] ELF_IDENT = { 32 (byte) 0x7F, (byte) 'E', (byte) 'L', (byte) 'F', 33 }; 34 35 private static final int EI_CLASS = 4; 36 private static final int EI_DATA = 5; 37 38 private static final int EM_386 = 3; 39 private static final int EM_MIPS = 8; 40 private static final int EM_ARM = 40; 41 42 /** Size of the e_ident[] structure in the ELF header. */ 43 private static final int EI_NIDENT = 16; 44 45 /** Offset from end of ident structure in half-word sizes. */ 46 private static final int OFFSET_TYPE = 0; 47 48 /** Machine type. */ 49 private static final int OFFSET_MACHINE = 1; 50 51 /** ELF version. */ 52 private static final int OFFSET_VERSION = 2; 53 54 /** 55 * The offset to which the system transfers control. e.g., the first thing 56 * executed. 57 */ 58 private static final int OFFSET_ENTRY = 4; 59 60 /** Program header offset in bytes. */ 61 private static final int OFFSET_PHOFF = 6; 62 63 /** Segment header offset in bytes. */ 64 private static final int OFFSET_SHOFF = 8; 65 66 /** Processor-specific flags for binary. */ 67 private static final int OFFSET_FLAGS = 10; 68 69 /** ELF header size in bytes. */ 70 private static final int OFFSET_EHSIZE = 12; 71 72 /** All program headers entry size in bytes. */ 73 private static final int OFFSET_PHENTSIZE = 13; 74 75 /** Number of program headers in ELF. */ 76 private static final int OFFSET_PHNUM = 14; 77 78 /** All segment headers entry size in bytes. */ 79 private static final int OFFSET_SHENTSIZE = 15; 80 81 /** Number of segment headers in ELF. */ 82 private static final int OFFSET_SHNUM = 16; 83 84 /** The section header index that refers to string table. */ 85 private static final int OFFSET_SHSTRNDX = 17; 86 87 /** Program header offset for type of this program header. */ 88 private static final int PHOFF_TYPE = 0; 89 90 /** Program header offset for absolute offset in file. */ 91 private static final int PHOFF_OFFSET = 2; 92 93 /** Program header offset for virtual address. */ 94 private static final int PHOFF_VADDR = 4; 95 96 /** Program header offset for physical address. */ 97 private static final int PHOFF_PADDR = 6; 98 99 /** Program header offset for file size in bytes. */ 100 private static final int PHOFF_FILESZ = 8; 101 102 /** Program header offset for memory size in bytes. */ 103 private static final int PHOFF_MEMSZ = 10; 104 105 /** Program header offset for flags. */ 106 private static final int PHOFF_FLAGS = 12; 107 108 /** 109 * Program header offset for required alignment. 0 or 1 means no alignment 110 * necessary. 111 */ 112 private static final int PHOFF_ALIGN = 14; 113 114 /** Index into string pool for segment name. */ 115 private static final long SHOFF_NAME = 0; 116 117 /** Segment header offset for type (half-words) */ 118 private static final long SHOFF_TYPE = 2; 119 120 /** Segment header offset for offset (meta!) (half-words) */ 121 private static final long SHOFF_OFFSET = 8; 122 123 /** Segment header offset for size (half-words) */ 124 private static final long SHOFF_SIZE = 10; 125 126 /** Data is presented in LSB format. */ 127 private static final int ELFDATA2LSB = 1; 128 129 /** Date is presented in MSB format. */ 130 private static final int ELFDATA2MSB = 2; 131 132 private static final int ELFCLASS32 = 1; 133 134 private static final int ELFCLASS64 = 2; 135 136 private static final long PT_LOAD = 1; 137 138 /** Section Type: Symbol Table */ 139 private static final int SHT_SYMTAB = 2; 140 141 /** Section Type: String Table */ 142 private static final int SHT_STRTAB = 3; 143 144 /** Section Type: Dynamic **/ 145 private static final int SHT_DYNAMIC = 6; 146 147 /** Section Type: Dynamic Symbol Table */ 148 private static final int SHT_DYNSYM = 11; 149 150 /** Symbol Table Entry: Name offset */ 151 private static final int SYMTAB_NAME = 0; 152 153 /** Symbol Table Entry: SymTab Info */ 154 private static final int SYMTAB_ST_INFO = 6; 155 156 /** Symbol Table Entry size (half-words) */ 157 private static final int SYMTAB_ENTRY_HALFWORD_SIZE = 7; 158 159 /** 160 * Symbol Table Entry size (extra in bytes) to cover "st_info" and 161 * "st_other" 162 */ 163 private static final int SYMTAB_ENTRY_BYTE_EXTRA_SIZE = 2; 164 165 public static class Symbol { 166 public static final int STB_LOCAL = 0; 167 168 public static final int STB_GLOBAL = 1; 169 170 public static final int STB_WEAK = 2; 171 172 public static final int STB_LOPROC = 13; 173 174 public static final int STB_HIPROC = 15; 175 176 public final String name; 177 178 public final int bind; 179 180 public final int type; 181 182 Symbol(String name, int st_info) { 183 this.name = name; 184 this.bind = (st_info >> 4) & 0x0F; 185 this.type = st_info & 0x0F; 186 } 187 }; 188 189 private final String mPath; 190 private final RandomAccessFile mFile; 191 private final byte[] mBuffer = new byte[512]; 192 private int mEndian; 193 private boolean mIsDynamic; 194 private boolean mIsPIE; 195 private int mType; 196 private int mWordSize; 197 private int mHalfWordSize; 198 199 /** Symbol Table offset */ 200 private long mSymTabOffset; 201 202 /** Symbol Table size */ 203 private long mSymTabSize; 204 205 /** Dynamic Symbol Table offset */ 206 private long mDynSymOffset; 207 208 /** Dynamic Symbol Table size */ 209 private long mDynSymSize; 210 211 /** Section Header String Table offset */ 212 private long mShStrTabOffset; 213 214 /** Section Header String Table size */ 215 private long mShStrTabSize; 216 217 /** String Table offset */ 218 private long mStrTabOffset; 219 220 /** String Table size */ 221 private long mStrTabSize; 222 223 /** Dynamic String Table offset */ 224 private long mDynStrOffset; 225 226 /** Dynamic String Table size */ 227 private long mDynStrSize; 228 229 /** Symbol Table symbol names */ 230 private Map<String, Symbol> mSymbols; 231 232 /** Dynamic Symbol Table symbol names */ 233 private Map<String, Symbol> mDynamicSymbols; 234 235 public static ReadElf read(File file) throws IOException { 236 return new ReadElf(file); 237 } 238 239 public boolean isDynamic() { 240 return mIsDynamic; 241 } 242 243 public int getType() { 244 return mType; 245 } 246 247 public boolean isPIE() { 248 return mIsPIE; 249 } 250 251 private ReadElf(File file) throws IOException { 252 mPath = file.getPath(); 253 mFile = new RandomAccessFile(file, "r"); 254 255 if (mFile.length() < EI_NIDENT) { 256 throw new IllegalArgumentException("Too small to be an ELF file: " + file); 257 } 258 259 readIdent(); 260 readHeader(); 261 } 262 263 public void close() { 264 try { 265 mFile.close(); 266 } catch (IOException ignored) { 267 } 268 } 269 270 protected void finalize() throws Throwable { 271 try { 272 close(); 273 } finally { 274 super.finalize(); 275 } 276 } 277 278 private void readHeader() throws IOException { 279 mType = readHalf(getHeaderOffset(OFFSET_TYPE)); 280 int e_machine = readHalf(getHeaderOffset(OFFSET_MACHINE)); 281 if (e_machine != EM_386 && e_machine != EM_MIPS && e_machine != EM_ARM) { 282 throw new IOException("Invalid ELF e_machine: " + e_machine + ": " + mPath); 283 } 284 285 final long shOffset = readWord(getHeaderOffset(OFFSET_SHOFF)); 286 final int shNumber = readHalf(getHeaderOffset(OFFSET_SHNUM)); 287 final int shSize = readHalf(getHeaderOffset(OFFSET_SHENTSIZE)); 288 final int shStrIndex = readHalf(getHeaderOffset(OFFSET_SHSTRNDX)); 289 290 readSectionHeaders(shOffset, shNumber, shSize, shStrIndex); 291 292 final long phOffset = readWord(getHeaderOffset(OFFSET_PHOFF)); 293 final int phNumber = readHalf(getHeaderOffset(OFFSET_PHNUM)); 294 final int phSize = readHalf(getHeaderOffset(OFFSET_PHENTSIZE)); 295 296 readProgramHeaders(phOffset, phNumber, phSize); 297 } 298 299 private void readSectionHeaders(long tableOffset, int shNumber, int shSize, int shStrIndex) 300 throws IOException { 301 // Read the Section Header String Table offset first. 302 { 303 final long shStrTabShOffset = tableOffset + shStrIndex * shSize; 304 final long type = readWord(shStrTabShOffset + mHalfWordSize * SHOFF_TYPE); 305 306 if (type == SHT_STRTAB) { 307 mShStrTabOffset = readWord(shStrTabShOffset + mHalfWordSize * SHOFF_OFFSET); 308 mShStrTabSize = readWord(shStrTabShOffset + mHalfWordSize * SHOFF_SIZE); 309 } 310 } 311 312 for (int i = 0; i < shNumber; i++) { 313 // Don't bother to re-read the Section Header StrTab. 314 if (i == shStrIndex) { 315 continue; 316 } 317 318 final long shOffset = tableOffset + i * shSize; 319 320 final long type = readWord(shOffset + mHalfWordSize * SHOFF_TYPE); 321 if ((type == SHT_SYMTAB) || (type == SHT_DYNSYM)) { 322 final long nameOffset = readWord(shOffset + mHalfWordSize * SHOFF_NAME); 323 final long offset = readWord(shOffset + mHalfWordSize * SHOFF_OFFSET); 324 final long size = readWord(shOffset + mHalfWordSize * SHOFF_SIZE); 325 326 final String symTabName = readShStrTabEntry(nameOffset); 327 if (".symtab".equals(symTabName)) { 328 mSymTabOffset = offset; 329 mSymTabSize = size; 330 } else if (".dynsym".equals(symTabName)) { 331 mDynSymOffset = offset; 332 mDynSymSize = size; 333 } 334 } else if (type == SHT_STRTAB) { 335 final long nameOffset = readWord(shOffset + mHalfWordSize * SHOFF_NAME); 336 final long offset = readWord(shOffset + mHalfWordSize * SHOFF_OFFSET); 337 final long size = readWord(shOffset + mHalfWordSize * SHOFF_SIZE); 338 339 final String strTabName = readShStrTabEntry(nameOffset); 340 if (".strtab".equals(strTabName)) { 341 mStrTabOffset = offset; 342 mStrTabSize = size; 343 } else if (".dynstr".equals(strTabName)) { 344 mDynStrOffset = offset; 345 mDynStrSize = size; 346 } 347 } else if (type == SHT_DYNAMIC) { 348 mIsDynamic = true; 349 } 350 } 351 } 352 353 private void readProgramHeaders(long phOffset, int phNumber, int phSize) throws IOException { 354 for (int i = 0; i < phNumber; i++) { 355 final long baseOffset = phOffset + i * phSize; 356 final long type = readWord(baseOffset); 357 if (type == PT_LOAD) { 358 final long virtAddress = readWord(baseOffset + mHalfWordSize * PHOFF_VADDR); 359 if (virtAddress == 0) { 360 mIsPIE = true; 361 } 362 } 363 } 364 } 365 366 private void readSymbolTable(Map<String, Symbol> symbolMap, long symStrOffset, long symStrSize, 367 long symOffset, long symSize) throws IOException { 368 final long symEnd = symOffset + symSize; 369 for (long off = symOffset; off < symEnd; off += SYMTAB_ENTRY_HALFWORD_SIZE * mHalfWordSize 370 + SYMTAB_ENTRY_BYTE_EXTRA_SIZE) { 371 long strOffset = readWord(off + SYMTAB_NAME); 372 if (strOffset == 0) { 373 continue; 374 } 375 376 final String symName = readStrTabEntry(symStrOffset, symStrSize, strOffset); 377 if (symName != null) { 378 final int st_info = readByte(off + SYMTAB_ST_INFO); 379 symbolMap.put(symName, new Symbol(symName, st_info)); 380 } 381 } 382 } 383 384 private String readShStrTabEntry(long strOffset) throws IOException { 385 if ((mShStrTabOffset == 0) || (strOffset < 0) || (strOffset >= mShStrTabSize)) { 386 return null; 387 } 388 389 return readString(mShStrTabOffset + strOffset); 390 } 391 392 private String readStrTabEntry(long tableOffset, long tableSize, long strOffset) 393 throws IOException { 394 if ((tableOffset == 0) || (strOffset < 0) || (strOffset >= tableSize)) { 395 return null; 396 } 397 398 return readString(tableOffset + strOffset); 399 } 400 401 private int getHeaderOffset(int halfWorldOffset) { 402 return EI_NIDENT + halfWorldOffset * mHalfWordSize; 403 } 404 405 private int readByte(long offset) throws IOException { 406 mFile.seek(offset); 407 mFile.readFully(mBuffer, 0, 1); 408 409 return mBuffer[0]; 410 } 411 412 private int readHalf(long offset) throws IOException { 413 mFile.seek(offset); 414 mFile.readFully(mBuffer, 0, mWordSize); 415 416 final int answer; 417 if (mEndian == ELFDATA2LSB) { 418 answer = mBuffer[1] << 8 | mBuffer[0]; 419 } else { 420 answer = mBuffer[0] << 8 | mBuffer[1]; 421 } 422 423 return answer; 424 } 425 426 private long readWord(long offset) throws IOException { 427 mFile.seek(offset); 428 mFile.readFully(mBuffer, 0, mWordSize); 429 430 int answer = 0; 431 if (mEndian == ELFDATA2LSB) { 432 for (int i = mWordSize - 1; i >= 0; i--) { 433 answer = (answer << 8) | (mBuffer[i] & 0xFF); 434 } 435 } else { 436 final int N = mWordSize - 1; 437 for (int i = 0; i <= N; i++) { 438 answer = (answer << 8) | mBuffer[i]; 439 } 440 } 441 442 return answer; 443 } 444 445 private String readString(long offset) throws IOException { 446 mFile.seek(offset); 447 mFile.readFully(mBuffer, 0, (int) Math.min(mBuffer.length, mFile.length() - offset)); 448 449 for (int i = 0; i < mBuffer.length; i++) { 450 if (mBuffer[i] == 0) { 451 return new String(mBuffer, 0, i); 452 } 453 } 454 455 return null; 456 } 457 458 private void readIdent() throws IOException { 459 mFile.seek(0); 460 mFile.readFully(mBuffer, 0, EI_NIDENT); 461 462 if ((mBuffer[0] != ELF_IDENT[0]) || (mBuffer[1] != ELF_IDENT[1]) 463 || (mBuffer[2] != ELF_IDENT[2]) || (mBuffer[3] != ELF_IDENT[3])) { 464 throw new IllegalArgumentException("Invalid ELF file: " + mPath); 465 } 466 467 int elfClass = mBuffer[EI_CLASS]; 468 if (elfClass == ELFCLASS32) { 469 mWordSize = 4; 470 mHalfWordSize = 2; 471 } else if (elfClass == ELFCLASS64) { 472 throw new IOException("Unsupported ELFCLASS64 file: " + mPath); 473 } else { 474 throw new IOException("Invalid ELF EI_CLASS: " + elfClass + ": " + mPath); 475 } 476 477 mEndian = mBuffer[EI_DATA]; 478 if (mEndian == ELFDATA2LSB) { 479 } else if (mEndian == ELFDATA2MSB) { 480 throw new IOException("Unsupported ELFDATA2MSB file: " + mPath); 481 } else { 482 throw new IOException("Invalid ELF EI_DATA: " + mEndian + ": " + mPath); 483 } 484 } 485 486 public Symbol getSymbol(String name) { 487 if ((mSymTabOffset == 0) && (mSymTabSize == 0)) { 488 return null; 489 } 490 491 if (mSymbols == null) { 492 mSymbols = new HashMap<String, Symbol>(); 493 try { 494 readSymbolTable(mSymbols, mStrTabOffset, mStrTabSize, mSymTabOffset, mSymTabSize); 495 } catch (IOException e) { 496 return null; 497 } 498 } 499 500 return mSymbols.get(name); 501 } 502 503 public Symbol getDynamicSymbol(String name) { 504 if ((mDynSymOffset == 0) && (mDynSymSize == 0)) { 505 return null; 506 } 507 508 if (mDynamicSymbols == null) { 509 mDynamicSymbols = new HashMap<String, Symbol>(); 510 try { 511 readSymbolTable(mDynamicSymbols, mDynStrOffset, mDynStrSize, mDynSymOffset, 512 mDynSymSize); 513 } catch (IOException e) { 514 return null; 515 } 516 } 517 518 return mDynamicSymbols.get(name); 519 } 520 } 521