package com.oracle.objectfile.pecoff;
import java.nio.ByteBuffer;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.function.Function;
import com.oracle.objectfile.BuildDependency;
import com.oracle.objectfile.ElementImpl;
import com.oracle.objectfile.LayoutDecisionMap;
import com.oracle.objectfile.ObjectFile;
import com.oracle.objectfile.ObjectFile.Element;
import com.oracle.objectfile.ObjectFile.RelocationMethod;
import com.oracle.objectfile.ObjectFile.RelocationRecord;
import com.oracle.objectfile.ObjectFile.Symbol;
import com.oracle.objectfile.io.AssemblyBuffer;
import com.oracle.objectfile.io.OutputAssembler;
import com.oracle.objectfile.pecoff.PECoff.IMAGE_RELOCATION;
import com.oracle.objectfile.pecoff.PECoffObjectFile.PECoffSection;
@SuppressWarnings("unchecked")
public class PECoffRelocationTable extends ObjectFile.Element {
@Override
public ElementImpl getImpl() {
return this;
}
interface PECoffRelocationMethod extends RelocationMethod {
long toLong();
}
private static final class Entry implements RelocationRecord {
final PECoffSection section;
final long offset;
final PECoffRelocationMethod t;
final PECoffSymtab.Entry sym;
final long addend;
Entry(PECoffSection section, long offset, PECoffRelocationMethod t, PECoffSymtab.Entry sym, long addend) {
this.section = section;
this.offset = offset;
this.t = t;
this.sym = sym;
this.addend = addend;
}
@Override
public long getOffset() {
return offset;
}
@Override
public Symbol getReferencedSymbol() {
return sym;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && getClass() == obj.getClass()) {
Entry other = (Entry) obj;
return Objects.equals(section, other.section) && offset == other.offset && Objects.equals(t, other.t) && Objects.equals(sym, other.sym) && addend == other.addend;
}
return false;
}
@Override
public int hashCode() {
return (((section.hashCode() * 31 + Long.hashCode(offset)) * 31 + t.hashCode()) * 31 + sym.hashCode()) * 31 + Long.hashCode(addend);
}
}
private final boolean withExplicitAddends;
private final PECoffSymtab syms;
private PECoffObjectFile owner = null;
PECoffRelocationTable(PECoffObjectFile owner, String name, PECoffSymtab syms, boolean withExplicitAddends) {
owner.super(name, 4);
this.owner = owner;
this.withExplicitAddends = withExplicitAddends;
this.syms = syms;
}
public Entry addEntry(PECoffSection s, long offset, PECoffRelocationMethod t, PECoffSymtab.Entry sym, Long explicitAddend) {
Map<Entry, Entry> entries = (Map<Entry, Entry>) s.getRelocEntries();
if (entries == null) {
entries = new TreeMap<>(Comparator.comparingLong(Entry::getOffset));
s.setRelocEntries(entries);
}
if (explicitAddend != null) {
if (!t.canUseExplicitAddend()) {
throw new IllegalArgumentException("cannot use relocation method " + t + " with explicit addends");
}
if (!withExplicitAddends) {
throw new IllegalStateException("cannot create relocation with addend in .rel section");
}
} else {
if (!t.canUseImplicitAddend()) {
throw new IllegalArgumentException("cannot use relocation method " + t + " with implicit addends");
}
if (withExplicitAddends) {
throw new IllegalStateException("cannot create relocation without addend in .rela section");
}
}
long addend = (explicitAddend != null) ? explicitAddend : 0L;
return entries.computeIfAbsent(new Entry(s, offset, t, sym, addend), Function.identity());
}
public int getRelocCount(int sectionIndex) {
int entryCount = 0;
PECoffSection s = getOwner().getSectionByIndex(sectionIndex + 1);
Map<Entry, Entry> entries = (Map<Entry, Entry>) s.getRelocEntries();
if (entries == null) {
return 0;
}
entryCount = entries.size();
if (entryCount > 65535) {
entryCount++;
}
return entryCount;
}
public int getRelocOffset(int sectionIndex) {
int offset = 0;
int maxSection = getOwner().getPECoffSections().size();
for (int i = 0; i < maxSection; i++) {
if (i == sectionIndex) {
return offset;
}
offset = offset + (getRelocCount(i) * IMAGE_RELOCATION.totalsize);
}
return offset;
}
@Override
public PECoffObjectFile getOwner() {
return owner;
}
@Override
public boolean isLoadable() {
return true;
}
PECoffRelocTableStruct relocTabStruct = null;
private PECoffRelocTableStruct getNativeReloctab() {
if (relocTabStruct != null) {
return relocTabStruct;
}
relocTabStruct = new PECoffRelocTableStruct(getOwner().getSections().size());
for (PECoffSection s : getOwner().getPECoffSections()) {
Map<Entry, Entry> entries = (Map<Entry, Entry>) s.getRelocEntries();
if (entries != null) {
for (Entry ent : entries.keySet()) {
long offset = ent.getOffset();
int sectionID = ent.section == null ? 0 : s.getSectionID();
relocTabStruct.createRelocationEntry(sectionID, (int) offset, syms.indexOf(ent.sym), (int) ent.t.toLong());
}
}
}
return relocTabStruct;
}
@Override
public Iterable<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) {
HashSet<BuildDependency> deps = ObjectFile.minimalDependencies(decisions, this);
return deps;
}
private int getWrittenSize() {
int size = 0;
for (PECoffSection s : getOwner().getPECoffSections()) {
Map<Entry, Entry> entries = (Map<Entry, Entry>) s.getRelocEntries();
if (entries != null) {
int esize = entries.size();
if (esize > 65535) {
esize++;
}
esize *= IMAGE_RELOCATION.totalsize;
size += esize;
}
}
return size;
}
@Override
public byte[] getOrDecideContent(Map<Element, LayoutDecisionMap> alreadyDecided, byte[] contentHint) {
PECoffRelocTableStruct rt = getNativeReloctab();
OutputAssembler oa = AssemblyBuffer.createOutputAssembler(ByteBuffer.allocate(getWrittenSize()).order(getOwner().getByteOrder()));
int sectionIndex = 0;
for (PECoffSection s : getOwner().getPECoffSections()) {
if (s.getSectionID() != sectionIndex) {
System.out.println("Out of order PECoffSection " + s.getSectionID() + " should be " + sectionIndex);
System.out.println(s);
}
sectionIndex++;
if (s.getRelocEntries() != null) {
oa.writeBlob(rt.getRelocData(s.getSectionID()));
}
}
return oa.getBlob();
}
@Override
public int getOrDecideOffset(Map<Element, LayoutDecisionMap> alreadyDecided, int offsetHint) {
return ObjectFile.defaultGetOrDecideOffset(alreadyDecided, this, offsetHint);
}
@Override
public int getOrDecideSize(Map<Element, LayoutDecisionMap> alreadyDecided, int sizeHint) {
return getWrittenSize();
}
@Override
public int getOrDecideVaddr(Map<Element, LayoutDecisionMap> alreadyDecided, int vaddrHint) {
return ObjectFile.defaultGetOrDecideVaddr(alreadyDecided, this, vaddrHint);
}
@Override
public LayoutDecisionMap getDecisions(LayoutDecisionMap copyingIn) {
return ObjectFile.defaultDecisions(this, copyingIn);
}
}