GP-5160: Handle section alignment flags in COFF header

This commit is contained in:
ghintern
2024-12-16 16:41:26 +00:00
parent 16388dc261
commit 83f4da2437
3 changed files with 56 additions and 30 deletions
@@ -175,8 +175,10 @@ public class SectionHeader implements StructConverter, ByteArrayConverter {
* Align on 8192-byte boundary. * Align on 8192-byte boundary.
*/ */
public final static int IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000; public final static int IMAGE_SCN_ALIGN_8192BYTES = 0x00E00000;
// Unused = 0x00F00000; /**
// public final static int IMAGE_SCN_ALIGN_MASK = 0x00F00000; * Mask for alignment flags
*/
public final static int IMAGE_SCN_ALIGN_MASK = 0x00F00000;
/** /**
* Section contains extended relocations. * Section contains extended relocations.
*/ */
@@ -538,7 +538,7 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
Map<CoffSectionHeader, Address> map) { Map<CoffSectionHeader, Address> map) {
// 1. loop over all sections // 1. loop over all sections
// put all sections not at 0 into address set // put all sections not at 0 into address set
// put all sections at 0 into "totals" map // put all sections at 0 into "totals" map, accounting for later alignment needs
// 2. look for space before minimum of taken addresses // 2. look for space before minimum of taken addresses
// 3. or, look for space after maximum of taken addresses // 3. or, look for space after maximum of taken addresses
@@ -548,24 +548,22 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
AddressSet nonZeroSet = new AddressSet(); AddressSet nonZeroSet = new AddressSet();
int totalSize = 0; int totalZeroSectionSize = 0;
TreeMap<String, Integer> zeroSectionSizes = new TreeMap<>(); Map<String, Integer> zeroSectionSizes = new TreeMap<>();
TreeMap<String, Integer> zeroSectionOffsets = new TreeMap<>(); Map<String, Integer> zeroSectionOffsets = new TreeMap<>();
List<CoffSectionHeader> sections = header.getSections(); List<CoffSectionHeader> sections = header.getSections();
for (CoffSectionHeader section : sections) { for (CoffSectionHeader section : sections) {
int physicalAddress = section.getPhysicalAddress(); int physicalAddress = section.getPhysicalAddress();
int size = section.getSize(language); int size = section.getSize(language);
if (physicalAddress == 0) { if (physicalAddress == 0) {
// We don't know the exact offset now, so assume worst-case alignment penalty
int alignedSize = size + getSectionAlignment(section) - 1;
String name = section.getName(); String name = section.getName();
Integer s = zeroSectionSizes.get(name); zeroSectionSizes.compute(name,
if (s == null) { (k, v) -> (v == null ? alignedSize : v + alignedSize));
zeroSectionSizes.put(name, size);
} totalZeroSectionSize += alignedSize;
else {
zeroSectionSizes.put(name, s + size);
}
totalSize += size;
} }
else { else {
if (size > 0) { if (size > 0) {
@@ -579,35 +577,47 @@ public class CoffLoader extends AbstractLibrarySupportLoader {
} }
} }
} }
Address maxAddress = nonZeroSet.getMaxAddress(); Address maxAddress = nonZeroSet.getMaxAddress();
int offset = (maxAddress == null ? EMPTY_START_OFFSET - 1 int offset = (maxAddress == null ? EMPTY_START_OFFSET - 1
: (int) (maxAddress.getOffset() & 0xffffffff)); : (int) (maxAddress.getOffset() & 0xffffffff));
long sum = offset; long sum = offset;
sum += totalSize; sum += totalZeroSectionSize;
if (sum <= 0x100000000L) { if (sum <= 0x100000000L) {
int start = align(offset + 1); offset += 1;
// Each group of sections with the same name should be aligned at least at 256 bytes
for (Entry<String, Integer> entry : zeroSectionSizes.entrySet()) { for (Entry<String, Integer> entry : zeroSectionSizes.entrySet()) {
zeroSectionOffsets.put(entry.getKey(), start); offset = (offset + DEFAULT_ALIGNMENT - 1) / DEFAULT_ALIGNMENT * DEFAULT_ALIGNMENT;
start = align(start + entry.getValue()); zeroSectionOffsets.put(entry.getKey(), offset);
offset += zeroSectionSizes.get(entry.getKey());
} }
int sectionNumber = 1; int sectionNumber = 1;
for (CoffSectionHeader section : sections) { for (CoffSectionHeader section : sections) {
int physicalAddress = section.getPhysicalAddress(); int physicalAddress = section.getPhysicalAddress();
if (physicalAddress == 0) { if (physicalAddress == 0) {
String name = section.getName(); String name = section.getName();
start = zeroSectionOffsets.get(name); int alignment = getSectionAlignment(section);
relocateSection(header, section, sectionNumber, start); offset = (zeroSectionOffsets.get(name) + alignment - 1) / alignment * alignment;
zeroSectionOffsets.put(name, start + section.getSize(language)); relocateSection(header, section, sectionNumber, offset);
zeroSectionOffsets.put(name, offset + section.getSize(language));
} }
++sectionNumber; ++sectionNumber;
} }
} }
} }
private static final int ALIGNMENT = 0x100; private static final int DEFAULT_ALIGNMENT = 0x100;
private int align(int i) { /**
return (i + ALIGNMENT - 1) / ALIGNMENT * ALIGNMENT; * Query a section header for alignment information. The base version of this method assumes
* no alignment information is stored in the section header. Subclasses may implement a
* platform-specific check for alignment information.
*
* @param section header object for the section
* @return the alignment requested by the section
*/
protected int getSectionAlignment(CoffSectionHeader section) {
return 1;
} }
private void relocateSection(CoffFileHeader header, CoffSectionHeader section, private void relocateSection(CoffFileHeader header, CoffSectionHeader section,
@@ -15,6 +15,9 @@
*/ */
package ghidra.app.util.opinion; package ghidra.app.util.opinion;
import ghidra.app.util.bin.format.coff.CoffSectionHeader;
import ghidra.app.util.bin.format.pe.SectionHeader;
public class MSCoffLoader extends CoffLoader { public class MSCoffLoader extends CoffLoader {
public final static String MSCOFF_NAME = "MS Common Object File Format (COFF)"; public final static String MSCOFF_NAME = "MS Common Object File Format (COFF)";
@@ -33,4 +36,15 @@ public class MSCoffLoader extends CoffLoader {
return true; return true;
} }
@Override
protected int getSectionAlignment(CoffSectionHeader section) {
// Alignment is packed as a 4-bit integer in the flags, value 2^(align_bits - 1)
int s_flags = section.getFlags();
int align_bits = (s_flags & SectionHeader.IMAGE_SCN_ALIGN_MASK) >> 20;
if (align_bits == 0 || align_bits >= 0xF) {
return 1;
}
return 1 << (align_bits - 1);
}
} }