package com.oracle.objectfile.pecoff;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
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.Section;
import com.oracle.objectfile.ObjectFile.Symbol;
import com.oracle.objectfile.SymbolTable;
import com.oracle.objectfile.io.AssemblyBuffer;
import com.oracle.objectfile.io.OutputAssembler;
import com.oracle.objectfile.pecoff.PECoff.IMAGE_SYMBOL;
import com.oracle.objectfile.pecoff.PECoffObjectFile.PECoffSection;
public class PECoffSymtab extends ObjectFile.Element implements SymbolTable {
@Override
public ElementImpl getImpl() {
return this;
}
static final class Entry implements ObjectFile.Symbol {
private final String name;
private final long value;
private final long size;
private final int symClass;
private final int symType;
private final PECoffSection referencedSection;
private final PseudoSection pseudoSection;
@Override
public boolean isDefined() {
return pseudoSection == null || pseudoSection != PseudoSection.UNDEF;
}
@Override
public boolean isAbsolute() {
return false;
}
@Override
public boolean isCommon() {
return false;
}
@Override
public boolean isFunction() {
return symType == IMAGE_SYMBOL.IMAGE_SYM_DTYPE_FUNCTION;
}
@Override
public boolean isGlobal() {
return symClass == IMAGE_SYMBOL.IMAGE_SYM_CLASS_EXTERNAL;
}
public boolean isNull() {
return name.isEmpty() && value == 0 && size == 0 && symClass == 0 && symType == 0 && referencedSection == null && pseudoSection == null;
}
@Override
public String getName() {
return name;
}
public int getSymClass() {
return symClass;
}
public int getSymType() {
return symType;
}
@Override
public long getDefinedOffset() {
if (!isDefined()) {
throw new IllegalStateException("queried offset of an undefined symbol");
} else {
return value;
}
}
@Override
public Section getDefinedSection() {
return getReferencedSection();
}
@Override
public long getDefinedAbsoluteValue() {
return 0L;
}
@Override
public long getSize() {
return size;
}
private Entry(String name, long value, long size, int symclass, int symtype, PECoffSection referencedSection, PseudoSection pseudoSection) {
this.name = name;
this.value = value;
this.size = size;
this.symClass = symclass;
this.symType = symtype;
this.referencedSection = referencedSection;
this.pseudoSection = pseudoSection;
assert ((referencedSection == null) != (pseudoSection == null)) || isNull();
}
Entry(String name, long value, long size, int symclass, int symtype, PECoffSection referencedSection) {
this(name, value, size, symclass, symtype, referencedSection, null);
}
Entry(String name, long value, long size, int symclass, int symtype, PseudoSection pseudoSection) {
this(name, value, size, symclass, symtype, null, pseudoSection);
}
PECoffSection getReferencedSection() {
return referencedSection;
}
}
public enum PseudoSection {
UNDEF;
}
private static int compareEntries(Entry a, Entry b) {
int cmp = -Boolean.compare(a.isNull(), b.isNull());
if (cmp == 0) {
cmp = Integer.compare(a.symClass, b.symClass);
}
if (cmp == 0) {
cmp = Integer.compare(a.symType, b.symType);
}
if (cmp == 0) {
cmp = Boolean.compare(a.isDefined(), b.isDefined());
}
if (cmp == 0 && a.isDefined()) {
cmp = Math.toIntExact(a.getDefinedOffset() - b.getDefinedOffset());
}
if (cmp == 0) {
return a.getName().compareTo(b.getName());
}
return cmp;
}
private SortedSet<Entry> entries = new TreeSet<>(PECoffSymtab::compareEntries);
private Map<String, Entry> entriesByName = new HashMap<>();
private Map<Entry, Integer> entriesToIndex;
private PECoffSymtabStruct symtabStruct;
public PECoffSymtab(PECoffObjectFile owner, String name) {
owner.super(name);
}
private PECoffSymtabStruct getNativeSymtab() {
if (symtabStruct != null) {
return symtabStruct;
}
symtabStruct = new PECoffSymtabStruct();
entriesToIndex = new HashMap<>();
int i = 0;
for (Entry e : entries) {
PECoffSection sect = e.getReferencedSection();
int sectID = sect == null ? -1 : sect.getSectionID();
long offset = e.isDefined() ? e.getDefinedOffset() : 0L;
symtabStruct.addSymbolEntry(e.getName(),
(byte) e.getSymType(),
(byte) e.getSymClass(),
(byte) sectID,
offset);
entriesToIndex.put(e, i++);
}
return symtabStruct;
}
@Override
public byte[] getOrDecideContent(Map<Element, LayoutDecisionMap> alreadyDecided, byte[] contentHint) {
PECoffSymtabStruct sts = getNativeSymtab();
ByteBuffer outBuffer = ByteBuffer.allocate(getWrittenSize()).order(getOwner().getByteOrder());
OutputAssembler out = AssemblyBuffer.createOutputAssembler(outBuffer);
out.writeBlob(sts.getSymtabArray());
out.writeBlob(sts.getStrtabArray());
return out.getBlob();
}
@Override
public int getOrDecideSize(Map<Element, LayoutDecisionMap> alreadyDecided, int sizeHint) {
return getWrittenSize();
}
private int getWrittenSize() {
PECoffSymtabStruct sts = getNativeSymtab();
return ((sts.getSymtabCount() * IMAGE_SYMBOL.totalsize) + sts.getStrtabSize());
}
@Override
public Iterable<BuildDependency> getDependencies(Map<Element, LayoutDecisionMap> decisions) {
return new ArrayList<>(ObjectFile.defaultDependencies(decisions, this));
}
@Override
public boolean isLoadable() {
return true;
}
@Override
public Symbol newDefinedEntry(String name, Section referencedSection, long referencedOffset, long size, boolean isGlobal, boolean isCode) {
int symClass;
int symType;
symClass = isGlobal ? IMAGE_SYMBOL.IMAGE_SYM_CLASS_EXTERNAL : IMAGE_SYMBOL.IMAGE_SYM_CLASS_STATIC;
symType = isCode ? IMAGE_SYMBOL.IMAGE_SYM_DTYPE_FUNCTION : IMAGE_SYMBOL.IMAGE_SYM_DTYPE_NONE;
return addEntry(new Entry(name, referencedOffset, size, symClass, symType, (PECoffSection) referencedSection));
}
@Override
public Symbol newUndefinedEntry(String name, boolean isCode) {
int symClass;
int symType;
symClass = IMAGE_SYMBOL.IMAGE_SYM_CLASS_EXTERNAL;
symType = isCode ? IMAGE_SYMBOL.IMAGE_SYM_DTYPE_FUNCTION : IMAGE_SYMBOL.IMAGE_SYM_DTYPE_NONE;
return addEntry(new Entry(name, 0, 0, symClass, symType, PseudoSection.UNDEF));
}
private Entry addEntry(Entry entry) {
if (symtabStruct != null) {
throw new IllegalStateException("Symbol table content is already decided.");
}
entries.add(entry);
entriesByName.put(entry.getName(), entry);
return entry;
}
public Entry getNullEntry() {
return entries.iterator().next();
}
public int indexOf(Symbol sym) {
if (symtabStruct == null) {
throw new IllegalStateException("Symbol table content is not decided yet.");
}
return entriesToIndex.get(sym);
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public Iterator<Symbol> iterator() {
return (Iterator) entries.iterator();
}
@Override
public Entry getSymbol(String name) {
return entriesByName.get(name);
}
public int getSymbolCount() {
int count = getNativeSymtab().getSymtabCount();
int entcount = entries.size();
if (entcount != count) {
System.out.println("Counts don't match, entcount: " + entcount + " count: " + count);
}
return entcount;
}
public int getDirectiveSize() {
return getNativeSymtab().getDirectiveSize();
}
public byte[] getDirectiveArray() {
return getNativeSymtab().getDirectiveArray();
}
@Override
public int getOrDecideOffset(Map<Element, LayoutDecisionMap> alreadyDecided, int offsetHint) {
return ObjectFile.defaultGetOrDecideOffset(alreadyDecided, this, offsetHint);
}
@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);
}
}