diff --git a/src/main/java/net/fornwall/jelf/ElfDynamicStructure.java b/src/main/java/net/fornwall/jelf/ElfDynamicStructure.java index 2fdeb24..2365fd2 100644 --- a/src/main/java/net/fornwall/jelf/ElfDynamicStructure.java +++ b/src/main/java/net/fornwall/jelf/ElfDynamicStructure.java @@ -140,7 +140,7 @@ public String toString() { } } - public ElfDynamicStructure(ElfParser parser, long offset, int size) { + public ElfDynamicStructure(final ElfParser parser, long offset, int size) { parser.seek(offset); int numEntries = size / 8; @@ -150,7 +150,7 @@ public ElfDynamicStructure(ElfParser parser, long offset, int size) { // necessary DT_STRSZ is read. loop: for (int i = 0; i < numEntries; i++) { long d_tag = parser.readIntOrLong(); - long d_val_or_ptr = parser.readIntOrLong(); + final long d_val_or_ptr = parser.readIntOrLong(); entries.add(new ElfDynamicSectionEntry(d_tag, d_val_or_ptr)); switch ((int) d_tag) { case DT_NULL: diff --git a/src/main/java/net/fornwall/jelf/ElfFile.java b/src/main/java/net/fornwall/jelf/ElfFile.java index 2582cc0..85eb663 100644 --- a/src/main/java/net/fornwall/jelf/ElfFile.java +++ b/src/main/java/net/fornwall/jelf/ElfFile.java @@ -6,6 +6,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.MappedByteBuffer; /** * An ELF (Executable and Linkable Format) file can be a relocatable, executable, shared or core file. @@ -290,11 +291,78 @@ public static ElfFile fromFile(File file) throws ElfException, IOException { public static ElfFile fromBytes(byte[] buffer) throws ElfException, IOException { return new ElfFile(new ByteArrayInputStream(buffer)); } + public ElfFile(MappedByteBuffer buffer, long startPosition) throws ElfException, IOException { + final ElfParser parser = new ElfParser(this, buffer, startPosition); + + //Parsing is a shitty thing to do in constructors. + byte[] ident = new byte[16]; + int bytesRead = parser.read(ident); + if (bytesRead != ident.length) + throw new ElfException("Error reading elf header (read " + bytesRead + "bytes - expected to read " + ident.length + "bytes)"); + + if (!(0x7f == ident[0] && 'E' == ident[1] && 'L' == ident[2] && 'F' == ident[3])) throw new ElfException("Bad magic number for file"); + + objectSize = ident[4]; + if (!(objectSize == CLASS_32 || objectSize == CLASS_64)) throw new ElfException("Invalid object size class: " + objectSize); + encoding = ident[5]; + if (!(encoding == DATA_LSB || encoding == DATA_MSB)) throw new ElfException("Invalid encoding: " + encoding); + int elfVersion = ident[6]; + if (elfVersion != 1) throw new ElfException("Invalid elf version: " + elfVersion); + // ident[7]; // EI_OSABI, target operating system ABI + // ident[8]; // EI_ABIVERSION, ABI version. Linux kernel (after at least 2.6) has no definition of it. + // ident[9-15] // EI_PAD, currently unused. + + file_type = parser.readShort(); + arch = parser.readShort(); + version = parser.readInt(); + entry_point = parser.readIntOrLong(); + ph_offset = parser.readIntOrLong(); + sh_offset = parser.readIntOrLong(); + flags = parser.readInt(); + eh_size = parser.readShort(); + ph_entry_size = parser.readShort(); + num_ph = parser.readShort(); + sh_entry_size = parser.readShort(); + num_sh = parser.readShort(); + if (num_sh == 0) { + throw new ElfException("e_shnum is SHN_UNDEF(0), which is not supported yet" + + " (the actual number of section header table entries is contained in the sh_size field of the section header at index 0)"); + } + sh_string_ndx = parser.readShort(); + if (sh_string_ndx == /* SHN_XINDEX= */0xffff) { + throw new ElfException("e_shstrndx is SHN_XINDEX(0xffff), which is not supported yet" + + " (the actual index of the section name string table section is contained in the sh_link field of the section header at index 0)"); + } + + sectionHeaders = MemoizedObject.uncheckedArray(num_sh); + for (int i = 0; i < num_sh; i++) { + final long sectionHeaderOffset = sh_offset + (i * sh_entry_size); + sectionHeaders[i] = new MemoizedObject() { + @Override + public ElfSection computeValue() throws ElfException, IOException { + return new ElfSection(parser, sectionHeaderOffset); + } + }; + } + + programHeaders = MemoizedObject.uncheckedArray(num_ph); + for (int i = 0; i < num_ph; i++) { + final long programHeaderOffset = ph_offset + (i * ph_entry_size); + programHeaders[i] = new MemoizedObject() { + @Override + public ElfSegment computeValue() throws IOException { + return new ElfSegment(parser, programHeaderOffset); + } + }; + } + + } + public ElfFile(ByteArrayInputStream baos) throws ElfException, IOException { - byte[] ident = new byte[16]; final ElfParser parser = new ElfParser(this, baos); + byte[] ident = new byte[16]; int bytesRead = parser.read(ident); if (bytesRead != ident.length) throw new ElfException("Error reading elf header (read " + bytesRead + "bytes - expected to read " + ident.length + "bytes)"); diff --git a/src/main/java/net/fornwall/jelf/ElfNote.java b/src/main/java/net/fornwall/jelf/ElfNote.java new file mode 100644 index 0000000..cce2724 --- /dev/null +++ b/src/main/java/net/fornwall/jelf/ElfNote.java @@ -0,0 +1,54 @@ +package net.fornwall.jelf; + +import java.io.IOException; + +class ElfNote { + private int nameSize; + private int descSize; + private int type; + private String name; + private String desc; + private byte[] descBytes; + ElfNote(ElfParser parser, long offset, int size) throws ElfException, IOException { + parser.seek(offset); + nameSize = parser.readInt(); + descSize = parser.readInt(); + type = parser.readInt(); + byte nameBytes[] = new byte[nameSize]; + descBytes = new byte[descSize]; + int bytesRead = parser.read(nameBytes); + if (bytesRead != nameSize) { + throw new ElfException("Error reading note (read " + bytesRead + "bytes - expected to " + "read " + nameSize + "bytes)"); + } + while (bytesRead % 4 != 0) { // finish reading the padding to the nearest 4 bytes + parser.readUnsignedByte(); + bytesRead += 1; + } + bytesRead = parser.read(descBytes); + if (bytesRead != descSize) { + throw new ElfException("Error reading note (read " + bytesRead + "bytes - expected to " + "read " + descSize + "bytes)"); + } + while (bytesRead % 4 != 0) { // finish reading the padding to the nearest 4 bytes + parser.readUnsignedByte(); + bytesRead += 1; + } + name = new String(nameBytes, 0, nameSize-1); // unnecessary trailing 0 + desc = new String(descBytes, 0, descSize); // There's no trailing 0 on desc + } + + String getName() { + return name; + } + + int getType() { + return type; + } + + String getDesc() { + return desc; + } + + byte[] getDescBytes() { + return descBytes; + } +} diff --git a/src/main/java/net/fornwall/jelf/ElfParser.java b/src/main/java/net/fornwall/jelf/ElfParser.java index 7c843ce..cf286ae 100644 --- a/src/main/java/net/fornwall/jelf/ElfParser.java +++ b/src/main/java/net/fornwall/jelf/ElfParser.java @@ -2,6 +2,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.nio.MappedByteBuffer; /** Package internal class used for parsing ELF files. */ class ElfParser { @@ -9,14 +10,33 @@ class ElfParser { final ElfFile elfFile; private final ByteArrayInputStream fsFile; + private final MappedByteBuffer mappedByteBuffer; + private final long mbbStartPosition; + + ElfParser(ElfFile elfFile, ByteArrayInputStream fsFile) { this.elfFile = elfFile; this.fsFile = fsFile; + mappedByteBuffer = null; + mbbStartPosition = -1; + } + + ElfParser(ElfFile elfFile, MappedByteBuffer byteBuffer, long mbbStartPos) { + this.elfFile = elfFile; + mappedByteBuffer = byteBuffer; + mbbStartPosition = mbbStartPos; + mappedByteBuffer.position((int)mbbStartPosition); + fsFile = null; } public void seek(long offset) { - fsFile.reset(); - if (fsFile.skip(offset) != offset) throw new ElfException("seeking outside file"); + if (fsFile != null) { + fsFile.reset(); + if (fsFile.skip(offset) != offset) throw new ElfException("seeking outside file"); + } + else if (mappedByteBuffer != null) { + mappedByteBuffer.position((int)(mbbStartPosition + offset)); // we may be limited to sub-4GB mapped filess + } } /** @@ -35,7 +55,14 @@ long byteSwap(long arg) { } short readUnsignedByte() { - int val = fsFile.read(); + int val = -1; + if (fsFile != null) { + val = fsFile.read(); + } else if (mappedByteBuffer != null) { + byte temp = mappedByteBuffer.get(); + val = temp & 0xFF; // bytes are signed in Java =_= so assigning them to a longer type risks sign extension. + } + if (val < 0) throw new ElfException("Trying to read outside file"); return (short) val; } @@ -111,7 +138,13 @@ long virtualMemoryAddrToFileOffset(long address) throws IOException { } public int read(byte[] data) throws IOException { - return fsFile.read(data); + if (fsFile != null) { + return fsFile.read(data); + } else if (mappedByteBuffer != null) { + mappedByteBuffer.get(data); + return data.length; + } + throw new IOException("No way to read from file or buffer"); } } diff --git a/src/main/java/net/fornwall/jelf/ElfSection.java b/src/main/java/net/fornwall/jelf/ElfSection.java index 841f4ab..cb5540a 100644 --- a/src/main/java/net/fornwall/jelf/ElfSection.java +++ b/src/main/java/net/fornwall/jelf/ElfSection.java @@ -125,11 +125,12 @@ public final class ElfSection { private MemoizedObject hashTable; /** For the {@link #SHT_DYNAMIC} ".dynamic" structure. */ private MemoizedObject dynamicStructure; + private MemoizedObject note; private final ElfFile elfHeader; /** Reads the section header information located at offset. */ - ElfSection(ElfParser parser, long offset) { + ElfSection(final ElfParser parser, long offset) { this.elfHeader = parser.elfFile; parser.seek(offset); @@ -190,6 +191,12 @@ protected ElfDynamicStructure computeValue() throws ElfException, IOException { }; break; case ElfSection.SHT_NOTE: + note = new MemoizedObject() { + @Override + protected ElfNote computeValue() throws ElfException, IOException { + return new ElfNote(parser, section_offset, (int)size); + } + }; break; case ElfSection.SHT_NOBITS: break; @@ -220,6 +227,9 @@ public ElfStringTable getStringTable() throws IOException { public ElfDynamicStructure getDynamicSection() throws IOException { return (dynamicStructure != null) ? dynamicStructure.getValue() : null; } + public ElfNote getNote() throws IOException { + return (note != null) ? note.getValue() : null; + } /** * Returns the hash table for this section or null if one does not exist. NOTE: currently the ELFHashTable does not diff --git a/src/main/java/net/fornwall/jelf/ElfSegment.java b/src/main/java/net/fornwall/jelf/ElfSegment.java index ae3abfd..112251f 100644 --- a/src/main/java/net/fornwall/jelf/ElfSegment.java +++ b/src/main/java/net/fornwall/jelf/ElfSegment.java @@ -70,7 +70,7 @@ public class ElfSegment { private MemoizedObject ptInterpreter; - ElfSegment(ElfParser parser, long offset) { + ElfSegment(final ElfParser parser, long offset) { parser.seek(offset); if (parser.elfFile.objectSize == ElfFile.CLASS_32) { // typedef struct {