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