Merge remote-tracking branch

'origin/GP-2363_dev747368_compressed_elf_sections--SQUASHED'
(Closes #3659)
This commit is contained in:
Ryan Kurtz
2023-04-13 09:00:47 -04:00
10 changed files with 484 additions and 83 deletions
@@ -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;
}
@@ -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;
@@ -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
@@ -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()));
}
}
/**
@@ -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.*/
@@ -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;