package jdk.tools.jaotc.binformat.elf;
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.elf.ElfSymbol;
import jdk.tools.jaotc.binformat.elf.ElfTargetInfo;
import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Ehdr;
import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Shdr;
import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Sym;
import jdk.tools.jaotc.binformat.elf.Elf.Elf64_Rela;
public class JELFRelocObject {
private final BinaryContainer binContainer;
private final ElfContainer elfContainer;
private final int segmentSize;
public JELFRelocObject(BinaryContainer binContainer, String outputFileName) {
this.binContainer = binContainer;
this.elfContainer = new ElfContainer(outputFileName);
this.segmentSize = binContainer.getCodeSegmentSize();
}
private static ElfSection createByteSection(ArrayList<ElfSection> sections,
String sectName,
byte[] scnData,
boolean hasRelocs,
int align,
int scnFlags,
int scnType) {
ElfSection sect = new ElfSection(sectName, scnData, scnFlags, scnType,
hasRelocs, align, sections.size());
sections.add(sect);
return (sect);
}
private void createByteSection(ArrayList<ElfSection> sections,
ByteContainer c, int scnFlags) {
ElfSection sect;
boolean hasRelocs = c.hasRelocations();
byte[] scnData = c.getByteArray();
int scnType = Elf64_Shdr.SHT_PROGBITS;
boolean zeros = !hasRelocs;
if (zeros) {
for (byte b : scnData) {
if (b != 0) {
zeros = false;
break;
}
}
if (zeros) {
scnType = Elf64_Shdr.SHT_NOBITS;
}
}
sect = createByteSection(sections, c.getContainerName(),
scnData, hasRelocs, segmentSize,
scnFlags, scnType);
c.setSectionId(sect.getSectionId());
}
private void createCodeSection(ArrayList<ElfSection> sections, CodeContainer c) {
createByteSection(sections, c, Elf64_Shdr.SHF_ALLOC | Elf64_Shdr.SHF_EXECINSTR);
}
private void createReadOnlySection(ArrayList<ElfSection> sections, ReadOnlyDataContainer c) {
createByteSection(sections, c, Elf64_Shdr.SHF_ALLOC);
}
private void createReadWriteSection(ArrayList<ElfSection> sections, ByteContainer c) {
createByteSection(sections, c, Elf64_Shdr.SHF_ALLOC | Elf64_Shdr.SHF_WRITE);
}
public void createELFRelocObject(Map<Symbol, List<Relocation>> relocationTable, Collection<Symbol> symbols) throws IOException {
ElfHeader eh = new ElfHeader();
ArrayList<ElfSection> sections = new ArrayList<>();
createByteSection(sections, null, null, false, 1, 0, 0);
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.getOopGotContainer());
createReadWriteSection(sections, binContainer.getMethodStateContainer());
createReadWriteSection(sections, binContainer.getExtLinkageGOTContainer());
ElfSymtab symtab = createELFSymbolTables(symbols);
ElfSection strTabSection = createByteSection(sections, ".strtab",
symtab.getStrtabArray(),
false, 1, 0,
Elf64_Shdr.SHT_STRTAB);
ElfSection symTabSection = createByteSection(sections, ".symtab",
symtab.getSymtabArray(),
false, 8, 0,
Elf64_Shdr.SHT_SYMTAB);
symTabSection.setLink(strTabSection.getSectionId());
symTabSection.setInfo(symtab.getNumLocalSyms());
ElfRelocTable elfRelocTable = createElfRelocTable(sections, relocationTable);
createElfRelocSections(sections, elfRelocTable, symTabSection.getSectionId());
ElfSection shStrTabSection = createByteSection(sections, ".shstrtab",
null, false, 1, 0,
Elf64_Shdr.SHT_STRTAB);
eh.setSectionStrNdx(shStrTabSection.getSectionId());
int file_offset = Elf64_Ehdr.totalsize;
file_offset = (file_offset + (sections.get(1).getDataAlign() - 1)) &
~((sections.get(1).getDataAlign() - 1));
for (int i = 1; i < sections.size(); i++) {
ElfSection sect = sections.get(i);
file_offset = (file_offset + (sect.getDataAlign() - 1)) &
~((sect.getDataAlign() - 1));
sect.setOffset(file_offset);
file_offset += sect.getSize();
}
file_offset = (file_offset + (ElfSection.getShdrAlign() - 1)) &
~((ElfSection.getShdrAlign() - 1));
eh.setSectionOff(file_offset);
eh.setSectionNum(sections.size());
elfContainer.writeBytes(eh.getArray());
for (int i = 1; i < sections.size(); i++) {
ElfSection sect = sections.get(i);
elfContainer.writeBytes(sect.getDataArray(), sect.getDataAlign());
}
for (int i = 0; i < sections.size(); i++) {
ElfSection sect = sections.get(i);
elfContainer.writeBytes(sect.getArray(), ElfSection.getShdrAlign());
}
elfContainer.close();
}
private static ElfSymtab createELFSymbolTables(Collection<Symbol> symbols) {
ElfSymtab symtab = new ElfSymtab();
symtab.addSymbolEntry("", (byte) 0, (byte) 0, Elf64_Shdr.SHN_UNDEF, 0, 0);
for (Symbol symbol : symbols) {
int secHdrIndex = symbol.getSection().getSectionId();
ElfSymbol elfSymbol = symtab.addSymbolEntry(symbol.getName(), getELFTypeOf(symbol), getELFBindOf(symbol), (byte) secHdrIndex, symbol.getOffset(), symbol.getSize());
symbol.setNativeSymbol(elfSymbol);
}
return (symtab);
}
private static byte getELFTypeOf(Symbol sym) {
Kind kind = sym.getKind();
if (kind == Symbol.Kind.NATIVE_FUNCTION || kind == Symbol.Kind.JAVA_FUNCTION) {
return Elf64_Sym.STT_FUNC;
} else if (kind == Symbol.Kind.OBJECT) {
return Elf64_Sym.STT_OBJECT;
}
return Elf64_Sym.STT_NOTYPE;
}
private static byte getELFBindOf(Symbol sym) {
Binding binding = sym.getBinding();
if (binding == Symbol.Binding.GLOBAL) {
return Elf64_Sym.STB_GLOBAL;
}
return Elf64_Sym.STB_LOCAL;
}
private ElfRelocTable createElfRelocTable(ArrayList<ElfSection> sections,
Map<Symbol, List<Relocation>> relocationTable) {
ElfRelocTable elfRelocTable = new ElfRelocTable(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, elfRelocTable);
}
}
for (Map.Entry<Symbol, Relocation> entry : binContainer.getUniqueRelocationTable().entrySet()) {
createRelocation(entry.getKey(), entry.getValue(), elfRelocTable);
}
return (elfRelocTable);
}
private static void createRelocation(Symbol symbol, Relocation reloc, ElfRelocTable elfRelocTable) {
RelocType relocType = reloc.getType();
int elfRelocType = getELFRelocationType(relocType);
ElfSymbol sym = (ElfSymbol) 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:
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);
}
elfRelocTable.createRelocationEntry(sectindex, offset, symno, elfRelocType, addend);
}
private static int getELFRelocationType(RelocType relocType) {
int elfRelocType = 0;
switch (ElfTargetInfo.getElfArch()) {
case Elf64_Ehdr.EM_X86_64:
if (relocType == RelocType.JAVA_CALL_DIRECT ||
relocType == RelocType.FOREIGN_CALL_INDIRECT_GOT) {
elfRelocType = Elf64_Rela.R_X86_64_PLT32;
} else if (relocType == RelocType.STUB_CALL_DIRECT) {
elfRelocType = Elf64_Rela.R_X86_64_PC32;
} else if (relocType == RelocType.JAVA_CALL_INDIRECT) {
elfRelocType = Elf64_Rela.R_X86_64_NONE;
} else if (relocType == RelocType.METASPACE_GOT_REFERENCE ||
relocType == RelocType.EXTERNAL_PLT_TO_GOT) {
elfRelocType = Elf64_Rela.R_X86_64_PC32;
} else if (relocType == RelocType.EXTERNAL_GOT_TO_PLT) {
elfRelocType = Elf64_Rela.R_X86_64_64;
} else {
assert false : "Unhandled relocation type: " + relocType;
}
break;
default:
System.out.println("Relocation Type mapping: Unhandled architecture");
}
return elfRelocType;
}
private static void createElfRelocSections(ArrayList<ElfSection> sections,
ElfRelocTable elfRelocTable,
int symtabsectidx) {
int count = sections.size();
for (int i = 0; i < count; i++) {
if (elfRelocTable.getNumRelocs(i) > 0) {
ElfSection sect = sections.get(i);
String relname = ".rela" + sect.getName();
ElfSection relocSection = createByteSection(sections, relname,
elfRelocTable.getRelocData(i),
false, 8, 0, Elf64_Shdr.SHT_RELA);
relocSection.setLink(symtabsectidx);
relocSection.setInfo(sect.getSectionId());
}
}
}
}