public LayoutDecision putDecidedValue(LayoutDecision.Kind k, Object v) { assert v != null; LayoutDecision value = new LayoutDecision(k, e, v); return decisions.put(k, value); }
private BuildDependency(LayoutDecision depending, LayoutDecision dependedOn) throws DuplicateDependencyException { assert depending != null; assert dependedOn != null; assert depending != dependedOn; // 1-cycle is bad (all cycles are bad) // must be same file assert depending.getElement().getOwner() == dependedOn.getElement().getOwner(); ObjectFile of = depending.getElement().getOwner(); this.depending = depending; this.dependedOn = dependedOn; // avoid adding duplicate entries if (depending.dependsOn().contains(dependedOn)) { assert dependedOn.dependedOnBy().contains(depending); BuildDependency existing = of.getExistingDependency(this); assert existing != null; throw new DuplicateDependencyException(existing); } depending.dependsOn().add(dependedOn); dependedOn.dependedOnBy().add(depending); of.putDependency(this); } }
protected static Iterable<LayoutDecision> decisionsByKind(final LayoutDecision.Kind kind, final Map<Element, LayoutDecisionMap> decisions) { return () -> StreamSupport.stream(decisions.values().spliterator(), false) .flatMap(layoutDecisionMap -> StreamSupport.stream(layoutDecisionMap.spliterator(), false)) .filter(decision -> decision.getKind() == kind).iterator(); }
@Override public int compare(LayoutDecision d0, LayoutDecision d1) { /* * Null/undecided values go either at the beginning or end; our client decides which. * Usually if we're searching for a maximum, we want null/undecided to be low, and if * we're searching for a minimum, we want null/undecided to be high. */ int off0 = (d0 == null || !d0.isTaken()) ? undecidedValue : (int) d0.getValue(); int off1 = (d1 == null || !d1.isTaken()) ? undecidedValue : (int) d1.getValue(); return Integer.compare(off0, off1); } }
@Override public int compare(LayoutDecision d0, LayoutDecision d1) { // elements of undecided property come first LayoutDecision sizeDecision0 = d0 == null ? null : alreadyDecided.get(d0.getElement()).getDecision(LayoutDecision.Kind.SIZE); LayoutDecision sizeDecision1 = d1 == null ? null : alreadyDecided.get(d1.getElement()).getDecision(LayoutDecision.Kind.SIZE); int defaultValue = nullsToTail ? Integer.MAX_VALUE : Integer.MIN_VALUE; int s0 = (sizeDecision0 == null || !sizeDecision0.isTaken()) ? defaultValue : (int) sizeDecision0.getValue(); int s1 = (sizeDecision1 == null || !sizeDecision1.isTaken()) ? defaultValue : (int) sizeDecision1.getValue(); return Integer.compare(s0, s1); } }
Element dependingElement = dep.depending.getElement(); Element dependedOnElement = dep.dependedOn.getElement(); assert dep.depending.getElement() == dependingElement; byDepending.add(dep); assert dep.dependedOn.getElement() == dependedOnElement; byDependedOn.add(dep); LayoutDecision dummyFinalDecision = new LayoutDecision(LayoutDecision.Kind.OFFSET, null, null); assert decision.getElement() != null; dummyFinalDecision.dependsOn().add(decision); decision.dependedOnBy().add(dummyFinalDecision); boolean sawBootstrapOffsetDecision = false; for (LayoutDecision d : allDecisions) { if (d.getKind() == LayoutDecision.Kind.OFFSET && d.getElement() == offsetBootstrapElement) { sawBootstrapOffsetDecision = true; for (LayoutDecision dependedOn : d.dependsOn()) { assert dependedOn.getKind() != LayoutDecision.Kind.OFFSET; if (d.getKind() == LayoutDecision.Kind.OFFSET && d.getElement() != offsetBootstrapElement && d.getElement() != null) { List<BuildDependency> l1 = dependenciesByDependingElement.get(d.getElement()); dependenciesByDependingElement.put(d.getElement(), l1); assert dep1.depending.getElement() == d.getElement();
protected Iterable<Element> elementsMappedOnPage(long vaddr, Map<Element, LayoutDecisionMap> alreadyDecided) { final long vaddrRoundedDown = (vaddr >> getPageSizeShift()) << getPageSizeShift(); // FIXME: use FilteringIterator instead of copying ArrayList<Element> ss = new ArrayList<>(); for (LayoutDecision d : decisionsByKind(LayoutDecision.Kind.VADDR, alreadyDecided)) { Element s = d.getElement(); assert d.getKind() == LayoutDecision.Kind.VADDR; int va = (int) d.getValue(); int sizeInMemory = d.getElement().getMemSize(alreadyDecided); assert sizeInMemory != -1; // if it begins before the end of this page // and doesn't end before the start, // it overlaps the page. int mappingBegin = va; int mappingEnd = va + sizeInMemory; long pageBegin = vaddrRoundedDown; long pageEnd = vaddrRoundedDown + getPageSize(); if (mappingBegin < pageEnd && mappingEnd > pageBegin) { ss.add(s); } } return ss; }
protected static int nextAvailableVaddr(final Map<Element, LayoutDecisionMap> alreadyDecided, int base, int defaultValue) { int nextAvailable = -1; List<LayoutDecision> maxVaddrDecisions = maximalDecisionValues(alreadyDecided, LayoutDecision.Kind.VADDR, new IntegerDecisionComparator(false)); // break ties using size (nulls to head) Collections.sort(maxVaddrDecisions, new SizeTiebreakComparator(alreadyDecided, false)); // we sorted into ascending size order, so get the biggest LayoutDecision maxVaddrDecision = maxVaddrDecisions.get(maxVaddrDecisions.size() - 1); if (maxVaddrDecision == null || !maxVaddrDecision.isTaken()) { /* * This means we have not decided any vaddr yet. We use the caller-supplied default * value. */ nextAvailable = defaultValue; } else { assert alreadyDecided.get(maxVaddrDecision.getElement()).getDecision(LayoutDecision.Kind.SIZE).isTaken(); int vaddr = (int) alreadyDecided.get(maxVaddrDecision.getElement()).getDecidedValue(LayoutDecision.Kind.VADDR); int size = maxVaddrDecision.getElement().getMemSize(alreadyDecided); nextAvailable = vaddr + size; } if (nextAvailable < base) { return base; } else { return nextAvailable; } }
@Override public int compareTo(LayoutDecision arg) { ObjectFile.Element ourElement = getElement(); int ourElementIndex = ourElement == null ? -1 : ourElement.getOwner().getElements().indexOf(ourElement); int ourKindOrdinal = getKind().ordinal(); ObjectFile.Element argElement = arg.getElement(); int argElementIndex = argElement == null ? -1 : argElement.getOwner().getElements().indexOf(argElement); int argKindOrdinal = arg.getKind().ordinal(); // we can only compare decisions about the same object file if (ourElement != null && argElement != null && ourElement.getOwner() != argElement.getOwner()) { throw new IllegalArgumentException("cannot compare decisions across object files"); } if (ourElementIndex < argElementIndex) { return -1; } else if (ourElementIndex > argElementIndex) { return 1; } else { if (ourKindOrdinal < argKindOrdinal) { return -1; } else if (ourKindOrdinal > argKindOrdinal) { return 1; } else { return 0; } } } }
int minVaddr = (minVaddrDecisions == null || minVaddrDecisions.size() == 0) ? 0 : (int) minVaddrDecisions.get(0).getValue(); int maxVaddr = (maxVaddrDecision == null) ? 0 : ((int) maxVaddrDecision.getValue() + maxVaddrDecision.getElement().getMemSize(alreadyDecided)); int vmSize = ObjectFile.nextIntegerMultiple(maxVaddr - minVaddr, getPageSize()); Element firstSectionByVaddr = (minVaddrDecisions == null) ? null : minVaddrDecisions.get(0).getElement(); @SuppressWarnings("unused") Element lastSectionByVaddr = (maxVaddrDecision == null) ? null : maxVaddrDecision.getElement(); int minOffset = (minOffsetDecisions == null || minOffsetDecisions.size() == 0) ? 0 : (int) minOffsetDecisions.get(0).getValue(); List<LayoutDecision> maxOffsetDecisions = ObjectFile.maximalDecisionValues(decidedAboutOurElements, LayoutDecision.Kind.OFFSET, new IntegerDecisionComparator(false)); Element firstElementByOffset = (minOffsetDecisions == null) ? null : minOffsetDecisions.get(0).getElement(); @SuppressWarnings("unused") Element lastElementByOffset = (maxOffsetDecision == null) ? null : maxOffsetDecision.getElement(); int maxOffset = (maxOffsetDecision == null) ? 0 : ((int) maxOffsetDecision.getValue() + (int) alreadyDecided.get(maxOffsetDecision.getElement()).getDecidedValue(LayoutDecision.Kind.SIZE)); int fileSize = maxOffset - minOffset;
public Object getDecidedValue(LayoutDecision.Kind key) { return getDecision(key).getValue(); }
@SuppressWarnings("unchecked") public static <T> T defaultGetOrDecide(Map<Element, LayoutDecisionMap> alreadyDecided, Element el, LayoutDecision.Kind k, T hint) { /* By default, we return what's already been decided or else take the hint. */ LayoutDecisionMap m = alreadyDecided.get(el); if (m != null && m.getDecision(k) != null && m.getDecision(k).isTaken()) { return (T) m.getDecidedValue(k); } else { return hint; } }
Element predElement = null; for (LayoutDecision d : sortedOffsetDecisions) { if (d.getElement() == el) { break; predElement = predDecision.getElement();
protected static int nextAvailableOffset(final Map<Element, LayoutDecisionMap> alreadyDecided) { int ret = -1; List<LayoutDecision> maxOffsetDecisions = maximalDecisionValues(alreadyDecided, LayoutDecision.Kind.OFFSET, new IntegerDecisionComparator(false)); // break ties using size (nulls to head) Collections.sort(maxOffsetDecisions, new SizeTiebreakComparator(alreadyDecided, false)); // we sorted into ascending size order, so get the biggest LayoutDecision maxOffsetDecision = maxOffsetDecisions.get(maxOffsetDecisions.size() - 1); if (maxOffsetDecision == null || !maxOffsetDecision.isTaken()) { // means we have not decided any offsets yet -- return 0 ret = 0; } else { assert alreadyDecided.get(maxOffsetDecision.getElement()).getDecision(LayoutDecision.Kind.SIZE).isTaken(); int offset = (int) alreadyDecided.get(maxOffsetDecision.getElement()).getDecision(LayoutDecision.Kind.OFFSET).getValue(); int size = (int) alreadyDecided.get(maxOffsetDecision.getElement()).getDecision(LayoutDecision.Kind.SIZE).getValue(); ret = offset + size; } return ret; }
@Override public int compare(Element e1, Element e2) { // if an offset is not decided, it is treated as maximal // i.e. can "float to the end" LayoutDecisionMap e1decisions = decisionsByElement.get(e1); LayoutDecision e1OffsetDecision = e1decisions.getDecision(LayoutDecision.Kind.OFFSET); int e1offset = (e1OffsetDecision != null && e1OffsetDecision.isTaken()) ? (int) e1OffsetDecision.getValue() : Integer.MAX_VALUE; LayoutDecisionMap e2decisions = decisionsByElement.get(e2); LayoutDecision e2OffsetDecision = e2decisions.getDecision(LayoutDecision.Kind.OFFSET); int e2offset = (e2OffsetDecision != null && e2OffsetDecision.isTaken()) ? (int) e2OffsetDecision.getValue() : Integer.MAX_VALUE; if (e1offset < e2offset) { return -1; } else if (e1offset > e2offset) { return 1; } else { return 0; } } }
public void putDecidedValues(LayoutDecisionMap copyingIn) { assert !copyingIn.getDecisions().stream().filter(d -> d.getValue() == null).findAny().isPresent(); decisions.putAll(copyingIn.decisions); }
public static int defaultGetOrDecideOffset(Map<Element, LayoutDecisionMap> alreadyDecided, Element el, int offsetHint) { // FIXME: in this implementation, we must not have decided the vaddr already! // We should instead support both cases, and if the vaddr is decided, apply // the modulo constraint (if necessary) here! assert (alreadyDecided.get(el).getDecision(LayoutDecision.Kind.VADDR) == null || !alreadyDecided.get(el).getDecision(LayoutDecision.Kind.VADDR).isTaken()); // now we are free to worry about the modulo constraint during vaddr assignment only // we take the hint, but bumped up to proper alignment return defaultGetOrDecide(alreadyDecided, el, LayoutDecision.Kind.OFFSET, nextIntegerMultiple(offsetHint, el.getAlignment())); }
@Override public byte[] getOrDecideContent(Map<Element, LayoutDecisionMap> alreadyDecided, byte[] contentHint) { OutputAssembler out = AssemblyBuffer.createOutputAssembler(getOwner().getByteOrder()); ArrayList<LayoutDecision> decisionsOfInterest = new ArrayList<>(); for (Section s : getSections()) { MachOSection ms = (MachOSection) s; if (ms.flags.contains(SectionFlag.SOME_INSTRUCTIONS)) { decisionsOfInterest.add(alreadyDecided.get(s).getDecision(LayoutDecision.Kind.OFFSET)); } } // sort these sections by their decided offset Collections.sort(decisionsOfInterest, new IntegerDecisionComparator(false)); // we should not have any undecideds! assert decisionsOfInterest.size() == 0 || decisionsOfInterest.get(0).isTaken(); EntryStruct ent = new EntryStruct(); for (int i = 0; i < decisionsOfInterest.size(); ++i) { LayoutDecision decision = decisionsOfInterest.get(i); ent.fileOffset = (int) decision.getValue(); int fileSize = (int) alreadyDecided.get(decision.getElement()).getDecidedValue(LayoutDecision.Kind.SIZE); int sectionEndInFile = ent.fileOffset + fileSize; Integer nextOffset = (i + 1 < decisionsOfInterest.size()) ? (int) decisionsOfInterest.get(i + 1).getValue() : null; int nextPageBoundary = (sectionEndInFile % getPageSize()) == 0 ? sectionEndInFile : (((sectionEndInFile >> getPageSizeShift()) + 1) << getPageSizeShift()); ent.length = (short) (nextOffset == null ? nextPageBoundary : Math.min(nextPageBoundary, nextOffset)); ent.entryKind = (short) 0; // FIXME ent.write(out); } return out.getBlob(); } }
public void writeBuffer(List<Element> sortedObjectFileElements, ByteBuffer out) { /* Emit each one! */ for (Element e : sortedObjectFileElements) { int off = (int) decisionsTaken.get(e).getDecision(LayoutDecision.Kind.OFFSET).getValue(); assert off != Integer.MAX_VALUE; // not allowed any more -- this was a broken approach out.position(off); int expectedSize = (int) decisionsTaken.get(e).getDecidedValue(LayoutDecision.Kind.SIZE); byte[] content = (byte[]) decisionsTaken.get(e).getDecidedValue(LayoutDecision.Kind.CONTENT); out.put(content); int emittedSize = out.position() - off; assert emittedSize >= 0; if (emittedSize != expectedSize) { throw new IllegalStateException("For element " + e + ", expected size " + expectedSize + " but emitted size " + emittedSize); } } }
public static LayoutDecisionMap defaultDecisions(Element e, LayoutDecisionMap copyingIn) { LayoutDecisionMap decisions = new LayoutDecisionMap(e); decisions.putUndecided(LayoutDecision.Kind.CONTENT); assert !decisions.getDecision(LayoutDecision.Kind.CONTENT).isTaken(); decisions.putUndecided(LayoutDecision.Kind.SIZE); decisions.putUndecided(LayoutDecision.Kind.OFFSET); if (e.isReferenceable()) { decisions.putUndecided(LayoutDecision.Kind.VADDR); } // any decisions present in copyingIn will be already "taken" // i.e. they're LayoutProperties decisions.putDecidedValues(copyingIn); return decisions; }