package jdk.tools.jaotc.binformat.pecoff;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import jdk.tools.jaotc.binformat.BinaryContainer;
import jdk.tools.jaotc.binformat.ByteContainer;
import jdk.tools.jaotc.binformat.CodeContainer;
import jdk.tools.jaotc.binformat.ReadOnlyDataContainer;
import jdk.tools.jaotc.binformat.Relocation;
import jdk.tools.jaotc.binformat.Relocation.RelocType;
import jdk.tools.jaotc.binformat.Symbol;
import jdk.tools.jaotc.binformat.Symbol.Binding;
import jdk.tools.jaotc.binformat.Symbol.Kind;
import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_FILE_HEADER;
import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_RELOCATION;
import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_SECTION_HEADER;
import jdk.tools.jaotc.binformat.pecoff.PECoff.IMAGE_SYMBOL;
public class JPECoffRelocObject {
private final BinaryContainer binContainer;
private final PECoffContainer pecoffContainer;
private final int sectionAlignment;
public JPECoffRelocObject(BinaryContainer binContainer, String outputFileName) {
this.binContainer = binContainer;
this.pecoffContainer = new PECoffContainer(outputFileName);
this.sectionAlignment = binContainer.getCodeSegmentSize();
}
private static PECoffSection createByteSection(ArrayList<PECoffSection> sections, String sectName, byte[] scnData,
boolean hasRelocs, int scnFlags, int sectAlign) {
PECoffSection sect = new PECoffSection(sectName, scnData, scnFlags, sectAlign, hasRelocs, sections.size());
sections.add(sect);
return (sect);
}
private static void createByteSection(ArrayList<PECoffSection> sections, ByteContainer c, int scnFlags, int sectAlign) {
PECoffSection sect;
boolean hasRelocs = c.hasRelocations();
byte[] scnData = c.getByteArray();
sect = createByteSection(sections, c.getContainerName(), scnData, hasRelocs, scnFlags, sectAlign);
c.setSectionId(sect.getSectionId());
}
private void createCodeSection(ArrayList<PECoffSection> sections, CodeContainer c) {
int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_EXECUTE | IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_CODE;
createByteSection(sections, c, scnFlags, sectionAlignment);
}
private void createReadOnlySection(ArrayList<PECoffSection> sections, ReadOnlyDataContainer c) {
int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_INITIALIZED_DATA;
createByteSection(sections, c, scnFlags, sectionAlignment);
}
private void createReadWriteSection(ArrayList<PECoffSection> sections, ByteContainer c) {
int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_READ | IMAGE_SECTION_HEADER.IMAGE_SCN_MEM_WRITE;
if (c.getByteArray().length > 0) {
scnFlags |= IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_INITIALIZED_DATA;
} else {
scnFlags |= IMAGE_SECTION_HEADER.IMAGE_SCN_CNT_UNINITIALIZED_DATA;
}
createByteSection(sections, c, scnFlags, sectionAlignment);
}
public void createPECoffRelocObject(Map<Symbol, List<Relocation>> relocationTable, Collection<Symbol> symbols) throws IOException {
ArrayList<PECoffSection> sections = new ArrayList<>();
createCodeSection(sections, binContainer.getCodeContainer());
createReadOnlySection(sections, binContainer.getMetaspaceNamesContainer());
createReadOnlySection(sections, binContainer.getKlassesOffsetsContainer());
createReadOnlySection(sections, binContainer.getMethodsOffsetsContainer());
createReadOnlySection(sections, binContainer.getKlassesDependenciesContainer());
createReadOnlySection(sections, binContainer.getMethodMetadataContainer());
createReadOnlySection(sections, binContainer.getStubsOffsetsContainer());
createReadOnlySection(sections, binContainer.getHeaderContainer().getContainer());
createReadOnlySection(sections, binContainer.getCodeSegmentsContainer());
createReadOnlySection(sections, binContainer.getConstantDataContainer());
createReadOnlySection(sections, binContainer.getConfigContainer());
createReadWriteSection(sections, binContainer.getKlassesGotContainer());
createReadWriteSection(sections, binContainer.getCountersGotContainer());
createReadWriteSection(sections, binContainer.getMetadataGotContainer());
createReadWriteSection(sections, binContainer.getMethodStateContainer());
createReadWriteSection(sections, binContainer.getOopGotContainer());
createReadWriteSection(sections, binContainer.getExtLinkageGOTContainer());
PECoffHeader header = new PECoffHeader();
PECoffSymtab symtab = createPECoffSymbolTables(symbols);
int scnFlags = IMAGE_SECTION_HEADER.IMAGE_SCN_LNK_INFO | IMAGE_SECTION_HEADER.IMAGE_SCN_LNK_REMOVE;
createByteSection(sections, ".drectve", symtab.getDirectiveArray(), false, scnFlags, 1 );
PECoffRelocTable pecoffRelocs = createPECoffRelocTable(sections, relocationTable);
int fileOffset = IMAGE_FILE_HEADER.totalsize +
(IMAGE_SECTION_HEADER.totalsize * sections.size());
header.setSectionCount(sections.size());
header.setSymbolCount(symtab.getSymtabCount());
header.setSymbolOff(fileOffset);
fileOffset += ((symtab.getSymtabCount() * IMAGE_SYMBOL.totalsize) +
symtab.getStrtabSize());
fileOffset = (fileOffset + (sections.get(0).getDataAlign() - 1)) &
~((sections.get(0).getDataAlign() - 1));
for (int i = 0; i < sections.size(); i++) {
PECoffSection sect = sections.get(i);
fileOffset = (fileOffset + (sect.getDataAlign() - 1)) &
~((sect.getDataAlign() - 1));
sect.setOffset(fileOffset);
fileOffset += sect.getSize();
}
for (int i = 0; i < sections.size(); i++) {
PECoffSection sect = sections.get(i);
if (sect.hasRelocations()) {
int nreloc = pecoffRelocs.getNumRelocs(i);
sect.setReloff(fileOffset);
sect.setRelcount(nreloc);
if (nreloc > 0xFFFF) {
nreloc++;
}
fileOffset += (nreloc * IMAGE_RELOCATION.totalsize);
}
}
pecoffContainer.writeBytes(header.getArray());
for (int i = 0; i < sections.size(); i++) {
PECoffSection sect = sections.get(i);
pecoffContainer.writeBytes(sect.getArray(), PECoffSection.getShdrAlign());
}
pecoffContainer.writeBytes(symtab.getSymtabArray(), 4);
pecoffContainer.writeBytes(symtab.getStrtabArray(), 1);
for (int i = 0; i < sections.size(); i++) {
PECoffSection sect = sections.get(i);
pecoffContainer.writeBytes(sect.getDataArray(), sect.getDataAlign());
}
for (int i = 0; i < sections.size(); i++) {
if (pecoffRelocs.getNumRelocs(i) > 0) {
pecoffContainer.writeBytes(pecoffRelocs.getRelocData(i));
}
}
pecoffContainer.close();
}
private static PECoffSymtab createPECoffSymbolTables(Collection<Symbol> symbols) {
PECoffSymtab symtab = new PECoffSymtab();
for (Symbol symbol : symbols) {
int secHdrIndex = symbol.getSection().getSectionId();
PECoffSymbol pecoffSymbol = symtab.addSymbolEntry(symbol.getName(), getPECoffTypeOf(symbol), getPECoffClassOf(symbol), (byte) secHdrIndex, symbol.getOffset());
symbol.setNativeSymbol(pecoffSymbol);
}
return (symtab);
}
private static byte getPECoffTypeOf(Symbol sym) {
Kind kind = sym.getKind();
if (kind == Symbol.Kind.NATIVE_FUNCTION || kind == Symbol.Kind.JAVA_FUNCTION) {
return IMAGE_SYMBOL.IMAGE_SYM_DTYPE_FUNCTION;
}
return IMAGE_SYMBOL.IMAGE_SYM_DTYPE_NONE;
}
private static byte getPECoffClassOf(Symbol sym) {
Binding binding = sym.getBinding();
if (binding == Symbol.Binding.GLOBAL) {
return IMAGE_SYMBOL.IMAGE_SYM_CLASS_EXTERNAL;
}
return IMAGE_SYMBOL.IMAGE_SYM_CLASS_STATIC;
}
private PECoffRelocTable createPECoffRelocTable(ArrayList<PECoffSection> sections, Map<Symbol, List<Relocation>> relocationTable) {
PECoffRelocTable pecoffRelocTable = new PECoffRelocTable(sections.size());
for (Map.Entry<Symbol, List<Relocation>> entry : relocationTable.entrySet()) {
List<Relocation> relocs = entry.getValue();
Symbol symbol = entry.getKey();
for (Relocation reloc : relocs) {
createRelocation(symbol, reloc, pecoffRelocTable);
}
}
for (Map.Entry<Symbol, Relocation> entry : binContainer.getUniqueRelocationTable().entrySet()) {
createRelocation(entry.getKey(), entry.getValue(), pecoffRelocTable);
}
return (pecoffRelocTable);
}
private static void createRelocation(Symbol symbol, Relocation reloc, PECoffRelocTable pecoffRelocTable) {
RelocType relocType = reloc.getType();
int pecoffRelocType = getPECoffRelocationType(relocType);
PECoffSymbol sym = (PECoffSymbol) symbol.getNativeSymbol();
int symno = sym.getIndex();
int sectindex = reloc.getSection().getSectionId();
int offset = reloc.getOffset();
int addend = 0;
switch (relocType) {
case JAVA_CALL_DIRECT:
case STUB_CALL_DIRECT:
case FOREIGN_CALL_INDIRECT_GOT: {
addend = -4;
offset = offset + reloc.getSize() + addend;
break;
}
case JAVA_CALL_INDIRECT: {
return;
}
case METASPACE_GOT_REFERENCE:
case EXTERNAL_PLT_TO_GOT: {
addend = -4;
offset = offset + addend;
break;
}
case EXTERNAL_GOT_TO_PLT: {
break;
}
default:
throw new InternalError("Unhandled relocation type: " + relocType);
}
pecoffRelocTable.createRelocationEntry(sectindex, offset, symno, pecoffRelocType);
}
private static int getPECoffRelocationType(RelocType relocType) {
int pecoffRelocType = 0;
switch (PECoffTargetInfo.getPECoffArch()) {
case IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE_AMD64:
if (relocType == RelocType.JAVA_CALL_DIRECT ||
relocType == RelocType.FOREIGN_CALL_INDIRECT_GOT) {
pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
} else if (relocType == RelocType.STUB_CALL_DIRECT) {
pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
} else if (relocType == RelocType.JAVA_CALL_INDIRECT) {
pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_ABSOLUTE;
} else if (relocType == RelocType.METASPACE_GOT_REFERENCE ||
relocType == RelocType.EXTERNAL_PLT_TO_GOT) {
pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_REL32;
} else if (relocType == RelocType.EXTERNAL_GOT_TO_PLT) {
pecoffRelocType = IMAGE_RELOCATION.IMAGE_REL_AMD64_ADDR64;
} else {
assert false : "Unhandled relocation type: " + relocType;
}
break;
default:
System.out.println("Relocation Type mapping: Unhandled architecture");
}
return pecoffRelocType;
}
}