package com.oracle.objectfile.elf;
import static java.lang.Math.toIntExact;
import java.nio.ByteBuffer;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Function;
import com.oracle.objectfile.BuildDependency;
import com.oracle.objectfile.ElementImpl;
import com.oracle.objectfile.LayoutDecision;
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.elf.ELFObjectFile.ELFSection;
import com.oracle.objectfile.elf.ELFObjectFile.ELFSectionFlag;
import com.oracle.objectfile.elf.ELFObjectFile.SectionType;
import com.oracle.objectfile.io.AssemblyBuffer;
import com.oracle.objectfile.io.OutputAssembler;
public class ELFRelocationSection extends ELFSection {
@Override
public ElementImpl getImpl() {
return this;
}
class EntryStruct {
long offset;
long info;
long addend;
EntryStruct() {
}
EntryStruct(long offset, long info, long addend) {
this.offset = offset;
this.info = info;
if (withExplicitAddends) {
this.addend = addend;
}
}
void write(OutputAssembler oa) {
switch (getOwner().getFileClass()) {
case ELFCLASS32:
oa.write4Byte(toIntExact(offset));
oa.write4Byte(toIntExact(info));
if (withExplicitAddends) {
oa.write4Byte(toIntExact(addend));
}
break;
case ELFCLASS64:
oa.write8Byte(offset);
oa.write8Byte(info);
if (withExplicitAddends) {
oa.write8Byte(addend);
}
break;
default:
throw new RuntimeException(getOwner().getFileClass().toString());
}
}
int getWrittenSize() {
ByteBuffer bb = ByteBuffer.allocate(24);
write(AssemblyBuffer.createOutputAssembler(bb));
return bb.position();
}
}
interface ELFRelocationMethod extends RelocationMethod {
long toLong();
}
private static final class Entry implements RelocationRecord {
final ELFSection section;
final long offset;
final ELFRelocationMethod t;
final ELFSymtab.Entry sym;
final long addend;
Entry(ELFSection section, long offset, ELFRelocationMethod t, ELFSymtab.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 ELFSection relocated;
private final ELFSymtab syms;
private final Map<Entry, Entry> entries = new TreeMap<>(Comparator.comparingLong(Entry::getOffset));
ELFRelocationSection(ELFObjectFile owner, String name, ELFSection relocated, ELFSymtab syms, boolean withExplicitAddends) {
owner.super(name, owner.getWordSizeInBytes(), withExplicitAddends ? SectionType.RELA : SectionType.REL, EnumSet.noneOf(ELFSectionFlag.class), -1);
this.withExplicitAddends = withExplicitAddends;
this.syms = syms;
this.relocated = relocated;
if (relocated == null) {
assert syms == null || syms.isDynamic();
flags.add(ELFSectionFlag.ALLOC);
} else {
assert name.equals((withExplicitAddends ? ".rela" : ".rel") + relocated.getName());
}
}
public Entry addEntry(ELFSection s, long offset, ELFRelocationMethod t, ELFSymtab.Entry sym, Long explicitAddend) {
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 boolean isDynamic() {
return syms.isDynamic();
}
@Override
public ELFSection getLinkedSection() {
return syms;
}
@Override
public long getLinkedInfo() {
return (relocated == null) ? 0 : getOwner().getIndexForSection(relocated);
}
@Override
public int getEntrySize() {
return (new EntryStruct()).getWrittenSize();
}
@Override
public Iterable<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) {
HashSet<BuildDependency> deps = ObjectFile.minimalDependencies(decisions, this);
LayoutDecision ourContent = decisions.get(this).getDecision(LayoutDecision.Kind.CONTENT);
if (relocated != null) {
LayoutDecision relocatedSectionContent = decisions.get(relocated).getDecision(LayoutDecision.Kind.CONTENT);
deps.add(BuildDependency.createOrGet(ourContent,
relocatedSectionContent));
}
if (isDynamic()) {
Set<ELFSection> referenced = new HashSet<>();
for (Entry ent : entries.keySet()) {
referenced.add(ent.section);
}
for (ELFSection es : referenced) {
deps.add(BuildDependency.createOrGet(ourContent, decisions.get(es).getDecision(LayoutDecision.Kind.VADDR)));
}
}
return deps;
}
@Override
public byte[] getOrDecideContent(Map<Element, LayoutDecisionMap> alreadyDecided, byte[] contentHint) {
OutputAssembler oa = AssemblyBuffer.createOutputAssembler(ByteBuffer.allocate(entries.size() * new EntryStruct().getWrittenSize()).order(getOwner().getByteOrder()));
for (Entry ent : entries.keySet()) {
long offset = !isDynamic() ? ent.offset : (int) alreadyDecided.get(ent.section).getDecidedValue(LayoutDecision.Kind.VADDR) + ent.offset;
long info;
int symIndex = syms.indexOf(ent.sym);
assert symIndex >= 0 : "symbol not found";
switch (getOwner().getFileClass()) {
case ELFCLASS32:
info = ((symIndex << 8) & 0xffffffffL) + (ent.t.toLong() & 0xffL);
break;
case ELFCLASS64:
info = (((long) symIndex) << 32) + (ent.t.toLong() & 0xffffffffL);
break;
default:
throw new RuntimeException(getOwner().getFileClass().toString());
}
long addend = ent.addend;
new EntryStruct(offset, info, addend).write(oa);
}
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 entries.size() * (new EntryStruct().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);
}
}