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.Symbol;
import jdk.tools.jaotc.binformat.Symbol.Binding;
import jdk.tools.jaotc.binformat.Symbol.Kind;
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;
public abstract class JELFRelocObject {
private final BinaryContainer binContainer;
private final ElfContainer elfContainer;
private final int segmentSize;
protected JELFRelocObject(BinaryContainer binContainer, String outputFileName) {
this.binContainer = binContainer;
this.elfContainer = new ElfContainer(outputFileName);
this.segmentSize = binContainer.getCodeSegmentSize();
}
public static JELFRelocObject newInstance(BinaryContainer binContainer, String outputFileName) {
String archStr = System.getProperty("os.arch").toLowerCase();
if (archStr.equals("amd64") || archStr.equals("x86_64")) {
return new AMD64JELFRelocObject(binContainer, outputFileName);
} else if (archStr.equals("aarch64")) {
return new AArch64JELFRelocObject(binContainer, outputFileName);
}
throw new InternalError("Unsupported platform: " + archStr);
}
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 fileOffset = Elf64_Ehdr.totalsize;
fileOffset = (fileOffset + (sections.get(1).getDataAlign() - 1)) &
~((sections.get(1).getDataAlign() - 1));
for (int i = 1; i < sections.size(); i++) {
ElfSection sect = sections.get(i);
fileOffset = (fileOffset + (sect.getDataAlign() - 1)) &
~((sect.getDataAlign() - 1));
sect.setOffset(fileOffset);
fileOffset += sect.getSize();
}
fileOffset = (fileOffset + (ElfSection.getShdrAlign() - 1)) &
~((ElfSection.getShdrAlign() - 1));
eh.setSectionOff(fileOffset);
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 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());
}
}
}
abstract void createRelocation(Symbol symbol, Relocation reloc, ElfRelocTable elfRelocTable);
}