mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-23 13:16:48 +08:00
Merge remote-tracking branch
'origin/GP-2363_dev747368_compressed_elf_sections--SQUASHED' (Closes #3659)
This commit is contained in:
+2
-2
@@ -215,8 +215,8 @@ public class DWARFAnalyzer extends AbstractAnalyzer {
|
||||
"Manually re-run the DWARF analyzer after adjusting the options or start it via Dwarf_ExtractorScript");
|
||||
}
|
||||
catch (DWARFException | IOException e) {
|
||||
log.appendMsg("Error during DWARFAnalyzer import: " + e.getMessage());
|
||||
Msg.error(this, "Error during DWARFAnalyzer import: " + e.getMessage(), e);
|
||||
log.appendMsg("Error during DWARFAnalyzer import: " + e);
|
||||
Msg.error(this, "Error during DWARFAnalyzer import: ", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import ghidra.formats.gfilesystem.FSUtilities;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
* An InputStream wrapper that suppresses any {@link IOException}s thrown by the wrapped stream
|
||||
* and starts returning 0 value bytes for all subsequent reads.
|
||||
*/
|
||||
public class FaultTolerantInputStream extends InputStream {
|
||||
|
||||
private InputStream delegate;
|
||||
private long currentPosition;
|
||||
private long totalLength;
|
||||
private Throwable error;
|
||||
private long faultPosition;
|
||||
private long faultByteCount;
|
||||
private BiConsumer<String, Throwable> errorConsumer;
|
||||
|
||||
/**
|
||||
* Creates instance.
|
||||
*
|
||||
* @param delegate {@link InputStream} to wrap
|
||||
* @param length expected length of the stream
|
||||
* @param errorConsumer consumer that will accept errors, if null Msg.error() will be used
|
||||
*/
|
||||
public FaultTolerantInputStream(InputStream delegate, long length,
|
||||
BiConsumer<String, Throwable> errorConsumer) {
|
||||
this.delegate = delegate;
|
||||
this.totalLength = length;
|
||||
this.errorConsumer = errorConsumer != null ? errorConsumer : this::defaultErrorHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
FSUtilities.uncheckedClose(delegate, null);
|
||||
if (error != null) {
|
||||
errorConsumer.accept(
|
||||
"Errors encountered when reading at position %d, %d bytes faulted, replaced with 0's"
|
||||
.formatted(faultPosition, faultByteCount),
|
||||
error);
|
||||
}
|
||||
}
|
||||
|
||||
private void defaultErrorHandler(String msg, Throwable th) {
|
||||
Msg.error(this, msg, th);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
byte[] buffer = new byte[1];
|
||||
int bytesRead = read(buffer, 0, 1);
|
||||
return bytesRead == 1 ? Byte.toUnsignedInt(buffer[0]) : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (error == null) {
|
||||
// haven't hit an error yet, try to read normally
|
||||
try {
|
||||
int bytesRead = delegate.read(b, off, len);
|
||||
currentPosition += bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
catch (IOException e) {
|
||||
faultPosition = currentPosition;
|
||||
error = e;
|
||||
}
|
||||
}
|
||||
|
||||
// there was an error, return 0's from now on
|
||||
long remaining = totalLength - currentPosition;
|
||||
if (remaining <= 0) {
|
||||
return 0;
|
||||
}
|
||||
len = (int) Math.min(len, remaining);
|
||||
Arrays.fill(b, off, off + len, (byte) 0);
|
||||
currentPosition += len;
|
||||
faultByteCount += len;
|
||||
return len;
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,53 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Hashtable;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import ghidra.app.util.bin.format.elf.ElfLoadHelper;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
|
||||
/**
|
||||
* <code>MemoryLoadable</code> is a marker interface which identifies a file format
|
||||
* object which uniquely identifies a memory loadable portion of a binary file.
|
||||
* <code>MemoryLoadable</code> serves as both a marker interface which identifies a memory
|
||||
* loadable portion of a binary file (supports use as a {@link Hashtable} key). In addition,
|
||||
* it serves to supply the neccessary input stream to create a {@link MemoryBlock}.
|
||||
*
|
||||
*/
|
||||
public interface MemoryLoadable {
|
||||
|
||||
/**
|
||||
* Determine if the use of input stream decompression or filtering via an extension is neccessary.
|
||||
* If this method returns true and a
|
||||
* {@link #getFilteredLoadInputStream(ElfLoadHelper, Address, long, BiConsumer) filtered stream}
|
||||
* is required and will prevent the use of a direct mapping to file bytes for affected memory
|
||||
* regions.
|
||||
* @param elfLoadHelper ELF load helper
|
||||
* @param start memory load address
|
||||
* @return true if the use of a filtered input stream is required
|
||||
*/
|
||||
public boolean hasFilteredLoadInputStream(ElfLoadHelper elfLoadHelper, Address start);
|
||||
|
||||
/**
|
||||
* Return filtered InputStream for loading a memory block (includes non-loaded OTHER blocks).
|
||||
* See {@link #hasFilteredLoadInputStream(ElfLoadHelper, Address)}.
|
||||
* @param elfLoadHelper ELF load helper
|
||||
* @param start memory load address
|
||||
* @param dataLength the in-memory data length in bytes (actual bytes read from dataInput may be more)
|
||||
* @param errorConsumer consumer that will accept errors which may occur during stream
|
||||
* decompression, if null Msg.error() will be used.
|
||||
* @return filtered input stream or original input stream
|
||||
* @throws IOException if error initializing filtered input stream
|
||||
*/
|
||||
public InputStream getFilteredLoadInputStream(ElfLoadHelper elfLoadHelper, Address start,
|
||||
long dataLength, BiConsumer<String, Throwable> errorConsumer) throws IOException;
|
||||
|
||||
/**
|
||||
* {@return raw data input stream associated with this loadable object.}
|
||||
* @throws IOException if error initializing input stream
|
||||
*/
|
||||
public InputStream getRawInputStream() throws IOException;
|
||||
|
||||
}
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.bin.format.elf;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
|
||||
/**
|
||||
* Header at the beginning of an ELF compressed section.
|
||||
* <p>
|
||||
* See https://docs.oracle.com/cd/E53394_01/html/E54813/section_compression.html
|
||||
* <p>
|
||||
* <pre>
|
||||
* typedef struct {
|
||||
* Elf32_Word ch_type;
|
||||
* Elf32_Word ch_size;
|
||||
* Elf32_Word ch_addralign;
|
||||
* } Elf32_Chdr;
|
||||
*
|
||||
* typedef struct {
|
||||
* Elf64_Word ch_type;
|
||||
* Elf64_Word ch_reserved;
|
||||
* Elf64_Xword ch_size;
|
||||
* Elf64_Xword ch_addralign;
|
||||
* } Elf64_Chdr;
|
||||
* </pre>
|
||||
*/
|
||||
public class ElfCompressedSectionHeader {
|
||||
public static final int ELFCOMPRESS_ZLIB = 1;
|
||||
//public static final int ELFCOMPRESS_LOOS = 0x60000000;
|
||||
//public static final int ELFCOMPRESS_HIOS = 0x6fffffff;
|
||||
//public static final int ELFCOMPRESS_LOPROC = 0x70000000;
|
||||
//public static final int ELFCOMPRESS_HIPROC = 0x7fffffff;
|
||||
private static final int SIZEOF_HEADER_32 = 12; // sizeof(word)*3 fields;
|
||||
private static final int SIZEOF_HEADER_64 = 24; // sizeof(word)*2 fields + sizeof(xword)*2 fields
|
||||
|
||||
/**
|
||||
* Reads an Elf(32|64)_Chdr from the current position in the supplied stream.
|
||||
*
|
||||
* @param reader stream to read from
|
||||
* @param elf ElfHeader that defines the format of the binary
|
||||
* @return new {@link ElfCompressedSectionHeader} instance, never null
|
||||
* @throws IOException if error reading the header
|
||||
*/
|
||||
public static ElfCompressedSectionHeader read(BinaryReader reader, ElfHeader elf)
|
||||
throws IOException {
|
||||
return elf.is32Bit() ? read32(reader) : read64(reader);
|
||||
}
|
||||
|
||||
private int ch_type; // compression algo
|
||||
private long ch_size; // size, in bytes, of uncompressed data
|
||||
private long ch_addralign; // alignment of the uncompressed data, sh_addralign
|
||||
private int headerSize; // metadata about this header, used to skip the header when re-reading
|
||||
|
||||
private ElfCompressedSectionHeader(int type, long size, long align, int headerSize) {
|
||||
this.ch_type = type;
|
||||
this.ch_size = size;
|
||||
this.ch_addralign = align;
|
||||
this.headerSize = headerSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the compression type, see ELFCOMPRESS_ZLIB}
|
||||
*/
|
||||
public int getCh_type() {
|
||||
return ch_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the uncompressed size}
|
||||
*/
|
||||
public long getCh_size() {
|
||||
return ch_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the address alignment value}.
|
||||
* <p>
|
||||
* See {@link ElfSectionHeader#getAddressAlignment()}
|
||||
*/
|
||||
public long getCh_addralign() {
|
||||
return ch_addralign;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the size of this header struct}
|
||||
*/
|
||||
public int getHeaderSize() {
|
||||
return headerSize;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------------
|
||||
|
||||
private static ElfCompressedSectionHeader read32(BinaryReader reader) throws IOException {
|
||||
int type = reader.readNextInt();
|
||||
long size = reader.readNextUnsignedInt();
|
||||
long align = reader.readNextUnsignedInt();
|
||||
|
||||
return new ElfCompressedSectionHeader(type, size, align, SIZEOF_HEADER_32);
|
||||
}
|
||||
|
||||
private static ElfCompressedSectionHeader read64(BinaryReader reader) throws IOException {
|
||||
int type = reader.readNextInt();
|
||||
/*long unused_reserved = */ reader.readNextUnsignedInt();
|
||||
long size = reader.readNextLong();
|
||||
long align = reader.readNextLong();
|
||||
|
||||
return new ElfCompressedSectionHeader(type, size, align, SIZEOF_HEADER_64);
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,11 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.elf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.format.elf.ElfRelocationTable.TableFormat;
|
||||
import ghidra.app.util.bin.format.elf.extend.ElfExtensionFactory;
|
||||
|
||||
+32
-2
@@ -16,11 +16,13 @@
|
||||
package ghidra.app.util.bin.format.elf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.format.MemoryLoadable;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.util.StringUtilities;
|
||||
|
||||
@@ -264,6 +266,34 @@ public class ElfProgramHeader
|
||||
return header.getLoadAdapter().getAdjustedLoadSize(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFilteredLoadInputStream(ElfLoadHelper elfLoadHelper, Address start) {
|
||||
return header.getLoadAdapter().hasFilteredLoadInputStream(elfLoadHelper, this, start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getFilteredLoadInputStream(ElfLoadHelper elfLoadHelper, Address start,
|
||||
long dataLength, BiConsumer<String, Throwable> errorConsumer) throws IOException {
|
||||
return header.getLoadAdapter()
|
||||
.getFilteredLoadInputStream(elfLoadHelper, this, start, dataLength,
|
||||
getRawInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getRawInputStream() throws IOException {
|
||||
return getRawByteProvider().getInputStream(0);
|
||||
}
|
||||
|
||||
private ByteProvider getRawByteProvider() {
|
||||
if (reader == null) {
|
||||
throw new UnsupportedOperationException("This ElfProgramHeader does not have a reader");
|
||||
}
|
||||
if (p_filesz <= 0) {
|
||||
return ByteProvider.EMPTY_BYTEPROVIDER;
|
||||
}
|
||||
return new ByteProviderWrapper(reader.getByteProvider(), p_offset, p_filesz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the binary reader.
|
||||
* @return the binary reader
|
||||
|
||||
+125
-34
@@ -18,13 +18,16 @@ package ghidra.app.util.bin.format.elf;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import ghidra.app.util.bin.BinaryReader;
|
||||
import ghidra.app.util.bin.StructConverter;
|
||||
import ghidra.app.util.bin.*;
|
||||
import ghidra.app.util.bin.format.MemoryLoadable;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.StringUtilities;
|
||||
|
||||
/**
|
||||
@@ -89,6 +92,8 @@ public class ElfSectionHeader implements StructConverter, MemoryLoadable {
|
||||
private boolean modified = false;
|
||||
private boolean bytesChanged = false;
|
||||
|
||||
private ElfCompressedSectionHeader compressedHeader;
|
||||
|
||||
public ElfSectionHeader(BinaryReader reader, ElfHeader header)
|
||||
throws IOException {
|
||||
this.reader = reader;
|
||||
@@ -121,9 +126,40 @@ public class ElfSectionHeader implements StructConverter, MemoryLoadable {
|
||||
sh_addralign = reader.readNextLong();
|
||||
sh_entsize = reader.readNextLong();
|
||||
}
|
||||
|
||||
if ((sh_flags & ElfSectionHeaderConstants.SHF_COMPRESSED) != 0) {
|
||||
compressedHeader = readCompressedSectionHeader();
|
||||
}
|
||||
//checkSize();
|
||||
}
|
||||
|
||||
private ElfCompressedSectionHeader readCompressedSectionHeader() {
|
||||
try {
|
||||
if (!isValidForCompressed(reader.length())) {
|
||||
throw new IOException(
|
||||
"Invalid compressed section: %s".formatted(getNameAsString()));
|
||||
}
|
||||
ElfCompressedSectionHeader result =
|
||||
ElfCompressedSectionHeader.read(getRawSectionReader(), header);
|
||||
if (!isSupportedCompressionType(result.getCh_type())) {
|
||||
throw new IOException("Unknown ELF section compression type 0x%x for section %s"
|
||||
.formatted(compressedHeader.getCh_type(), getNameAsString()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.warn(this, "Error reading compressed section information: " + e);
|
||||
Msg.debug(this, "Error reading compressed section information", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isValidForCompressed(long streamLength) {
|
||||
long endOffset = sh_offset + sh_size;
|
||||
return !isAlloc() && sh_offset >= 0 && sh_size > 0 && endOffset > 0 &&
|
||||
endOffset <= streamLength;
|
||||
}
|
||||
|
||||
ElfSectionHeader(ElfHeader header, MemoryBlock block, int sh_name, long imageBase)
|
||||
throws MemoryAccessException {
|
||||
|
||||
@@ -201,7 +237,9 @@ public class ElfSectionHeader implements StructConverter, MemoryLoadable {
|
||||
* @return the section address alignment constraints
|
||||
*/
|
||||
public long getAddressAlignment() {
|
||||
return sh_addralign;
|
||||
return compressedHeader == null
|
||||
? sh_addralign
|
||||
: compressedHeader.getCh_addralign();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,6 +285,28 @@ public class ElfSectionHeader implements StructConverter, MemoryLoadable {
|
||||
return header.getLoadAdapter().isSectionAllocated(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this section is compressed in a supported manner. This does NOT include
|
||||
* sections that carry compressed data, such as ".zdebuginfo" type sections.
|
||||
*
|
||||
* @return true if the section was compressed and needs to be decompressed, false if normal
|
||||
* section
|
||||
*/
|
||||
public boolean isCompressed() {
|
||||
return compressedHeader != null;
|
||||
}
|
||||
|
||||
private boolean isSupportedCompressionType(int compressionType) {
|
||||
return switch ( compressionType ) {
|
||||
case ElfCompressedSectionHeader.ELFCOMPRESS_ZLIB -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isNoBits() {
|
||||
return sh_type == ElfSectionHeaderConstants.SHT_NOBITS;
|
||||
}
|
||||
|
||||
/**
|
||||
* This member holds extra information, whose interpretation
|
||||
* depends on the section type.
|
||||
@@ -382,14 +442,35 @@ public class ElfSectionHeader implements StructConverter, MemoryLoadable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the adjusted size of the section in bytes (i.e., memory block) which relates to this section header; it may be zero
|
||||
* if no block should be created. The returned value reflects any adjustment the ElfExtension may require
|
||||
* based upon the specific processor/language implementation which may require filtering of file bytes
|
||||
* as read into memory.
|
||||
* @return the number of bytes in the resulting memory block
|
||||
* Returns the logical size of this section, possibly affected by compression.
|
||||
*
|
||||
* @return logical size of this section, see {@link #getSize()}
|
||||
*/
|
||||
public long getAdjustedSize() {
|
||||
return header.getLoadAdapter().getAdjustedSize(this);
|
||||
public long getLogicalSize() {
|
||||
return compressedHeader == null
|
||||
? sh_size
|
||||
: compressedHeader.getCh_size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFilteredLoadInputStream(ElfLoadHelper elfLoadHelper, Address start) {
|
||||
return isCompressed() ||
|
||||
header.getLoadAdapter().hasFilteredLoadInputStream(elfLoadHelper, this, start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getFilteredLoadInputStream(ElfLoadHelper elfLoadHelper, Address start,
|
||||
long dataLength, BiConsumer<String, Throwable> errorConsumer) throws IOException {
|
||||
InputStream is = isCompressed()
|
||||
? getDecompressedDataStream(dataLength, errorConsumer)
|
||||
: getRawInputStream();
|
||||
return header.getLoadAdapter()
|
||||
.getFilteredLoadInputStream(elfLoadHelper, this, start, dataLength, is);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getRawInputStream() throws IOException {
|
||||
return getRawSectionByteProvider().getInputStream(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,36 +494,46 @@ public class ElfSectionHeader implements StructConverter, MemoryLoadable {
|
||||
return "SHT_0x" + StringUtilities.pad(Integer.toHexString(sh_type), '0', 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual data bytes from the file for this section
|
||||
* @return the actual data bytes from the file for this section
|
||||
* @throws IOException if an I/O error occurs while reading the file
|
||||
*/
|
||||
public byte[] getData() throws IOException {
|
||||
if (sh_type == ElfSectionHeaderConstants.SHT_NOBITS) {
|
||||
return new byte[0];
|
||||
private InputStream getDecompressedDataStream(long dataLength,
|
||||
BiConsumer<String, Throwable> errorConsumer) throws IOException {
|
||||
if (compressedHeader == null || dataLength != compressedHeader.getCh_size()) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
if (data != null) {
|
||||
return data;
|
||||
}
|
||||
if (reader == null) {
|
||||
throw new UnsupportedOperationException("This ElfSectionHeader does not have a reader");
|
||||
}
|
||||
return reader.readByteArray(sh_offset, (int) sh_size);
|
||||
|
||||
int skip = compressedHeader.getHeaderSize();
|
||||
InputStream is = getRawSectionByteProvider().getInputStream(skip);
|
||||
|
||||
is = getDecompressionStream(is);
|
||||
|
||||
return new FaultTolerantInputStream(is, compressedHeader.getCh_size(), errorConsumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an input stream starting at offset into
|
||||
* the byte provider.
|
||||
* NOTE: Do not use this method if you have called setData().
|
||||
* @return the input stream
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public InputStream getDataStream() throws IOException {
|
||||
private ByteProvider getRawSectionByteProvider() {
|
||||
if (reader == null) {
|
||||
throw new UnsupportedOperationException("This ElfSectionHeader does not have a reader");
|
||||
}
|
||||
return reader.getByteProvider().getInputStream(sh_offset);
|
||||
if (isNoBits()) {
|
||||
return ByteProvider.EMPTY_BYTEPROVIDER;
|
||||
}
|
||||
return new ByteProviderWrapper(reader.getByteProvider(), sh_offset, sh_size);
|
||||
}
|
||||
|
||||
private BinaryReader getRawSectionReader() throws IOException {
|
||||
return new BinaryReader(getRawSectionByteProvider(), header.isLittleEndian());
|
||||
}
|
||||
|
||||
private InputStream getDecompressionStream(InputStream compressedStream) throws IOException {
|
||||
switch (compressedHeader.getCh_type()) {
|
||||
case ElfCompressedSectionHeader.ELFCOMPRESS_ZLIB:
|
||||
Msg.debug(this,
|
||||
"Decompressing ELF section %s, original/decompressed size: 0x%x/0x%x"
|
||||
.formatted(getNameAsString(), sh_size, compressedHeader.getCh_size()));
|
||||
return new InflaterInputStream(compressedStream);
|
||||
default:
|
||||
throw new IOException("Unknown ELF section compression type 0x%x for section %s"
|
||||
.formatted(compressedHeader.getCh_type(), getNameAsString()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
@@ -141,6 +141,8 @@ public class ElfSectionHeaderConstants {
|
||||
public static final int SHF_GROUP = (1 << 9);
|
||||
/**The section that holds thread-local data.*/
|
||||
public static final int SHF_TLS = (1 << 10);
|
||||
/**The bytes of the section are compressed */
|
||||
public static final int SHF_COMPRESSED = (1 << 11);
|
||||
/**This section is excluded from the final executable or shared library.*/
|
||||
public static final int SHF_EXCLUDE = 0x80000000;
|
||||
/**The section contains OS-specific data.*/
|
||||
|
||||
+13
-4
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package ghidra.app.util.bin.format.elf.extend;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
@@ -463,13 +464,19 @@ public class ElfLoadAdapter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the memory section size in bytes for the specified section header.
|
||||
* The returned value will be consistent with any byte filtering which may be required.
|
||||
* Returns the memory section size in bytes for the specified section header.
|
||||
* <p>
|
||||
* The returned value will be consistent with any byte filtering and decompression which
|
||||
* may be required.
|
||||
* <p>
|
||||
* The default implementation returns the section's
|
||||
* {@link ElfSectionHeader#getLogicalSize() logical size}
|
||||
*
|
||||
* @param section the section header
|
||||
* @return preferred memory block size in bytes which corresponds to the specified section header
|
||||
*/
|
||||
public long getAdjustedSize(ElfSectionHeader section) {
|
||||
return section.getSize();
|
||||
return section.getLogicalSize();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -482,9 +489,11 @@ public class ElfLoadAdapter {
|
||||
* @param dataLength the in-memory data length in bytes (actual bytes read from dataInput may be more)
|
||||
* @param dataInput the source input stream
|
||||
* @return filtered input stream or original input stream
|
||||
* @throws IOException if error initializing filtered stream
|
||||
*/
|
||||
public InputStream getFilteredLoadInputStream(ElfLoadHelper elfLoadHelper,
|
||||
MemoryLoadable loadable, Address start, long dataLength, InputStream dataInput) {
|
||||
MemoryLoadable loadable, Address start, long dataLength, InputStream dataInput)
|
||||
throws IOException {
|
||||
return dataInput;
|
||||
}
|
||||
|
||||
|
||||
@@ -329,18 +329,18 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elf.getLoadAdapter().hasFilteredLoadInputStream(this, loadable, start)) {
|
||||
byte[] bytes = new byte[(int) length];
|
||||
int bytesRead;
|
||||
if (loadable.hasFilteredLoadInputStream(this, start)) {
|
||||
// block is unable to map directly to file bytes - read from filtered input stream
|
||||
try (InputStream dataInput =
|
||||
getInitializedBlockInputStream(loadable, start, fileOffset, length)) {
|
||||
byte[] bytes = new byte[(int) length];
|
||||
return dataInput.read(bytes) == bytes.length && isZeroedArray(bytes, bytes.length);
|
||||
try (InputStream is = loadable.getFilteredLoadInputStream(this, start, length, null)) {
|
||||
bytesRead = is.read(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[(int) length];
|
||||
return fileBytes.getModifiedBytes(fileOffset, bytes) == bytes.length &&
|
||||
isZeroedArray(bytes, bytes.length);
|
||||
else {
|
||||
bytesRead = fileBytes.getModifiedBytes(fileOffset, bytes);
|
||||
}
|
||||
return bytesRead == length && isZeroedArray(bytes, bytes.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -3332,7 +3332,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
||||
if (elfSectionToLoad.isAlloc() && addr != 0) {
|
||||
AddressSpace loadSpace = getSectionAddressSpace(elfSectionToLoad);
|
||||
if (loadSpace.equals(space)) {
|
||||
long sectionByteLength = elfSectionToLoad.getAdjustedSize(); // size in bytes
|
||||
long sectionByteLength = elf.getLoadAdapter().getAdjustedSize(elfSectionToLoad); // size in bytes
|
||||
long sectionLength = sectionByteLength / space.getAddressableUnitSize();
|
||||
relocStartAddr = Math.max(relocStartAddr, addr + sectionLength);
|
||||
}
|
||||
@@ -3399,7 +3399,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
||||
throws AddressOutOfBoundsException {
|
||||
|
||||
long addr = elfSectionToLoad.getAddress();
|
||||
long sectionByteLength = elfSectionToLoad.getAdjustedSize(); // size in bytes
|
||||
long sectionByteLength = elf.getLoadAdapter().getAdjustedSize(elfSectionToLoad); // size in bytes
|
||||
long loadOffset = elfSectionToLoad.getOffset(); // file offset in bytes
|
||||
Long nextRelocOffset = null;
|
||||
|
||||
@@ -3569,22 +3569,6 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
||||
return sym;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a suitable input stream for loading a memory block defined by a specified loadable.
|
||||
* @param loadable Corresponding ElfSectionHeader or ElfProgramHeader for the memory block to be created.
|
||||
* @param start memory load address
|
||||
* @param fileOffset byte provider offset
|
||||
* @param dataLength the in-memory data length in bytes (actual bytes read from dataInput may be more)
|
||||
* @return input stream for loading memory block
|
||||
* @throws IOException if failed to obtain input stream
|
||||
*/
|
||||
private InputStream getInitializedBlockInputStream(MemoryLoadable loadable, Address start,
|
||||
long fileOffset, long dataLength) throws IOException {
|
||||
InputStream dataInput = elf.getByteProvider().getInputStream(fileOffset);
|
||||
return elf.getLoadAdapter()
|
||||
.getFilteredLoadInputStream(this, loadable, start, dataLength, dataInput);
|
||||
}
|
||||
|
||||
private String formatFloat(float value, int maxDecimalPlaces) {
|
||||
NumberFormat format = NumberFormat.getNumberInstance();
|
||||
format.setMaximumIntegerDigits(maxDecimalPlaces);
|
||||
@@ -3642,26 +3626,40 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
||||
// Msg.debug(this,
|
||||
// "Loading block " + name + " at " + start + " from file offset " + fileOffset);
|
||||
|
||||
long endOffset = fileOffset + revisedLength - 1;
|
||||
if (endOffset >= fileBytes.getSize()) {
|
||||
long compSectOrigSize = loadable instanceof ElfSectionHeader header && header.isCompressed()
|
||||
? header.getSize()
|
||||
: -1;
|
||||
|
||||
String blockComment = comment;
|
||||
if (compSectOrigSize >= 0) {
|
||||
blockComment +=
|
||||
" (decompressed, original length: 0x%x)".formatted(compSectOrigSize);
|
||||
}
|
||||
else if ((fileOffset + revisedLength - 1) >= fileBytes.getSize()) {
|
||||
// ensure valid length for non-compressed items
|
||||
revisedLength = fileBytes.getSize() - fileOffset;
|
||||
log("Truncating block load for " + name + " which exceeds file length");
|
||||
}
|
||||
|
||||
String blockComment = comment;
|
||||
if (dataLength != revisedLength) {
|
||||
// either gt MAX_BINARY_SIZE or gt fileBytes size
|
||||
blockComment += " (section truncated)";
|
||||
}
|
||||
|
||||
MemoryBlock block = null;
|
||||
try {
|
||||
if (elf.getLoadAdapter().hasFilteredLoadInputStream(this, loadable, start)) {
|
||||
if (loadable != null && loadable.hasFilteredLoadInputStream(this, start)) {
|
||||
// block is unable to map directly to file bytes - load from input stream
|
||||
try (InputStream dataInput =
|
||||
getInitializedBlockInputStream(loadable, start, fileOffset, revisedLength)) {
|
||||
try (InputStream is =
|
||||
loadable.getFilteredLoadInputStream(this, start, revisedLength,
|
||||
(errorMsg, th) -> {
|
||||
String loadableTypeStr =
|
||||
compSectOrigSize >= 0 ? "compressed section " : "";
|
||||
log.appendMsg("Error when reading %s[%s]: %s".formatted(loadableTypeStr,
|
||||
name, errorMsg));
|
||||
Msg.error(this, errorMsg, th);
|
||||
})) {
|
||||
block = MemoryBlockUtils.createInitializedBlock(program, isOverlay, name, start,
|
||||
dataInput, revisedLength, blockComment, BLOCK_SOURCE_NAME, r, w, x, log,
|
||||
monitor);
|
||||
is, revisedLength, blockComment, BLOCK_SOURCE_NAME, r, w, x, log, monitor);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -3674,8 +3672,8 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper {
|
||||
finally {
|
||||
if (block == null) {
|
||||
Address end = start.addNoWrap(revisedLength - 1);
|
||||
log("Unexpected ELF memory bock load conflict when creating '" + name + "' at " +
|
||||
start.toString(true) + "-" + end.toString(true));
|
||||
log("Unexpected ELF memory block load conflict when creating '" + name +
|
||||
"' at " + start.toString(true) + "-" + end.toString(true));
|
||||
}
|
||||
}
|
||||
return block;
|
||||
|
||||
Reference in New Issue
Block a user