diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/ElfBinaryAnalysisCommand.java b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/ElfBinaryAnalysisCommand.java index f3481ac1a7..8e88d655d0 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/ElfBinaryAnalysisCommand.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/cmd/formats/ElfBinaryAnalysisCommand.java @@ -213,7 +213,7 @@ public class ElfBinaryAnalysisCommand extends FlatProgramAPI private void processProgramHeaders(ElfHeader elf, Listing listing) throws Exception { - int headerCount = elf.e_phnum(); + int headerCount = elf.getProgramHeaderCount(); int size = elf.e_phentsize() * headerCount; if (size == 0) { return; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfConstants.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfConstants.java index 6f89d99c02..a78be26c37 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfConstants.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfConstants.java @@ -512,4 +512,10 @@ public interface ElfConstants { /** used by NetBSD/avr32 - AVR 32-bit */ public static final short EM_AVR32_unofficial = 0x18ad; + /** + * PN_XNUM: Used by e_phnum field to signal alternate storage of program header count + * within section[0] sh_info field. + */ + public static final short PN_XNUM = (short) 0xffff; + } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java index 8670f3fbd4..470fdbc43b 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfDefaultGotPltMarkup.java @@ -60,7 +60,7 @@ public class ElfDefaultGotPltMarkup { } public void process(TaskMonitor monitor) throws CancelledException { - if (elf.e_shnum() == 0) { + if (elf.getSectionHeaderCount() == 0) { processDynamicPLTGOT(ElfDynamicType.DT_PLTGOT, ElfDynamicType.DT_JMPREL, monitor); } else { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfHeader.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfHeader.java index 15b11988db..3506725254 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfHeader.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/elf/ElfHeader.java @@ -68,9 +68,9 @@ public class ElfHeader implements StructConverter, Writeable { private int e_flags; //processor-specific flags private short e_ehsize; //elf header size private short e_phentsize; //size of entries in the program header table - private short e_phnum; //number of enties in the program header table + private int e_phnum; //number of enties in the program header table (may not be preserved) private short e_shentsize; //size of entries in the section header table - private short e_shnum; //number of enties in the section header table + private int e_shnum; //number of enties in the section header table (may not be preserved) private short e_shstrndx; //section index of the section name string table private Structure headerStructure; @@ -78,6 +78,7 @@ public class ElfHeader implements StructConverter, Writeable { private boolean parsed = false; private boolean parsedSectionHeaders = false; + private ElfSectionHeader section0 = null; private ElfSectionHeader[] sectionHeaders = new ElfSectionHeader[0]; private ElfProgramHeader[] programHeaders = new ElfProgramHeader[0]; private ElfStringTable[] stringTables = new ElfStringTable[0]; @@ -161,9 +162,9 @@ public class ElfHeader implements StructConverter, Writeable { e_version = reader.readNextInt(); if (is32Bit()) { - e_entry = reader.readNextInt() & 0xffffffffL; - e_phoff = reader.readNextInt() & 0xffffffffL; - e_shoff = reader.readNextInt() & 0xffffffffL; + e_entry = reader.readNextUnsignedInt(); + e_phoff = reader.readNextUnsignedInt(); + e_shoff = reader.readNextUnsignedInt(); } else if (is64Bit()) { e_entry = reader.readNextLong(); @@ -178,23 +179,70 @@ public class ElfHeader implements StructConverter, Writeable { e_flags = reader.readNextInt(); e_ehsize = reader.readNextShort(); + e_phentsize = reader.readNextShort(); - e_phnum = reader.readNextShort(); - if (e_phnum < 0) { - e_phnum = 0; // protect against stripped program headers - } + e_phnum = reader.readNextUnsignedShort(); + e_shentsize = reader.readNextShort(); - e_shnum = reader.readNextShort(); - if (e_shnum < 0) { - e_shnum = 0; // protect against stripped section headers (have seen -1) - } + e_shnum = reader.readNextUnsignedShort(); + e_shstrndx = reader.readNextShort(); + + if (e_shnum >= Short.toUnsignedInt(ElfSectionHeaderConstants.SHN_LORESERVE)) { + e_shnum = readExtendedSectionHeaderCount(); // use extended stored section header count + } + + if (e_phnum == Short.toUnsignedInt(ElfConstants.PN_XNUM)) { + e_phnum = readExtendedProgramHeaderCount(); // use extended stored program header count + } } catch (IOException e) { throw new ElfException(e); } } + private ElfSectionHeader getSection0() throws IOException { + if (section0 == null && e_shnum != 0) { + long index = e_shoff; + if (!providerContainsRegion(index, e_shentsize)) { + return null; + } + reader.setPointerIndex(index); + section0 = new ElfSectionHeader(reader, this); + } + return section0; + } + + /** + * Read extended program header count stored in first section header (ST_NULL) sh_info field value. + * Returned value is restricted to the range 0..0x7fffffff. + * @return extended program header count or 0 if not found or out of range + * @throws IOException if file IO error occurs + */ + private int readExtendedProgramHeaderCount() throws IOException { + ElfSectionHeader s = getSection0(); + if (s != null && s.getType() == ElfSectionHeaderConstants.SHT_NULL) { + long val = s.getInfo(); + return (val < 0 || val > Integer.MAX_VALUE) ? 0 : (int) val; + } + return 0; + } + + /** + * Read extended section header count stored in first section header (ST_NULL) sh_size field value. + * Returned value is restricted to the range 0..0x7fffffff. + * @return extended section header count or 0 if not found or out of range + * @throws IOException if file IO error occurs + */ + private int readExtendedSectionHeaderCount() throws IOException { + ElfSectionHeader s = getSection0(); + if (s != null && s.getType() == ElfSectionHeaderConstants.SHT_NULL) { + long val = s.getSize(); + return (val < 0 || val > Integer.MAX_VALUE) ? 0 : (int) val; + } + return 0; + } + private void initElfLoadAdapter() { programHeaderTypeMap = new HashMap<>(); @@ -951,6 +999,10 @@ public class ElfHeader implements StructConverter, Writeable { sectionHeaders[i] = new ElfSectionHeader(reader, this); } + if (sectionHeaders.length != 0) { + section0 = sectionHeaders[0]; + } + //note: we cannot retrieve all the names //until after we have read all the section headers. //this is because one of the section headers @@ -1283,11 +1335,13 @@ public class ElfHeader implements StructConverter, Writeable { /** * This member holds the number of entries in the program header table. Thus the product - * of e_phentsize and e_phnum gives the table's size in bytes. If a file has no program - * header table, e_phnum holds the value zero. + * of e_phentsize and unsigned e_phnum gives the table's size in bytes. If original + * e_phnum equals PNXNUM (0xffff) an attempt will be made to obtained the extended size + * from section[0].sh_info field. If a file has no program header table, e_phnum holds + * the value zero. * @return the number of entries in the program header table */ - public short e_phnum() { + public int getProgramHeaderCount() { return e_phnum; } @@ -1311,11 +1365,11 @@ public class ElfHeader implements StructConverter, Writeable { /** * This member holds the number of entries in the section header table. Thus the product - * of e_shentsize and e_shnum gives the section header table's size in bytes. If a file + * of e_shentsize and unsigned e_shnum gives the section header table's size in bytes. If a file * has no section header table, e_shnum holds the value zero. * @return the number of entries in the section header table */ - public short e_shnum() { + public int getSectionHeaderCount() { return e_shnum; } @@ -1911,7 +1965,7 @@ public class ElfHeader implements StructConverter, Writeable { programHeaders = tmp; - e_phnum = (short) programHeaders.length; + e_phnum = programHeaders.length; } @@ -1947,8 +2001,16 @@ public class ElfHeader implements StructConverter, Writeable { raf.write(dc.getBytes(e_flags)); raf.write(dc.getBytes(e_ehsize)); raf.write(dc.getBytes(e_phentsize)); + if (e_phnum >= Short.toUnsignedInt(ElfConstants.PN_XNUM)) { + throw new IOException( + "Unsupported program header count serialization: " + e_phnum); + } raf.write(dc.getBytes(e_phnum)); raf.write(dc.getBytes(e_shentsize)); + if (e_shnum >= Short.toUnsignedInt(ElfSectionHeaderConstants.SHN_LORESERVE)) { + throw new IOException( + "Unsupported section header count serialization: " + e_shnum); + } raf.write(dc.getBytes(e_shnum)); raf.write(dc.getBytes(e_shstrndx)); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java index 0d43d28b54..a9b656d3c4 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/ElfProgramBuilder.java @@ -115,6 +115,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { int id = program.startTransaction("Load ELF program"); boolean success = false; try { + monitor.setIndeterminate(true); addProgramProperties(monitor); @@ -135,9 +136,11 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { processProgramHeaders(monitor); processSectionHeaders(monitor); + monitor.setIndeterminate(false); + resolve(monitor); - if (elf.e_shnum() == 0) { + if (elf.getSectionHeaderCount() == 0) { // create/expand segments to their fullsize if no sections are defined expandProgramHeaderBlocks(monitor); } @@ -151,6 +154,10 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { markupElfHeader(monitor); markupProgramHeaders(monitor); markupSectionHeaders(monitor); + + monitor.setProgress(0); + monitor.setIndeterminate(true); + markupDynamicTable(monitor); markupInterpreter(monitor); @@ -222,11 +229,11 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { // Ignore header regions which will always be allocated to blocks int elfHeaderSize = elf.toDataType().getLength(); fileMap.paintRange(0, elfHeaderSize - 1, -4); // -4: header block - int programHeaderSize = elf.e_phentsize() * elf.e_phnum(); + int programHeaderSize = elf.e_phentsize() * elf.getProgramHeaderCount(); if (programHeaderSize != 0) { fileMap.paintRange(elf.e_phoff(), elf.e_phoff() + programHeaderSize - 1, -4); // -4: header block } - int sectionHeaderSize = elf.e_shentsize() * elf.e_shnum(); + int sectionHeaderSize = elf.e_shentsize() * elf.getSectionHeaderCount(); if (sectionHeaderSize != 0) { fileMap.paintRange(elf.e_shoff(), elf.e_shoff() + sectionHeaderSize - 1, -4); // -4: header block } @@ -288,7 +295,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { private boolean isDiscardableFillerSegment(MemoryLoadable loadable, String blockName, Address start, long fileOffset, long length) throws IOException { - if (elf.e_shnum() == 0 || elf.e_phnum() == 0) { + if (elf.getSectionHeaderCount() == 0 || elf.getProgramHeaderCount() == 0) { return false; // only prune if both sections and program headers are present } if (length > DISCARDABLE_SEGMENT_SIZE || !blockName.startsWith(SEGMENT_NAME_PREFIX)) { @@ -735,13 +742,14 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { } private void processRelocations(TaskMonitor monitor) throws CancelledException { - monitor.setMessage("Processing relocation tables..."); ElfRelocationTable[] relocationTables = elf.getRelocationTables(); if (relocationTables.length == 0) { return; } + monitor.setMessage("Processing relocation tables..."); + boolean processRelocations = ElfLoaderOptionsFactory.performRelocations(options); if (processRelocations && ElfRelocationHandlerFactory.getHandler(elf) == null) { log("ELF relocation handler extension not found! Unable to process relocations."); @@ -1012,12 +1020,14 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { private void markupProgramHeaders(TaskMonitor monitor) { - int headerCount = elf.e_phnum(); + int headerCount = elf.getProgramHeaderCount(); int size = elf.e_phentsize() * headerCount; if (size == 0) { return; } + monitor.setMessage("Markup Program Headers ..."); + Structure phStructDt = (Structure) elf.getProgramHeaders()[0].toDataType(); phStructDt = phStructDt.clone(program.getDataTypeManager()); @@ -1047,8 +1057,12 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { } ElfProgramHeader[] programHeaders = elf.getProgramHeaders(); + monitor.setMaximum(programHeaders.length); int vaddrFieldIndex = elf.is64Bit() ? 3 : 2; // p_vaddr structure element index for (int i = 0; i < programHeaders.length; i++) { + monitor.checkCanceled(); + monitor.incrementProgress(1); + Data d = array.getComponent(i); d.setComment(CodeUnit.EOL_COMMENT, programHeaders[i].getComment()); if (programHeaders[i].getType() == ElfProgramHeaderConstants.PT_NULL) { @@ -1073,16 +1087,18 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { private void markupSectionHeaders(TaskMonitor monitor) { - int headerCount = elf.e_shnum(); + int headerCount = elf.getSectionHeaderCount(); int size = elf.e_shentsize() * headerCount; if (size == 0) { return; } + monitor.setMessage("Markup Section Headers ..."); + Structure shStructDt = (Structure) elf.getSections()[0].toDataType(); shStructDt = shStructDt.clone(program.getDataTypeManager()); - Array arrayDt = new ArrayDataType(shStructDt, elf.e_shnum(), elf.e_shentsize()); + Array arrayDt = new ArrayDataType(shStructDt, headerCount, elf.e_shentsize()); Address headerAddr = findLoadAddress(elf.e_shoff(), size); @@ -1108,7 +1124,10 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { } ElfSectionHeader[] sections = elf.getSections(); + monitor.setMaximum(sections.length); for (int i = 0; i < sections.length; i++) { + monitor.checkCanceled(); + monitor.incrementProgress(1); Data d = array.getComponent(i); String comment = sections[i].getNameAsString(); @@ -2758,9 +2777,15 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { * @throws CancelledException */ private void expandProgramHeaderBlocks(TaskMonitor monitor) throws CancelledException { + + monitor.setMessage("Exapanding Program Segments..."); + ElfProgramHeader[] elfProgramHeaders = elf.getProgramHeaders(); + monitor.setMaximum(elfProgramHeaders.length); for (int i = 0; i < elfProgramHeaders.length; ++i) { monitor.checkCanceled(); + monitor.incrementProgress(1); + ElfProgramHeader elfProgramHeader = elfProgramHeaders[i]; if (elfProgramHeaders[i].getType() == ElfProgramHeaderConstants.PT_LOAD) { @@ -2862,9 +2887,9 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { private void processProgramHeaders(TaskMonitor monitor) throws CancelledException { - if (elf.isRelocatable() && elf.e_phnum() != 0) { + if (elf.isRelocatable() && elf.getProgramHeaderCount() != 0) { log("Ignoring unexpected program headers for relocatable ELF (e_phnum=" + - elf.e_phnum() + ")"); + elf.getProgramHeaderCount() + ")"); return; } @@ -2933,7 +2958,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { long loadSizeBytes = elfProgramHeader.getAdjustedLoadSize(); long fullSizeBytes = elfProgramHeader.getAdjustedMemorySize(); - boolean maintainExecuteBit = elf.e_shnum() == 0; + boolean maintainExecuteBit = elf.getSectionHeaderCount() == 0; if (fullSizeBytes <= 0) { log("Skipping zero-length segment [" + segmentNumber + "," + @@ -2949,7 +2974,7 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { try { // Only allow segment fragmentation if section headers are defined - boolean isFragmentationOK = (elf.e_shnum() != 0); + boolean isFragmentationOK = (elf.getSectionHeaderCount() != 0); String comment = getSectionComment(addr, fullSizeBytes, space.getAddressableUnitSize(), elfProgramHeader.getDescription(), address.isLoadedMemoryAddress()); @@ -3355,8 +3380,8 @@ class ElfProgramBuilder extends MemorySectionResolver implements ElfLoadHelper { x = false; } - Msg.debug(this, - "Loading block " + name + " at " + start + " from file offset " + fileOffset); +// Msg.debug(this, +// "Loading block " + name + " at " + start + " from file offset " + fileOffset); long endOffset = fileOffset + revisedLength - 1; if (endOffset >= fileBytes.getSize()) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MemorySectionResolver.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MemorySectionResolver.java index e89a807870..d3f3e78529 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MemorySectionResolver.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MemorySectionResolver.java @@ -34,6 +34,8 @@ public abstract class MemorySectionResolver { protected final Program program; + private Set usedBlockNames = new HashSet<>(); + private List sections = new ArrayList<>(); // built-up prior to resolve private Map sectionIndexMap = new HashMap<>(); @@ -43,6 +45,9 @@ public abstract class MemorySectionResolver { public MemorySectionResolver(Program program) { this.program = program; + if (!program.getMemory().isEmpty()) { + throw new IllegalStateException("program memory blocks already exist - unsupported"); + } } /** @@ -124,16 +129,15 @@ public abstract class MemorySectionResolver { else { baseName = "NO-NAME"; } - Memory mem = program.getMemory(); String name = baseName; int index = 0; - while (mem.getBlock(name) != null) { + while (usedBlockNames.contains(name)) { name = baseName + "-" + (++index); } return name; } - private String getUniqueSectionChunkName(MemorySection section, Memory memory, + private String getUniqueSectionChunkName(MemorySection section, int preferredIndex) { String sectionName = section.getSectionName(); int index = preferredIndex; @@ -142,7 +146,7 @@ public abstract class MemorySectionResolver { if (index >= 0) { name += "." + index; } - if (memory.getBlock(name) == null) { + if (!usedBlockNames.contains(name)) { return name; } if (index <= 0) { @@ -245,6 +249,8 @@ public abstract class MemorySectionResolver { */ public void resolve(TaskMonitor monitor) throws CancelledException { + monitor.setMessage("Loading memory blocks..."); + if (sectionMemoryMap != null) { throw new IllegalStateException("already resolved"); } @@ -263,9 +269,11 @@ public abstract class MemorySectionResolver { // process sections in reverse order - last-in takes precedence int sectionCount = sections.size(); + monitor.initialize(sectionCount); for (int index = sectionCount - 1; index >= 0; --index) { monitor.checkCanceled(); resolveSectionMemory(sections.get(index), fileAllocationMap, monitor); + monitor.incrementProgress(1); } } @@ -352,7 +360,7 @@ public abstract class MemorySectionResolver { continue; // skip range } - String blockName = getUniqueSectionChunkName(section, memory, rangeIndex); + String blockName = getUniqueSectionChunkName(section, rangeIndex); Address physicalStartAddr = section.getMinPhysicalAddress().add(sectionByteOffset); @@ -381,6 +389,7 @@ public abstract class MemorySectionResolver { if (block != null) { minAddr = block.getStart(); maxAddr = block.getEnd(); + usedBlockNames.add(blockName); } else { // block may be null due to unexpected conflict or pruning - allow to continue diff --git a/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/extend/PowerPC_ElfExtension.java b/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/extend/PowerPC_ElfExtension.java index c783f20e86..771edcac6b 100644 --- a/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/extend/PowerPC_ElfExtension.java +++ b/Ghidra/Processors/PowerPC/src/main/java/ghidra/app/util/bin/format/elf/extend/PowerPC_ElfExtension.java @@ -273,7 +273,7 @@ public class PowerPC_ElfExtension extends ElfExtension { RegisterValue enableVLE = new RegisterValue(vleContextReg, BigInteger.ONE); ElfHeader elf = elfLoadHelper.getElfHeader(); - if (elf.e_shnum() != 0) { + if (elf.getSectionHeaderCount() != 0) { // Rely on section headers if present for (ElfSectionHeader section : elf.getSections( ElfSectionHeaderConstants.SHT_PROGBITS)) {