Files
react-native/ReactAndroid/src/main/java/com/facebook/soloader/MinElf.java
Martin Konicek 42eb5464fd Release React Native for Android
This is an early release and there are several things that are known
not to work if you're porting your iOS app to Android.

See the Known Issues guide on the website.

We will work with the community to reach platform parity with iOS.
2015-09-14 18:13:39 +01:00

283 lines
7.8 KiB
Java

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.soloader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
/**
* Extract SoLoader boottsrap information from an ELF file. This is not a general purpose ELF
* library.
*
* See specification at http://www.sco.com/developers/gabi/latest/contents.html. You will not be
* able to verify the operation of the functions below without having read the ELF specification.
*/
public final class MinElf {
public static final int ELF_MAGIC = 0x464c457f;
public static final int DT_NULL = 0;
public static final int DT_NEEDED = 1;
public static final int DT_STRTAB = 5;
public static final int PT_LOAD = 1;
public static final int PT_DYNAMIC = 2;
public static final int PN_XNUM = 0xFFFF;
public static String[] extract_DT_NEEDED(File elfFile) throws IOException {
FileInputStream is = new FileInputStream(elfFile);
try {
return extract_DT_NEEDED(is.getChannel());
} finally {
is.close(); // Won't throw
}
}
/**
* Treating {@code bb} as an ELF file, extract all the DT_NEEDED entries from its dynamic section.
*
* @param fc FileChannel referring to ELF file
* @return Array of strings, one for each DT_NEEDED entry, in file order
*/
public static String[] extract_DT_NEEDED(FileChannel fc)
throws IOException {
//
// All constants below are fixed by the ELF specification and are the offsets of fields within
// the elf.h data structures.
//
ByteBuffer bb = ByteBuffer.allocate(8 /* largest read unit */);
// Read ELF header.
bb.order(ByteOrder.LITTLE_ENDIAN);
if (getu32(fc, bb, Elf32_Ehdr.e_ident) != ELF_MAGIC) {
throw new ElfError("file is not ELF");
}
boolean is32 = (getu8(fc, bb, Elf32_Ehdr.e_ident + 0x4) == 1);
if (getu8(fc, bb, Elf32_Ehdr.e_ident + 0x5) == 2) {
bb.order(ByteOrder.BIG_ENDIAN);
}
// Offsets above are identical in 32- and 64-bit cases.
// Find the offset of the dynamic linking information.
long e_phoff = is32
? getu32(fc, bb, Elf32_Ehdr.e_phoff)
: get64(fc, bb, Elf64_Ehdr.e_phoff);
long e_phnum = is32
? getu16(fc, bb, Elf32_Ehdr.e_phnum)
: getu16(fc, bb, Elf64_Ehdr.e_phnum);
int e_phentsize = is32
? getu16(fc, bb, Elf32_Ehdr.e_phentsize)
: getu16(fc, bb, Elf64_Ehdr.e_phentsize);
if (e_phnum == PN_XNUM) { // Overflowed into section[0].sh_info
long e_shoff = is32
? getu32(fc, bb, Elf32_Ehdr.e_shoff)
: get64(fc, bb, Elf64_Ehdr.e_shoff);
long sh_info = is32
? getu32(fc, bb, e_shoff + Elf32_Shdr.sh_info)
: getu32(fc, bb, e_shoff + Elf64_Shdr.sh_info);
e_phnum = sh_info;
}
long dynStart = 0;
long phdr = e_phoff;
for (long i = 0; i < e_phnum; ++i) {
long p_type = is32
? getu32(fc, bb, phdr + Elf32_Phdr.p_type)
: getu32(fc, bb, phdr + Elf64_Phdr.p_type);
if (p_type == PT_DYNAMIC) {
long p_offset = is32
? getu32(fc, bb, phdr + Elf32_Phdr.p_offset)
: get64(fc, bb, phdr + Elf64_Phdr.p_offset);
dynStart = p_offset;
break;
}
phdr += e_phentsize;
}
if (dynStart == 0) {
throw new ElfError("ELF file does not contain dynamic linking information");
}
// Walk the items in the dynamic section, counting the DT_NEEDED entries. Also remember where
// the string table for those entries lives. That table is a pointer, which we translate to an
// offset below.
long d_tag;
int nr_DT_NEEDED = 0;
long dyn = dynStart;
long ptr_DT_STRTAB = 0;
do {
d_tag = is32
? getu32(fc, bb, dyn + Elf32_Dyn.d_tag)
: get64(fc, bb, dyn + Elf64_Dyn.d_tag);
if (d_tag == DT_NEEDED) {
if (nr_DT_NEEDED == Integer.MAX_VALUE) {
throw new ElfError("malformed DT_NEEDED section");
}
nr_DT_NEEDED += 1;
} else if (d_tag == DT_STRTAB) {
ptr_DT_STRTAB = is32
? getu32(fc, bb, dyn + Elf32_Dyn.d_un)
: get64(fc, bb, dyn + Elf64_Dyn.d_un);
}
dyn += is32 ? 8 : 16;
} while (d_tag != DT_NULL);
if (ptr_DT_STRTAB == 0) {
throw new ElfError("Dynamic section string-table not found");
}
// Translate the runtime string table pointer we found above to a file offset.
long off_DT_STRTAB = 0;
phdr = e_phoff;
for (int i = 0; i < e_phnum; ++i) {
long p_type = is32
? getu32(fc, bb, phdr + Elf32_Phdr.p_type)
: getu32(fc, bb, phdr + Elf64_Phdr.p_type);
if (p_type == PT_LOAD) {
long p_vaddr = is32
? getu32(fc, bb, phdr + Elf32_Phdr.p_vaddr)
: get64(fc, bb, phdr + Elf64_Phdr.p_vaddr);
long p_memsz = is32
? getu32(fc, bb, phdr + Elf32_Phdr.p_memsz)
: get64(fc, bb, phdr + Elf64_Phdr.p_memsz);
if (p_vaddr <= ptr_DT_STRTAB && ptr_DT_STRTAB < p_vaddr + p_memsz) {
long p_offset = is32
? getu32(fc, bb, phdr + Elf32_Phdr.p_offset)
: get64(fc, bb, phdr + Elf64_Phdr.p_offset);
off_DT_STRTAB = p_offset + (ptr_DT_STRTAB - p_vaddr);
break;
}
}
phdr += e_phentsize;
}
if (off_DT_STRTAB == 0) {
throw new ElfError("did not find file offset of DT_STRTAB table");
}
String[] needed = new String[nr_DT_NEEDED];
nr_DT_NEEDED = 0;
dyn = dynStart;
do {
d_tag = is32
? getu32(fc, bb, dyn + Elf32_Dyn.d_tag)
: get64(fc, bb, dyn + Elf64_Dyn.d_tag);
if (d_tag == DT_NEEDED) {
long d_val = is32
? getu32(fc, bb, dyn + Elf32_Dyn.d_un)
: get64(fc, bb, dyn + Elf64_Dyn.d_un);
needed[nr_DT_NEEDED] = getSz(fc, bb, off_DT_STRTAB + d_val);
if (nr_DT_NEEDED == Integer.MAX_VALUE) {
throw new ElfError("malformed DT_NEEDED section");
}
nr_DT_NEEDED += 1;
}
dyn += is32 ? 8 : 16;
} while (d_tag != DT_NULL);
if (nr_DT_NEEDED != needed.length) {
throw new ElfError("malformed DT_NEEDED section");
}
return needed;
}
private static String getSz(FileChannel fc, ByteBuffer bb, long offset)
throws IOException {
StringBuilder sb = new StringBuilder();
short b;
while ((b = getu8(fc, bb, offset++)) != 0) {
sb.append((char) b);
}
return sb.toString();
}
private static void read(FileChannel fc, ByteBuffer bb, int sz, long offset)
throws IOException {
bb.position(0);
bb.limit(sz);
if (fc.read(bb, offset) != sz) {
throw new ElfError("ELF file truncated");
}
bb.position(0);
}
private static long get64(FileChannel fc, ByteBuffer bb, long offset)
throws IOException {
read(fc, bb, 8, offset);
return bb.getLong();
}
private static long getu32(FileChannel fc, ByteBuffer bb, long offset)
throws IOException {
read(fc, bb, 4, offset);
return bb.getInt() & 0xFFFFFFFFL; // signed -> unsigned
}
private static int getu16(FileChannel fc, ByteBuffer bb, long offset)
throws IOException {
read(fc, bb, 2, offset);
return bb.getShort() & (int) 0xFFFF; // signed -> unsigned
}
private static short getu8(FileChannel fc, ByteBuffer bb, long offset)
throws IOException {
read(fc, bb, 1, offset);
return (short) (bb.get() & 0xFF); // signed -> unsigned
}
private static class ElfError extends RuntimeException {
ElfError(String why) {
super(why);
}
}
}